@@ -4,6 +4,64 @@ All notable changes to dirge are documented here. The format follows
44[ Keep a Changelog] ( https://keepachangelog.com/en/1.1.0/ ) and the project
55adheres to [ Semantic Versioning] ( https://semver.org/spec/v2.0.0.html ) .
66
7+ ## [ 0.21.12] - 2026-08-08
8+
9+ ### Fixed
10+ - The gate tally never emitted ` gate_claim_gate ` or ` gate_source_gate ` . Both
11+ variants were recorded by ` record_gate ` — ` run.rs ` maps them through an
12+ exhaustive ` From<FollowUpSource> ` — and then dropped by ` emit ` , which carried
13+ a hand-written list of ` gate_* ` fields. So the ` dirge::gates ` line, the only
14+ surface that reports this, could not say whether either had fired, and the
15+ two gates that exist to catch fabricated verification were the two whose own
16+ firing was unobservable. The unit test that should have caught it iterated
17+ its own copy of that list with the same two missing, so it asserted exactly
18+ what the emitter did and stayed green.
19+
20+ ` GateSource::ALL ` / ` BoundaryNudge::ALL ` now drive the tests, ` index ` is the
21+ discriminant rather than a second hand-written mapping, and the count arrays
22+ are sized off ` ALL ` . A new variant fails to compile in ` field_name ` and fails
23+ three tests until ` emit ` carries it.
24+
25+ - Four silent bugs in the A/B reporter (` scripts/loop-ab.sh ` ), all
26+ under-reporting. The N-arm cross-model summary sat one brace inside the
27+ missing-tally branch, so it printed only when a run had failed to produce a
28+ gates line and was unreachable on a healthy one. Co-occurrence events came
29+ out in awk hash order rather than run order (` A;B;C;D;E ` reported as
30+ ` B, C, D, E, A ` ), and the selftest asserted that scrambled order as its
31+ expected value. The control/treatment comparison divided by ` n[arm] `
32+ unguarded, so a model present under one arm and absent under the other killed
33+ awk and produced no report at all — the N-arm block twenty lines below
34+ already carried that guard. And no ` gate_* ` field was scraped at all, so
35+ ` nudges_fired ` , the mechanism check, was structurally 0 for every
36+ finalization-gate A/B.
37+
38+ Also: unanimous-flat was labelled ` MIXED ` , a ` split() ` was walked against a
39+ hardcoded 10, an absent column rendered as ` (..) ` instead of announcing
40+ itself, and a blank line in ` results.tsv ` minted an arm and a model named
41+ ` "" ` that appeared in every section. ` gates_fired ` is added as column 20,
42+ appended so an older ` results.tsv ` still reports.
43+
44+ - A delegated run that hit the turn cap returned ` error_max_turns ` with
45+ ` result: "" ` , so a caller could not tell finished-but-unpolished from
46+ broken-and-abandoned — while the same envelope already carried the turn
47+ count, the changed files and the verification status. A run that ends
48+ without writing a closing answer now gets a summary built from those fields.
49+ It never replaces text the model actually wrote, never touches a successful
50+ run, and states outright that it is not a completeness verdict.
51+
52+ - The pre-write syntax gate's rejection said nothing about its own scope. A
53+ model blocked by it concluded the parser was at fault and planned to write
54+ the whole file with ` write ` instead — which goes through the same gate — and
55+ the stand-down it actually wanted (a file already failing the check before
56+ the edit is written through verbatim) was invisible from the reject path.
57+ Rejections now carry both facts. Valid doctest and macro shapes are pinned as
58+ passing, with a companion test so that check cannot go vacuous.
59+
60+ ### Changed
61+ - ` scripts/loop-ab-selftest.sh ` is a discrimination harness: every claim is
62+ checked against a fixture where the answer must be different, and the file is
63+ mutation-tested — reintroduce each bug and it must go red.
64+
765## [ 0.21.11] - 2026-08-07
866
967### Fixed
0 commit comments