|
| 1 | +# Claude Rules |
| 2 | + |
| 3 | +> **This file**: Core principles, behavioral constraints, and development rules. |
| 4 | +> The principles section is the foundation — internalize it before any work. |
| 5 | +
|
| 6 | +## Core Principles |
| 7 | + |
| 8 | +These principles govern every change to this project. They are not |
| 9 | +guidelines — they are hard constraints. When in doubt, the principle |
| 10 | +wins over convenience. |
| 11 | + |
| 12 | +### Architecture |
| 13 | + |
| 14 | +1. **Separation of Concerns is non-negotiable.** Each module does one |
| 15 | + thing. `patterns/` loads and merges patterns. `sanitization/` removes |
| 16 | + PII. `capture/` records traffic. `validation/` checks results. |
| 17 | + `cli/` wires commands. No module reaches across boundaries. |
| 18 | + |
| 19 | +1. **DRY is non-negotiable.** If the same logic appears in 2+ places, |
| 20 | + extract a shared helper. Duplicated pattern loading, redaction |
| 21 | + checks, or detection logic are architecture bugs, not tech debt. |
| 22 | + |
| 23 | +1. **The core library has no CLI dependency.** `cli/` is a thin |
| 24 | + wrapper over the library. If a CLI command requires non-trivial |
| 25 | + logic, it belongs in the library, not the CLI module. API consumers |
| 26 | + (`sanitize_har_file()`, `validate_har()`) never import from `cli/`. |
| 27 | + |
| 28 | +1. **New features are additive only.** New domain pattern, new |
| 29 | + heuristic detector, new PII pattern, new scanner pass — none of |
| 30 | + these change existing code. Add a JSON file, register it, done. |
| 31 | + If adding a feature requires modifying unrelated modules, the |
| 32 | + architecture is wrong. |
| 33 | + |
| 34 | +1. **The core is domain-agnostic.** The sanitization engine has no |
| 35 | + knowledge of any particular device or application. Domain-specific |
| 36 | + knowledge (safe values, detectors, HTML scanner config) lives in |
| 37 | + domain pattern files loaded at runtime via `--patterns`. Core |
| 38 | + pattern files (`pii.json`, `sensitive.json`, `allowlist.json`) |
| 39 | + contain only universal PII rules. |
| 40 | + |
| 41 | +1. **Extensibility via data, not code.** Adding support for a new |
| 42 | + product category requires a JSON file, not code changes. Heuristic |
| 43 | + detectors, HTML scanners, PII patterns, and safe values are all |
| 44 | + configured through domain pattern files. If a domain pattern |
| 45 | + section requires code knowledge, the abstraction is wrong. |
| 46 | + |
| 47 | +### Specs and Documentation |
| 48 | + |
| 49 | +7. **Specs are the authority.** Code follows specs. No silent |
| 50 | + deviations. If the code needs to diverge, discuss the gap first, |
| 51 | + update the spec, then update the code. |
| 52 | + |
| 53 | +1. **Design decisions land in specs, not in conversation.** Every |
| 54 | + architectural decision made during a session must be committed to |
| 55 | + the relevant spec or architecture doc before the session ends. |
| 56 | + Conversation history is ephemeral — specs are durable. |
| 57 | + |
| 58 | +1. **Docs and code move together.** Every change reconciles the |
| 59 | + affected specs (ARCHITECTURE, CAPTURE_SPEC, SANITIZATION_SPEC, |
| 60 | + PATTERN_SPEC, VALIDATION_SPEC). A code change without a |
| 61 | + corresponding spec update is incomplete. |
| 62 | + |
| 63 | +### Code Quality |
| 64 | + |
| 65 | +10. **No shortcuts, no deferred structure.** If a better design is |
| 66 | + obvious, use it now. Don't optimise for speed of first draft. |
| 67 | + When a module grows past its natural boundary, restructure the |
| 68 | + whole module — don't bolt on the new thing and leave the rest. |
| 69 | + |
| 70 | +01. **Quality gates are not negotiable.** If mypy, ruff, or pytest |
| 71 | + fails, fix the code. Don't exclude files, skip checks, or weaken |
| 72 | + thresholds. Never bypass pre-commit hooks — fix failures, don't |
| 73 | + skip them. If hooks break, fix the hook setup first. |
| 74 | + |
| 75 | +01. **Test overrides are a code smell.** If reaching coverage requires |
| 76 | + heavy mocking, monkeypatching, or test overrides, the code |
| 77 | + structure is wrong. Restructure the code (extract dependency, make |
| 78 | + injectable), don't paper over it with test complexity. |
| 79 | + |
| 80 | +### Testing |
| 81 | + |
| 82 | +13. **Table-driven tests by default.** Identify the pattern BEFORE |
| 83 | + writing tests. If 3+ tests share the same setup→call→assert |
| 84 | + structure, start with `@pytest.mark.parametrize`. |
| 85 | + |
| 86 | +01. **Test data lives in JSON fixtures.** No inline data blobs in |
| 87 | + test files. Large test data (dicts, pattern lists, test case |
| 88 | + tables) goes in `tests/fixtures/*.json`. Test files load fixtures |
| 89 | + and convert to tuples for parametrize. Schema tests use fixture |
| 90 | + files; behavioural tests stay inline. |
| 91 | + |
| 92 | +01. **Coverage threshold is 75%.** Defined in `pyproject.toml`. Patch |
| 93 | + target 80% informational (`codecov.yml`). Don't game coverage — |
| 94 | + if a module is hard to test, restructure it. |
| 95 | + |
| 96 | +### Process |
| 97 | + |
| 98 | +16. **Only the developer merges PRs and takes irreversible actions.** |
| 99 | + Never merge a PR, force push, delete branches, or create releases |
| 100 | + without explicit approval. "Ready to merge?" is not "merge it." |
| 101 | + |
| 102 | +01. **No external actions without discussion.** Never create GitHub |
| 103 | + issues, PRs, pushes, label changes, or any external-facing action |
| 104 | + without explicit discussion first. |
| 105 | + |
| 106 | +01. **Conventional commits.** Commitizen pre-commit hook requires the |
| 107 | + format: `type(scope): message` (e.g., `feat(patterns):`, |
| 108 | + `fix(sanitization):`, `docs:`, `chore(release):`). |
| 109 | + |
| 110 | +## Architecture and Specifications |
| 111 | + |
| 112 | +| Document | Scope | |
| 113 | +| --------------------------------- | --------------------------------------------------------- | |
| 114 | +| `docs/ARCHITECTURE.md` | Design constraints, system shape, component relationships | |
| 115 | +| `docs/specs/CAPTURE_SPEC.md` | Playwright session, wait-for-data, workflow phases | |
| 116 | +| `docs/specs/SANITIZATION_SPEC.md` | HAR/HTML/heuristic engines, two-pass model, hasher | |
| 117 | +| `docs/specs/PATTERN_SPEC.md` | Pattern file schemas, domain files, merge order, loader | |
| 118 | +| `docs/specs/VALIDATION_SPEC.md` | PII leak detection, check functions, pre-commit hook | |
| 119 | +| `docs/USE_CASES.md` | User-facing use case catalog | |
| 120 | + |
| 121 | +## Project Layout |
| 122 | + |
| 123 | +``` |
| 124 | +src/har_capture/ |
| 125 | +├── patterns/ # Pattern loading, merging, redaction checking, hashing |
| 126 | +│ └── domains/ # Built-in domain pattern files (network_device.json) |
| 127 | +├── sanitization/ # HAR engine, HTML engine, heuristic engine |
| 128 | +├── capture/ # Playwright recording, wait-for-data (optional dep) |
| 129 | +├── validation/ # PII leak detection for pre-commit and CLI |
| 130 | +└── cli/ # Typer commands (get, sanitize, validate, patterns) |
| 131 | +tests/ |
| 132 | +├── fixtures/ # JSON test data (one per test module) |
| 133 | +├── test_capture/ |
| 134 | +├── test_sanitization/ |
| 135 | +├── test_patterns/ |
| 136 | +├── test_validation/ |
| 137 | +└── test_cli/ |
| 138 | +``` |
| 139 | + |
| 140 | +## Development |
| 141 | + |
| 142 | +```bash |
| 143 | +# Run tests (excludes integration tests requiring Playwright) |
| 144 | +.venv/bin/python3 -m pytest tests/ -v --tb=short -m "not integration" |
| 145 | + |
| 146 | +# Release (after merge to main) |
| 147 | +git checkout main && git pull && python scripts/release.py X.Y.Z |
| 148 | +``` |
| 149 | + |
| 150 | +## Release Flow |
| 151 | + |
| 152 | +All work ships in **one PR**: code + tests + changelog + version bump. |
| 153 | +No separate release PR. No tagging from feature branches. |
| 154 | + |
| 155 | +### PR Checklist (before merge) |
| 156 | + |
| 157 | +- [ ] Version bumped in **both** `pyproject.toml` and `src/har_capture/__init__.py` |
| 158 | +- [ ] `CHANGELOG.md` has a `## [X.Y.Z] - YYYY-MM-DD` section with changes |
| 159 | +- [ ] `CHANGELOG.md` has a `[X.Y.Z]` comparison link at the bottom |
| 160 | +- [ ] `CHANGELOG.md` `[unreleased]` link updated to compare from `vX.Y.Z` |
| 161 | +- [ ] Tests pass: `.venv/bin/python3 -m pytest tests/ -v --tb=short -m "not integration"` |
| 162 | +- [ ] Pre-commit hooks pass: `.venv/bin/python3 -m pre_commit run --all-files` |
| 163 | +- [ ] Commit message follows conventional format: `type(scope): message` |
| 164 | + |
| 165 | +### Pipeline: PR → PyPI |
| 166 | + |
| 167 | +``` |
| 168 | +1. Push to feature branch |
| 169 | + └─ No CI (only main + PRs trigger CI) |
| 170 | +
|
| 171 | +2. Create PR targeting main |
| 172 | + └─ ci.yml triggers: tests on Python 3.10-3.13, coverage + integration tests |
| 173 | + └─ PR must pass before merge |
| 174 | +
|
| 175 | +3. Merge PR to main (developer only) |
| 176 | + └─ ci.yml triggers again on the merge commit |
| 177 | + └─ This is the commit release.py will validate |
| 178 | +
|
| 179 | +4. Run release script (developer only) |
| 180 | + $ git checkout main && git pull |
| 181 | + $ python scripts/release.py X.Y.Z # or --dry-run first |
| 182 | + └─ Validates: on main, clean worktree, no existing tag |
| 183 | + └─ Validates: CI passed on HEAD commit (via GitHub API) |
| 184 | + └─ Validates: version consistent across pyproject.toml, __init__.py, CHANGELOG.md |
| 185 | + └─ Runs tests + ruff + mypy locally |
| 186 | + └─ Creates and pushes annotated tag vX.Y.Z |
| 187 | +
|
| 188 | +5. Tag push triggers three GitHub Actions workflows: |
| 189 | + ├─ tag-protection.yml: verifies tag → main, CI passed, version matches |
| 190 | + ├─ release.yml: extracts CHANGELOG section → creates GitHub Release |
| 191 | + └─ publish.yml: builds sdist+wheel → publishes to PyPI (trusted publishing) |
| 192 | +``` |
| 193 | + |
| 194 | +### CHANGELOG Format |
| 195 | + |
| 196 | +Uses [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Update in |
| 197 | +the same PR as the code change. Don't forget the comparison link at the |
| 198 | +bottom and the `[unreleased]` link update. |
| 199 | + |
| 200 | +### Recovery |
| 201 | + |
| 202 | +If tag push doesn't trigger workflows within ~60s, delete and re-push: |
| 203 | + |
| 204 | +```bash |
| 205 | +git tag -d vX.Y.Z && git push origin :refs/tags/vX.Y.Z |
| 206 | +git tag -a vX.Y.Z -m "Release X.Y.Z" && git push origin vX.Y.Z |
| 207 | +``` |
0 commit comments