@mmnto/cli 1.69.0 → 1.70.1

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,395 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import { wrapUntrustedXml } from '@mmnto/totem';
3
+ import { assertLiveLlmAllowed, assertPipelineProductive, buildClassifyUserPrompt, buildExtractUserPrompt, buildReplayProvenance, LiveDraftClassifier, LiveDraftExtractor, LiveLlmInCiError, LlmAdapterConfigError, MINER_CLASSIFY_SYSTEM_PROMPT, MINER_EXTRACT_SYSTEM_PROMPT, parseClassifierOutput, parseExtractorOutput, SystemicPipelineError, verifyLlmAdapterConfig, wrapUntrusted, } from './spine-llm-adapters.js';
4
+ import { ClassifierResultLocalSchema, computeArtifactHash, RecordingDraftClassifier, RecordingDraftExtractor, ReplayArtifactSchema, ReplayDraftClassifier, ReplayDraftExtractor, ReplayProvenanceSchema, ReplayRecordSink, } from './spine-llm-replay.js';
5
+ const MERGE_SHA = 'a'.repeat(40);
6
+ // ─── Stub LLM seam (deterministic; NO network, NO real LLM) ──────────────────
7
+ function stubInvoke(content) {
8
+ return () => Promise.resolve({ content, inputTokens: null, outputTokens: null, durationMs: 0 });
9
+ }
10
+ /** A seam that throws — models a dead provider / network failure on every call. */
11
+ function throwingInvoke() {
12
+ return () => Promise.reject(new Error('boom: provider unreachable'));
13
+ }
14
+ /** A seam whose output depends on the prompt — lets one stub serve many inputs. */
15
+ function routedInvoke(route) {
16
+ return (opts) => Promise.resolve({
17
+ content: route(opts.prompt),
18
+ inputTokens: null,
19
+ outputTokens: null,
20
+ durationMs: 0,
21
+ });
22
+ }
23
+ // Construct live adapters with a NON-CI env by default so the fold-H guard does
24
+ // not trip when this very suite runs under CI.
25
+ const NO_CI = {};
26
+ function makeExtractor(invoke, env = NO_CI) {
27
+ return new LiveDraftExtractor({
28
+ invoke,
29
+ model: 'test-model',
30
+ cwd: '/tmp',
31
+ totemDir: '.totem',
32
+ provider: 'anthropic',
33
+ credentialPresent: true,
34
+ env,
35
+ });
36
+ }
37
+ function makeClassifier(invoke, env = NO_CI) {
38
+ return new LiveDraftClassifier({
39
+ invoke,
40
+ model: 'test-model',
41
+ cwd: '/tmp',
42
+ totemDir: '.totem',
43
+ provider: 'anthropic',
44
+ credentialPresent: true,
45
+ env,
46
+ });
47
+ }
48
+ function content(opts) {
49
+ return {
50
+ pr: opts?.pr ?? 100,
51
+ mergeCommitSha: MERGE_SHA,
52
+ threads: opts?.threads ?? [
53
+ {
54
+ path: 'a.ts',
55
+ isResolved: false,
56
+ isOutdated: false,
57
+ comments: [{ author: 'jane', body: 'no exec' }],
58
+ },
59
+ ],
60
+ };
61
+ }
62
+ function draft(dslSource = '**Pattern:** no-foo') {
63
+ return {
64
+ provenance: { mergedPr: 100, reviewThread: 'pulls/100/comments', commitSha: MERGE_SHA },
65
+ dslSource,
66
+ };
67
+ }
68
+ // ─── 1. Extractor output parse ────────────────────────
69
+ describe('parseExtractorOutput', () => {
70
+ it('parses a JSON array of DSL bodies', () => {
71
+ expect(parseExtractorOutput('["**Pattern:** a", "**Pattern:** b"]')).toEqual([
72
+ '**Pattern:** a',
73
+ '**Pattern:** b',
74
+ ]);
75
+ });
76
+ it('treats the NONE sentinel (any case) as an empty draft', () => {
77
+ expect(parseExtractorOutput('NONE')).toEqual([]);
78
+ expect(parseExtractorOutput(' none ')).toEqual([]);
79
+ });
80
+ it('returns [] on empty / non-JSON / non-array output (fail-soft, never throws)', () => {
81
+ expect(parseExtractorOutput('')).toEqual([]);
82
+ expect(parseExtractorOutput('here are some lessons:')).toEqual([]);
83
+ expect(parseExtractorOutput('{"disposition":"structural"}')).toEqual([]);
84
+ expect(parseExtractorOutput('42')).toEqual([]);
85
+ });
86
+ it('strips a markdown code fence and parses the inner JSON', () => {
87
+ expect(parseExtractorOutput('```json\n["**Pattern:** x"]\n```')).toEqual(['**Pattern:** x']);
88
+ });
89
+ it('filters non-string / empty / whitespace elements and trims', () => {
90
+ expect(parseExtractorOutput('["**Pattern:** a", 7, "", " ", " **Pattern:** b "]')).toEqual([
91
+ '**Pattern:** a',
92
+ '**Pattern:** b',
93
+ ]);
94
+ });
95
+ });
96
+ // ─── 2. Classifier output parse (fold G — closed set) ─
97
+ describe('parseClassifierOutput (fold G)', () => {
98
+ it('returns classified for a single unambiguous structural/behavioral label', () => {
99
+ expect(parseClassifierOutput('{"disposition":"structural"}')).toEqual({
100
+ disposition: 'structural',
101
+ dispositionSource: 'classified',
102
+ });
103
+ expect(parseClassifierOutput('```json\n{"disposition":"behavioral"}\n```')).toEqual({
104
+ disposition: 'behavioral',
105
+ dispositionSource: 'classified',
106
+ });
107
+ });
108
+ it('safe-defaults on refusal / invalid JSON / prose', () => {
109
+ expect(parseClassifierOutput('I cannot classify this.')).toEqual({
110
+ disposition: 'behavioral',
111
+ dispositionSource: 'error-default',
112
+ });
113
+ expect(parseClassifierOutput('')).toEqual({
114
+ disposition: 'behavioral',
115
+ dispositionSource: 'error-default',
116
+ });
117
+ });
118
+ it('safe-defaults on missing / out-of-set / wrong-typed / multi-valued label', () => {
119
+ const sd = { disposition: 'behavioral', dispositionSource: 'error-default' };
120
+ expect(parseClassifierOutput('{"foo":"bar"}')).toEqual(sd); // missing label
121
+ expect(parseClassifierOutput('{"disposition":"maybe"}')).toEqual(sd); // out of set
122
+ expect(parseClassifierOutput('{"disposition":["structural","behavioral"]}')).toEqual(sd); // multi
123
+ expect(parseClassifierOutput('["structural"]')).toEqual(sd); // array, not object
124
+ expect(parseClassifierOutput('null')).toEqual(sd);
125
+ });
126
+ it('never mints the illegal {structural, error-default} pair (the local schema rejects it)', () => {
127
+ expect(() => ClassifierResultLocalSchema.parse({
128
+ disposition: 'structural',
129
+ dispositionSource: 'error-default',
130
+ })).toThrow();
131
+ });
132
+ });
133
+ // ─── 2b. Fail-loud on UNEXPECTED parse errors (Tenet 4) ──────────────────────
134
+ describe('fail-loud on unexpected (non-SyntaxError) parse errors', () => {
135
+ afterEach(() => vi.restoreAllMocks());
136
+ it('parseExtractorOutput rethrows a non-SyntaxError (a real bug fails loud, not fail-soft)', () => {
137
+ vi.spyOn(JSON, 'parse').mockImplementationOnce(() => {
138
+ throw new TypeError('unexpected non-syntax failure');
139
+ });
140
+ expect(() => parseExtractorOutput('["**Pattern:** a"]')).toThrow(TypeError);
141
+ });
142
+ it('parseClassifierOutput rethrows a non-SyntaxError', () => {
143
+ vi.spyOn(JSON, 'parse').mockImplementationOnce(() => {
144
+ throw new TypeError('unexpected non-syntax failure');
145
+ });
146
+ expect(() => parseClassifierOutput('{"disposition":"structural"}')).toThrow(TypeError);
147
+ });
148
+ it('an unexpected parser error propagates out of draft (fail-loud on a bug; the per-PR catch covers the INVOKE only)', async () => {
149
+ const ext = makeExtractor(stubInvoke('["**Pattern:** a"]'));
150
+ vi.spyOn(JSON, 'parse').mockImplementationOnce(() => {
151
+ throw new TypeError('unexpected non-syntax failure');
152
+ });
153
+ await expect(ext.draft(content())).rejects.toThrow(TypeError);
154
+ // the invoke SUCCEEDED — it is not miscounted as a dead-provider failure.
155
+ expect(ext.succeeded).toBe(1);
156
+ });
157
+ });
158
+ // ─── 3. Live extractor (stubbed seam) ─────────────────
159
+ describe('LiveDraftExtractor', () => {
160
+ it('drafts parsed DSL bodies from the LLM and counts a success', async () => {
161
+ const ext = makeExtractor(stubInvoke('["**Pattern:** no-exec"]'));
162
+ await expect(ext.draft(content())).resolves.toEqual(['**Pattern:** no-exec']);
163
+ expect(ext.attempts).toBe(1);
164
+ expect(ext.succeeded).toBe(1);
165
+ });
166
+ it('returns [] and counts a failure when the live invoke throws (per-PR fail-soft)', async () => {
167
+ const ext = makeExtractor(throwingInvoke());
168
+ await expect(ext.draft(content())).resolves.toEqual([]);
169
+ expect(ext.attempts).toBe(1);
170
+ expect(ext.succeeded).toBe(0);
171
+ });
172
+ it('a successful-but-empty (NONE) call still counts as succeeded (genuine sparsity ≠ failure)', async () => {
173
+ const ext = makeExtractor(stubInvoke('NONE'));
174
+ await expect(ext.draft(content())).resolves.toEqual([]);
175
+ expect(ext.succeeded).toBe(1);
176
+ });
177
+ it('isolates a per-item failure — other items still succeed, no throw', async () => {
178
+ const ext = makeExtractor(routedInvoke((p) => p.includes('666')
179
+ ? (() => {
180
+ throw new Error('x');
181
+ })()
182
+ : '["**Pattern:** ok"]'));
183
+ await expect(ext.draft(content({ pr: 1 }))).resolves.toEqual(['**Pattern:** ok']);
184
+ await expect(ext.draft(content({ pr: 666 }))).resolves.toEqual([]);
185
+ expect(ext.attempts).toBe(2);
186
+ expect(ext.succeeded).toBe(1);
187
+ });
188
+ });
189
+ // ─── 4. Live classifier (stubbed seam) ────────────────
190
+ describe('LiveDraftClassifier', () => {
191
+ it('classifies from the LLM and counts a success', async () => {
192
+ const clf = makeClassifier(stubInvoke('{"disposition":"structural"}'));
193
+ await expect(clf.classify(draft())).resolves.toEqual({
194
+ disposition: 'structural',
195
+ dispositionSource: 'classified',
196
+ });
197
+ expect(clf.succeeded).toBe(1);
198
+ });
199
+ it('safe-defaults and counts a failure when the invoke throws', async () => {
200
+ const clf = makeClassifier(throwingInvoke());
201
+ await expect(clf.classify(draft())).resolves.toEqual({
202
+ disposition: 'behavioral',
203
+ dispositionSource: 'error-default',
204
+ });
205
+ expect(clf.attempts).toBe(1);
206
+ expect(clf.succeeded).toBe(0);
207
+ });
208
+ });
209
+ // ─── 5. Fold H — no live LLM in CI ────────────────────
210
+ describe('assertLiveLlmAllowed (fold H)', () => {
211
+ it('throws under CI without the explicit override', () => {
212
+ expect(() => assertLiveLlmAllowed({ CI: 'true' })).toThrow(LiveLlmInCiError);
213
+ });
214
+ it('allows CI with ALLOW_LIVE_LLM_IN_CI, and any non-CI env', () => {
215
+ expect(() => assertLiveLlmAllowed({ CI: 'true', ALLOW_LIVE_LLM_IN_CI: '1' })).not.toThrow();
216
+ expect(() => assertLiveLlmAllowed({})).not.toThrow();
217
+ });
218
+ it('a live adapter refuses to construct under CI (the guard runs at construction)', () => {
219
+ expect(() => makeExtractor(stubInvoke('NONE'), { CI: 'true' })).toThrow(LiveLlmInCiError);
220
+ expect(() => makeClassifier(stubInvoke('NONE'), { CI: 'true' })).toThrow(LiveLlmInCiError);
221
+ });
222
+ });
223
+ // ─── 6. Fold C — construction-time config + end-of-run floor ──────────────────
224
+ describe('verifyLlmAdapterConfig (fold C, construction-time)', () => {
225
+ const ok = { provider: 'anthropic', model: 'm', credentialPresent: true, systemPrompt: 'sp' };
226
+ it('passes when all preconditions are present', () => {
227
+ expect(() => verifyLlmAdapterConfig(ok)).not.toThrow();
228
+ });
229
+ it('throws naming every missing precondition (no live call)', () => {
230
+ try {
231
+ verifyLlmAdapterConfig({
232
+ provider: '',
233
+ model: '',
234
+ credentialPresent: false,
235
+ systemPrompt: '',
236
+ });
237
+ throw new Error('expected throw');
238
+ }
239
+ catch (err) {
240
+ expect(err).toBeInstanceOf(LlmAdapterConfigError);
241
+ expect(err.problems).toHaveLength(4);
242
+ }
243
+ });
244
+ it('throws on a missing credential alone (the dead-provider precursor)', () => {
245
+ expect(() => verifyLlmAdapterConfig({ ...ok, credentialPresent: false })).toThrow(LlmAdapterConfigError);
246
+ });
247
+ it('the live adapter constructor enforces the FULL check (greptile #2211) — credential-absent throws AT construction', () => {
248
+ const deps = {
249
+ invoke: stubInvoke('NONE'),
250
+ model: 'm',
251
+ cwd: '/tmp',
252
+ totemDir: '.totem',
253
+ provider: 'anthropic',
254
+ credentialPresent: false,
255
+ env: NO_CI,
256
+ };
257
+ expect(() => new LiveDraftExtractor(deps)).toThrow(LlmAdapterConfigError);
258
+ expect(() => new LiveDraftClassifier(deps)).toThrow(LlmAdapterConfigError);
259
+ });
260
+ });
261
+ describe('assertPipelineProductive (fold C, end-of-run floor)', () => {
262
+ it('throws when ≥1 attempted and 0 succeeded (dead provider)', () => {
263
+ expect(() => assertPipelineProductive({ attempted: 5, succeeded: 0 })).toThrow(SystemicPipelineError);
264
+ });
265
+ it('does NOT throw on genuine sparsity (calls succeeded, just empty)', () => {
266
+ expect(() => assertPipelineProductive({ attempted: 5, succeeded: 5 })).not.toThrow();
267
+ expect(() => assertPipelineProductive({ attempted: 5, succeeded: 1 })).not.toThrow();
268
+ });
269
+ it('does NOT throw when nothing was attempted', () => {
270
+ expect(() => assertPipelineProductive({ attempted: 0, succeeded: 0 })).not.toThrow();
271
+ });
272
+ it('integrates: an all-failing extractor trips the floor; an all-empty one does not', async () => {
273
+ const dead = makeExtractor(throwingInvoke());
274
+ for (const pr of [1, 2, 3])
275
+ await dead.draft(content({ pr }));
276
+ expect(() => assertPipelineProductive({ attempted: dead.attempts, succeeded: dead.succeeded })).toThrow(SystemicPipelineError);
277
+ const sparse = makeExtractor(stubInvoke('NONE'));
278
+ for (const pr of [1, 2, 3])
279
+ await sparse.draft(content({ pr }));
280
+ expect(() => assertPipelineProductive({ attempted: sparse.attempts, succeeded: sparse.succeeded })).not.toThrow();
281
+ });
282
+ });
283
+ // ─── 7. Fold F — provenance + integrity coupling ──────
284
+ describe('buildReplayProvenance (fold F)', () => {
285
+ const input = {
286
+ extractSystemPrompt: MINER_EXTRACT_SYSTEM_PROMPT,
287
+ classifySystemPrompt: MINER_CLASSIFY_SYSTEM_PROMPT,
288
+ provider: 'anthropic',
289
+ model: 'claude-x',
290
+ temperature: 0,
291
+ orchestratorVersion: 'orch-1',
292
+ totemVersion: '1.69.0',
293
+ };
294
+ it('is deterministic and schema-valid', () => {
295
+ const a = buildReplayProvenance(input);
296
+ const b = buildReplayProvenance(input);
297
+ expect(a).toEqual(b);
298
+ expect(() => ReplayProvenanceSchema.parse(a)).not.toThrow();
299
+ expect(a.provider).toBe('anthropic');
300
+ expect(a.adapterKind).toBe('extractor+classifier');
301
+ });
302
+ it('a prompt edit flips both prompt hashes (forces a re-record)', () => {
303
+ const base = buildReplayProvenance(input);
304
+ const edited = buildReplayProvenance({
305
+ ...input,
306
+ extractSystemPrompt: MINER_EXTRACT_SYSTEM_PROMPT + ' edit',
307
+ });
308
+ expect(edited.promptTemplateHash).not.toBe(base.promptTemplateHash);
309
+ expect(edited.systemPromptHash).not.toBe(base.systemPromptHash);
310
+ });
311
+ it('a provenance edit flips the whole-artifact integrity hash (fold F gate)', () => {
312
+ const sink = new ReplayRecordSink();
313
+ const base = computeArtifactHash(sink.freeze(buildReplayProvenance(input)));
314
+ const edited = computeArtifactHash(sink.freeze(buildReplayProvenance({ ...input, model: 'claude-y' })));
315
+ expect(edited).not.toBe(base);
316
+ });
317
+ });
318
+ // ─── 8. End-to-end: live adapter composes with the 5b-i record/replay scaffold ─
319
+ describe('live adapter ∘ 5b-i record/replay', () => {
320
+ it('records a live draft then replays it byte-identically, zero further LLM calls', async () => {
321
+ const sink = new ReplayRecordSink();
322
+ const live = makeExtractor(stubInvoke('["**Pattern:** no-exec"]'));
323
+ const recording = new RecordingDraftExtractor(live, sink);
324
+ const recorded = await recording.draft(content());
325
+ const provenance = buildReplayProvenance({
326
+ extractSystemPrompt: live.systemPrompt,
327
+ classifySystemPrompt: MINER_CLASSIFY_SYSTEM_PROMPT,
328
+ provider: 'anthropic',
329
+ model: 'test-model',
330
+ temperature: 0,
331
+ orchestratorVersion: 'orch-1',
332
+ totemVersion: '1.69.0',
333
+ });
334
+ const artifact = sink.freeze(provenance);
335
+ const expectedHash = computeArtifactHash(artifact);
336
+ const replay = new ReplayDraftExtractor(ReplayArtifactSchema.parse(artifact), expectedHash);
337
+ await expect(replay.draft(content())).resolves.toEqual(recorded);
338
+ });
339
+ it('records and replays a live classification', async () => {
340
+ const sink = new ReplayRecordSink();
341
+ const live = makeClassifier(stubInvoke('{"disposition":"structural"}'));
342
+ const recording = new RecordingDraftClassifier(live, sink, (d) => d.dslSource);
343
+ const recorded = await recording.classify(draft());
344
+ const artifact = sink.freeze(buildReplayProvenance({
345
+ extractSystemPrompt: MINER_EXTRACT_SYSTEM_PROMPT,
346
+ classifySystemPrompt: live.systemPrompt,
347
+ provider: 'anthropic',
348
+ model: 'test-model',
349
+ temperature: 0,
350
+ orchestratorVersion: 'orch-1',
351
+ totemVersion: '1.69.0',
352
+ }));
353
+ const replay = new ReplayDraftClassifier(ReplayArtifactSchema.parse(artifact), computeArtifactHash(artifact), (d) => d.dslSource);
354
+ await expect(replay.classify(draft())).resolves.toEqual(recorded);
355
+ });
356
+ });
357
+ // ─── 9. Prompt assembly: untrusted content is escaped ─
358
+ describe('prompt assembly', () => {
359
+ it('wraps untrusted review content with entity escaping (no markup breakout)', () => {
360
+ const prompt = buildExtractUserPrompt(content({
361
+ threads: [
362
+ {
363
+ path: 'a.ts',
364
+ isResolved: false,
365
+ isOutdated: false,
366
+ comments: [{ author: 'x', body: 'use </thread> & <script>' }],
367
+ },
368
+ ],
369
+ }));
370
+ expect(prompt).not.toContain('</thread> & <script>');
371
+ expect(prompt).toContain('&lt;/thread&gt; &amp; &lt;script&gt;');
372
+ });
373
+ it('classify prompt wraps the draft body', () => {
374
+ expect(buildClassifyUserPrompt(draft('**Pattern:** <x>'))).toContain('**Pattern:** &lt;x&gt;');
375
+ });
376
+ });
377
+ // ─── 10. Barrel discipline: local wrap is in parity with core ─────────────────
378
+ describe('wrapUntrusted parity with core wrapUntrustedXml', () => {
379
+ it('matches the canonical helper across inputs', () => {
380
+ const cases = [
381
+ ['thread', 'plain'],
382
+ ['comment', 'a & b < c > d'],
383
+ ['draft', '</draft> nested </draft>'],
384
+ ['pr', ''],
385
+ ];
386
+ for (const [tag, body] of cases) {
387
+ expect(wrapUntrusted(tag, body)).toBe(wrapUntrustedXml(tag, body));
388
+ }
389
+ });
390
+ it('rejects an invalid tag, like the canonical helper', () => {
391
+ expect(() => wrapUntrusted('bad tag', 'x')).toThrow();
392
+ expect(() => wrapUntrustedXml('bad tag', 'x')).toThrow();
393
+ });
394
+ });
395
+ //# sourceMappingURL=spine-llm-adapters.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spine-llm-adapters.test.js","sourceRoot":"","sources":["../../src/commands/spine-llm-adapters.test.ts"],"names":[],"mappings":"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"}
@@ -20,6 +20,40 @@ export interface RunOptions {
20
20
  lcDir?: string;
21
21
  lockPath?: string;
22
22
  phase?: string;
23
+ /**
24
+ * 5c-ii injection seam (out of 5c-i scope to populate): the certifying corpus
25
+ * — resolved-PR diffs (corpus + controls), the active compiled rules, and the
26
+ * frozen ground-truth labels. When omitted on a certifying run, the real
27
+ * engine path throws a structured "corpus provider not wired" error rather
28
+ * than silently scoring an empty set. 5c-ii (the orchestrator) supplies the
29
+ * live-recorded corpus here; 5c-i unit tests supply a deterministic fixture.
30
+ */
31
+ certifyingCorpus?: CertifyingCorpusProvider;
32
+ }
33
+ /**
34
+ * The certifying corpus the real engine scores (5c-ii supplies this; 5c-i
35
+ * defines the seam + the deterministic engine that consumes it). Returns the
36
+ * active compiled rules (archived MUST be excluded — fold-F throws otherwise),
37
+ * the resolved-PR diffs (corpus + positive/negative controls), and the frozen
38
+ * ground-truth labels keyed by firingLabelId.
39
+ */
40
+ export type CertifyingCorpusProvider = (lock: WindtunnelLock) => Promise<CertifyingCorpus> | CertifyingCorpus;
41
+ export interface CertifyingCorpus {
42
+ rules: import('@mmnto/totem').CompiledRule[];
43
+ prDiffs: import('@mmnto/totem').ResolvedPrDiff[];
44
+ groundTruth: Map<string, import('@mmnto/totem').GroundTruthLabel>;
45
+ }
46
+ /** Internal shape the run command's scorer consumes (engine-agnostic). */
47
+ interface EngineResult {
48
+ mintedRuleIds: string[];
49
+ firings: import('@mmnto/totem').RuleFiring[];
50
+ groundTruth: Map<string, import('@mmnto/totem').GroundTruthLabel>;
51
+ positiveControlTargets: Array<{
52
+ pr: number;
53
+ targetRuleId: string;
54
+ }>;
55
+ /** C2 — real touched-file exposure (0 for the harness mock). */
56
+ filesTouchedInWindow: number;
23
57
  }
