Skip to content

id-377 trial A — id-292 bid-literal liveness re-verification

Trial A — bid→procurement rename residual sweep (id-292 re-verification)

Section titled “Trial A — bid→procurement rename residual sweep (id-292 re-verification)”

Date: 2026-07-29. Corpus: canonical @ ast-dataflow-efficacy-trials (clean, head 8b397be1). Protocol: for each question — tool answer, then independent rg/Read ground truth, then verdict. Tool run as a user (CLI cold path only; MCP warm server not registered — see G7). No canonical edits were made; no trial-blocking tool bug was found (every miss below is by-design or a detector gap, not a defect in what the tool claims to do).

#questioncommandwall clock (durationMs)answerground truthverdictgap_class
R1Class 1: TS sites value-comparing primary_focus vs 'bid_writing'bun run ast-dataflow string-literal-uses --value bid_writing7.9s (5790)1 site: __tests__/hooks/use-primary-focus.test.ts:50 (kind argument)rg: ~12 sites. Misses ALL prod sites: lib/user-focus-constants.ts:1 (type union), :4 (CV object value), hooks/use-primary-focus.ts:9 (array element), components/dashboard/organisation-profile-nudge.tsx:18 (property key), test property values. No caveat in output states the corpus limit or the kind filter — an Auth-user_metadata surface note is likewise absentgapdetector-missing + output-shape
R2Class 2: files value-comparing 'bid_discovered'bun run ast-dataflow string-literal-uses --value bid_discovered9.4s (7088)0 resultsrg: lib/freshness.ts:103 (=== comparison, load-bearing), :19 (type union), test property values, 2 migration SQL sitesgapdetector-missing
R3Class 2: who reads/writes record_lifecycle.lifecycle_type (TS)column-reads / column-writes --table record_lifecycle --column lifecycle_type5.4s + 4.4s (2289/1715)5 reads (app/api/cron/freshness-transitions/route.ts:160, app/api/freshness/calculate/route.ts:57, + 3 wildcard) / 5 writes (app/api/governance/review/route.ts:231, e2e/fixtures/test-data-fixture.ts:305, lib/mcp/tools/content.ts:1226, lib/mcp/tools/governance.ts:606,1257)READS clean: freshness/calculate:57 + freshness-transitions:160 are real .select(...lifecycle_type...) strings, the other 3 are honestly-labelled wildcard. WRITES over-report at the COLUMN level: only test-data-fixture.ts:305 (literal key) and content.ts:1226 (...facetUpdate, FACET_FIELDS-gated) actually write lifecycle_type. governance.ts:606 upserts governance_review_status only; governance.ts:1257 and governance/review/route.ts:231 pass a record_lifecycle['Update']-typed updateData whose every branch is governance-only — 3 of 5 are table-level writes reported under an exact columnPath (see G8)sufficient (reads) / gap (writes)detector-precision
R4Class 2 cross-language: does the pipeline touch lifecycle_type?bun run ast-dataflow-py column-reads --table record_lifecycle --column lifecycle_type2.5s (2315)1 exact read: scripts/cocoindex_pipeline/sources/l_records.py:1194 (raw SQL, method: select)rg agrees; a surface the TS tool cannot see, caught via sqlglotsufficient
R5Class 3: confirm ZERO TS refs for prior_bid_response; can anything see the q_a_extractions CHECK?string-literal-uses --value prior_bid_response; column-reads --table q_a_extractions --column extractor_kind; schema-coverage --table q_a_extractions12.9s + 8.5s + 5.2sAll zero/empty for the literal; schema-coverage enumerates generated-type columns only — the CHECK constraint values (squash L951) appear in NO queryrg: only supabase/migrations/20260617130000_squash_baseline.sql:951. ZERO TS refs claim CONFIRMED. CHECK invisible as expected — {377.4}-tail (pg_proc/migrations blind spot), logged as known, not novelsufficient (claim) / known gap (CHECK)corpus-boundary
R6Class 4: all bid_detail sitesstring-literal-uses --value bid_detail32.0s outlier (5534 on rerun)20 sites, ALL test argument positions; zero prodrg prod: lib/layer-inference.ts:48 (const init), lib/client-config.ts:167 (property value), supabase/seed.sql:378 (CV seed, SQL), migration comment. Both TS prod sites invisiblegapdetector-missing
R7Class 4: does any query connect LAYER_BID_DETAIL'bid_detail'?references --symbol 'lib/layer-inference.ts:LAYER_BID_DETAIL'; enum-uses --enum LAYER_BID_DETAIL2.9s (672) / 2.8s (35)references: 8 exact sites, all in-file (inferLayer rule arms — the const is module-private). enum-uses: out_of_corpus (plain consts unsupported). No query surfaces the const’s literal valueCorrect as far as it goes; the const→value link stays manual (rg the initializer)opportunitydetector-missing
R8Class 4: all bid_library sitesstring-literal-uses --value bid_library8.6s (5988)0 resultsrg: lib/layer-inference.ts:94 (=== 'bid_library' — the LOAD-BEARING inference rule), :21 (union), test property valuesgapdetector-missing
R9Class 5: from_bid zero production refs (S485 claim)string-literal-uses --value from_bid8.2s (6220)0 results — correct zerorg: survivors are comments only (app/procurement/[id]/session/page.tsx:413-415, e2e/tests/bid-session.spec.ts:322-390 — all {135.32} tombstone commentary). Even the page.tsx:412 literal named in id-292’s PR#121 note is now a comment. S485 resolution CONFIRMED and completesufficient
R10Class 6: all kb-bid-view localStorage sitesstring-literal-uses --value kb-bid-view8.2s (5855)1 site: app/procurement/page.tsx:71 (useViewMode('kb-bid-view','grid'), kind argument, enclosing fn:FormsPage)rg: exactly 1 — perfect matchsufficient
R11Class 7: load-bearing workspaces.type === 'bid' comparisonscolumn-reads --table workspaces --column type; string-literal-uses --value bid3.7s (1958) / 9.5scolumn-reads: silent [] — but workspaces.type no longer EXISTS (generated types show application_type_id; a garbage-column probe also returns silent [], no unknown_column error). Literal sweep: 8 sites (components/provenance/audit-tab.tsx:70 jsxProp, lib/validation/schemas.ts:1185 z.literal, scripts/mcp-eval/functional-correctness.ts:1096)Premise CHANGED: the type text column was dropped post-T2; the legacy 'bid' alias now lives at the INPUT layer — lib/mcp/tools/workspaces.ts:76 (args.type === 'bid', load-bearing back-compat, MISSED: comparison), :36 (enum list, MISSED: array element), app/api/workspaces/route.ts:140 (STALE COMMENT ONLY — no mapping code; WorkspaceCreateBodySchema.type is z.enum(getValidTypeValues()) and lib/workspace-types.ts:23-30 lists 6 keys without 'bid', so the REST surface rejects the alias at validation), components/dashboard/activity-feed.tsx:117,25 (MISSED: return + union). lib/queue/handlers/batch-reclassify.ts and app/api/items/route.ts no longer contain 'bid'. Load-bearing-vs-stale stays human judgment — the tool contributes enclosing context onlygap ×2detector-missing + ergonomics (silent unknown column)
R12Class 8: source_bid_* GONE (TS + Python)string-literal-uses --value source_bid_id (+_name); ast-dataflow-py schema-uses --exclude-tests10.7s / 0.6s0 TS; py evidence sidecar (765 rows) contains no source_bid_*; the only “bid” match in it is the FILENAME scripts/bid_worker.pyrg repo-wide: zero. GONE confirmed both languagessufficient
R13KEEP list still live?enum-uses --enum VALID_BID_STATUSES; --enum FINAL_AWARD_FORM_TYPES; --enum VALID_PUBLICATION_STATUSES; string-literal-uses --value bid_response~3s eachAll three enum probes: out_of_corpus. bid_response: 3 test-arg sites onlyKEEP list is stale: VALID_BID_STATUSES / TERMINAL_BID_STATUSES have ZERO repo-wide hits — retired with the bid-status vocabulary (types/bid.ts is gone too; no bid-named successor). bid_response reorient tag IS live (types/reorient.ts:55 union + emit network across 7 prod files — all invisible to the literal sweep, same detector hole). 'bid' form_types CV live (schemas.ts:1140,1185). Separately: enum-uses cannot see as-const arraysFINAL_AWARD_FORM_TYPES and VALID_PUBLICATION_STATUSES are both LIVE yet error out_of_corpus; the KH CV convention is arrays, so the documented “as const idiom” support misses the dominant idiomgapdetector-missing
R14Corpus boundary: which class literals sit out-of-corpus?contrast R11 sweep vs rg; fixture-uses --needle bid_library then --needle 'scripts/import_bid_library.py'9.1s / 0.4sfixture-uses bid_library: 0. Exact full value: 1 hit (__tests__/fixtures/reference-doc-paths.json:36, requiredPaths[27]) — needle is whole-token exact-match, embedded substrings invisibleOut-of-corpus class literals, named: scripts/seed-id145-w1f-exemplar.ts:129,141 (.eq('form_type','bid') — call-arg kind that WOULD match, proving script exclusion, not kind exclusion); supabase/seed.sql:378 (bid_detail CV seed row); squash L951 CHECK; migration SQL bid_discovered sites. In-corpus surprises: e2e/fixtures/test-data-fixture.ts IS in (R3 hit line 305), scripts/mcp-eval/functional-correctness.ts IS in (R11 hit). No output field states corpus membership either waygapcorpus-boundary + ergonomics

