# Option C Linter
**Version:** 0.3.0

A strict, deterministic static analysis tool enforcing the Option C coding
standard across Python projects. The linter is part of a broader development
ecosystem and is designed to be reusable across all current and future
projects.

---

## Purpose

The Option C Linter validates and enforces:

1. Mandatory 4‑line header block
2. Module docstring structure
3. Diagnostics profile block
4. `_dbg` usage in non‑trivial modules
5. `func = "..."` pattern in functions that call `_dbg()`
6. No hardcoded secrets (§6b Secret Hygiene — AST‑based detection)
7. Syntax warnings (invalid escape sequences)
8. Type hint coverage
9. Presence of regression tests
10. Project‑level documentation requirements
11. Directory exclusion rules
12. Colorized terminal summary and log output

It can also automatically fix header blocks using `--fix-headers`.

---

## Mandatory Header Block

Every Python file in an Option C project must begin with:

#!/usr/bin/env python3  
# -*- coding: utf-8 -*-  
# <project_name>/<relative/path/to/file.py>  

Followed by exactly one blank line and then the module docstring.

The linter validates and enforces:

- Correctness  
- Ordering  
- Uniqueness  
- No leading whitespace  
- No comments before shebang  
- Exactly one blank line after the header block  

---

## `func = "..."` Pattern Check

Every non-trivial function (in files longer than 20 lines) that calls `_dbg()` must
define `func = "function_name"` as a local variable before the first `_dbg()` call:

```python
def my_function(data):
    func = "my_function"
    _dbg("L3", "Processing data", domain="DOMAIN", func=func)
```

The linter scans each function body using an indent-based parser. For each function
containing a `_dbg()` call, it verifies that `func = "..."` (or `func = '...'`) was
assigned first. Violations are reported per function name.

Functions that contain no `_dbg()` calls are not flagged.

---

## `no_hardcoded_secrets` Check (§6b Secret Hygiene)

Hardcoded secrets in source files are a blocking lint violation at `compact` and `strict`
profiles. The check uses AST inspection to detect three patterns:

**Pattern A — Direct assignment:**
```python
# VIOLATION
password = "hunter2"
API_KEY = "sk-abc123"
password: str = "hunter2"  # annotated assignment

# SAFE (not flagged)
password = os.getenv("PASSWORD")
API_KEY = os.environ["API_KEY"]
password = get_password()
```

**Pattern B — Dict literal with secret-like key:**
```python
# VIOLATION
config = {"password": "secret", "api_key": "sk-123"}

# SAFE (not flagged)
config = {"password": os.getenv("DB_PASS")}
config = {"max_retries": "3"}
```

**Pattern C — Function default argument:**
```python
# VIOLATION
def connect(password="default"):
    ...

# SAFE (not flagged)
def connect(host="localhost"):
    ...
```

Secret-like names include: `password`, `passwd`, `pwd`, `secret`, `secret_key`,
`private_key`, `api_key`, `apikey`, `api_secret`, `token`, `auth_token`,
`access_token`, `refresh_token`, `connection_string`, `conn_str`, `dsn`,
`database_url`, `db_url`, `aws_secret`, `aws_key`, `aws_access`, `credentials`,
`client_secret`.

The correct pattern is to load secrets from environment variables at runtime:
```python
import os
API_KEY = os.environ["API_KEY"]
DATABASE_URL = os.getenv("DATABASE_URL")
```

Waivers are supported via the inline waiver mechanism:
```python
password = "test"  # OCT-LINT: disable=no_hardcoded_secrets reason="Test fixture"
```

---

## Usage

From any Option C project root:

```
oct lint
```

To automatically fix header blocks:

```
oct lint --fix-headers
```

A log file is written to the project’s `logs/` directory.

---

## Test Suite

The linter includes a full test suite located in `tests_linter/`.

### Test Types

- Header validation tests
- Header fixing tests
- `no_hardcoded_secrets` tests (21 tests covering all patterns, safe patterns, edge cases)
- Integration tests
- Regression tests
- Fixtures (intentionally malformed files)

Run tests with:

pytest tests_linter

---

## Continuous Integration

A GitHub Actions workflow is included in:

.github/workflows/linter-tests.yml

It runs the full test suite on every push or pull request.

---

## Project Philosophy

The linter is itself an Option C project:

- Fully documented  
- Fully tested  
- Fully deterministic  
- Fully compliant with the Option C standard  

It is designed to evolve alongside the ecosystem and remain reusable across all
future projects.

---

## License

Internal development tool — license as appropriate for your ecosystem.