@dzhechkov/harness-core 0.8.37 → 0.8.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.dz-manifest.json CHANGED
@@ -9,7 +9,7 @@
9
9
  },
10
10
  {
11
11
  "path": "README.md",
12
- "sha256": "8681fe97661ea41329df7e8d544edb9a9eb71bd563888dfcdf56c593e8f8de0f"
12
+ "sha256": "e245c714c75f1e53186aea9bb38c0767c72a1911c4abc3066f270d206ae410b7"
13
13
  },
14
14
  {
15
15
  "path": "dist/__tests__/golden-baseline.test.d.ts",
@@ -685,19 +685,19 @@
685
685
  },
686
686
  {
687
687
  "path": "dist/cross-family-control.d.ts",
688
- "sha256": "2bc158ecece08d9d3d829adadcfc82f309dd7921633cee96d808d9d3d24ac09b"
688
+ "sha256": "d8c21e3cc28e57c56ee22b4eee3dff98c0fbf0b6df93f5b8acbacf1c787b16f1"
689
689
  },
690
690
  {
691
691
  "path": "dist/cross-family-control.d.ts.map",
692
- "sha256": "87284ca644e3b95d796ffba24095b2b70572e160246841f00b65c9ce2904307b"
692
+ "sha256": "b8489d2f519a9090ad9053b9613952a733e258b6b58509a4a213534d8d987b4d"
693
693
  },
694
694
  {
695
695
  "path": "dist/cross-family-control.js",
696
- "sha256": "61916c0c548741c299867992856f74289575f730c2bf541b24cac95d503c1135"
696
+ "sha256": "046fa4d0e04a56b3b8dd6bba542de25a99b000f03f27e9d192046043702e1213"
697
697
  },
698
698
  {
699
699
  "path": "dist/cross-family-control.js.map",
700
- "sha256": "eb25f9acdb75bfd6414d78c5d3d4eaf9c7ee55fdc473572e555f55d0ede1a269"
700
+ "sha256": "36cb70ec0ea7b130e420c3c1baea0543958b5fbeb9d75e6f8064eedd7df89a25"
701
701
  },
702
702
  {
703
703
  "path": "dist/debt-ratchet.d.ts",
@@ -977,15 +977,15 @@
977
977
  },
978
978
  {
979
979
  "path": "dist/feature-adr-routing.d.ts.map",
980
- "sha256": "08e2f3281134246237badf405152df859b8c2b3c798bbf96e6a2cdea4e697551"
980
+ "sha256": "fc45729c438028377eb646c9c66eaa5bee82ed37aa08bc2d14db64e3eb3cdc29"
981
981
  },
982
982
  {
983
983
  "path": "dist/feature-adr-routing.js",
984
- "sha256": "5beac314ff52187934da91d8243e417156cb73f214c4bd6485ef1f7c4948f138"
984
+ "sha256": "5a3de377ea062c34da34b2ed315997fdaf11a4ea90ce70c2f867294e87fb53bc"
985
985
  },
986
986
  {
987
987
  "path": "dist/feature-adr-routing.js.map",
988
- "sha256": "0952a178e1942572adfde30c7449f8f11c6d57265fac93607cacb1cb9829d3f5"
988
+ "sha256": "d5db78aadc6c4ae5482b44513e873b36347b1a5b9888c3bb18be9e49dff6b34b"
989
989
  },
990
990
  {
991
991
  "path": "dist/feature-adr-setup.d.ts",
@@ -1117,19 +1117,19 @@
1117
1117
  },
1118
1118
  {
1119
1119
  "path": "dist/index.d.ts",
1120
- "sha256": "5f0446cc54af8c90e525bad6189f93873ad1592c1d1824c18a2f0991dd5959b9"
1120
+ "sha256": "5323d4f19ccbcf5929a61a4849b17e0cb39c248205a14967610dda85635c6ddb"
1121
1121
  },
1122
1122
  {
1123
1123
  "path": "dist/index.d.ts.map",
1124
- "sha256": "62df6d6a2f6f861b8485e9890c272a335cc9d697bee4064faa83de36fe3d7413"
1124
+ "sha256": "9371106d16ee809aa6bc19410087491840654c1f268f20928b8ccc148a52e7fd"
1125
1125
  },
1126
1126
  {
1127
1127
  "path": "dist/index.js",
1128
- "sha256": "3d61d50137956af1322911629db07737f857d81fde22cf1594dfc7aed5b03341"
1128
+ "sha256": "8c1913c94939655785cf62534d8c61bbb8a0e85d8e77f432de4ecabb73ac6d73"
1129
1129
  },
1130
1130
  {
1131
1131
  "path": "dist/index.js.map",
1132
- "sha256": "ffe8d30bc1182349088f2712a080cfb32240111a874c3fd3217a0e123953f2d7"
1132
+ "sha256": "7a46163decf648a7214ece84dadad0bef80fbc47d0f9f123f6cab475da40e1ba"
1133
1133
  },
