FRESH REVIEW 2026-07-24 · snapshot: master 1517bba · rendered from SOURCE.md by /architecture
This review supersedes the 2026-07-19 one at 56262f6, whose line references had decayed across 20 commits and +4,036 src lines. Every file:line below was RE-DERIVED at 1517bba and verified first-hand, so the decay banner is retired: the references are current again.
Build state: v1 is CLOSED. All DESIGN section 16 slices landed and tagged (14 tags, slice-01 to slice-13 including 12a and 12b); the exit review was held; --hljs and --theme ruled; C8 is BUILT.
Method: three Explore lens scans (pipeline core / orchestration / testability) over the git hot-spot census, then first-hand verification of every load-bearing claim before it entered this board - a lens report is EVIDENCE to reconcile, never a settled finding. The contract in contract/ is locked and is never edited by this board.
In plain English.
This is a fresh code review. Three read-only agents scanned the code from different angles, then every claim they made was double-checked by hand before it went on this page, so the line numbers are trustworthy again. The headline: the single most valuable change moved to the top of the list, because the ticket that was meant to absorb it finished without doing so and a bug of exactly the kind it predicts turned up in the meantime.
Top recommendation (re-ranked this review)
C3 - Make the risky transforms public and I/O-freePROPOSEDStrong
Moved from 3rd to 1st, replacing C1. Highest correctness stakes in the codebase (the two riskiest verbs), the cost is MEASURED not argued, the private surface is actively widening, and its designated home - ticket 12b - closed without folding in the seam. The 12a/12b timing objection that placed it 3rd is spent.
The board - re-ranked 2026-07-24
1st · C3 - Make the risky transforms public and I/O-free
only I/O-bound entry points reach any of it: share() :331, plan/apply_relocate :617/:962
Problem. The correctness-critical logic of the two riskiest verbs is pure, deterministic string transformation, sealed behind underscore-private helpers with only I/O-bound entry points public. There is no seam to reach it except a full store-plus-render-plus-write shell.
Cost, MEASURED not inferred (the testability lens).
Every test crosses a process boundary (run_ccw is a real subprocess). test_relocate_json_matrix.py drives its 29-shape census - about 120 assertions - through ONE ccw relocate --apply subprocess because _rewrite_bytes is not callable. test_share_regressions.py runs about 27 subprocess invocations plus a fresh temp warehouse per test. Zero in-memory private-helper calls exist across tests/.
The surface is WIDENING: _tree_matches and _references were added in 12b (commit 7a3b4b5), and 12b's headline defect - an escaped path form invisible to the scan - is exactly this card's predicted failure class, added with no seam.
Solution direction. Promote thin public transforms - redact_payload(text, patterns), scan_secrets(text), rewrite_bytes(path, text, patterns) - so the shape census asserts against the transform directly and the subprocess covers only real I/O.
the interface becomes the test surface for the riskiest logic
a new redaction rule or path shape is a one-line in-memory assertion
render.py and parser.py already prove the pattern (public transforms, in-memory tests)
Tier: VERIFIED at 1517bba (all six private helpers per module confirmed; cost measured; git-confirmed the two new helpers)
Problem. The deep lock primitive has a two-function bool interface, but four verbs each re-wrap it with an identical sentinel-triple plus acquire-or-refuse plus finally-release, and cli.py mirrors the report-and-tally half four more times (_run_sweep :310-318, _run_build :375-383, _run_migrate :757-768, _run_relocate :882-908). reports.py is a 19-line shell owning only the error filter.
Solution direction. A with_lock(root, name) -> BatchReport runner plus a shared render_batch(report) -> exit_code in reports.py owning the sentinel-triple, the release, the per-action counts and the exit policy.
Note 2026-07-24: this GREW. The last snapshot recorded 5 CLI blocks; the migrate / project / --EXPOSED work made it 4 module plus 4 CLI copies. Best done before v1.1 ccw import adds a fifth.
flowchart LR
A[one payload] --> M[render_markdown 892-893]
A --> H[render_html 1813-1814]
A --> B[build_manifest 1829]
M --> P1[build_conversation + parse_session]
H --> P2[build_conversation + parse_session]
B --> P3[build_conversation]
Problem, RE-LOCATED by the review. Parse-once is honored INSIDE parser.py: parse_session (:161) and build_conversation (:597) both route through one _extract_entries (:122). The "both re-extract" hypothesis is refuted. The real violation is the orchestration seam: build._projection_files (:87-100) invokes all three emitters, each taking raw bytes and privately re-deriving the model, so one projection re-walks the entries build_conversation x3 plus parse_session x2. sha256_hex is likewise recomputed at render.py :894/:1816/:1831.
Solution direction. Hoist parsing to the orchestrator: build the models once in _projection_files and pass them into the three emitters, keeping a thin bytes wrapper for the black-box tests.
Why 3rd (was 1st): still Strong and zero contract risk, but the review ranked C3 above it - higher stakes, measured cost. C1 stays a clean self-verifying refactor.
4th · C4 - Give the head concept one home: a catalog read seam
PROPOSEDStrong
build.py · share.py · cli.py · catalog.py
Problem. The head predicate s.hash NOT IN (SELECT supersedes ...) is typed verbatim in _heads (build.py:143) and head_for_short (:173), and the whole SELECT column list is duplicated too (:141-142 vs :170-171) - while head_for_short's docstring (:167) claims "one owner, R9", an R8/F6 overclaim. _Resolved re-declares the head shape in share.py:75.
Solution direction. One _HEAD_SELECT / _HEAD_WHERE fragment and a public head-record type both callers compose; reads stay catalog-only (R6).
Contract ties: R6, R8, R9 · Tier: VERIFIED at 1517bba
5th · C6 - cli.py: verbs hand back results, not cursors
PROPOSEDStrong
cli.py · build.py · registry.py · relocate.py
Problem. Verb logic lives in cli.py that should live in the verb's module: the render no-row-vs-superseded policy stranded in _render_session (:431-444) while build.head_for_short owns only the head selection; project-exists SELECTs inline in _run_project (:585-682) though registry.py owns the edits; _out_under_warehouse (:475-491) a CLI-only guard that the two non-CLI callers of write_projection lose; and halted-run recovery string-matching relocate action detail (:886, :894-905).
Solution direction. Verbs own their reads and guards and return a typed result; registry.py gains the project read side; the write-guard moves into write_projection; relocate exposes typed accessors so cli never string-matches.
Contract ties: F9 · overlaps C2 and C4 · Tier: VERIFIED at 1517bba
6th · C9 - One source walker, two policies
PROPOSEDStrongwas Speculative
sweep.py · migrate.py
Problem.sweep._walk_source (:44-76) and migrate._walk (:42-82) are the same scaffold verbatim - the is_dir guard, the _on_error body, os.walk(onerror), the .jsonl filter, the sorted return - and even the _JSONL_SUFFIX constant is declared twice (sweep :36, migrate :21). The only real divergence is the per-file policy.
Solution direction. One walk_transcripts(root, *, classify) owning the scaffold, each verb supplying only its filename policy.
Bumped Speculative -> Strong: the review found the duplication is total (scaffold plus onerror body plus constant), so the F7 under-capture lesson it encodes has two homes that can drift.
Tier: VERIFIED at 1517bba
7th · C5 - One turn walker, two serializers
PROPOSEDWorth exploring
render.py
Problem. The turn traversal is written twice. Markdown: _turn_body (:730-746) and _render_turn (:789-805). HTML: _claude_inner (:1617-1630) and _turn_html (:1633-1697). Deliberately partial: leaf block rendering IS single-owner via _render_block (:356) - only the walk is doubled.
Solution direction. One turn-walk yielding typed events (Phase / Reply / UserHalf), each emitter supplying only leaf rendering.
Contract ties: R9 · pairs with C1 · Tier: VERIFIED at 1517bba
8th · C7 - Type the Block discriminant
PROPOSEDWorth exploring
parser.py · render.py
Problem.Block.kind (parser.py:291) is a bare str whose legal set is only in a docstring, switched on at about seven sites (parser _segment_category :356-369, group_segments :394; render _render_block :356-384, _ROW_ICONS :1479-1492, _row_label :1497-1528, _row_icon :1531-1537). A new kind compiles clean while several render switches fall through silently - the F6 class, invisible to pyright strict.
Solution direction. A typed discriminant (Literal or enum, stdlib, R7-safe) driving render dispatch through mappings, so a missing arm is a type error.
Contract ties: F6, R7 · Tier: VERIFIED at 1517bba
9th · C11 - Split the short-key band math from its query
PROPOSEDWorth exploringNEW 2026-07-24
catalog.py
Problem._short_key (catalog.py:131-148) decides whether a 12-hex citation key must extend and to what length - pure string math (the prefix band at :137-140) - but it is welded to conn.execute at :142-145, so the only seam to test the extension rule is standing up a database with a colliding row.
Solution direction. Split the pure "choose a length given existing prefixes" function from the collision lookup. Same public-pure-transform shape as C3 in a lower-risk module.
Contract ties: F5 (keep the PK-index band query) · Tier: VERIFIED at 1517bba, surfaced by the pipeline lens
10th · C10 - A catalog read-scope for relocate
TICKETED-12bwas Speculative
relocate.py
Partially closed, re-verified this review. NOT marked BUILT.
The O(N)-per-candidate churn F5 named is GONE: _encoded_moves threads the single shared connection into its candidate loop (:497, using the conn opened once in _compute :560). What remains is O(1) per-apply churn: _project_for_cwd (:527-534) and _encoded_owner (:751-758) each open their own connection from _preflight - constant per apply, not a scaling bug.
Direction: have _preflight borrow the live conn and inline project_for_path, retiring the two helper opens. Overlaps C2 and C6.
Contract ties: F5; ticket 12b list · Tier: VERIFIED + CONTRACT at 1517bba
In plain English.
Nine ideas for making the code easier to change and test safely, plus one that's half-done. The clearest win is making the two most dangerous pieces of code (the bit that scrubs secrets before sharing, and the bit that rewrites paths) testable with a plain function call instead of launching a whole subprocess each time - the tests currently pay that cost roughly 150 times.
Verified healthy - the deep end, leave it alone
The store foundation.store.py hides O_EXCL takeover races and atomic writes behind put / get / acquire_lock / verify_walk. Deep by construction.
The ingestion seam.capture_transcript is one deep leverage point with three adapters (hook, sweep, migrate).
The parser and render public seam.parse_session, build_conversation, render_markdown, render_html and build_manifest are public and tested in-memory. This is the positive model C3 asks the two risky verbs to match.
The oracle suite discipline.Black-box only, the F5 zero-object-reads negative proven with an audit hook, atomic_write fault-injected cleanly.
atomic_write mode preservation.relocate exposed a silent permission reset present since slice 01; fixed once in the one primitive, every caller inherited it.
C8 page-chrome seam - BUILT (2026-07-24).The RenderOptions.hljs mode plus render._hljs_block, with share setting inline at both call sites (commit 0cd4146), implemented DESIGN 15 item 8. Two real adapters justify the seam; moved off the active board as the built exemplar.
Not on the board - owned elsewhere
The remaining hand-rolled flag scanners in cli.py
Ticket 13 closed WITHOUT removing them; two survive deliberately (_sweep_source, _render_flags) and now document why. Whether they are a candidate is a fresh-review question; no ticket owns them now.
config.toml parsed in three modules - RESOLVED 2026-07-24
config.py is now the only module in src/ importing tomllib (relocate's reader went in 03ca402, share's in f9b7bbd). It was understated as R9 duplication: in share it was a publish-path leak, an XDG-tier redact_patterns rule ignored and its content published.