@dzhechkov/harness-core 0.8.36 → 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.
Files changed (112) hide show
  1. package/.dz-manifest.json +216 -76
  2. package/README.md +349 -8
  3. package/dist/agentdb-index.d.ts +87 -7
  4. package/dist/agentdb-index.d.ts.map +1 -1
  5. package/dist/agentdb-index.js +416 -57
  6. package/dist/agentdb-index.js.map +1 -1
  7. package/dist/apply-leg.d.ts +19 -1
  8. package/dist/apply-leg.d.ts.map +1 -1
  9. package/dist/apply-leg.js +187 -36
  10. package/dist/apply-leg.js.map +1 -1
  11. package/dist/codex-rollouts.d.ts +118 -0
  12. package/dist/codex-rollouts.d.ts.map +1 -0
  13. package/dist/codex-rollouts.js +297 -0
  14. package/dist/codex-rollouts.js.map +1 -0
  15. package/dist/cost-ledger.d.ts +56 -4
  16. package/dist/cost-ledger.d.ts.map +1 -1
  17. package/dist/cost-ledger.js +176 -20
  18. package/dist/cost-ledger.js.map +1 -1
  19. package/dist/cross-family-control.d.ts +380 -0
  20. package/dist/cross-family-control.d.ts.map +1 -0
  21. package/dist/cross-family-control.js +848 -0
  22. package/dist/cross-family-control.js.map +1 -0
  23. package/dist/debt-ratchet.d.ts +53 -0
  24. package/dist/debt-ratchet.d.ts.map +1 -0
  25. package/dist/debt-ratchet.js +107 -0
  26. package/dist/debt-ratchet.js.map +1 -0
  27. package/dist/embedding-config.d.ts +42 -0
  28. package/dist/embedding-config.d.ts.map +1 -1
  29. package/dist/embedding-config.js +106 -10
  30. package/dist/embedding-config.js.map +1 -1
  31. package/dist/feature-adr-checkpoints.d.ts +6 -0
  32. package/dist/feature-adr-checkpoints.d.ts.map +1 -1
  33. package/dist/feature-adr-checkpoints.js +29 -0
  34. package/dist/feature-adr-checkpoints.js.map +1 -1
  35. package/dist/feature-adr-decision-recall.d.ts +2 -2
  36. package/dist/feature-adr-decision-recall.d.ts.map +1 -1
  37. package/dist/feature-adr-decision-recall.js +5 -3
  38. package/dist/feature-adr-decision-recall.js.map +1 -1
  39. package/dist/feature-adr-envelope.d.ts +96 -0
  40. package/dist/feature-adr-envelope.d.ts.map +1 -0
  41. package/dist/feature-adr-envelope.js +183 -0
  42. package/dist/feature-adr-envelope.js.map +1 -0
  43. package/dist/feature-adr-routing.d.ts +64 -0
  44. package/dist/feature-adr-routing.d.ts.map +1 -1
  45. package/dist/feature-adr-routing.js +133 -3
  46. package/dist/feature-adr-routing.js.map +1 -1
  47. package/dist/feature-adr-stage-canon.d.ts +79 -0
  48. package/dist/feature-adr-stage-canon.d.ts.map +1 -0
  49. package/dist/feature-adr-stage-canon.js +117 -0
  50. package/dist/feature-adr-stage-canon.js.map +1 -0
  51. package/dist/index.d.ts +21 -9
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +15 -5
  54. package/dist/index.js.map +1 -1
  55. package/dist/loop-blobs.generated.js +4 -4
  56. package/dist/loop-blobs.generated.js.map +1 -1
  57. package/dist/mutation-gate.d.ts +51 -0
  58. package/dist/mutation-gate.d.ts.map +1 -1
  59. package/dist/mutation-gate.js +295 -0
  60. package/dist/mutation-gate.js.map +1 -1
  61. package/dist/qe-bridge.d.ts +8 -0
  62. package/dist/qe-bridge.d.ts.map +1 -1
  63. package/dist/qe-bridge.js +4 -2
  64. package/dist/qe-bridge.js.map +1 -1
  65. package/dist/qe-findings.d.ts +107 -0
  66. package/dist/qe-findings.d.ts.map +1 -0
  67. package/dist/qe-findings.js +417 -0
  68. package/dist/qe-findings.js.map +1 -0
  69. package/dist/recap.d.ts +1 -1
  70. package/dist/recap.d.ts.map +1 -1
  71. package/dist/recap.js +4 -2
  72. package/dist/recap.js.map +1 -1
  73. package/dist/review-cost.d.ts +51 -0
  74. package/dist/review-cost.d.ts.map +1 -0
  75. package/dist/review-cost.js +110 -0
  76. package/dist/review-cost.js.map +1 -0
  77. package/dist/round.d.ts +207 -1
  78. package/dist/round.d.ts.map +1 -1
  79. package/dist/round.js +321 -4
  80. package/dist/round.js.map +1 -1
  81. package/dist/run-records.d.ts +97 -0
  82. package/dist/run-records.d.ts.map +1 -1
  83. package/dist/run-records.js +336 -2
  84. package/dist/run-records.js.map +1 -1
  85. package/dist/score.d.ts +44 -1
  86. package/dist/score.d.ts.map +1 -1
  87. package/dist/score.js +78 -5
  88. package/dist/score.js.map +1 -1
  89. package/package.json +1 -1
  90. package/sbom.json +425 -75
  91. package/src/agentdb-index.ts +423 -60
  92. package/src/apply-leg.ts +187 -36
  93. package/src/codex-rollouts.ts +374 -0
  94. package/src/cost-ledger.ts +232 -24
  95. package/src/cross-family-control.ts +1038 -0
  96. package/src/debt-ratchet.ts +143 -0
  97. package/src/embedding-config.ts +131 -10
  98. package/src/feature-adr-checkpoints.ts +29 -0
  99. package/src/feature-adr-decision-recall.ts +6 -4
  100. package/src/feature-adr-envelope.ts +242 -0
  101. package/src/feature-adr-routing.ts +150 -3
  102. package/src/feature-adr-stage-canon.ts +141 -0
  103. package/src/index.ts +65 -6
  104. package/src/loop-blobs.generated.ts +4 -4
  105. package/src/mutation-gate.ts +316 -0
  106. package/src/qe-bridge.ts +12 -2
  107. package/src/qe-findings.ts +463 -0
  108. package/src/recap.ts +10 -3
  109. package/src/review-cost.ts +139 -0
  110. package/src/round.ts +481 -6
  111. package/src/run-records.ts +388 -2
  112. package/src/score.ts +115 -6