1134
1134
  {
1135
1135
  "path": "dist/integration-apply.d.ts",
@@ -1805,11 +1805,11 @@
1805
1805
  },
1806
1806
  {
1807
1807
  "path": "dist/qe-bridge.d.ts",
1808
- "sha256": "7bf6a9f4bbd79eef7858d3a5e073fbb897d7af01accf9257894635664432f449"
1808
+ "sha256": "f1964fe74508371aa7abc3ac067310e141548d3a5f99e1ebfecf8403d5e68daa"
1809
1809
  },
1810
1810
  {
1811
1811
  "path": "dist/qe-bridge.d.ts.map",
1812
- "sha256": "73072e6cd684b6bb3638881f3719f1dd39b8d1b02c9ea1f2a1efb8b3ebfa6a83"
1812
+ "sha256": "1b323500c270897632a1322a07bf5955eaf08e67421096f573913ac3c6723322"
1813
1813
  },
1814
1814
  {
1815
1815
  "path": "dist/qe-bridge.js",
@@ -1817,7 +1817,7 @@
1817
1817
  },
1818
1818
  {
1819
1819
  "path": "dist/qe-bridge.js.map",
1820
- "sha256": "f5bf74e490c5a0f0b49042fa89f2c9156dae1ca60fdef822a14be21f8fc378a8"
1820
+ "sha256": "1f3d05d697dc24e811ea5ef194439c4b25d992dbf01b8600d266091ea5a52aeb"
1821
1821
  },
1822
1822
  {
1823
1823
  "path": "dist/qe-findings.d.ts",
@@ -2091,6 +2091,22 @@
2091
2091
  "path": "dist/retro.js.map",
2092
2092
  "sha256": "dbf3dd2ad4597b1a065629c1db6c313fd9b837db4368b798982a78a7245e760b"
2093
2093
  },
2094
+ {
2095
+ "path": "dist/review-cost.d.ts",
2096
+ "sha256": "0488bbb484a390759262da1244870b2f3df10abe96169d7604e8585a618d7fd2"
2097
+ },
2098
+ {
2099
+ "path": "dist/review-cost.d.ts.map",
2100
+ "sha256": "0a533e5d143a0e0ab3cc84c6218dfed3c8dfe3c86f3af25835f4d700b0fb1527"
2101
+ },
2102
+ {
2103
+ "path": "dist/review-cost.js",
2104
+ "sha256": "5daa7df5632004fc6ccf4fd30425787c7ea7780ab1f2af183eafe4055d87fa36"
2105
+ },
2106
+ {
2107
+ "path": "dist/review-cost.js.map",
2108
+ "sha256": "3c8abfadf727f2b4dfea0cdc58af3a7c73765a0636417e061fb1e010419fec6c"
2109
+ },
2094
2110
  {
2095
2111
  "path": "dist/risk-scoring.d.ts",
2096
2112
  "sha256": "a40c687718dee46c83e2babf2862cb43b3dcbe80b021d91339486b609a446b43"
@@ -2125,19 +2141,19 @@
2125
2141
  },
2126
2142
  {
2127
2143
  "path": "dist/round.d.ts",
2128
- "sha256": "c952216bc03cd2f8ad1d2b17ba5402b70bc8f1ec9d9557b25b3b064192ff56be"
2144
+ "sha256": "3f789196d023177040a7d0c4e2690186855714a8139b6d995b85022588bc6cbd"
2129
2145
  },
2130
2146
  {
2131
2147
  "path": "dist/round.d.ts.map",
2132
- "sha256": "dee616e546cfa366853c82707046b4cf2463f522632c121e584c1231efd299ef"
2148
+ "sha256": "93610d2de68db800c91478697a10864e3dd438d25b7b45aeeae62a3d5a5438f0"
2133
2149
  },
2134
2150
  {
2135
2151
  "path": "dist/round.js",
2136
- "sha256": "6f746137311636982727ba5da3ae62f625cc19b2b78f1ebaa056017dbf91d6c8"
2152
+ "sha256": "c731c0fb46ba714cba4dc7b8129245de73c81d21a0e585053d16199ffd7b3011"
2137
2153
  },
2138
2154
  {
2139
2155
  "path": "dist/round.js.map",
2140
- "sha256": "1d5c036f42c6d213b11a7441f3ab52186cd8e94a9aed9f5ce2b3ce2548c576ba"
2156
+ "sha256": "d4ba5ce8b11a36075d103d3be3d0785bd521ffc0fd5f0bfc1a0a9138410e730c"
2141
2157
  },
2142
2158
  {
2143
2159
  "path": "dist/routing-outcomes.d.ts",
@@ -2173,19 +2189,19 @@
2173
2189
  },
