@mmnto/totem 1.82.0 → 1.84.0

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.
@@ -0,0 +1,507 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { AuthoredNonEmissionSchema, deriveAuthoredControls, } from './authored-controls.js';
3
+ import { getRulePolicy } from './rule-policy.js';
4
+ import { resolveSplit, SplitCoverError } from './split.js';
5
+ // Wrap the two cross-module seams in pass-through spies (mirrors compile.test.ts):
6
+ // - `resolveSplit` — to assert deriveAuthoredControls NEVER routes through it (the
7
+ // train-side controls use a SEPARATE path); the mirror test still calls the real
8
+ // impl (vi.fn passes through to `actual`).
9
+ // - `getRulePolicy` — to drive the §9 producer-mismatch fail-loud guards with a
10
+ // one-shot wrong policy; every other test passes through to the real frozen one.
11
+ vi.mock('./split.js', async (importOriginal) => {
12
+ const actual = await importOriginal();
13
+ return { ...actual, resolveSplit: vi.fn(actual.resolveSplit) };
14
+ });
15
+ vi.mock('./rule-policy.js', async (importOriginal) => {
16
+ const actual = await importOriginal();
17
+ return { ...actual, getRulePolicy: vi.fn(actual.getRulePolicy) };
18
+ });
19
+ // ─── Constants ──────────────────────────────────────
20
+ /** A 16-hex `hashLesson` codomain id (the lesson preimageSource's immutable ref). */
21
+ const LESSON_REF = 'deadbeefdeadbeef';
22
+ /** Per-index delay unit for the determinism probe (slow-FIRST). */
23
+ const DELAY_UNIT_MS = 4;
24
+ const sha = (n) => String(n).padStart(40, '0');
25
+ // ─── Fixture / rule helpers ─────────────────────────
26
+ function posFixture(pr, contentHash, filePath = 'src/a.ts') {
27
+ return {
28
+ pr,
29
+ preimageSource: {
30
+ kind: 'lesson',
31
+ lessonRef: LESSON_REF,
32
+ badExample: 'bad()',
33
+ goodExample: 'good()',
34
+ },
35
+ filePath,
36
+ matchedSpan: 'L1-L2',
37
+ contentHash,
38
+ };
39
+ }
40
+ function negFixture(filePath, matchedSpan) {
41
+ return {
42
+ filePath,
43
+ matchedSpan,
44
+ nearMissSource: { kind: 'lesson', example: 'nearMiss()' },
45
+ };
46
+ }
47
+ function authoredRule(lessonHash, positiveFixtures, negativeFixtures) {
48
+ const provenance = {
49
+ kind: 'authored',
50
+ author: 'agent-x',
51
+ authoredAt: '2026-06-01',
52
+ targetDefect: 'a real lc defect',
53
+ positiveFixtures,
54
+ ...(negativeFixtures ? { negativeFixtures } : {}),
55
+ };
56
+ return {
57
+ lessonHash,
58
+ lessonHeading: 'Authored rule',
59
+ pattern: 'bad',
60
+ message: 'no bad',
61
+ engine: 'regex',
62
+ compiledAt: '2026-06-01T00:00:00Z',
63
+ legitimacy: { provenance, positiveControl: true, negativeControl: true },
64
+ };
65
+ }
66
+ /**
67
+ * Build the SIDECAR `provenanceByRule` map the reshaped builder reads (D1 fold #1):
68
+ * `lessonHash → provenance`. `authoredRule` still stamps `legitimacy.provenance` (a
69
+ * convenient carrier for the test's intent), but the FUNCTION reads only this map —
70
+ * never `rule.legitimacy` — so routing the same provenance through the map keeps the
71
+ * tests faithful to the real assembly seam (compiled rules carry no legitimacy there).
72
+ */
73
+ function provBy(rules) {
74
+ return new Map(rules.map((r) => [r.lessonHash, r.legitimacy.provenance]));
75
+ }
76
+ /**
77
+ * Test driver: invoke the reshaped builder with the SIDECAR provenance map (D1 fold #1)
78
+ * derived from each rule's stamped provenance. The builder reads ONLY the map; this keeps
79
+ * every existing case faithful to the assembly seam (where compiled rules carry no
80
+ * legitimacy) without threading the map by hand. Cases that need a DIVERGENT map (missing /
81
+ * mined provenance) call `deriveAuthoredControls` directly instead.
82
+ */
83
+ function derive(args) {
84
+ return deriveAuthoredControls({
85
+ rules: args.rules,
86
+ split: args.split,
87
+ provenanceByRule: provBy(args.rules),
88
+ ...(args.deps ? { deps: args.deps } : {}),
89
+ });
90
+ }
91
+ /** A minimal valid SplitArtifact — only `trainPrs` is read by the builder. */
92
+ function splitWithTrain(trainPrs, heldOutPrs = []) {
93
+ return {
94
+ asOfCommit: sha(999),
95
+ trainPrs,
96
+ heldOutPrs,
97
+ excludedPrs: [],
98
+ positiveControlPrs: [],
99
+ negativeControlPrs: [],
100
+ splitRule: { predicate: 'code-touching non-bot', cutIndex: Math.max(trainPrs.length, 1) },
101
+ };
102
+ }
103
+ // ─── Differential-evaluator stubs (no git, no engine) ─
104
+ function makeResult(outcome, reason) {
105
+ return {
106
+ outcome,
107
+ sourceKind: 'lesson',
108
+ firesOnPreimage: null,
109
+ silentOnPostimage: null,
110
+ preimageMatchCount: null,
111
+ postimageMatchCount: null,
112
+ ...(reason !== undefined ? { reason } : {}),
113
+ };
114
+ }
115
+ /** Scripts a differential result per fixture (keyed on its unique contentHash). */
116
+ function scriptedDeps(byContentHash) {
117
+ return {
118
+ evaluate: (_rule, fixture) => {
119
+ const result = byContentHash[fixture.contentHash];
120
+ if (result === undefined) {
121
+ throw new Error(`test stub: no scripted result for contentHash '${fixture.contentHash}'`);
122
+ }
123
+ return Promise.resolve(result);
124
+ },
125
+ };
126
+ }
127
+ /** An evaluator that throws if ever invoked — proves negatives never run the differential. */
128
+ const throwingDeps = {
129
+ evaluate: () => {
130
+ throw new Error('the §4 differential must not run for negative (silence-only) controls');
131
+ },
132
+ };
133
+ /** Slow-FIRST delays: the earliest task waits the longest, so a push-on-settle impl would reorder. */
134
+ function slowFirstDeps(total) {
135
+ let calls = 0;
136
+ return {
137
+ evaluate: (_rule, _fixture) => {
138
+ const idx = calls++;
139
+ const delayMs = (total - idx) * DELAY_UNIT_MS;
140
+ return new Promise((resolve) => setTimeout(() => resolve(makeResult('differential-holds')), delayMs));
141
+ },
142
+ };
143
+ }
144
+ // ─── 1. Per-outcome matrix (the §4 gate, exact-equality both directions) ─────
145
+ describe('deriveAuthoredControls — per-outcome matrix', () => {
146
+ it('emits a positive ONLY for differential-holds; the other 5 outcomes become classed non-emissions', async () => {
147
+ const rule = authoredRule('rule-auth-1', [
148
+ posFixture(1, 'ch-holds'),
149
+ posFixture(2, 'ch-fix'),
150
+ posFixture(3, 'ch-over'),
151
+ posFixture(4, 'ch-vac'),
152
+ posFixture(5, 'ch-needs'),
153
+ posFixture(6, 'ch-unsup'),
154
+ ]);
155
+ const deps = scriptedDeps({
156
+ 'ch-holds': makeResult('differential-holds'),
157
+ 'ch-fix': makeResult('fix-shaped'),
158
+ 'ch-over': makeResult('over-match'),
159
+ 'ch-vac': makeResult('vacuous-silent'),
160
+ 'ch-needs': makeResult('needs-adjudication', 'engine refused: invalid regex'),
161
+ 'ch-unsup': makeResult('unsupported-source', 'commit-pair source deferred to a later slice'),
162
+ });
163
+ const result = await derive({
164
+ rules: [rule],
165
+ split: splitWithTrain([1, 2, 3, 4, 5, 6]),
166
+ deps,
167
+ });
168
+ // Exactly ONE positive — the holds fixture, carrying ITS locus. This kills
169
+ // the `outcome !== 'fix-shaped'` mutant (which would emit 5 positives).
170
+ expect(result.positive).toEqual([
171
+ { pr: 1, targetRuleId: 'rule-auth-1', filePath: 'src/a.ts', matchedSpan: 'L1-L2' },
172
+ ]);
173
+ // The other five are KEPT (never silently dropped) with the exact source outcome
174
+ // + the correct class.
175
+ expect(result.nonEmissions).toHaveLength(5);
176
+ const byOutcome = new Map(result.nonEmissions.map((n) => [n.outcome, n]));
177
+ expect(byOutcome.get('fix-shaped')).toEqual({
178
+ targetRuleId: 'rule-auth-1',
179
+ pr: 2,
180
+ outcome: 'fix-shaped',
181
+ class: 'illegitimate',
182
+ });
183
+ expect(byOutcome.get('over-match')).toEqual({
184
+ targetRuleId: 'rule-auth-1',
185
+ pr: 3,
186
+ outcome: 'over-match',
187
+ class: 'illegitimate',
188
+ });
189
+ expect(byOutcome.get('vacuous-silent')).toEqual({
190
+ targetRuleId: 'rule-auth-1',
191
+ pr: 4,
192
+ outcome: 'vacuous-silent',
193
+ class: 'illegitimate',
194
+ });
195
+ expect(byOutcome.get('needs-adjudication')).toEqual({
196
+ targetRuleId: 'rule-auth-1',
197
+ pr: 5,
198
+ outcome: 'needs-adjudication',
199
+ class: 'undecidable',
200
+ reason: 'engine refused: invalid regex',
201
+ });
202
+ expect(byOutcome.get('unsupported-source')).toEqual({
203
+ targetRuleId: 'rule-auth-1',
204
+ pr: 6,
205
+ outcome: 'unsupported-source',
206
+ class: 'deferred',
207
+ reason: 'commit-pair source deferred to a later slice',
208
+ });
209
+ // Exact-equality, the OTHER direction: NONE of the 5 non-holds outcomes leaked
210
+ // into positives (the holds one is the sole positive).
211
+ expect(result.positive.map((p) => p.pr)).toEqual([1]);
212
+ });
213
+ });
214
+ // ─── 2. Two-loci-one-PR disambiguation (strategy#777 Q1(a)) ──────────────────
215
+ describe('deriveAuthoredControls — two-loci-one-PR disambiguation', () => {
216
+ it('a PR contributing two distinct-locus fixtures emits ONLY the holding one, carrying ITS locus', async () => {
217
+ const rule = authoredRule('rule-2loci', [
218
+ posFixture(7, 'ch-A', 'src/a.ts'),
219
+ posFixture(7, 'ch-B', 'src/b.ts'),
220
+ ]);
221
+ const deps = scriptedDeps({
222
+ 'ch-A': makeResult('differential-holds'),
223
+ 'ch-B': makeResult('fix-shaped'),
224
+ });
225
+ const result = await derive({
226
+ rules: [rule],
227
+ split: splitWithTrain([7]),
228
+ deps,
229
+ });
230
+ // The positive carries the src/a.ts locus (the holding fixture) — NOT src/b.ts; the
231
+ // locus disambiguator prevents the wrong-exemplar miscert under a shared pr.
232
+ expect(result.positive).toEqual([
233
+ { pr: 7, targetRuleId: 'rule-2loci', filePath: 'src/a.ts', matchedSpan: 'L1-L2' },
234
+ ]);
235
+ expect(result.nonEmissions).toEqual([
236
+ { targetRuleId: 'rule-2loci', pr: 7, outcome: 'fix-shaped', class: 'illegitimate' },
237
+ ]);
238
+ });
239
+ // strategy#777 §6: the LOCUS (filePath, matchedSpan) is the disambiguator, so two
240
+ // HOLDING fixtures in one PR at DISTINCT loci both emit — even with byte-identical
241
+ // span content (same contentHash). This is the legitimate two-loci-one-PR rule the
242
+ // old contentHash key wrongly FORBADE; emitting two is the whole point of the fix.
243
+ it('emits TWO positives for two HOLDING fixtures sharing a PR at distinct loci (even identical content)', async () => {
244
+ const rule = authoredRule('rule-2loci-hold', [
245
+ posFixture(7, 'ch-same', 'src/a.ts'),
246
+ posFixture(7, 'ch-same', 'src/b.ts'),
247
+ ]);
248
+ const result = await derive({
249
+ rules: [rule],
250
+ split: splitWithTrain([7]),
251
+ deps: scriptedDeps({ 'ch-same': makeResult('differential-holds') }),
252
+ });
253
+ expect(result.positive).toEqual([
254
+ { pr: 7, targetRuleId: 'rule-2loci-hold', filePath: 'src/a.ts', matchedSpan: 'L1-L2' },
255
+ { pr: 7, targetRuleId: 'rule-2loci-hold', filePath: 'src/b.ts', matchedSpan: 'L1-L2' },
256
+ ]);
257
+ });
258
+ // A TRUE duplicate — same pr + filePath + matchedSpan — IS an indistinguishable §6
259
+ // answer-key entry (a real authoring error, not the two-loci rule): fail loud,
260
+ // regardless of content hash (here the two fixtures even differ in contentHash).
261
+ it('throws when two HOLDING fixtures share pr AND filePath AND matchedSpan (same locus)', async () => {
262
+ const rule = authoredRule('rule-dup-pos', [
263
+ posFixture(7, 'ch-x', 'src/a.ts'),
264
+ posFixture(7, 'ch-y', 'src/a.ts'),
265
+ ]);
266
+ await expect(derive({
267
+ rules: [rule],
268
+ split: splitWithTrain([7]),
269
+ deps: scriptedDeps({
270
+ 'ch-x': makeResult('differential-holds'),
271
+ 'ch-y': makeResult('differential-holds'),
272
+ }),
273
+ })).rejects.toThrow(/duplicate positive control/);
274
+ });
275
+ it('throws when two near-misses share filePath AND matchedSpan (indistinguishable negative key)', async () => {
276
+ const rule = authoredRule('rule-dup-neg', [posFixture(1, 'ch-1')], [negFixture('src/a.ts', 'L1-L2'), negFixture('src/a.ts', 'L1-L2')]);
277
+ await expect(derive({
278
+ rules: [rule],
279
+ split: splitWithTrain([1]),
280
+ deps: scriptedDeps({ 'ch-1': makeResult('differential-holds') }),
281
+ })).rejects.toThrow(/duplicate negative control/);
282
+ });
283
+ // greptile P2 regression guard: the join-key is `JSON.stringify([...])`, NOT a
284
+ // `\0`-joined template — so two DISTINCT loci that merely shift the delimiter
285
+ // boundary (filePath 'a.ts' + span 'L1\0L2' vs filePath 'a.ts\0L1' + span 'L2')
286
+ // no longer collide into ONE key. Both emit; the old delimited key falsely threw
287
+ // "duplicate" on a load-bearing D-join target.
288
+ it('distinct negative loci with an embedded delimiter do NOT collide (injection-proof key)', async () => {
289
+ const rule = authoredRule('rule-delim-neg', [posFixture(1, 'ch-1')], [negFixture('a.ts', 'L1\0L2'), negFixture('a.ts\0L1', 'L2')]);
290
+ const result = await derive({
291
+ rules: [rule],
292
+ split: splitWithTrain([1]),
293
+ deps: scriptedDeps({ 'ch-1': makeResult('differential-holds') }),
294
+ });
295
+ expect(result.negative).toEqual([
296
+ { targetRuleId: 'rule-delim-neg', filePath: 'a.ts', matchedSpan: 'L1\0L2' },
297
+ { targetRuleId: 'rule-delim-neg', filePath: 'a.ts\0L1', matchedSpan: 'L2' },
298
+ ]);
299
+ });
300
+ });
301
+ // ─── 3. Determinism (Tenet-15; slow-first must NOT reorder) ──────────────────
302
+ describe('deriveAuthoredControls — determinism', () => {
303
+ it('emits in stable declared order regardless of settle timing, byte-identical across re-runs', async () => {
304
+ const contentHashes = ['ch-0', 'ch-1', 'ch-2', 'ch-3'];
305
+ const rule = authoredRule('rule-det', contentHashes.map((ch, i) => posFixture(i + 1, ch)));
306
+ const split = splitWithTrain([1, 2, 3, 4]);
307
+ const runA = await derive({
308
+ rules: [rule],
309
+ split,
310
+ deps: slowFirstDeps(contentHashes.length),
311
+ });
312
+ const runB = await derive({
313
+ rules: [rule],
314
+ split,
315
+ deps: slowFirstDeps(contentHashes.length),
316
+ });
317
+ expect(runA).toEqual(runB);
318
+ // Declared order preserved — a push-on-settle impl with slow-first delays would
319
+ // invert this to [4, 3, 2, 1].
320
+ expect(runA.positive.map((p) => p.pr)).toEqual([1, 2, 3, 4]);
321
+ });
322
+ });
323
+ // ─── 4. Boundary: never routes through resolveSplit ──────────────────────────
324
+ describe('deriveAuthoredControls — boundary vs resolveSplit', () => {
325
+ it('does NOT invoke resolveSplit (train-side controls use a SEPARATE path)', async () => {
326
+ vi.mocked(resolveSplit).mockClear();
327
+ const rule = authoredRule('rule-bnd', [posFixture(1, 'ch-1')]);
328
+ await derive({
329
+ rules: [rule],
330
+ split: splitWithTrain([1]),
331
+ deps: scriptedDeps({ 'ch-1': makeResult('differential-holds') }),
332
+ });
333
+ expect(vi.mocked(resolveSplit)).not.toHaveBeenCalled();
334
+ });
335
+ it('mirror: a TRAIN pr fed as a resolveSplit positive control throws (controls⊄heldOut) — WHY the separate path exists', () => {
336
+ const corpus = [1, 2, 3];
337
+ const mergeCommitByPr = new Map(corpus.map((pr) => [pr, sha(pr)]));
338
+ // cutIndex 2 → train [1,2], heldOut [3]; pr 1 is TRAIN, so as a positive control
339
+ // it violates controls ⊆ heldOut — the exact leakage the C2b path sidesteps.
340
+ expect(() => resolveSplit({
341
+ asOfCommit: sha(999),
342
+ corpus,
343
+ orderedNewestFirst: [3, 2, 1],
344
+ excludedPrs: [],
345
+ cutIndex: 2,
346
+ positiveControlPrs: [1],
347
+ negativeControlPrs: [],
348
+ predicate: 'code-touching',
349
+ mergeCommitByPr,
350
+ })).toThrow(SplitCoverError);
351
+ });
352
+ });
353
+ // ─── 5. Fail-loud guards (§5 leakage + §9 producer mismatch) ─────────────────
354
+ describe('deriveAuthoredControls — fail-loud guards', () => {
355
+ it('throws when a positive fixture pr is held-out (∉ trainPrs) — an ADR-112 §5 leakage violation', async () => {
356
+ const rule = authoredRule('rule-leak', [posFixture(99, 'ch-leak')]);
357
+ await expect(derive({
358
+ rules: [rule],
359
+ split: splitWithTrain([1, 2], [99]), // 99 is held-out, not train
360
+ deps: scriptedDeps({ 'ch-leak': makeResult('differential-holds') }),
361
+ })).rejects.toThrow(/leakage/i);
362
+ });
363
+ it('throws when authored policy.positiveControlSide is not train (§6 producer mismatch)', async () => {
364
+ vi.mocked(getRulePolicy).mockReturnValueOnce({
365
+ labelScope: 'whole-window',
366
+ positiveControlSide: 'held-out',
367
+ exposureControlSide: 'held-out',
368
+ positiveControlGate: 'preimage-differential',
369
+ });
370
+ const rule = authoredRule('rule-pol', [posFixture(1, 'ch-1')]);
371
+ await expect(derive({
372
+ rules: [rule],
373
+ split: splitWithTrain([1]),
374
+ deps: scriptedDeps({ 'ch-1': makeResult('differential-holds') }),
375
+ })).rejects.toThrow(/positiveControlSide/);
376
+ });
377
+ it('throws when authored policy.positiveControlGate is not preimage-differential (§4 producer mismatch)', async () => {
378
+ vi.mocked(getRulePolicy).mockReturnValueOnce({
379
+ labelScope: 'whole-window',
380
+ positiveControlSide: 'train',
381
+ exposureControlSide: 'train',
382
+ positiveControlGate: 'none',
383
+ });
384
+ const rule = authoredRule('rule-pol2', [posFixture(1, 'ch-1')]);
385
+ await expect(derive({
386
+ rules: [rule],
387
+ split: splitWithTrain([1]),
388
+ deps: scriptedDeps({ 'ch-1': makeResult('differential-holds') }),
389
+ })).rejects.toThrow(/positiveControlGate/);
390
+ });
391
+ });
392
+ // ─── 6. Negatives are DECLARATIVE (no differential, no silence gate) ─────────
393
+ describe('deriveAuthoredControls — declarative negatives', () => {
394
+ it('emits every declared near-miss as {targetRuleId,filePath,matchedSpan} WITHOUT running the evaluator', async () => {
395
+ const rule = authoredRule('rule-neg', [], // no positive fixtures → the evaluator must never run
396
+ [negFixture('src/x.ts', 'L5-L9'), negFixture('src/y.ts', 'N[2]')]);
397
+ // A throwing evaluator proves the §4 differential/smoke-gate is NOT invoked for
398
+ // negatives — a near-miss is emitted even though no evaluator runs on it.
399
+ const result = await derive({
400
+ rules: [rule],
401
+ split: splitWithTrain([1]),
402
+ deps: throwingDeps,
403
+ });
404
+ expect(result.negative).toEqual([
405
+ { targetRuleId: 'rule-neg', filePath: 'src/x.ts', matchedSpan: 'L5-L9' },
406
+ { targetRuleId: 'rule-neg', filePath: 'src/y.ts', matchedSpan: 'N[2]' },
407
+ ]);
408
+ expect(result.positive).toEqual([]);
409
+ expect(result.nonEmissions).toEqual([]);
410
+ });
411
+ it('emits negatives alongside positives in stable rule/declared order', async () => {
412
+ const rule = authoredRule('rule-mixed', [posFixture(1, 'ch-1')], [negFixture('src/n.ts', 'L1')]);
413
+ const result = await derive({
414
+ rules: [rule],
415
+ split: splitWithTrain([1]),
416
+ deps: scriptedDeps({ 'ch-1': makeResult('differential-holds') }),
417
+ });
418
+ expect(result.positive).toEqual([
419
+ { pr: 1, targetRuleId: 'rule-mixed', filePath: 'src/a.ts', matchedSpan: 'L1-L2' },
420
+ ]);
421
+ expect(result.negative).toEqual([
422
+ { targetRuleId: 'rule-mixed', filePath: 'src/n.ts', matchedSpan: 'L1' },
423
+ ]);
424
+ });
425
+ });
426
+ // ─── 7. positiveControlGate present + frozen on both policies ────────────────
427
+ describe('positiveControlGate — present + frozen (§4 / strategy#777 Q3(ii))', () => {
428
+ it('mined gate is none, authored gate is preimage-differential', () => {
429
+ expect(getRulePolicy('mined').positiveControlGate).toBe('none');
430
+ expect(getRulePolicy('authored').positiveControlGate).toBe('preimage-differential');
431
+ });
432
+ it('the policy is frozen — a caller cannot flip the gate process-wide', () => {
433
+ const policy = getRulePolicy('authored');
434
+ expect(Object.isFrozen(policy)).toBe(true);
435
+ expect(() => Object.assign(policy, { positiveControlGate: 'none' })).toThrow();
436
+ });
437
+ it('deriveAuthoredControls reads the (real) authored policy and completes on valid input', async () => {
438
+ const rule = authoredRule('rule-ok', [posFixture(1, 'ch-1')]);
439
+ const result = await derive({
440
+ rules: [rule],
441
+ split: splitWithTrain([1]),
442
+ deps: scriptedDeps({ 'ch-1': makeResult('differential-holds') }),
443
+ });
444
+ expect(result.positive).toHaveLength(1);
445
+ });
446
+ });
447
+ // ─── 8. Sidecar provenance (D1 fold #1): the builder reads ONLY provenanceByRule ──
448
+ describe('deriveAuthoredControls — sidecar provenance (D1 fold #1)', () => {
449
+ it('throws when a rule has no provenance in provenanceByRule (sidecar miss)', async () => {
450
+ const rule = authoredRule('rule-sidecar-miss', [posFixture(1, 'ch-1')]);
451
+ await expect(deriveAuthoredControls({
452
+ rules: [rule],
453
+ split: splitWithTrain([1]),
454
+ provenanceByRule: new Map(), // empty: the rule's lessonHash is absent
455
+ deps: scriptedDeps({ 'ch-1': makeResult('differential-holds') }),
456
+ })).rejects.toThrow(/no provenance in provenanceByRule/);
457
+ });
458
+ it('throws when the sidecar provenance is MINED, never reading rule.legitimacy', async () => {
459
+ const rule = authoredRule('rule-sidecar-mined', [posFixture(1, 'ch-1')]);
460
+ // The sidecar entry is MINED even though the rule's (now-ignored) legitimacy is
461
+ // authored — proving the function reads the MAP, not rule.legitimacy.
462
+ const minedProv = { mergedPr: 1, reviewThread: 'pr#1', commitSha: sha(1) };
463
+ await expect(deriveAuthoredControls({
464
+ rules: [rule],
465
+ split: splitWithTrain([1]),
466
+ provenanceByRule: new Map([[rule.lessonHash, minedProv]]),
467
+ deps: scriptedDeps({ 'ch-1': makeResult('differential-holds') }),
468
+ })).rejects.toThrow(/is not authored/);
469
+ });
470
+ it('emits from a rule with NO legitimacy — the REAL assembly-seam shape (provenance only from the map)', async () => {
471
+ // The cert-assembly seam passes compiled rules that carry no `legitimacy` (stamped
472
+ // post-scoring); provenance rides the `c.provenance` sidecar. Drop legitimacy here so
473
+ // the happy path runs against that exact shape — proving the builder needs only the map.
474
+ // `ruleNoLegit` is typed `Omit<CompiledRule, 'legitimacy'>` — the field is gone at the
475
+ // type level (and at runtime), exactly the compiled-rule shape at the assembly seam.
476
+ const { legitimacy, ...ruleNoLegit } = authoredRule('rule-no-legit', [posFixture(1, 'ch-1')]);
477
+ const result = await deriveAuthoredControls({
478
+ rules: [ruleNoLegit],
479
+ split: splitWithTrain([1]),
480
+ provenanceByRule: new Map([[ruleNoLegit.lessonHash, legitimacy.provenance]]),
481
+ deps: scriptedDeps({ 'ch-1': makeResult('differential-holds') }),
482
+ });
483
+ expect(result.positive).toEqual([
484
+ { pr: 1, targetRuleId: 'rule-no-legit', filePath: 'src/a.ts', matchedSpan: 'L1-L2' },
485
+ ]);
486
+ });
487
+ });
488
+ // ─── Exported boundary guard (CR outside-diff): the schema must reject what the
489
+ // trichotomy makes structurally impossible, not merely admit any enum member. ──
490
+ describe('AuthoredNonEmissionSchema — outcome/class boundary guard', () => {
491
+ const base = { targetRuleId: 'rule-x', pr: 1, outcome: 'fix-shaped', class: 'illegitimate' };
492
+ it('accepts a well-formed non-emission (outcome → its classOf class)', () => {
493
+ expect(AuthoredNonEmissionSchema.safeParse(base).success).toBe(true);
494
+ });
495
+ it('rejects the EMITTING outcome — a non-emission can never carry differential-holds', () => {
496
+ expect(AuthoredNonEmissionSchema.safeParse({ ...base, outcome: 'differential-holds' }).success).toBe(false);
497
+ });
498
+ it('rejects a class that contradicts its outcome (classOf mismatch)', () => {
499
+ // needs-adjudication is `undecidable`, never `illegitimate`.
500
+ expect(AuthoredNonEmissionSchema.safeParse({
501
+ ...base,
502
+ outcome: 'needs-adjudication',
503
+ class: 'illegitimate',
504
+ }).success).toBe(false);
505
+ });
506
+ });
507
+ //# sourceMappingURL=authored-controls.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authored-controls.test.js","sourceRoot":"","sources":["../../src/spine/authored-controls.test.ts"],"names":[],"mappings":"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"}
@@ -12,11 +12,19 @@ export type ProducerKind = 'mined' | 'authored';
12
12
  * authored against the train slice).