24
58
  /**
25
59
  * `totem spine windtunnel run`
@@ -92,5 +126,25 @@ export declare function enumeratePrMetas(asOfCommit: string, lcDir: string, safe
92
126
  parseRevertSha: (body: string) => string | undefined;
93
127
  isBotIdentity: (author: string) => boolean;
94
128
  }): PrMeta[];
129
+ /**
130
+ * Run the REAL engine for the certifying phase (5c-i — #2189 item 1).
131
+ *
132
+ * Replaces the mock for `--phase certifying`: drives each resolved-PR diff
133
+ * through `buildFirings` (core), which runs `enrichWithAstContext` +
134
+ * `applyAstRulesToAdditions` with the shared post-image `readStrategy` (S1/C1)
135
+ * and maps every violation to a `RuleFiring` (content-based labelId). Then:
136
+ * - **fold-F**: `buildFirings` throws if any archived rule is in the scored set
137
+ * (the engine never runs on an archived rule).
138
+ * - **A1 (fold-D)**: `assertUniqueFiringLabels` hard-gates labelId uniqueness
139
+ * BEFORE scoring (throws on collision, surfacing the offending refs).
140
+ * - **C2**: `filesTouchedInWindow` is the real distinct-file exposure.
141
+ * - **fold-H**: neg-control firings flow through as `controlKind:'negative'`;
142
+ * unlabeled firings route to needsAdjudication via the scorer.
143
+ *
144
+ * The corpus itself (resolved-PR diffs + active rules + frozen ground truth) is
145
+ * supplied by the 5c-ii orchestrator via the `certifyingCorpus` seam. 5c-i owns
146
+ * the deterministic engine; it does NOT fetch/compile live data (out of scope).
147
+ */
148
+ export declare function runCertifyingEngine(lock: WindtunnelLock, readStrategy: (file: string) => Promise<string | null>, corpusProvider?: CertifyingCorpusProvider): Promise<EngineResult>;
95
149
  export {};