2174
2190
  {
2175
2191
  "path": "dist/run-records.d.ts",
2176
- "sha256": "ea6253a14d0eeb65ab671a989f44876088752a64e86fb7a2c6631f43aba552f4"
2192
+ "sha256": "4c139f0e13f547a8e03bcfbfb7530c9f0862c2b6d80d7d1f9d9fcd4dd4512c96"
2177
2193
  },
2178
2194
  {
2179
2195
  "path": "dist/run-records.d.ts.map",
2180
- "sha256": "72922e63f075128f46650dd7484db67571777dfc3a388e39efe2e7dad7ac007c"
2196
+ "sha256": "7085330bdcd668ab0a8e881ecb4f0737fe1e6cb360fa4fc58093e5e5b02ad6c4"
2181
2197
  },
2182
2198
  {
2183
2199
  "path": "dist/run-records.js",
2184
- "sha256": "c4931905e808eb7cab306b5e3d350bd255b9a0633d6de2c8827e511b3cc91a29"
2200
+ "sha256": "edc206a0b721e8248945f24c2654efd6f425fa8daa4a3d8760575569117ec1ee"
2185
2201
  },
2186
2202
  {
2187
2203
  "path": "dist/run-records.js.map",
2188
- "sha256": "b395c6d2d0f423a361ef0539a66ae84268537461877506739d9e3f58a6739a23"
2204
+ "sha256": "ccfa3e492154b92519af8cf40ab9af7f4710135020776775b8cdb3be208626a2"
2189
2205
  },
2190
2206
  {
2191
2207
  "path": "dist/run-registry.d.ts",
@@ -2829,7 +2845,7 @@
2829
2845
  },
2830
2846
  {
2831
2847
  "path": "package.json",
2832
- "sha256": "9307673a743a014bc30e5897796a0dff724152f9325946aee2b4d35c05f91820"
2848
+ "sha256": "e9095406e69e4c15757b0489f742834880a940176c106aaeb7e9bab561dc87c0"
2833
2849
  },
2834
2850
  {
2835
2851
  "path": "src/__tests__/golden-baseline.test.ts",
@@ -3001,7 +3017,7 @@
3001
3017
  },
3002
3018
  {
3003
3019
  "path": "src/cross-family-control.ts",
3004
- "sha256": "e1e447ab6cda0afb4518c73e482dda7bb27a6fe676e65999b8077da3b8c0a8b3"
3020
+ "sha256": "9236bcc66ddfa060fae3a7dfee4324f5a9a7c687aca109f6578d2d01bf34acb4"
3005
3021
  },
3006
3022
  {
3007
3023
  "path": "src/deadwood-allowlist.json",
@@ -3077,7 +3093,7 @@
3077
3093
  },
3078
3094
  {
3079
3095
  "path": "src/feature-adr-routing.ts",
3080
- "sha256": "89458ef7d48bfca28cab0cb47da3c97d65c3730b92005cae742f4414c27f84dc"
3096
+ "sha256": "8811f5256553c85d4f9fd5c817621467e3b492d8b3e1640e74c4b23b07320a16"
3081
3097
  },
3082
3098
  {
3083
3099
  "path": "src/feature-adr-setup.ts",
@@ -3113,7 +3129,7 @@
3113
3129
  },
3114
3130
  {
3115
3131
  "path": "src/index.ts",
3116
- "sha256": "94826e7248002d6b3ae5cf1909018d0ee17a0eecb22c310dc0d94589ab74c5ad"
3132
+ "sha256": "54ec8ac2203964a7a409bf2987557c784137d9b0c9caf213db994b1b4784986a"
3117
3133
  },
3118
3134
  {
3119
3135
  "path": "src/integration-apply.ts",
@@ -3289,7 +3305,7 @@
3289
3305
  },
3290
3306
  {
3291
3307
  "path": "src/qe-bridge.ts",
3292
- "sha256": "b8f881ddfa3c8c597b6c09d8e70f6548eb2318adf74b80d4942e6e665bb936cf"
3308
+ "sha256": "dc21e4af7023dbc15c19e951ed8b99228bcba8916cfe713cadc9694a10a8308b"
3293
3309
  },
3294
3310
  {
3295
3311
  "path": "src/qe-findings.ts",
@@ -3355,6 +3371,10 @@
3355
3371
  "path": "src/restart-advisor.ts",
3356
3372
  "sha256": "ba13e85c465aada9ddded13bc679f3df30bdbe839d92e19b9f231eabbefecdf8"
3357
3373
  },
3374
+ {
3375
+ "path": "src/review-cost.ts",
3376
+ "sha256": "e4442e3732c494f700f3e651a6416c3c47136c3c1cb9c9ad86237326f5ae7894"
3377
+ },
3358
3378
  {
3359
3379
  "path": "src/risk-scoring.ts",
3360
3380
  "sha256": "dcae3c879f114899b12ca57324e776e083f5753590763647690de1fdbd40198d"
@@ -3365,7 +3385,7 @@
3365
3385
  },
