| 1 | Auth decorator / scope change — diff touches auth.py, auth_api.py, validator_cookie.py, or adds/edits @login_required, validator-cookie path, or dev-bypass gates | Update module docstrings of all three auth files so scope claims still match; add a test asserting the new boundary; consider an ADR-supersede |
| 2 | Env var read/write — added line matches `os.environ(.get( | [)["'][A-Z_]in any.py` |
| 3 | New file-upload endpoint — diff adds a route reading request.files / multipart | Use utils.validate_upload(request, field_name=, allowed_mime=, max_bytes=) (ADR-025); add oversize + empty + bad-MIME route tests |
| 4 | New external API caller — added line adds requests.post/get( or a new Whisper/Claude/Vision call | Route through egress.safe_call_api(url=, headers=, vendor=) — never raw requests.* (ADR-023); key in Authorization/vendor header, NEVER URL query (ADR-007); scrub_sensitive regex covers the key; add test_strips_<vendor>_key in test_logging.py. User-controlled URL → egress.safe_fetch_user_url (ADR-006) |
| 5 | New state-mutating route — diff adds @bp.post/patch/delete/put or a methods=[...] containing POST/PATCH/DELETE/PUT | @login_required (real OAuth — validator cookie can't auth mutations); NEVER add GET to a mutating route's methods (#190/#185 CSRF — SameSite=Lax doesn't block top-level GET); rate-limit if user-controlled; validate input |
| 6 | New state-reading route — diff adds @bp.get / a GET-only route | @login_required; validator cookie WILL auth it on GET (intentional) — document if it exposes anything sensitive |
| 7 | New static asset — a new file under static/ (*.css/*.js/icon) appears in the diff (git diff --name-only --diff-filter=A) | static/sw.js APP_SHELL includes it; health.py EXPECTED_STATIC_FILES includes it; bump CACHE_VERSION |
| 8 | New HTML template / route renderer — new templates/*.html or a new render_template(...) route | Add to nav in base.html if user-visible (or a capture-bar button); set active_page; Phase 6 desktop + mobile (Playwright/bandit don't substitute); update ARCHITECTURE.md Components + Data Flows + Route catalog (arch_sync_check enforces) |
| 9 | New background job — diff adds APScheduler add_job( / a new cron closure in app.py _init_scheduler | ARCHITECTURE.md Components (scheduler box + the job by job_id + what it does); Data Flows if user-observable; literal job_id must appear in the Route catalog (arch_sync_check enforces) |
| 10 | New /api/… endpoint — diff adds @bp.get/post/... under an /api/... blueprint | ARCHITECTURE.md Data Flows (request→response); literal URL pattern in the Route catalog (arch_sync_check enforces) |
| 11 | New DB column / enum member — added db.Column( / mapped_column( / Mapped[ in models.py, or a new enum value | ARCHITECTURE.md PostgreSQL box + matching Components bullet (ER diagram auto-generates from db.Model.registry); if user-visible (new tier/field), also templates/docs.html per row 15 |
| 12 | New db.Model subclass (new table) — added class X(db.Model) | Add the table to _ER_TABLE_GROUPS (core/ops/auth) + _ER_TABLE_ORDER, and a _SCHEMA_DESCRIPTIONS entry (blurb + columns:{col:{desc,notes,fk_target?}}). Resolve the real definition site with grep -rn "_SCHEMA_DESCRIPTIONS =" — it now lives in architecture_schemas.py (re-exported into architecture_service.py), not where older docs say. Drift-gate tests test_every_model_table_has_a_group + test_every_column_has_a_description fail otherwise |
| 13 | New column on an existing model — added db.Column( / mapped_column( / Mapped[ on a model that already exists | If not in _HIDDEN_ER_COLUMNS/_DESCRIPTION_OPTIONAL_COLUMNS, add _SCHEMA_DESCRIPTIONS[table]['columns'][col] (in architecture_schemas.py — grep for it) with FK cols including fk_target. test_every_column_has_a_description fails otherwise |
| 14 | New gate in run_all_gates.sh — diff touches scripts/run_all_gates.sh adding a gate/check | Update the "11 quality gates" OR "Drift-prevention gates" table in templates/architecture.html (#54); note the bug it prevents in the Prevents-recurrence-of column; update the Engineering-details run-command if syntax changed |
| 15 | User-visible behavior — diff touches parse_capture.js, tier rules, a voice-memo prompt, recurring spawn timing, a user-facing error message / button label / time window, or any new affordance | Update templates/docs.html (Help, #33/#40); walk the user-facing fact-check SOP (every claim cited to file:line); add a section if genuinely new; verify desktop + mobile in Phase 6 |
| 16 | Process flow with a sequence diagram — diff touches the recurring-spawn, voice-memo, or auth flow (others added later) | Update the Mermaid sequence diagram in templates/architecture.html to match the code path (re-walk per fact-check SOP); keep the prose + "Related ADRs" list accurate; new flow worth a diagram → draft one + add a TOC entry |
| 17 | New function called from static/app.js init() (or a transitive helper like renderBoard→updateTodayWarning) | Null-guard every getElementById/querySelector for a board-only element BEFORE reading any property (const el = …; if (!el) return;) — subpages load app.js too and an unguarded access stops init + downstream loaders. (Burned twice: updateTodayWarning 2026-04-19 + /completed 2026-04-20) |
| 18 | Extending a feature from one task type to multiple — diff broadens a type === "work" / type == "work" (or personal) gate | grep all type === "work" AND type == "work" (+ personal equivalents) across static/*.js, route guards, templates — decide whether each gate is still valid. Write the payload round-trip test FIRST (#57 silent project_id drop) |
| 19 | Security-sensitive function refactor — broadened a scope / changed an auth check (overlaps row 1) | New ADR superseding the old one in docs/adr/; grep all docstrings/comments for the OLD claim and update; add a regression test asserting the new scope |
| 20 | Dependency bump — diff touches requirements.txt / requirements-dev.txt / package.json | pip-audit / npm audit clean; pytest still passes (some bumps break APIs) |
| 21 | New SOP rule / process change in CLAUDE.md — diff touches CLAUDE.md | Mention it in the next commit message so future sessions notice; consider whether run_all_gates.sh can enforce it. If you changed the cascade table itself, also update THIS skill (see below) |