96
150
  //# sourceMappingURL=spine-windtunnel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"spine-windtunnel.d.ts","sourceRoot":"","sources":["../../src/commands/spine-windtunnel.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAuB,cAAc,EAAE,MAAM,cAAc,CAAC;AAShF,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAoFtE;AAID,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAmKhE;AAID,KAAK,UAAU,GAAG,cAAc,cAAc,EAAE,QAAQ,CAAC;AAEzD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,UAAU,GACnB,OAAO,CAWT;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,UAAU,GACnB,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkB1C;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,GAAG,IAAI,CA0DhG;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EAAE,EACrB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,UAAU,GACnB,MAAM,GAAG,IAAI,CA+Cf;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EAAE,EACrB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,UAAU,GACnB,IAAI,CAmBN;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,UAAU,GACnB,OAAO,CAAC,IAAI,CAAC,CAqGf;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,UAAU,EACpB,OAAO,EAAE;IACP,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAClD,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACrD,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;CAC5C,GACA,MAAM,EAAE,CA+DV"}
1
+ {"version":3,"file":"spine-windtunnel.d.ts","sourceRoot":"","sources":["../../src/commands/spine-windtunnel.ts"],"names":[],"mappings":"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"}
@@ -149,12 +149,16 @@ export async function runCommand(opts) {
149
149
  // simple null-returning strategy (all files → skip classification = fail-open).
150
150
  // When lcDir is provided, resolve post-image blobs from the lc clone.
151
151
  const readStrategy = buildReadStrategy(lcDir, lock.corpus.selectionRule.asOfCommit, safeExec);
152
- // Enrich with AST context for any additions (harness: no diff, so no additions).
153
- // In the real certifying run, the caller would build additions from PR diffs
154
- // and pass them through enrichWithAstContext + applyAstRulesToAdditions with
155
- // the shared readStrategy (S1/C1 same content for regex astContext + AST).
156
- // Run the engine harness phase uses mock engines.
157
- const { mintedRuleIds, firings, groundTruth, positiveControlTargets } = await runMockEngine(lock, readStrategy);
152
+ // Run the engine. Harness phase mock engine (no real rules yet). Certifying
153
+ // phase the REAL engine path (5c-i): build additions from each resolved-PR
154
+ // diff enrichWithAstContext + applyAstRulesToAdditions with the shared
155
+ // post-image readStrategy RuleFiring[] A1 unique-label hard-gate score.
156
+ // C2: filesTouchedInWindow is the real exposure computed from the diffs (no
157
+ // longer the hard-coded 0).
158
+ const engineResult = lock.phase === 'certifying'
159
+ ? await runCertifyingEngine(lock, readStrategy, opts.certifyingCorpus)
160
+ : await runMockEngineAdapter(lock, readStrategy);
161
+ const { mintedRuleIds, firings, groundTruth, positiveControlTargets, filesTouchedInWindow } = engineResult;
158
162
  // Score
159
163
  const verdict = scoreWindtunnel({
160
164
  firings,
@@ -169,7 +173,7 @@ export async function runCommand(opts) {
169
173
  },
170
174
  actualExposure: {
171
175
  activeRulesEvaluated: mintedRuleIds.length,
172
- filesTouchedInWindow: 0,
176
+ filesTouchedInWindow,
173
177
  positiveControlsExercised: positiveControlTargets.length,
174
178
  },
175
179
  });
@@ -531,4 +535,84 @@ async function runMockEngine(lock, _readStrategy) {
531
535
  }
532
536
  return { mintedRuleIds, firings, groundTruth, positiveControlTargets };
533
537
  }