3366
3386
  {
3367
3387
  "path": "src/round.ts",
3368
- "sha256": "ea21e8206f9a571a36a90f3219459466b94d0c5c3ea9c600979bcd07b015ed2d"
3388
+ "sha256": "33f933caad731cb20c95d04075214a308b024399b36259f6fb1bd2ce742ffef3"
3369
3389
  },
3370
3390
  {
3371
3391
  "path": "src/routing-outcomes.ts",
@@ -3377,7 +3397,7 @@
3377
3397
  },
3378
3398
  {
3379
3399
  "path": "src/run-records.ts",
3380
- "sha256": "73fe9ae5a262a30e2351df434e8e586090b660e4f9968e094c42f79ec169471a"
3400
+ "sha256": "f18c291059dcf8d8e103df61ef9a175b85763ec946a0fb9a26ed15613dc3f568"
3381
3401
  },
3382
3402
  {
3383
3403
  "path": "src/run-registry.ts",
@@ -3545,5 +3565,5 @@
3545
3565
  }
3546
3566
  ]
3547
3567
  },
3548
- "signature": "hvPXsT3dnA+lSjn0UDbA1K8muYwF0SXkielu/WDnDrqDPktFb79ZTOyb9E2zF8APOnk2KZaY3XPxY9tDB1i4Cg=="
3568
+ "signature": "a9JPU5bWc75LnviZIU5Eex58gOVjZJanfbTuuDc3jcwQ5nkjzKtvzUSuH9EVuB/OMW8oTh7t/yCdb395UKjgDw=="
3549
3569
  }
