pipelines/CRITIC_gates_GO-WITH-FIXES.json
JSON did not parse: Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1 (char 0)
{"type":"result","key":"v2:54636e32823c92db4c50d0d61f5a26b20e67c01a9b885ca1c3ee457b1dd84ed2","agentId":"a1a455a0b90e323fa","result":{"verdict":"GO-WITH-FIXES","findings":[{"target":"gates/gates_enrollment.md §2/§4/§5 vs check_arc_continuity.py L990 + check_promise_ledger.py L624 (FIX_DIR)","severity":"MAJOR","problem":"THE LANDING PROCEDURE AS WRITTEN BRICKS BOTH GATES. Both checkers hard-code `FIX_DIR = Path(__file__).resolve().parent / \"fixtures\"`, which resolves to `harness/fixtures/` once the scripts land in `harness/`. The enrollment's file table, its DOC_MAP row, and its §5 step-6 hand-run command all send the 7 CSVs to `harness/fixtures/arc_schema/`. Landing exactly as documented makes `fixture_path()` raise on the very first `--self-test` at step 2, and every subsequent suite run exits 2 with 'fixture ... is MISSING'. The doc's only guard is inverted — it says 'if the fixtures land anywhere OTHER than harness/fixtures/arc_schema/, change the one line', when it is that documented destination that is wrong. Verified: `harness/fixtures/` does not exist in the repo, so nothing masks it.","fix":"Pick the namespaced path and make the code match: change both to `FIX_DIR = Path(__file__).resolve().parent / \"fixtures\" / \"arc_schema\"`. That keeps §2/§4/§5 and the DOC_MAP row correct as written and avoids dumping arc fixtures into a bare `harness/fixtures/` that future gates will share. Then re-run both `--self-test`s from the landed location before step 3, not after."},{"target":"check_arc_continuity.py L394-411 `is_anchor_disambiguated` / `carried_cells` (T-9 ANCHOR half)","severity":"MAJOR","problem":"T-9's headline number counts a MECHANICAL DEFAULT as authored judgement, and then punishes the real judgement with exit 2. 421 of the 443 ANCHOR-disambiguation cells carry `derivation_source` reading verbatim 'default ANCHOR->PRESENT; the SEED|PRESENT disambiguation is NOT derivable and is the arc pass one-time authored work' — i.e. the schema itself declares these are NOT yet authored. Two consequences, both proven by simulation against the live grid: (a) an identical re-derivation reproduces all 421 by the same default rule, so 95% of T-9's protected surface cannot fire on the loss it exists to catch; (b) when the arc pass does the real read and correctly reclassifies a cell to any enum member outside {SEED,PRESENT} — DORMANT is legal and is T-3's own vocabulary — `is_anchor_disambiguated` stops counting it, the count falls 443->440, and the gate reports CARRY-FORWARD LOSS and exits 2 on correct work (SIM1: loss=True, 4 loss findings). The enrollment's DOC_MAP row compounds this by describing the 443 as authored cells.","fix":"Split the population. Only count a cell as CARRIED when its `derivation_source` records a HUMAN resolution (e.g. a distinct token such as `ANCHOR->SEED|PRESENT [arc-pass read]`), never the `default ANCHOR->PRESENT` string; carry the 421 defaults as a separately-reported DERIVED-DEFAULT count that is not baselined. Widen the resolved-set from {SEED,PRESENT} to 'any member of the closed enum other than the default', so a legitimate re-read cannot read as a loss. Correct the DOC_MAP one-liner to say '25 authored stage_role + 421 ANCHOR defaults awaiting the arc-pass read + N resolved'. Add a must-fire fixture for the DORMANT re-read case asserting it does NOT fire."},{"target":"check_arc_continuity.py L655-722 `r_t9_carry_forward` + `conflict_flag` (GRID_COLUMNS L235)","severity":"MAJOR","problem":"T-9 enforces only half of the §2.5 contract. It detects DROPS (a triple that held a value now holds none) but is completely blind to OVERWRITES. §2.5's COLLISION RULE — 'the emitter keeps the authored value and emits a CONFLICT row; it never overwrites and it never silently drops' — is quoted inside T-9's own finding text and enforced by no ruler. Proven: overwriting ARC_0001's authored `stage_role` BIRTHRIGHT/SEED with the map-consistent ABSENCE/PRESENT passes T-9 green (loss=False) AND produces no new S-1 finding, because S-1(c) only checks stage_role-vs-progression_stage consistency, which a re-derivation preserves by construction. `conflict_flag` is a pinned column, blank on all 1231 rows, and read by zero rulers anywhere in either gate. A silent overwrite of the judgement layer is exactly the data-loss event §2.5 exists to make impossible.","fix":"Extend T-9's baseline manifest to record the VALUE per triple, not just its presence, and add a T-9(c) clause: a carried triple whose value CHANGED across the baseline must carry a non-empty `conflict_flag`, or it is an ERROR at the same exit-2 severity as a drop. Add a must-fire fixture (`arc_grid_post_regen_MUSTFIRE_overwrites_stage_role.csv`) and a must-not-fire twin where the same overwrite carries `conflict_flag`. Also add a ruler that reads `conflict_flag` at all, so a pinned column is not a rot surface."},{"target":"check_arc_continuity.py L1446-1500 `run()` — `--pre-regen`/`--post-regen` read only in the populated branch","severity":"MAJOR","problem":"THE CHARTER'S NAMED MUST-FIRE CAN BE INVOKED AND SILENTLY IGNORED. `pre_rows`/`post_rows` are only read inside the `else` (all three arc registries populated) branch. Verified against the live repo, where the arc registries have not landed: `check_arc_continuity.py --pre-regen fixtures/arc_grid_pre_regen.csv --post-regen fixtures/..._MUSTFIRE_drops_stage_role.csv` exits **0 PENDING**, never compares the two files, and does not even print the T-9(b) NOT-ARMED note (that note is generated inside `r_t9_carry_forward`, which is never called). The enrollment offers precisely this state as a supported option (§3: land the gates first with `targets: []`), and §5 step 6 is the hand-run that is supposed to prove T-9 at the process level — it would pass vacuously.","fix":"Hoist the pre/post comparison out of the registry-state branch: if a `--pre-regen`/`--post-regen` pair is supplied, run T-9(b) unconditionally and let a loss exit 2 regardless of registry state. If the pair is supplied but the branch is skipped for any other reason, raise GateError rather than exiting 0. Add a process-level self-test asserting exit 2 for the pair against an absent-registry repo."},{"target":"check_arc_continuity.py L1446-1465 (PENDING/empty branch skips `r_unarmed` and `r_t9_carry_forward`)","severity":"MAJOR","problem":"PASS-ON-EMPTY DOES NOT DECLARE NOT-ARMED PER UNARMED ASSERTION. Charter L706 requires 'PASS-on-empty with LOUDLY DECLARED NOT-ARMED per unarmed assertion every run'. In the PENDING (all-absent) and FAIL (empty/partial) branches, `r_unarmed()` and `r_t9_carry_forward()` are never called, so the per-ruler arming declarations for T-7, A3, A4's companion half, A5, A6, T-3's dormancy debt, T-8's density debt and T-9(b) are all absent. Verified: the PENDING run prints 8 notes, none of them per-ruler — only one blanket 'PENDING MINT' line. The RULER ROSTER block does print every ruler, but it prints the ruler's CLAIM and a severity tier, never its armed/not-armed state, so a reader of a PENDING scorecard cannot tell which of the 18 rulers asserted anything. This is the noisy-gate-conceals class inverted: the loud line hides eight quiet ones.","fix":"Call `r_unarmed(IDX_COLUMNS, GRID_COLUMNS, [], [])` (or a header-only variant) in every branch so the arming notes and their arming conditions print unconditionally, and emit the T-9(a)/(b) arming notes from `run()` rather than from inside the ruler. Better: add an ARMED/NOT-ARMED column to the RULER ROSTER block so every run states, per ruler, whether it asserted anything — that single change makes the requirement structurally satisfied instead of branch-dependent."},{"target":"check_arc_continuity.py L725-744 `r_a1_a2_a4` — `if not spec: continue` precedes the scope counters","severity":"MAJOR","problem":"THE A1 'DECLARED SCOPE' NOTE IS A FALSE ACCOUNTING. The scope counters (`typed_multi`/`typed_single`/`untyped`) are incremented only after `spec` resolves, so