538
+ /** Adapt the harness mock engine to the EngineResult shape (filesTouched = 0). */
539
+ async function runMockEngineAdapter(lock, readStrategy) {
540
+ const mock = await runMockEngine(lock, readStrategy);
541
+ return { ...mock, filesTouchedInWindow: 0 };
542
+ }
543
+ // ─── Real engine (certifying phase, 5c-i) ────────────
544
+ /**
545
+ * Run the REAL engine for the certifying phase (5c-i — #2189 item 1).
546
+ *
547
+ * Replaces the mock for `--phase certifying`: drives each resolved-PR diff
548
+ * through `buildFirings` (core), which runs `enrichWithAstContext` +
549
+ * `applyAstRulesToAdditions` with the shared post-image `readStrategy` (S1/C1)
550
+ * and maps every violation to a `RuleFiring` (content-based labelId). Then:
551
+ * - **fold-F**: `buildFirings` throws if any archived rule is in the scored set
552
+ * (the engine never runs on an archived rule).
553
+ * - **A1 (fold-D)**: `assertUniqueFiringLabels` hard-gates labelId uniqueness
554
+ * BEFORE scoring (throws on collision, surfacing the offending refs).
555
+ * - **C2**: `filesTouchedInWindow` is the real distinct-file exposure.
556
+ * - **fold-H**: neg-control firings flow through as `controlKind:'negative'`;
557
+ * unlabeled firings route to needsAdjudication via the scorer.
558
+ *
559
+ * The corpus itself (resolved-PR diffs + active rules + frozen ground truth) is
560
+ * supplied by the 5c-ii orchestrator via the `certifyingCorpus` seam. 5c-i owns
561
+ * the deterministic engine; it does NOT fetch/compile live data (out of scope).
562
+ */
563
+ export async function runCertifyingEngine(lock, readStrategy, corpusProvider) {
564
+ const { buildFirings, assertUniqueFiringLabels, resolveGitRoot, TotemError, FiringLabelCollisionError, } = await import('@mmnto/totem');
565
+ if (!corpusProvider) {
566
+ // No silent empty-set scoring: a certifying run with no corpus provider is a
567
+ // wiring error (5c-ii supplies it). Fail loud + actionable (Tenet 4).
568
+ throw new TotemError('CONFIG_INVALID', 'Wind-tunnel certifying run: no certifying-corpus provider wired (5c-ii orchestration).', 'The deterministic real-engine firing path (5c-i) is in place, but the corpus ' +
569
+ '(resolved-PR diffs + compiled rules + ground truth) is supplied by the 5c-ii ' +
570
+ 'orchestrator. Run via the certifying orchestrator once it lands.');
571
+ }
572
+ const corpus = await corpusProvider(lock);
573
+ const cwd = resolveGitRoot(process.cwd()) ?? process.cwd();
574
+ const ruleEngineCtx = {
575
+ logger: { warn: (msg) => console.error(`[WindtunnelRun] ${msg}`) },
576
+ state: { hasWarnedShieldContext: false },
577
+ };
578
+ // buildFirings runs fold-F (archived assert) internally before the engine; its
579
+ // only throw is ArchivedRuleInScopeError, which propagates. A1 (labelId
580
+ // collision) is deliberately NOT raised here — it is the caller's pre-score gate
581
+ // below (assertUniqueFiringLabels), so the structured per-collision report is
582
+ // threaded there rather than swallowed at construction. (greptile #2215 P2.)
583
+ const built = await buildFirings({
584
+ rules: corpus.rules,
585
+ prDiffs: corpus.prDiffs,
586
+ cwd,
587
+ readStrategy,
588
+ ruleEngineCtx,
589
+ onWarn: (msg) => console.error(`[WindtunnelRun] ${msg}`),
590
+ });
591
+ // A1 (fold-D): hard-gate labelId uniqueness BEFORE scoring (Tenet 4).
592
+ try {
593
+ assertUniqueFiringLabels(built.firings);
594
+ }
595
+ catch (err) {
596
+ if (err instanceof FiringLabelCollisionError) {
597
+ console.error(`[WindtunnelRun] A1 firing-label collision (fold-D):`);
598
+ for (const c of err.collisions) {
599
+ console.error(` • ${c.labelId.slice(0, 12)}… ×${c.evidenceRefs.length}`);
600
+ }
601
+ throw new TotemError('CONFIG_INVALID', err.message, 'A firing-label collision voids the run — the labelId→evidenceRef join would overwrite. ' +
602
+ 'Measure on the frozen corpus; if collisions occur, extend the label with a diff-hunk span (NOT an ordinal).', err);
603
+ }
604
+ throw err;
605
+ }
606
+ const mintedRuleIds = corpus.rules.map((r) => r.lessonHash);
607
+ console.error(`[WindtunnelRun] Certifying engine: ${mintedRuleIds.length} rule(s), ` +
608
+ `${corpus.prDiffs.length} PR diff(s), ${built.firings.length} firing(s), ` +
609
+ `${built.filesTouchedInWindow} file(s) touched.`);
610
+ return {
611
+ mintedRuleIds,
612
+ firings: built.firings,
613
+ groundTruth: corpus.groundTruth,
614
+ positiveControlTargets: built.positiveControlTargets,
615
+ filesTouchedInWindow: built.filesTouchedInWindow,
616
+ };
617
+ }
534
618
  //# sourceMappingURL=spine-windtunnel.js.map