13
13
  * - `exposureControlSide` — §5.3: which slice `positiveControlsExercised`
14
14
  * counts. Mirrors `positiveControlSide`.
15
+ * - `positiveControlGate` — §4 (strategy#777 Q3(ii)): how a positive control is
16
+ * admitted. Mined controls are corpus-firing evidence
17
+ * (`none`); authored controls must clear the
18
+ * preimage-differential gate (fire-on-preimage /
19
+ * silent-on-postimage). FROZEN-ENUM DATA only — no
20
+ * consumer branches on the table itself (Tenet 9); the
21
+ * authored-controls builder READS it as the §9 home.
15
22
  */
16
23
  export interface RulePolicy {
17
24
  readonly labelScope: 'held-out-only' | 'whole-window';
18
25
  readonly positiveControlSide: 'held-out' | 'train';
19
26
  readonly exposureControlSide: 'held-out' | 'train';
27
+ readonly positiveControlGate: 'none' | 'preimage-differential';
20
28
  }
21
29
  /**
22
30
  * ADR-112 §9 — resolve the override config for a producer kind. Pure +
@@ -1 +1 @@
1
- {"version":3,"file":"rule-policy.d.ts","sourceRoot":"","sources":["../../src/spine/rule-policy.ts"],"names":[],"mappings":"AAgBA,8EAA8E;AAC9E,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,UAAU,CAAC;AAEhD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,UAAU;IAIzB,QAAQ,CAAC,UAAU,EAAE,eAAe,GAAG,cAAc,CAAC;IACtD,QAAQ,CAAC,mBAAmB,EAAE,UAAU,GAAG,OAAO,CAAC;IACnD,QAAQ,CAAC,mBAAmB,EAAE,UAAU,GAAG,OAAO,CAAC;CACpD;AAcD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,UAAU,CAa5D"}
1
+ {"version":3,"file":"rule-policy.d.ts","sourceRoot":"","sources":["../../src/spine/rule-policy.ts"],"names":[],"mappings":"AAgBA,8EAA8E;AAC9E,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,UAAU,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,UAAU;IAIzB,QAAQ,CAAC,UAAU,EAAE,eAAe,GAAG,cAAc,CAAC;IACtD,QAAQ,CAAC,mBAAmB,EAAE,UAAU,GAAG,OAAO,CAAC;IACnD,QAAQ,CAAC,mBAAmB,EAAE,UAAU,GAAG,OAAO,CAAC;IACnD,QAAQ,CAAC,mBAAmB,EAAE,MAAM,GAAG,uBAAuB,CAAC;CAChE;AAgBD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,UAAU,CAa5D"}
@@ -17,11 +17,13 @@ const MINED_POLICY = Object.freeze({
17
17
  labelScope: 'held-out-only',
18
18
  positiveControlSide: 'held-out',
19
19
  exposureControlSide: 'held-out',
20
+ positiveControlGate: 'none',
20
21
  });
21
22
  const AUTHORED_POLICY = Object.freeze({
22
23
  labelScope: 'whole-window',
23
24
  positiveControlSide: 'train',
24
25
  exposureControlSide: 'train',
26
+ positiveControlGate: 'preimage-differential',
25
27
  });
26
28
  /**
27
29
  * ADR-112 §9 — resolve the override config for a producer kind. Pure +
@@ -1 +1 @@
1
- {"version":3,"file":"rule-policy.js","sourceRoot":"","sources":["../../src/spine/rule-policy.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,EAAE;AACF,+EAA+E;AAC/E,4EAA4E;AAC5E,kFAAkF;AAClF,+EAA+E;AAC/E,iEAAiE;AACjE,EAAE;AACF,gFAAgF;AAChF,gFAAgF;AAChF,0EAA0E;AAC1E,4EAA4E;AAC5E,6EAA6E;AAC7E,8EAA8E;AAC9E,iFAAiF;AA2BjF,MAAM,YAAY,GAAe,MAAM,CAAC,MAAM,CAAC;IAC7C,UAAU,EAAE,eAAe;IAC3B,mBAAmB,EAAE,UAAU;IAC/B,mBAAmB,EAAE,UAAU;CAChC,CAAC,CAAC;AAEH,MAAM,eAAe,GAAe,MAAM,CAAC,MAAM,CAAC;IAChD,UAAU,EAAE,cAAc;IAC1B,mBAAmB,EAAE,OAAO;IAC5B,mBAAmB,EAAE,OAAO;CAC7B,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAkB;IAC9C,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,OAAO;YACV,OAAO,YAAY,CAAC;QACtB,KAAK,UAAU;YACb,OAAO,eAAe,CAAC;QACzB,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,WAAW,GAAU,IAAI,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,uDAAuD,MAAM,CAAC,WAAW,CAAC,GAAG,CAC9E,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"rule-policy.js","sourceRoot":"","sources":["../../src/spine/rule-policy.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,EAAE;AACF,+EAA+E;AAC/E,4EAA4E;AAC5E,kFAAkF;AAClF,+EAA+E;AAC/E,iEAAiE;AACjE,EAAE;AACF,gFAAgF;AAChF,gFAAgF;AAChF,0EAA0E;AAC1E,4EAA4E;AAC5E,6EAA6E;AAC7E,8EAA8E;AAC9E,iFAAiF;AAmCjF,MAAM,YAAY,GAAe,MAAM,CAAC,MAAM,CAAC;IAC7C,UAAU,EAAE,eAAe;IAC3B,mBAAmB,EAAE,UAAU;IAC/B,mBAAmB,EAAE,UAAU;IAC/B,mBAAmB,EAAE,MAAM;CAC5B,CAAC,CAAC;AAEH,MAAM,eAAe,GAAe,MAAM,CAAC,MAAM,CAAC;IAChD,UAAU,EAAE,cAAc;IAC1B,mBAAmB,EAAE,OAAO;IAC5B,mBAAmB,EAAE,OAAO;IAC5B,mBAAmB,EAAE,uBAAuB;CAC7C,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAkB;IAC9C,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,OAAO;YACV,OAAO,YAAY,CAAC;QACtB,KAAK,UAAU;YACb,OAAO,eAAe,CAAC;QACzB,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,WAAW,GAAU,IAAI,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,uDAAuD,MAAM,CAAC,WAAW,CAAC,GAAG,CAC9E,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -1,23 +1,29 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import { getRulePolicy } from './rule-policy.js';
3
3
  describe('getRulePolicy (ADR-112 §9 single-home)', () => {
4
- it('mined → held-out-only label scope + held-out controls (the LIVE, byte-identical path)', () => {
4
+ it('mined → held-out-only label scope + held-out controls + no positive-control gate (the LIVE, byte-identical path)', () => {
5
5
  expect(getRulePolicy('mined')).toEqual({
6
6
  labelScope: 'held-out-only',
7
7
  positiveControlSide: 'held-out',
8
8
  exposureControlSide: 'held-out',
9
+ positiveControlGate: 'none',
9
10
  });
10
11
  });
11
- it('authored → whole-window labels + train-side controls (DEFINED for slice D; not yet wired live)', () => {
12
+ it('authored → whole-window labels + train-side controls + preimage-differential gate (DEFINED for slice C2b/D)', () => {
12
13
  expect(getRulePolicy('authored')).toEqual({
13
14
  labelScope: 'whole-window',
14
15
  positiveControlSide: 'train',
15
16
  exposureControlSide: 'train',
17
+ positiveControlGate: 'preimage-differential',
16
18
  });
17
19
  });
18
20
  it('mined and authored diverge on label scope (the §9 amendment the scorer reads in slice D)', () => {
19
21
  expect(getRulePolicy('mined').labelScope).not.toBe(getRulePolicy('authored').labelScope);
20
22
  });
23
+ it('mined and authored diverge on the positive-control gate (§4 — authored is differential-gated)', () => {
24
+ expect(getRulePolicy('mined').positiveControlGate).toBe('none');
25
+ expect(getRulePolicy('authored').positiveControlGate).toBe('preimage-differential');
26
+ });
21
27
  it('returns a frozen singleton — a caller cannot mutate policy resolution process-wide (#2259)', () => {
22
28
  const policy = getRulePolicy('mined');
23
29
  // Frozen ⇒ any property write throws in strict mode (ESM), so policy resolution can't be
@@ -1 +1 @@
1
- {"version":3,"file":"rule-policy.test.js","sourceRoot":"","sources":["../../src/spine/rule-policy.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,QAAQ,CAAC,wCAAwC,EAAE,GAAG,EAAE;IACtD,EAAE,CAAC,uFAAuF,EAAE,GAAG,EAAE;QAC/F,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;YACrC,UAAU,EAAE,eAAe;YAC3B,mBAAmB,EAAE,UAAU;YAC/B,mBAAmB,EAAE,UAAU;SAChC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gGAAgG,EAAE,GAAG,EAAE;QACxG,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC;YACxC,UAAU,EAAE,cAAc;YAC1B,mBAAmB,EAAE,OAAO;YAC5B,mBAAmB,EAAE,OAAO;SAC7B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0FAA0F,EAAE,GAAG,EAAE;QAClG,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,UAAU,CAAC,CAAC;IAC3F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4FAA4F,EAAE,GAAG,EAAE;QACpG,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QACtC,yFAAyF;QACzF,6FAA6F;QAC7F,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAC9E,+DAA+D;QAC/D,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"rule-policy.test.js","sourceRoot":"","sources":["../../src/spine/rule-policy.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,QAAQ,CAAC,wCAAwC,EAAE,GAAG,EAAE;IACtD,EAAE,CAAC,kHAAkH,EAAE,GAAG,EAAE;QAC1H,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;YACrC,UAAU,EAAE,eAAe;YAC3B,mBAAmB,EAAE,UAAU;YAC/B,mBAAmB,EAAE,UAAU;YAC/B,mBAAmB,EAAE,MAAM;SAC5B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6GAA6G,EAAE,GAAG,EAAE;QACrH,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC;YACxC,UAAU,EAAE,cAAc;YAC1B,mBAAmB,EAAE,OAAO;YAC5B,mBAAmB,EAAE,OAAO;YAC5B,mBAAmB,EAAE,uBAAuB;SAC7C,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0FAA0F,EAAE,GAAG,EAAE;QAClG,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,UAAU,CAAC,CAAC;IAC3F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+FAA+F,EAAE,GAAG,EAAE;QACvG,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAChE,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IACtF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4FAA4F,EAAE,GAAG,EAAE;QACpG,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QACtC,yFAAyF;QACzF,6FAA6F;QAC7F,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAC9E,+DAA+D;QAC/D,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}