@@ -0,0 +1,380 @@
1
+ /**
2
+ * cross-family-control-branch (ADR-001, tier M): the pure core of `dz control-review` and
3
+ * `dz score --by-family` — normalization, matching, diffing and ledger aggregation for the
4
+ * measurement of foreign-unique findings between two INDEPENDENT reviews of the same tree.
5
+ *
6
+ * PURE, deliberately: no node:fs / node:child_process import here (NFR-1, guarded by
7
+ * test/core-boundary.test.ts). Every file read, subprocess spawn or hash computation belongs to
8
+ * the CLI (`dz control-review`), same as every other core module (qe-findings.ts, qe-bridge.ts).
9
+ *
10
+ * Context (ADR-001): every review in the run-cost ledger and every qe-bridge signoff is ONE
11
+ * direction over one tree — there was no observation of whether the OTHER family finds what the
12
+ * coder's own family misses. This module answers that by diffing two closed-vocabulary finding
13
+ * lists (Codex's `## Findings ledger` table, Claude's qe-bridge signoff) over the SAME scope.
14
+ *
15
+ * ── Fix round 1 (codex-r1-verdict.txt, Grade C, 17 findings) — the vocabulary shift ──────────────
16
+ * Round 1 called an automatic title/location match "matched" — an OVERLAP claim. Codex r1 finding 1
17
+ * proved that claim false with two real counter-examples (a 4/6-token false pair; two unrelated
18
+ * findings in the same file three lines apart). The fix is not a smarter matcher — a smarter matcher
19
+ * still guesses — it is an honest vocabulary: automatic pairs are **candidates**, never overlap.
20
+ * Only a human adjudication produces a **confirmed** pair. Every output (`ControlDiff`, the ledger
21
+ * row, `dz score --by-family`) now keeps FOUR buckets apart: `confirmed`, `candidate`, `onlyCodex`,
22
+ * `onlyClaude` — and the word "overlap"/"matched" is reserved for `confirmed` alone.
23
+ */
24
+ import { type QeSeverity, type QeStatus } from './qe-findings.js';
25
+ /**
26
+ * The Codex control brief and the Claude qe-bridge signoff both speak the informal
27
+ * critical/major/minor vocabulary (`buildBridgePrompt`'s own brief text: "a severity
28
+ * (critical/major/minor)"). This maps that vocabulary onto the CLOSED `QeSeverity` dictionary
29
+ * qe-findings.ts already defines — `critical`→`CRITICAL`, `major`→`HIGH`, `minor`→`LOW` — and
30
+ * refuses (returns `null`) anything else, case/whitespace-insensitive. The CALLER decides what a
31
+ * refusal means (A4: the finding is written into a `refused` bucket with a named reason, never
32
+ * coerced to the nearest known value — coercion here would make the resulting severity tally
33
+ * unprovable, the same argument qe-findings.ts already makes for its own closed dictionaries).
34
+ */
35
+ export declare function normalizeBridgeSeverity(s: string): QeSeverity | null;
36
+ /**
37
+ * Lowercase, strip punctuation/backticks (anything that is not a Unicode letter or digit becomes
38
+ * a separator), split on whitespace, keep tokens of length >= 3, deduplicate, sort. The resulting
39
+ * token SET is what `matchFindings`/`dedupeWithinFamily` compare with Jaccard similarity — a
40
+ * bag-of-words match, not a substring one, so word order never matters.
41
+ */
42
+ export declare function normalizeFindingTitle(t: string): string[];
43
+ export interface ControlFinding {
44
+ readonly family: 'codex' | 'claude';
45
+ readonly id: string;
46
+ readonly severity: QeSeverity;
47
+ readonly title: string;
48
+ readonly file?: string;
49
+ readonly line?: number;
50
+ readonly status?: QeStatus;
51
+ }
52
+ export interface MatchPair {
53
+ readonly a: string;
54
+ readonly b: string;
55
+ readonly rule: 'title-jaccard' | 'file-line';
56
+ readonly score: number;
57
+ }
58
+ /**
59
+ * Deterministic MAXIMUM-CARDINALITY matching between two finding lists, sum-of-score as the
60
+ * secondary objective (ADR-001, amended after Codex r1 finding 2 — greedy-by-score needlessly
61
+ * drops valid pairs: titles `A1/B1="alpha beta gamma delta"`, `A2="gamma delta"`,
62
+ * `B2="alpha beta"` greedily keep only A1–B1 and lose two genuine pairs A1–B2/A2–B1).
63
+ *
64
+ * Solved as a weighted bipartite ASSIGNMENT (Hungarian): a real candidate edge costs
65
+ * `-(BONUS + score)` with `BONUS = 1 + min(rows, cols)`; a non-candidate edge costs `0`. Lead delta
66
+ * after Codex r2 (new HIGH #1): a bonus of `1` did NOT make cardinality dominant — two score-1 edges
67
+ * (weight 4) beat three score-0.25 edges (weight 3.75). Since every score is <= 1, the total score of
68
+ * ANY matching is < min(rows, cols) + 1 = BONUS, so one extra edge always outweighs any score
69
+ * difference: cardinality first, total score second, in one assignment.
70
+ *
71
+ * Two rules propose CANDIDATES (never confirmed overlap — Codex r1 finding 1: an automatic pair,
72
+ * however matched, is a candidate for lead adjudication, printed as such everywhere it travels):
73
+ * - title Jaccard >= `opts.jaccard` (default 0.5) on tokens of length >= 3, AND a compatible file
74
+ * (r1-1: a file that DISAGREES between the two findings disqualifies an otherwise-good title
75
+ * match — two findings about the "same" defect in two different files are two defects);
76
+ * - same file with `|line delta| <= opts.lineSlack` (default 3) AND title Jaccard >=
77
+ * `opts.fileLineJaccard` (default 0.2) — r1-1's second counter-example: same file, adjacent
78
+ * lines, ZERO shared vocabulary used to pair for free; a location match now needs SOME
79
+ * corroborating text, not just proximity.
80
+ * A pair meeting neither rule's threshold is never proposed — "below-threshold titles stay
81
+ * unique" remains the load-bearing property this module protects.
82
+ */
83
+ export declare function matchFindings(a: readonly ControlFinding[], b: readonly ControlFinding[], opts?: {
84
+ readonly jaccard?: number;
85
+ readonly lineSlack?: number;
86
+ readonly fileLineJaccard?: number;
87
+ }): {
88
+ readonly pairs: readonly MatchPair[];
89
+ };
90
+ /**
91
+ * A5: collapse near-duplicate findings WITHIN one reviewer's own list (Jaccard >= 0.8 on titles,
92
+ * a tighter threshold than cross-family matching since these are the SAME reviewer restating
93
+ * itself, e.g. across multiple table rows). The first occurrence in list order is kept; every
94
+ * later near-duplicate is recorded in `collapsed` rather than silently dropped.
95
+ *
96
+ * r1-3 (Codex r1 finding 3): collapsing on title tokens ALONE let two DISTINCT defects with the
97
+ * same generic title ("missing null check in parser") in two different files collapse into one —
98
+ * a real defect silently lost. A location disagreement now blocks the collapse: two findings only
99
+ * collapse when their files are COMPATIBLE (both absent, or identical) — same rule `matchFindings`
100
+ * applies across families, applied here within one.
101
+ */
102
+ export declare function dedupeWithinFamily(list: readonly ControlFinding[], jaccardThreshold?: number): {
103
+ readonly kept: ControlFinding[];
104
+ readonly collapsed: ReadonlyArray<{
105
+ readonly kept: string;
106
+ readonly dropped: string;
107
+ }>;
108
+ };
109
+ /**
110
+ * Explicit lead adjudication, read from `--adjudicate <file>`: named `pairs` OVERRIDE the
111
+ * automatic match for the ids they name (in either direction — a pair may correct a wrong auto
112
+ * match or supply one the automatic rule missed) and become CONFIRMED overlap. `none` marks
113
+ * findings the lead has confirmed have NO counterpart in the other family (excluded from the
114
+ * automatic pool, so they land in `onlyCodex`/`onlyClaude` on purpose rather than by omission).
115
+ *
116
+ * r1-5 (Codex r1 finding 5): `none` entries are now FAMILY-QUALIFIED (`"codex:<id>"` /
117
+ * `"claude:<id>"`) — a bare id like `"1"` used to apply to BOTH families whenever they happened to
118
+ * share an id, silently excluding the wrong finding from the automatic pool. A bare legacy entry
119
+ * is refused outright, not guessed.
120
+ */
121
+ export interface Adjudication {
122
+ readonly pairs: ReadonlyArray<{
123
+ readonly codex: string | number;
124
+ readonly claude: string | number;
125
+ }>;
126
+ readonly none: readonly string[];
127
+ }
128
+ export interface ControlDiffBySeverity {
129
+ readonly confirmed: Readonly<Record<string, number>>;
130
+ readonly candidate: Readonly<Record<string, number>>;
131
+ readonly onlyCodex: Readonly<Record<string, number>>;
132
+ readonly onlyClaude: Readonly<Record<string, number>>;
133
+ }
134
+ export interface ControlDiff {
135
+ /** Adjudicated pairs ONLY — the sole bucket allowed to be called "overlap"/"matched" anywhere
136
+ * this diff travels (r1-1). */
137
+ readonly confirmed: ReadonlyArray<{
138
+ readonly codex: string;
139
+ readonly claude: string;
140
+ }>;
141
+ /** Automatic pairs — a LOWER-BOUND CANDIDATE, never confirmed overlap, until a lead adjudicates. */
142
+ readonly candidate: ReadonlyArray<{
143
+ readonly codex: string;
144
+ readonly claude: string;
145
+ readonly rule: MatchPair['rule'];
146
+ readonly score: number;
147
+ }>;
148
+ readonly onlyCodex: readonly string[];
149
+ readonly onlyClaude: readonly string[];
150
+ readonly bySeverity: ControlDiffBySeverity;
151
+ readonly matchRule: string;
152
+ readonly adjudicated: boolean;
153
+ readonly collapsed: {
154
+ readonly codex: number;
155
+ readonly claude: number;
156
+ };
157
+ }
158
+ /** A7: an adjudication naming an id neither list carries is a RESULT, never a thrown exception —
159
+ * the CLI must be able to print the reason and exit 1, not crash. */
160
+ export type ControlDiffResult = ({
161
+ readonly ok: true;
162
+ } & ControlDiff) | {
163
+ readonly ok: false;
164
+ readonly reason: string;
165
+ };
166
+ export declare function diffFamilyFindings(codex: readonly ControlFinding[], claude: readonly ControlFinding[], adjudication?: Adjudication): ControlDiffResult;
167
+ export interface ControlLedgerRow {
168
+ readonly slug: string;
169
+ readonly stage: 'control';
170
+ readonly runId: string;
171
+ /** Who wrote the code under review — the same family this control run's `--coder-family` (and
172
+ * qe-bridge's own recorded debt) named. Decides which half's `onlyX` count is FOREIGN. */
173
+ readonly coderFamily: 'codex' | 'claude';
174
+ readonly scope: readonly string[];
175
+ readonly treeShaBefore: string;
176
+ readonly treeShaAfterClaude: string;
177
+ readonly treeShaAfterCodex: string;
178
+ readonly codexGrade: string | null;
179
+ readonly claudeGrade: string | null;
180
+ readonly confirmed: number;
181
+ readonly candidate: number;
182
+ readonly onlyCodex: number;
183
+ readonly onlyClaude: number;
184
+ readonly bySeverity: ControlDiffBySeverity;
185
+ readonly matchRule: string;
186
+ readonly adjudicated: boolean;
187
+ readonly collapsed: {
188
+ readonly codex: number;
189
+ readonly claude: number;
190
+ };
191
+ /** r1-8: findings refused for cause (an unnormalizable severity, an out-of-scope file) are
192
+ * recorded DURABLY here, in the ledger row itself — never only in a best-effort convenience
193
+ * file written after the ledger, which a witnessed-reread gate never protects. */
194
+ readonly refused: {
195
+ readonly claude: number;
196
+ readonly codex: number;
197
+ readonly reasons: readonly string[];
198
+ };
199
+ /** false whenever ANY finding was refused for cause (`refused.claude + refused.codex > 0`) —
200
+ * the row still measures something real, but it is an INCOMPLETE measurement, printed as such. */
201
+ readonly complete: boolean;
202
+ readonly tokens: number | null;
203
+ readonly minutes: number | null;
204
+ }
205
+ export interface BuildControlRowInput {
206
+ readonly slug: string;
207
+ readonly runId: string;
208
+ readonly coderFamily: 'codex' | 'claude';
209
+ readonly scope: readonly string[];
210
+ readonly tree: {
211
+ readonly before: string;
212
+ readonly afterClaude: string;
213
+ readonly afterCodex: string;
214
+ };
215
+ readonly codex: {
216
+ readonly accepted: boolean;
217
+ readonly grade: string | null;
218
+ };
219
+ readonly claude: {
220
+ readonly accepted: boolean;
221
+ readonly grade: string | null;
222
+ };
223
+ readonly diff: ControlDiff;
224
+ readonly refused?: {
225
+ readonly claude?: number;
226
+ readonly codex?: number;
227
+ readonly reasons?: readonly string[];
228
+ };
229
+ readonly tokens?: number | null;
230
+ readonly minutes?: number | null;
231
+ }
232
+ export type BuildControlRowResult = {
233
+ readonly ok: true;
234
+ readonly row: ControlLedgerRow;
235
+ } | {
236
+ readonly ok: false;
237
+ readonly reason: string;
238
+ };
239
+ /**
240
+ * Refuses (never throws) on:
241
+ * - an empty scope (nothing was reviewed);
242
+ * - either required half missing, malformed, or carrying an EMPTY object where a real result was
243
+ * required (the empty-required-object lesson);
244
+ * - either half lacking an accepted findings table (A1) — an accepted-HOLLOW half (a genuine
245
+ * zero-findings verdict) is fine; a half whose only table was rejected, or that has none, is not;
246
+ * - a tree-hash drift across EITHER half (A2) — the claude half compares `before` to
247
+ * `afterClaude`, the codex half compares `afterClaude` to `afterCodex`; a control whose halves
248
+ * did not see the identical tree writes NO ledger row.
249
+ * Unnormalizable-severity / out-of-scope findings do NOT refuse the row outright (they are a
250
+ * partial-measurement fact, not a total failure) — they land in `refused`/`complete:false` instead.
251
+ */
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
+ }
275
+ export interface ParsedControlRows {
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[];
281
+ readonly roundRows: readonly Record<string, unknown>[];
282
+ readonly fullRows: readonly Record<string, unknown>[];
283
+ /** A8: a line that is not parseable JSON, or parses to something that is not a plain object, or
284
+ * is a `stage:'control'` row that fails FULL schema validation (r1-12), is counted here — never
285
+ * silently skipped and never crashing the aggregate. A well-formed line whose `stage` this
286
+ * module does not read (plan/impl/fix/loop-run/round-exec/the header comment row) is NOT
287
+ * unreadable: it was read fine, this module simply has nothing to do with it. */
288
+ readonly unreadable: number;
289
+ }
290
+ /**
291
+ * Reads every line of a run-cost-ledger.jsonl body, classifying `stage:'control'` rows (this
292
+ * feature's own, fully schema-validated — r1-12), `stage:'round'` and `stage:'full'` rows (the two
293
+ * existing per-review stages `aggregateByFamily` reads for its per-pair table), and counting
294
+ * everything unreadable (A8).
295
+ */
296
+ export declare function parseControlRows(lines: readonly string[]): ParsedControlRows;
297
+ export interface FamilyPairAggregate {
298
+ readonly n: number;
299
+ readonly grades: Readonly<Record<string, number>>;
300
+ readonly shippedShare: number | 'unknown';
301
+ /** r1-14: round/full rows for this pair with an EXPLICIT non-shipped outcome (refuted/blocked/
302
+ * abandoned) — named separately so a reader never mistakes "excluded from draftToShipped" for
303
+ * "no data". */
304
+ readonly notShipped: number;
305
+ readonly fixRounds: {
306
+ readonly n: number;
307
+ readonly mean: number | 'unknown';
308
+ };
309
+ readonly foreignUnique: {
310
+ /** r1-13: the count of `stage:'control'` rows folded into THIS pair — `0` means every other
311
+ * field below is `'unknown'`, never a fabricated non-observation. */
312
+ readonly n: number;
313
+ /** Lead delta after Codex r2 (question d / new HIGH #2): control rows with `complete:false`
314
+ * (refused entries) are COUNTED here and EXCLUDED from every measured figure below — an
315
+ * incomplete measurement never contaminates the totals, and its presence marks the whole
316
+ * aggregate `incomplete`. */
317
+ readonly incompleteRuns: number;
318
+ readonly bySeverity: Readonly<Record<string, number>> | 'unknown';
319
+ /** Foreign-unique FINDINGS (summed) from complete control rows that were NOT adjudicated — a
320
+ * candidate-only figure. `'unknown'` only when `n===0`. (Codex r2 new HIGH #3: these used to
321
+ * count ROWS under a findings label; the row counts now live in `autoRuns`/`adjudicatedRuns`.) */
322
+ readonly auto: number | 'unknown';
323
+ /** Foreign-unique FINDINGS (summed) from complete control rows a lead DID adjudicate — the
324
+ * trustworthy figure. `'unknown'` only when `n===0`. */
325
+ readonly adjudicated: number | 'unknown';
326
+ readonly autoRuns: number | 'unknown';
327
+ readonly adjudicatedRuns: number | 'unknown';
328
+ };
329
+ readonly refutedShare: {
330
+ readonly n: number;
331
+ readonly value: number | 'unknown';
332
+ };
333
+ readonly costPerConfirmed: {
334
+ readonly n: number;
335
+ readonly value: number | 'unknown';
336
+ };
337
+ readonly draftToShipped: ReadonlyArray<{
338
+ readonly slug: string;
339
+ readonly first: string;
340
+ readonly final: string;
341
+ readonly finals: number;
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;
347
+ }
348
+ export interface FamilyAggregate {
349
+ /** Keyed `<coderFamily>:<reviewerFamily>`, e.g. `codex:claude`. */
350
+ readonly pairs: Readonly<Record<string, FamilyPairAggregate>>;
351
+ /** A8: true when `parsed.unreadable > 0` — the aggregate is honest about what it could not read. */
352
+ readonly incomplete: boolean;
353
+ /** control rows with `complete:false` — excluded from the measured figures (r2 delta) */
354
+ readonly incompleteControlRows: number;
355
+ /** A6: the raw count of `stage:'control'` rows folded in — `0` means every `foreignUnique`
356
+ * figure below is a true, honestly-printed absence, not a fabricated non-observation. */
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;
363
+ }
364
+ /**
365
+ * `dz score --by-family`'s aggregate: per (coder family, reviewer family) pair, how many rounds
366
+ * ran, their grade distribution, `shippedShare`/`notShipped`, mean `fixRounds`, the FOREIGN-unique
367
+ * findings measured by `control` rows for that pair's cross-family direction (split `auto` vs
368
+ * `adjudicated`, r1-1/r1-13), `refutedShare` and `costPerConfirmed` from `full` rows' findings
369
+ * tables, and `draftToShipped` — the earliest known verdict for a slug (a qe-bridge signoff, or
370
+ * this run's own claude-half grade) next to its final SHIPPED `round`/`full` grade for that exact
371
+ * (slug, family-pair) key (r1-14). Every ratio is `'unknown'`, never a fabricated `0`, when its
372
+ * denominator is zero (NFR-4: absent data is reported as absent).
373
+ */
374
+ export declare function aggregateByFamily(parsed: ParsedControlRows, signoffs?: ReadonlyArray<{
375
+ readonly slug: string;
376
+ readonly emittedAt: string;
377
+ readonly grade: string;
378
+ readonly coderFamily: string;
379
+ }>): FamilyAggregate;
380
+ //# sourceMappingURL=cross-family-control.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cross-family-control.d.ts","sourceRoot":"","sources":["../src/cross-family-control.ts"],"names":[],"mappings":"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"}