Refreshed per-class liveness table (id-292, re-verified 2026-07-29)

Section titled “Refreshed per-class liveness table (id-292, re-verified 2026-07-29)”
classliteral(s)live sites (file:line)disposition
1bid_writinglib/user-focus-constants.ts:1,4 (union + CV value, label already ‘Procurement writing’); hooks/use-primary-focus.ts:9; components/dashboard/organisation-profile-nudge.tsx:18 (key); tests ×4 filesunchanged — Auth user_metadata backfill still the blocker
2bid_discoveredlib/freshness.ts:19,103 (union + load-bearing compare); supabase/migrations/20260702130000_id131_freshness_rpcs.sql:95 (RPC body!); squash L4460; testspremise-changedlifecycle_type now lives on the record_lifecycle facet (content_items eliminated, id-131). Class SURVIVES but grew a new coupled surface: the id-131 freshness RPC compares the literal in SQL, so the “backfill UPDATE + TS literals atomically” plan must now also patch compute_freshness-family RPCs. TS read surface: 5 readers (2 exact — app/api/freshness/calculate/route.ts:57, app/api/cron/freshness-transitions/route.ts:160 — + 3 wildcard). TS write surface: 2 real writers, lib/mcp/tools/content.ts:1226 and e2e/fixtures/test-data-fixture.ts:305 — the other 3 the tool returned (governance.ts:606,1257, governance/review/route.ts:231) write governance columns only, see G8; pipeline reads it at l_records.py:1194
3prior_bid_responsesupabase/migrations/20260617130000_squash_baseline.sql:951 (CHECK) only; ZERO TS refs re-confirmedunchanged — leave-with-rationale
4bid_detail / bid_librarylib/layer-inference.ts:48 (LAYER_BID_DETAIL const + 7 in-file uses), :21,94 (bid_library union + load-bearing rule); lib/client-config.ts:124,167; supabase/seed.sql:378 (CV seed); testsunchanged
5from_bid + derivedcomments only (app/procurement/[id]/session/page.tsx:413-415; e2e/tests/bid-session.spec.ts tombstone block)resolved (S485/{135.32} confirmed; last literal now a comment)
6kb-bid-viewapp/procurement/page.tsx:71 (sole localStorage site)unchanged
7workspaces.type==='bid'lib/mcp/tools/workspaces.ts:36,76 (input-alias enum + load-bearing compare — the ONLY live alias surface); app/api/workspaces/route.ts:140 (stale comment only — no mapping code, and the route’s zod enum has no 'bid' member); components/dashboard/activity-feed.tsx:25,117 (activity-category vocab, arguably separate surface); out-of-corpus: scripts/seed-id145-w1f-exemplar.ts:129,141 (form_type='bid', different column)premise-changed — the workspaces.type TEXT COLUMN IS DROPPED (post-T2; discriminator = application_type_id). “DB still emits the alias” is no longer true; what survives is the CLIENT-INPUT alias ('bid' accepted by the MCP list_workspaces tool, which answers it with a redirect-to-form-tools message). Retirement is now an MCP-tool-deprecation decision, not a DB data migration — and narrower than first written: the REST route carries no alias, only a comment describing one. batch-reclassify.ts no longer carries the literal and app/api/items/route.ts no longer exists
8source_bid_id/_namenone (zero repo-wide, TS + Python)GONE (re-confirmed)
KEEPVALID_BID_STATUSES / TERMINAL_BID_STATUSESzero repo-wide hits — retired with the bid-status vocabulary (types/bid.ts gone; no bid-named successor)KEEP entry now moot — nothing left to protect
KEEPbid_response reorient tagtypes/reorient.ts:52,55,67 + emit/validator network (lib/activity/team-changes.ts, app/api/reorient/route.ts, components/dashboard/{reorient-section,activity-feed}.tsx, mcp-apps/reorient-me/*)live — keep protecting
KEEPbid form_types CVlib/validation/schemas.ts:1140 (FINAL_AWARD_FORM_TYPES), :1185 (z.literal('bid'))live — keep protecting

Net: 5 live classes (1, 2, 3, 4, 6), one premise-changed toward cheaper retirement (7), one resolved (5), one gone (8). Two of three KEEP entries live; one moot.

idgap_classwhatreproproposed fix
G1detector-missingstring-literal-uses classifies only 5 call-site contexts (viMock/jsxProp/sqlTag/envKey/argument) and silently DROPS comparisons (=== 'x'), object-property values, array elements, type-position literals, return statements, const initializers — i.e. exactly the shapes CV/rename sweeps need. Confirmed intentional (queries/string-literal-uses.ts:118-120 “call-site context search, not raw text search”), but 6 of 8 id-292 classes under-report, three to zerobun run ast-dataflow string-literal-uses --value bid_discovered → 0 vs freshness.ts:103Add kinds comparison, caseClause, propertyValue, arrayElement, typeLiteral, initializer (or an --all-contexts flag defaulting on), keeping the current set as a filter
G2output-shapeZero results carry no caveat — indistinguishable from “no sites exist”. Neither the kind-filter (G1) nor corpus membership is stated in any responseR2/R8 outputsAdd a caveats block to every query response (schema-coverage already has one): kinds searched, corpus summary, excluded-surface note
G3ergonomicscolumn-reads/column-writes on a nonexistent column return silent [] — no unknown_column (which schema-coverage does emit). “0 readers” and “column was dropped” are indistinguishable; R11 hit this live on the dropped workspaces.typebun run ast-dataflow column-reads --table workspaces --column zzz_not_realValidate table+column against database.types.ts in column-reads/writes and reuse the existing unknown_column error kind
G4detector-missingenum-uses supports enums and as-const objects but not as-const arrays — the actual KH CV convention. Live FINAL_AWARD_FORM_TYPES and VALID_PUBLICATION_STATUSES both error out_of_corpus (a misleading kind for “declared but unsupported idiom”)bun run ast-dataflow enum-uses --enum VALID_PUBLICATION_STATUSESSupport as const array declarations (members = element literals); distinguish “unsupported idiom” from out_of_corpus
G5ergonomicsfixture-uses needle is whole-token exact-match; embedded occurrences (paths, compound keys — bid_library inside "scripts/import_bid_library.py") invisible with no flag to widenfixture-uses --needle bid_library → 0 vs reference-doc-paths.json:36--substring flag (or substring default with a matched field showing the containing token)
G6corpus-boundaryKnown {377.4} tail, live-confirmed here: out-of-corpus surfaces holding class literals — scripts/seed-id145-w1f-exemplar.ts (tsconfig-excluded script; its .eq(...,'bid') args are the kind the tool WOULD catch), supabase/seed.sql, migration bodies, the squash CHECK. No query states membershipR11/R14Short term: per-response corpus caveat (G2) + a CLI corpus-info command (exists on MCP only); long term: {377.4} declarative/SQL detectors
G7performance / mcp-surfaceCold CLI pays ~3-6s project load per query (one 32s outlier under load); this 20-query trial spent ~2.5 min on redundant loads. Warm MCP server exists but is unregistered by design until extractionany two consecutive queriesFor sweep workflows, accept multiple --value args per invocation (amortise one load across a literal set) — cheaper than registering the MCP server early
G8detector-precision(added by the verification pass — not found by the trial author.) column-writes reports table-level writes under an EXACT columnPath. A .update(updateData) / .upsert({...}) whose payload never mentions the queried column still returns columnPath: "<queried column>", confidence: "indirect" — the only signal separating it from a real write is that indirect label, which column-reads also uses for genuine exact-column hits. 3 of 5 lifecycle_type writers are false: governance.ts:606 (upserts governance_review_status), governance.ts:1257 + governance/review/route.ts:231 (governance-only updateData branches). Mirror-risk: any rename sweep trusting column-writes will patch files that never touched the columnbun run ast-dataflow column-writes --table record_lifecycle --column lifecycle_type → 5 hits vs 2 real (content.ts:1226, test-data-fixture.ts:305)Resolve the payload object/variable and emit columnPath: "*" + confidence: "wildcard" when the queried column is not provably a key — i.e. give writes the same wildcard honesty reads already have
O1opportunityNo query connects a const to its literal value (LAYER_BID_DETAIL'bid_detail'): references tracks the symbol, string-literal-uses the string, nothing joins themR7references result rows for string-const declarations could carry literalValue; or extend enum-uses to plain string consts
O2opportunityLoad-bearing-vs-stale classification (R11) stays human judgment. enclosing + kind help; a comparison kind (G1) with the compared-expression text (args.type, lifecycle_type) would get most of the wayR11In a future comparison kind, include the LHS expression text so a human can bucket compat-shim vs dead comparisons quickly

Tool scorecard (this task’s shape: CV-literal rename-residual sweep)

Section titled “Tool scorecard (this task’s shape: CV-literal rename-residual sweep)”
  • column-reads / py schema-uses: the stars. Zero over-reports across spot-checks; found live lifecycle_type sites the id-292 inventory never listed; the py sidecar caught the raw-SQL pipeline read the TS tool cannot see. column-writes is the weaker sibling — same shape, but 3 of its 5 lifecycle_type hits are table-level writes dressed in an exact columnPath (G8).
  • string-literal-uses: right tool name, wrong default scope for this job. For 6 of 8 classes rg was strictly better; the tool’s exact-match + AST kinds would BEAT rg (no comment/substring noise — its correct-zero on from_bid comments is the proof) if G1’s kinds existed.
  • references: precise where a symbol exists to hang it on.
  • enum-uses: missed every probe in this trial (plain const, as-const arrays ×2).
  • Verdict pattern (post-verification): 5 clean sufficient / 8 rows carrying a gap / 1 opportunity across 14 rows. (R3 was logged sufficient (strong); the verification pass split it — reads sufficient, writes a gap.)

Independent audit by a second agent (the trial author died on a usage limit before self-checking). Read-only against canonical @ ast-dataflow-efficacy-trials, head 8b397be1, working tree clean. Every gap repro re-run; premise claims re-derived from source, not from the trial’s own tool output.

1. Gap register — 7 confirmed / 0 refuted

Section titled “1. Gap register — 7 confirmed / 0 refuted”
idresultevidence from this pass
G1✅ PASSstring-literal-uses --value bid_discovered"results":[] (6295ms). Ground truth rg: lib/freshness.ts:103 if (lifecycle_type === 'bid_discovered'), :19 union. Comparison kind genuinely absent
G2✅ PASSNo caveats key in ANY response observed this pass (7 distinct queries). Zero-result and populated payloads are structurally identical
G3✅ PASS — stronger than loggedcolumn-reads --table workspaces --column zzz_not_real → silent []. Also --table zzz_not_a_table --column id → silent []: the unknown-table case is equally silent, so the fix in G3 must validate both axes, not just the column
G4✅ PASSBoth probes error out_of_corpus. Both symbols are live as-const arrays: lib/validation/schemas.ts:1140 (FINAL_AWARD_FORM_TYPES), lib/governance/publication-transitions.ts:31 (VALID_PUBLICATION_STATUSES, consumed at lib/mcp/tools/governance.ts:960 + components/shared/publication-status-badge.tsx:78)
G5✅ PASSfixture-uses --needle bid_library[]; --needle scripts/import_bid_library.py → 1 hit, reference-doc-paths.json:36, context: requiredPaths[27]. Whole-token semantics confirmed
G6✅ PASStsconfig.json exclude contains scripts, supabase — hence seed-id145-w1f-exemplar.ts:129,141 (.eq('form_type','bid')) and seed.sql:378 are out. The trial’s own “in-corpus surprise” holds and is worth keeping: scripts/mcp-eval/functional-correctness.ts:1096 DID return, so scripts/ exclusion is not uniform — membership is genuinely unpredictable from the config alone. No corpus-info in the CLI’s Valid queries: list
G7✅ PASSCold-load spread this pass: 3938 / 4871 / 5272 / 5370 / 6295 / 7146 / 7461 / 8794 / 13510 ms. The 32s outlier did not recur, consistent with the trial’s own “under load” note
O1, O2✅ consistentBoth restate evidence already carried by R7/R11; no independent claim to falsify

Class 2 — CONFIRMED, with one over-stated number (corrected in place above). supabase/migrations/20260702130000_id131_freshness_rpcs.sql:95 reads AND rl.lifecycle_type = 'bid_discovered' — a real SQL value-compare at the stated line, so the “atomic backfill must also patch the RPC body” conclusion stands. scripts/cocoindex_pipeline/sources/l_records.py:1194 is await self._pool.fetch(_SQL_RECORD_LIFECYCLE_FOR_OWNERS, ...), and that constant (l_records.py:456-463) selects lifecycle_type — pipeline read confirmed. The facet move is confirmed by the writers themselves (.from('record_lifecycle') everywhere). But “5 readers + 5 writers” was half wrong: reads are honest, writes are not — 3 of 5 do not touch the column. See G8. The class-2 disposition is unaffected; only the surface count shrank.

Class 7 — headline CONFIRMED, one sub-claim REFUTED (corrected in place above). Confirmed: Tables<'workspaces'> (sed-range read of the generated types, Read-tool deny respected) has NO type key in Row/Insert/Update on either the table or the view block; it carries application_type_id with an FK to application_types. The column is gone. Refuted: app/api/workspaces/route.ts contains no alias mapping. Its sole 'bid' occurrence is the comment at :140 (“Map the legacy ‘bid’ input alias to the ‘procurement’ application_types row”) — the code below it does const appTypeKey = rawTypeKey and looks the key up verbatim. WorkspaceCreateBodySchema.type is z.enum(getValidTypeValues()) and lib/workspace-types.ts:23-30 returns exactly six keys, none of them 'bid', so a 'bid' body is rejected at validation and never reaches the lookup. The comment describes intent that was never implemented (or was later removed). Net effect on the disposition: class 7 is cheaper still than the trial concluded — the surviving alias is one MCP tool (lib/mcp/tools/workspaces.ts:36,76), not an MCP tool plus a REST route. Minor: app/api/items/route.ts does not merely lack 'bid', it does not exist.

rg -n "VALID_BID_STATUSES|TERMINAL_BID_STATUSES" repo-wide → zero hits. ls types/bid.tsNo such file or directory. The KEEP entry is genuinely moot. The other two KEEP entries re-confirmed live: types/reorient.ts:52,55,67 plus a 16-file bid_response network including components/dashboard/reorient-section.tsx:81,94,115 and activity-feed.tsx:116; schemas.ts:1140,1185 for the 'bid' form_types CV value.

4. Protocol-row sampling — 4 of 5 sampled rows clean, 1 refuted

Section titled “4. Protocol-row sampling — 4 of 5 sampled rows clean, 1 refuted”
rowresult
R6✅ answer exact — 20 results, every one under __tests__/, every one kind:"argument". Prod sites layer-inference.ts:48, client-config.ts:167, seed.sql:378 all absent. gap justified by its own evidence
R9✅ answer exact — []. Survivors are comment text only, so the correct-zero reading is right and sufficient is earned, not asserted
R10✅ answer exact — 1 hit, app/procurement/page.tsx:71, kind:"argument", enclosing:"fn:FormsPage", matching rg 1:1
R13✅ answer exact — both enum probes out_of_corpus while both symbols are live; the as-const-array conclusion follows from the evidence shown
R3REFUTED — the row claimed “zero over-reports” on the strength of spot-checking governance.ts:606. That upsert is real, but its payload is owner_kind + owner FK + governance_review_status: null; it never writes lifecycle_type. “Real upsert to record_lifecycle” was mistaken for “real write of lifecycle_type”. Row and scorecard corrected; new gap G8 filed

git status --short in canonical: empty. The trial was read-only as declared; no tools/ast-dataflow* edits exist, consistent with “no fix declared”. Docs-site attributable to Trial A: tasks/id-292.md (one appended Notes line — frontmatter, status and all prior Notes lines untouched) plus the untracked trials/ dir. tasks/id-396.md, tasks/id-399.md and specs/id-396-corpus-model/ are the parallel lane and were not touched by this pass.

Safe for the synthesis wave to consume, with the corrections above applied. All seven proposed tool-work items are real and reproduce, so none of the synthesis effort is wasted; G8 adds an eighth that the trial missed and that matters more than most, because column-writes was the query the scorecard told readers to trust. Both premise rewrites survive — class 2 as written, class 7 with its scope reduced to a single MCP tool.