Implementation plan
Concrete steps to get from the current bundle-based review system to the gate-based design in design.md. Each step produces a working state — no step depends on completing all subsequent steps.
Step 1: Create gate files from existing review checks
Decompose the 4 existing review bundles into individual gate files under kb/instructions/review-gates/{lens}/.
The exact initial gate inventory:
| Gate ID | Source | Watches | Staleness |
|---|---|---|---|
| frontmatter/description-discrimination | frontmatter-review check 1 | [title, description] |
changed |
| frontmatter/title-composability | frontmatter-review check 2 | [title] |
changed |
| frontmatter/claim-strength | frontmatter-review check 3 | [title] |
changed |
| frontmatter/title-body-alignment | frontmatter-review check 4 | [title, body] |
rewrite(0.5) |
| prose/source-residue | prose-review check 1 | [body] |
changed |
| prose/pseudo-formalism | prose-review check 2 | [body] |
changed |
| prose/confidence-miscalibration | prose-review check 3 | [body] |
changed |
| prose/proportion-mismatch | prose-review check 4 | [body] |
changed |
| prose/orphan-references | prose-review check 5 | [body] |
changed |
| prose/unbridged-cross-domain | prose-review check 6 | [body] |
changed |
| prose/redundant-restatement | prose-review check 7 | [body] |
changed |
| prose/anthropomorphic-framing | prose-review check 8 | [body] |
changed |
| semantic/completeness-boundary-cases | semantic-review step 2 | [body] |
changed |
| semantic/grounding-alignment | semantic-review step 3 (includes domain coverage sub-check) | [body] |
changed |
| semantic/internal-consistency | semantic-review step 4 | [body] |
changed |
| complexity/claim-to-section-ratio | complexity-review check 1 | [body] |
changed |
| complexity/framework-decoration | complexity-review check 2 | [body] |
changed |
| complexity/connection-inflation | complexity-review check 3 | [body] |
changed |
| complexity/could-be-a-paragraph | complexity-review check 4 | [body] |
changed |
| structural/broken-link-path | existing gate | [body] |
changed |
| structural/compound-bullet | existing gate | [body] |
changed |
| structural/bullet-capitalization | existing gate | [body] |
changed |
| structural/general-before-specific | existing gate | [body] |
changed |
23 gates total. Note: semantic-review has 3 checks, not 4 — "domain coverage" is a sub-check of grounding-alignment (step 3.3 in the current instructions), not a standalone check.
Existing gate files in kb/instructions/gates/ move to kb/instructions/review-gates/structural/ with gate_id, watches, and staleness added to frontmatter.
Each gate file gets the frontmatter from the design:
---
gate_id: {lens}/{name}
name: Human name
lens: {lens}
watches: [relevant regions]
staleness: changed # or rewrite(0.5) for title-body-alignment
---
Body: failure mode, test, examples — extracted from the corresponding check section in the current bundle document.
Done when: kb/instructions/review-gates/ has 23 gate files covering all checks from all 4 bundles plus the 4 existing gates.
Step 2: Keep bundle names as lens aliases
Keep the current bundle names as stable invocation targets by resolving each one to all gate files under kb/instructions/review-gates/{bundle}/.
Done when: bundle expansion comes directly from the gate directories and there is no separate bundle-manifest tree to maintain.
Step 3: Build the gate selector
New script: scripts/review_target_selector.py. This replaces notes_selector.py + selector_engine.py for gate-based review.
Reviewable note scope: top-level *.md files in kb/notes/ that have YAML frontmatter and are not indexes (same rule as review_state.list_reviewable_notes). Subdirectory notes (e.g. kb/notes/related-systems/, kb/notes/definitions/) are excluded for now. If the scope needs to expand later, change it in one place.
Inputs: a bundle name (or --all-gates), optional note path filter.
Logic:
1. Expand the bundle name to all gate files in its kb/instructions/review-gates/{bundle}/ directory.
2. Load gate frontmatter (watches, staleness) for each gate.
3. Load gate_reviews.csv (or create empty if missing).
4. For each (note, gate) pair:
- Gate change check: compute git blob hash of gate file, compare to gate_hash in CSV. Mismatch = stale.
- Git filter: if note file hasn't changed since recorded_commit, skip (fresh).
- Staleness check: for changed gates, hash watched regions and compare. For rewrite(threshold) gates, check non-body watched fields exactly, then measure body diff ratio.
- Missing review: no CSV row = stale.
5. Emit stale (note, gate) pairs.
Output: JSON list of {note_path, gate_id, reason}, or plain paths grouped by note.
Reuse from existing code:
- review_state.py: list_reviewable_notes, has_frontmatter, extract_body_lines, build_note_snapshot
- review_metadata.py: git_blob_sha, read_blob, run_git
- selector_engine.py: body_change_ratio
New code needed:
- Gate frontmatter parser (read YAML, extract gate_id, watches, staleness)
- Watched-region hasher (hash only title / description / frontmatter / body based on watches)
- CSV reader/writer for gate_reviews.csv
- Gate file blob hash computation
Tests:
- build a fixture test repo with real git history, not mocked git responses
- fixture should contain:
- a small kb/notes/ tree with reviewable notes
- a small kb/instructions/review-gates/ tree
- a small kb/instructions/review-gates/ tree with multiple lens directories
- recorded gate review files plus generated gate_reviews.csv
- at least two commits so tests can exercise recorded_commit vs HEAD
- use this fixture repo for selector tests covering:
- missing review -> stale
- gate file changed -> stale
- watched hash changed -> stale
- rewrite threshold not exceeded -> fresh
- rewrite threshold exceeded -> stale
- unchanged note outside git diff set -> fresh
Done when: uv run scripts/review_target_selector.py frontmatter-review emits stale (note, gate) pairs. Initially everything will be stale (no recorded reviews yet).
Step 4: Write recorded gate reviews
New script or function: after a gate review is performed, write the recorded review file and update the CSV.
Review file path: kb/reports/reviews/gates/{encoded-note-path}/{gate-id}.md
Metadata comment format per design.md. CSV columns: note_path, gate_id, gate_hash, recorded_commit, watched_hash, recorded_at.
Tests: - use the fixture repo to verify write -> regenerate CSV -> select round-trips correctly - verify recorded review metadata is sufficient to restore freshness without reparsing review prose
Done when: review files can be written and the CSV regenerated. The gate selector reads them and correctly reports fresh/stale.
Step 5: Wire up execution
Update the review skill (or create a new one) to use the gate-based flow:
- Run gate selector → get stale (note, gate) pairs
- Group by (note, lens)
- For each group: load note + gate files → run review → produce findings
- Write recorded review files + update CSV
The applicator stage (step 4 in the design) can be manual for now — the reviewer produces findings, a human or separate call decides what to act on. Don't build applicator infrastructure until the review stage is proven.
Done when: a gate-based review can run end-to-end on a note and produce stored, selector-visible results.
Step 6: Delete old bundle review infrastructure
Once gate-based reviews work:
- Delete old review files in kb/reports/reviews/*.{type}-review.md (or archive them)
- Remove selector_engine.py's SELECTOR_POLICIES dict and _frontmatter_decision / _full_text_decision
- Remove or redirect notes_selector.py to use review_target_selector.py
- Delete the monolithic review instruction files (the check prose is now in gate files)
Done when: only gate-based review infrastructure remains.
What to skip
- Summarize/reporting scripts:
summarize_reviews.pyand its CSV reports can be rebuilt later once gate reviews accumulate. Don't port them preemptively. - Applicator infrastructure: humans do prioritization for now. Build the applicator when the review stage is stable and you have data on what findings look like.
- Gate extraction workflow:
/extract-gatesfrom the selector-loaded gates note. Build this after the gate store exists and you have more manual edits to learn from. - Gate ack script:
gate_ack.pyfor acknowledging trivial changes without re-reviewing. Useful but not needed until gate reviews are running and trivial-change noise becomes a real problem.