package/README.md CHANGED
@@ -1448,6 +1448,120 @@ surface on top of the prose, never replacing it:
1448
1448
  producer-side prompt wiring (Step 8's `QE-VERDICT:` + table instruction) and the six September
1449
1449
  reports hand-marked from their own prose as the real-corpus proof (`qe-findings-corpus.test.ts`).
1450
1450
 
1451
+ ## Task identity (`round.ts` + `run-records.ts`, experiment-instrument ADR-001)
1452
+
1453
+ A prospective audit found no task identifier joining ledger rows, review signoffs, control rows and
1454
+ training pairs across the whole pipeline — three writers each minting (or not minting) their own idea
1455
+ of "what this run was about". This feature mints `taskId` in exactly ONE place and propagates it
1456
+ fill-only-null everywhere else, never guessing.
1457
+
1458
+ - **`openRound` mints it.** A new optional `RoundState.taskId`/`RoundLedgerRow.taskId` field: `--task
1459
+ <id>` (validated — non-empty, ≤120 chars, no control characters, else `{ok:false, exit:2}`), or the
1460
+ default `<slug>@<startedAt>`. `closeRound` copies it into every row; a state predating this feature
1461
+ carries no `taskId` key on disk, so `closeRound` derives the SAME default and marks
1462
+ `taskIdSource:'derived-legacy'` on the row (never on a fresh round's row).
1463
+ - **`closeRound` also carries the ship anchor**, entirely as CLI-supplied data (the core never shells
1464
+ out — `git rev-parse` lives in the cli): `shipSha: string | null` and an optional `shipShaReason`,
1465
+ present only for a FINISHED outcome (`shipped|refuted`), alongside `shippedAt` (= this row's own
1466
+ `closedAt`). A finished row with a null sha ALWAYS carries a non-empty `shipShaReason` (`'not
1467
+ provided'` when the caller gave none). Because `HEAD` does not identify a dirty working tree, the
1468
+ same row also carries `shipTreeDirty: boolean` (resolved by the cli from `git status --porcelain`)
1469
+ or, when unresolved, a non-empty `shipTreeDirtyReason` (`'not provided'` default) — one of the two is
1470
+ always present on a finished row; a dirty tree is recorded, never refused. `blocked|abandoned`
1471
+ carries none of these, even when a sha is passed in.
1472
+ - **`readOpenRoundTaskId(states, slug)`** is the single pure lookup every OTHER writer in the pipeline
1473
+ consults: `{taskId, source}` where `source` is `'open-round'` (exactly one match — `taskId` is its
1474
+ own), `'derived-legacy'` (exactly one match whose state predates this feature — the default is
1475
+ derived, and the label says so), `'no-open-round'` (zero matches — `taskId: null`), `'ambiguous'`
1476
+ (two or more, or a readable state next to an unreadable one — `taskId: null`, never "the latest
1477
+ wins") or `'unavailable'` (only unreadable matching state files — an unreadable round is never read
1478
+ as absence). It does not touch a filesystem; the caller (the cli, walking `.dz/rounds/<slug>-*.json`)
1479
+ hands in the already-read states plus the count it could not parse.
1480
+ - **`applyTaskId(row, lookup)`** (`run-records.ts`) fills a ledger/training-pair payload's `taskId`
1481
+ from that lookup, fill-only-null: a payload that already names a non-empty `taskId` is left alone
1482
+ UNLESS it disagrees with `lookup.taskId`, in which case the disagreement is recorded as
1483
+ `taskIdConflict: {payload, round}` (the payload's own value still wins — never silently overwritten).
1484
+ A payload with no `taskId` is filled, including the honest `null` case (`taskIdSource` names why) —
1485
+ absence with a reason beats silent absence.
1486
+ - **`decideRecordWrite` also judges AUTO-ROW COMPLETENESS.** For `kind:'ledger'` with `auto:true`:
1487
+ `minutes` is fill-only-null from a payload `wallSec` (`minutes = round(wallSec/60, 1)`,
1488
+ `minutesSource:'wallSec'`); a row that ends up with neither a real `minutes` nor a resolvable
1489
+ `tokens` (no number, no `tokensSource`) is written `complete:false` with `incompleteReasons`
1490
+ (`['minutes']`, `['tokens']`, or both) — new optional `strict?: boolean` turns that into a refusal
1491
+ (`exit 2`, nothing written) instead. A manual (non-`auto`) row gains none of these three keys, ever.
1492
+
1493
+ All new fields are additive, appended after every existing key (NFR-1) — every pre-existing test of
1494
+ `round.ts`/`run-records.ts` keeps passing unmodified except the handful of exact key-order/exact-value
1495
+ assertions that the new fields legitimately extend (documented in the feature's own change manifest).
1496
+
1497
+ ## Reviewer price on the round ledger row (`review-cost.ts`, ADR-001 `review-cost-ledger`)
1498
+
1499
+ Cross-family review cost sat un-tracked: the qe-bridge reviewer's own price (Claude CLI's
1500
+ `total_cost_usd`/`usage.*`/`duration_ms` on the last line of its raw stdout) was measured 40/40 for
1501
+ every recent signoff, but nothing in `harness-core` read it, and a finished (`shipped|refuted`) round
1502
+ could close with no reviewer named at all — unmeasurable by definition.
1503
+
1504
+ - **`parseQeBridgeStdoutCost(text)`** (new module `review-cost.ts`, exported from `index.ts`) is a
1505
+ pure parser over the reviewer's raw stdout TEXT (this module owns no filesystem access — NFR-2). It
1506
+ reads the LAST non-empty line and returns `{status:'ok', costUsd, tokens:{input, output,
1507
+ cacheCreation, cacheRead, total, tokensPartial?}, durationMs, numTurns}` for a usable JSON cost
1508
+ object, `{status:'absent', reason?}` when there is no non-empty line at all, or
1509
+ `{status:'unparseable', reason}` for anything else (not JSON, not an object, a missing/negative/NaN
1510
+ `total_cost_usd`) — a price is NEVER guessed from an absence, and a missing usage component becomes
1511
+ `0` with `tokensPartial:true` rather than silently blending with a genuine zero.
1512
+ - **`closeRound` gains `reviewSidecar.cost?: QeBridgeCost`** and, on the row, `reviewerCostUsd`,
1513
+ `reviewerTokens` (sum of the four components — `null` when the sidecar's own sum was only partial,
1514
+ see below), `reviewerTokensBreakdown:{input, output, cacheCreation, cacheRead, partial?}`,
1515
+ `reviewerCostSource:'qe-bridge-stdout'|'unavailable'` and (only when `'unavailable'`)
1516
+ `reviewerCostReason` — additive, appended after every existing key (NFR-1; a round closed with no
1517
+ sidecar is byte-identical to before this feature).
1518
+ - **Reviewer identity is TIED to the sidecar (ADR-001 п.2, amended by fix-round-1 after Codex r1's
1519
+ BLOCKER/CRITICAL pair) in exactly two cases**: (a) `reviewer` is FILLED from the sidecar (no explicit
1520
+ `--reviewer`), or (b) an explicit `--reviewer` AGREES with the sidecar's own `gradedBy`
1521
+ (case-insensitive `family:model` equality, or a family-only flag like `claude` matching the sidecar's
1522
+ family) — `reviewSource:'flag+qe-bridge'` names that second case. This exists because the pipeline's
1523
+ normal path ALWAYS passes `--reviewer` (`.claude/workflows/feature-adr.js`); under the pre-fix-round-1
1524
+ rule ("an explicit reviewer never trusts the sidecar for cost, ever") the price this feature exists
1525
+ to record was never written on that path. A `--reviewer` that DISAGREES with a real sidecar `gradedBy`
1526
+ is refused outright, `exit 2`, naming both values — the same discipline `--grade` already follows
1527
+ against a disagreeing sidecar grade — never a silent win and never a silent price omission. The price
1528
+ itself is written ONLY in the two tied cases above; a sidecar that is NOT tied to the row's reviewer
1529
+ (an empty `gradedBy` — the CLI's Codex-no-signoff synthesis, below) can still explain a NAMED limit,
1530
+ but an `'ok'`-status price on an untied sidecar is NEVER attributed, whatever the number.
1531
+ - **A finished review now REQUIRES a reviewer.** `closeRound` refuses `exit 2` (reason names
1532
+ `--reviewer`) for `outcome ∈ {shipped, refuted}` with neither an explicit `--reviewer` nor one filled
1533
+ from the sidecar — a row with no reviewer named is not auditable. `blocked|abandoned` carry no such
1534
+ requirement, same as they carry no grade requirement.
1535
+ - **A partial token sum never reads as an exact zero.** When the sidecar's own `tokens.tokensPartial`
1536
+ is `true` (at least one usage component was missing from the source JSON), `reviewerTokens` is `null`
1537
+ and `reviewerTokensBreakdown.partial` is `true` — the price stays valid, only the aggregate count is
1538
+ withheld, so a genuinely partial review is never indistinguishable from a real zero-token one.
1539
+ - **Every token component is validated as a nonnegative safe integer** (`Number.isSafeInteger`); the
1540
+ four-way sum is checked the same way. A component that is fractional, negative, or beyond
1541
+ `Number.MAX_SAFE_INTEGER` (adversarial: `1e308`) turns the WHOLE result `unparseable` with a reason
1542
+ naming the offending field, rather than silently overflowing into `Infinity` (which used to serialize
1543
+ as `null` on the row while the in-memory type still claimed `number`).
1544
+ - **The cli (`readQeBridgeCostSidecar`, next to `findQeBridgeSignoffForRound`)** reads a found
1545
+ signoff's own `rawStdoutFile` (repo-relative, resolved from the project root; an absolute path or one
1546
+ escaping the root via `..` is rejected, never read) and parses it best-effort — never throws; any
1547
+ failure (removed file, unreadable, unsafe path) becomes `reviewerCostSource:'unavailable'` with a
1548
+ named reason, same as an intact-but-unparseable stdout. The path is additionally scoped to THIS
1549
+ round's own `features/<slug>/.fa-state/qe-bridge/signoff-<runId>.stdout.txt` — a path that resolves
1550
+ inside the repository root but under a DIFFERENT slug's directory, or names a DIFFERENT signoff's
1551
+ runId, is rejected before it is opened (never another review's price silently imported), followed by
1552
+ an `lstat` check that the resolved file exists and is not a symlink. A Codex reviewer supplied via
1553
+ `--reviewer` with NO matching bridge signoff (structural — Codex reviews leave none, so their tokens
1554
+ are never visible to this instrument) still gets an honest `reviewerCostSource:'unavailable'` +
1555
+ `reviewerCostReason:'no qe-bridge signoff for this round (codex tokens are not visible to the
1556
+ instrument)'`, while the reviewer identity itself stays the flag's value.
1557
+ - **The `/feature-adr` conveyor's own `round close` now passes `--grade` and `--reviewer`.** Measured
1558
+ 2026-09-17: `roundCloseCmd` in `.claude/workflows/feature-adr.js` (and its packaged twin) omitted
1559
+ BOTH since the measurement-integrity feature made `--grade` mandatory — every ultracode-graded run
1560
+ closed with `roundClosed:false`. Both flags are now built from the same values the run already
1561
+ computed (`roundGrade`, `modelsUsed.qe || qeReviewerUsed`); the wiring test pins the literal
1562
+ construction and reproduces the regression as a RED/GREEN mutation (removing either flag on one twin
1563
+ fails the pin).
1564
+
1451
1565
  ## Status
1452
1566
 
1453
1567
  `next` — staged, not yet versioned or published. Feature `measurement-integrity` (ADR-001, tier M): five
@@ -250,8 +250,34 @@ export type BuildControlRowResult = {
250
250
  * partial-measurement fact, not a total failure) — they land in `refused`/`complete:false` instead.
251
251
  */
252
252
  export declare function buildControlRow(input: BuildControlRowInput): BuildControlRowResult;
253
+ /**
254
+ * experiment-instrument FR-4/A6 (ADR-001): a `stage:'control'` row `dz control-review` writes when
255
+ * the run REFUSED before producing a diff — the failure-leaves-a-row half of the ADR's safety
256
+ * property. Distinguished from {@link ControlLedgerRow} by `outcome:'refused'`, which a successful
257
+ * row never carries; the two schemas share nothing else structurally on purpose — a refused run has
258
+ * no diff, no tree hashes, no grades to validate.
259
+ */
260
+ export interface ControlRefusedRow {
261
+ readonly slug: string;
262
+ readonly stage: 'control';
263
+ readonly outcome: 'refused';
264
+ /** Which half was responsible: the claude review, the codex review, or neither (a setup/tree/
265
+ * aggregation failure that belongs to neither half specifically). */
266
+ readonly half: 'claude' | 'codex' | 'setup';
267
+ readonly reason: string;
268
+ readonly runId: string;
269
+ /** Known only when the coder family was determined before the refusal — absent for the earliest
270
+ * failures (the claude half itself failing before it can report which family it reviewed). When
271
+ * present, the SAME `bucket(coderFamily, reviewerOfInterest)` a successful control row uses. */
272
+ readonly coderFamily?: 'codex' | 'claude';
273
+ readonly minutes: number | null;
274
+ }
253
275
  export interface ParsedControlRows {
254
276
  readonly rows: readonly ControlLedgerRow[];
277
+ /** experiment-instrument FR-4/A6: `stage:'control'` rows with `outcome:'refused'` — a run that
278
+ * never produced a diff. Schema-validated (`isValidControlRefusedRow`) the same way `rows` is;
279
+ * one that fails validation is `unreadable`, same as a malformed successful row. */
280
+ readonly refusedRows: readonly ControlRefusedRow[];
255
281
  readonly roundRows: readonly Record<string, unknown>[];
256
282
  readonly fullRows: readonly Record<string, unknown>[];
257
283
  /** A8: a line that is not parseable JSON, or parses to something that is not a plain object, or
@@ -314,6 +340,10 @@ export interface FamilyPairAggregate {
314
340
  readonly final: string;
315
341
  readonly finals: number;
316
342
  }>;
343
+ /** experiment-instrument FR-4/A6: `stage:'control'` rows refused for THIS pair (attributed by a
344
+ * known `coderFamily` on the refused row) — a real cost line (the run was attempted and failed),
345
+ * never counted in `n` (which measures completed reviews). */
346
+ readonly refusedRuns: number;
317
347
  }
318
348
  export interface FamilyAggregate {
319
349
  /** Keyed `<coderFamily>:<reviewerFamily>`, e.g. `codex:claude`. */
@@ -325,6 +355,11 @@ export interface FamilyAggregate {
325
355
  /** A6: the raw count of `stage:'control'` rows folded in — `0` means every `foreignUnique`
326
356
  * figure below is a true, honestly-printed absence, not a fabricated non-observation. */
327
357
  readonly controlRows: number;
358
+ /** experiment-instrument FR-4/A6: EVERY `stage:'control'` refused row seen, attributed or not —
359
+ * the total accounting figure `refusedRuns` (per pair) can never exceed, and the gap between the
360
+ * sum of per-pair `refusedRuns` and this total is exactly how many refusals had no determinable
361
+ * coderFamily (the earliest failures — printed here rather than silently dropped). */
362
+ readonly refusedControlRows: number;
328
363
  }
329
364
  /**
330
365
  * `dz score --by-family`'s aggregate: per (coder family, reviewer family) pair, how many rounds
@@ -1 +1 @@
1
- {"version":3,"file":"cross-family-control.d.ts","sourceRoot":"","sources":["../src/cross-family-control.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cross-family-control.d.ts","sourceRoot":"","sources":["../src/cross-family-control.ts"],"names":[],"mappings":"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"}
@@ -464,6 +464,21 @@ export function buildControlRow(input) {
464
464
  },
465
465
  };
466
466
  }
467
+ function isValidControlRefusedRow(obj) {
468
+ if (typeof obj['slug'] !== 'string' || obj['slug'].trim() === '')
469
+ return false;
470
+ if (typeof obj['runId'] !== 'string' || obj['runId'].trim() === '')
471
+ return false;
472
+ if (obj['half'] !== 'claude' && obj['half'] !== 'codex' && obj['half'] !== 'setup')
473
+ return false;
474
+ if (typeof obj['reason'] !== 'string' || obj['reason'].trim() === '')
475
+ return false;
476
+ if (!(obj['minutes'] === null || (typeof obj['minutes'] === 'number' && Number.isFinite(obj['minutes']))))
477
+ return false;
478
+ if (obj['coderFamily'] !== undefined && obj['coderFamily'] !== 'codex' && obj['coderFamily'] !== 'claude')
479
+ return false;
480
+ return true;
481
+ }
467
482
  const CONTROL_BY_SEVERITY_KEYS = ['confirmed', 'candidate', 'onlyCodex', 'onlyClaude'];
468
483
  const QE_SEVERITY_SET = new Set(QE_SEVERITIES);
469
484
  function isNonNegInt(v) {
@@ -572,6 +587,7 @@ function isValidControlRow(obj) {
572
587
  */
573
588
  export function parseControlRows(lines) {
574
589
  const rows = [];
590
+ const refusedRows = [];
575
591
  const roundRows = [];
576
592
  const fullRows = [];
577
593
  let unreadable = 0;
@@ -594,6 +610,17 @@ export function parseControlRows(lines) {
594
610
  const obj = parsed;
595
611
  const stage = obj['stage'];
596
612
  if (stage === 'control') {
613
+ // experiment-instrument FR-4/A6: `outcome:'refused'` is a DIFFERENT schema from a successful
614
+ // control row — checked FIRST, so a refused row is never mistaken for a malformed successful
615
+ // one (which would count it `unreadable`, losing exactly the receipt this feature adds).
616
+ if (obj['outcome'] === 'refused') {
617
+ if (!isValidControlRefusedRow(obj)) {
618
+ unreadable++;
619
+ continue;
620
+ }
621
+ refusedRows.push(obj);
622
+ continue;
623
+ }
597
624
  if (!isValidControlRow(obj)) {
598
625
  unreadable++;
599
626
  continue;
@@ -609,7 +636,7 @@ export function parseControlRows(lines) {
609
636
  // Every other stage (plan/impl/fix/loop-run/round-exec/…) and the header/comment row (no
610
637
  // string `stage`) are readable, just not addressed by this module.
611
638
  }
612
- return { rows, roundRows, fullRows, unreadable };
639
+ return { rows, refusedRows, roundRows, fullRows, unreadable };
613
640
  }
614
641
  /**
615
642
  * r1-15 (Codex r1 finding 15): a provider-qualified spec like `anthropic/claude-sonnet-4` or
@@ -639,7 +666,7 @@ function newPair() {
639
666
  return {
640
667
  n: 0, grades: {}, shipped: 0, shippedTotal: 0, fixRoundsList: [],
641
668
  foreignN: 0, foreignBySeverity: {}, foreignAuto: 0, foreignAdjudicated: 0, foreignAutoRuns: 0, foreignAdjudicatedRuns: 0, foreignIncomplete: 0,
642
- refutedN: 0, refutedSum: 0, costN: 0, costSum: 0, drafts: [],
669
+ refutedN: 0, refutedSum: 0, costN: 0, costSum: 0, drafts: [], refusedRuns: 0,
643
670
  };
644
671
  }
645
672
  /**
@@ -726,6 +753,18 @@ export function aggregateByFamily(parsed, signoffs) {
726
753
  b.foreignAuto += foreignTotal;
727
754
  }
728
755
  }
756
+ // experiment-instrument FR-4/A6: a refused row is bucketed the SAME way a successful control row
757
+ // is — by its own coderFamily and the complementary reviewer — but ONLY when coderFamily is known
758
+ // (the earliest failures, before the claude half reports which family it reviewed, cannot be
759
+ // attributed to a pair; they still count toward `refusedControlRows` at the top level below,
760
+ // never silently dropped). `n` is deliberately untouched: `n` measures COMPLETED reviews.
761
+ for (const rr of parsed.refusedRows) {
762
+ if (rr.coderFamily === undefined)
763
+ continue;
764
+ const reviewerOfInterest = rr.coderFamily === 'codex' ? 'claude' : 'codex';
765
+ const b = bucket(rr.coderFamily, reviewerOfInterest);
766
+ b.refusedRuns++;
767
+ }
729
768
  // draftToShipped: earliest known verdict per slug (a qe-bridge signoff, or this control row's
730
769
  // own claude-half grade when no signoff was given) against the slug's final grade — keyed by
731
770
  // slug PLUS the normalized (coder,reviewer) family pair (r1-14: two final rows for the same slug
@@ -794,9 +833,16 @@ export function aggregateByFamily(parsed, signoffs) {
794
833
  refutedShare: { n: b.refutedN, value: b.refutedN > 0 ? b.refutedSum / b.refutedN : 'unknown' },
795
834
  costPerConfirmed: { n: b.costN, value: b.costN > 0 ? b.costSum / b.costN : 'unknown' },
796
835
  draftToShipped: b.drafts,
836
+ refusedRuns: b.refusedRuns,
797
837
  };
798
838
  }
799
839
  const incompleteControlRows = parsed.rows.filter((r) => !r.complete).length;
800
- return { pairs: out, incomplete: parsed.unreadable > 0 || incompleteControlRows > 0, incompleteControlRows, controlRows: parsed.rows.length };
840
+ return {
841
+ pairs: out,
842
+ incomplete: parsed.unreadable > 0 || incompleteControlRows > 0,
843
+ incompleteControlRows,
844
+ controlRows: parsed.rows.length,
845
+ refusedControlRows: parsed.refusedRows.length,
846
+ };
801
847
  }
802
848
  //# sourceMappingURL=cross-family-control.js.map