# SPEC.md — Engineering rules for G.U.I.D.E. This file defines the **non-negotiable process rules** for changing this codebase. They exist to stop the kinds of drift and breakage we have actually hit while iterating. Read this before opening a change; the rules here are enforced by the sanity suite and the git pre-commit hook. > **Sister docs:** `CLAUDE.md` describes the architecture; the `openspec/` > workflow describes *what* to build. **SPEC.md describes how every change must > be made and verified.** --- ## 1. Definition of Done (every change) A change is not complete until **all** of the following hold: 1. **Sanity tests pass** — `python -m pytest tests/test_sanity.py -q` is green. No code may be committed while sanity tests fail (enforced by the pre-commit hook — see §4). 2. **The “How To Guide” tab is in sync** — see §2. 3. **Docs reflect the change** — if you changed architecture, tabs, endpoints, env vars, or invariants, update `CLAUDE.md` (and `README.md` if user-facing). 4. **No secrets added** — API keys/tokens live only in the git-ignored `.env`. Never paste them into source, tests, or commit messages. ## 2. The “How To Guide” tab MUST be kept in sync The first tab in `ui/app.py` (`📖 How To Guide`) is the user's onboarding doc. **Any UI change that affects what the user sees or does must be reflected there**, specifically: - Adding, removing, or renaming a **tab** → update the *“What Each Tab Does”* table (and the *Step-by-Step Flow* table if it changes the main flow). - Adding or changing a **user-facing feature** (e.g. PDF export, the redaction reveal panel, the privacy audit) → describe it in the relevant tab row and, if it touches privacy, in the *“Your Privacy”* section. - Changing the **privacy behaviour** → keep the *“Your Privacy”* section honest. This rule is partially **automated**: `test_how_to_guide_lists_every_tab` fails if any user-facing tab is not mentioned in the How To Guide. The judgement calls (feature descriptions, privacy wording) are on the author — keep them current. ## 3. Sanity test suite Location: `tests/test_sanity.py`. It is **fast** (no ML checkpoints, no spaCy model, no Anthropic API, no network) and covers: | Check | Guards against | |-------|----------------| | Gradio app builds (`build_app()`) | event/component wiring, output-count, scope bugs | | API models + routes import & validate | request/response schema breakage | | `RedactionResult.spans` contract | the redaction-reveal data contract | | Redaction-reveal / audit-log render helpers | the two privacy features silently breaking | | PDF export produces a real `%PDF-` file | the export feature breaking | | Friendly LLM-error mapping | overloaded/rate-limit/timeout → user-friendly message | | How To Guide lists every tab | the guide drifting out of sync (see §2) | **When you add a feature, add a sanity check for it here** if it is cheap to verify without heavy dependencies. Keep the whole suite under ~15 seconds. The deeper unit tests under `tests//` (classifier, ner, agent, privacy, etc.) are **not** part of the commit gate — run them with `pytest tests/` when you touch those layers. ## 4. Enforcement — git pre-commit hook A pre-commit hook runs the sanity suite and **blocks the commit if it fails**. Install it once after cloning (idempotent): ```bash bash scripts/install-hooks.sh ``` This copies `scripts/hooks/pre-commit` into `.git/hooks/` and marks it executable. The hook runs `tests/test_sanity.py` in the project `.venv` (falling back to `python3`). To bypass in a genuine emergency only: `git commit --no-verify` — but the change is then **not** Done per §1. ## 5. Checklist to paste into a PR / change description ``` - [ ] Sanity tests pass: pytest tests/test_sanity.py -q - [ ] How To Guide tab updated (tabs / features / privacy wording) - [ ] CLAUDE.md / README.md updated if architecture or behaviour changed - [ ] No secrets added to source or commits - [ ] Added a sanity check for any new user-facing feature (if cheap) ```