@mmnto/cli 1.15.9 → 1.16.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,379 @@
1
+ import * as fs from 'node:fs';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
+ // Mock @clack/prompts confirm to a no-op returning false.
6
+ vi.mock('@clack/prompts', () => ({
7
+ confirm: vi.fn().mockResolvedValue(false),
8
+ isCancel: vi.fn().mockReturnValue(false),
9
+ }));
10
+ // Mock the GitHubCliPrAdapter and gh-utils used by recurrence-stats.
11
+ const mockFetchPr = vi.fn();
12
+ const mockFetchReviewComments = vi.fn();
13
+ const mockGhFetchAndParse = vi.fn();
14
+ vi.mock('../adapters/github-cli-pr.js', () => ({
15
+ // Use a real class so `new GitHubCliPrAdapter(...)` is a valid construct.
16
+ GitHubCliPrAdapter: class GitHubCliPrAdapter {
17
+ fetchPr(num) {
18
+ return mockFetchPr(num);
19
+ }
20
+ fetchReviewComments(num) {
21
+ return mockFetchReviewComments(num);
22
+ }
23
+ },
24
+ }));
25
+ vi.mock('../adapters/gh-utils.js', () => ({
26
+ ghFetchAndParse: (...args) => mockGhFetchAndParse(...args),
27
+ handleGhError: (err) => {
28
+ throw err;
29
+ },
30
+ }));
31
+ // Mock loadConfig + resolveConfigPath to a tmp totemDir.
32
+ let tmpDir;
33
+ let totemDir;
34
+ vi.mock('../utils.js', () => ({
35
+ loadConfig: vi.fn().mockImplementation(async () => ({ totemDir: '.totem' })),
36
+ resolveConfigPath: vi.fn().mockReturnValue(''),
37
+ }));
38
+ import { runRecurrenceStats } from './recurrence-stats.js';
39
+ // ─── Test setup ────────────────────────────────────────
40
+ beforeEach(() => {
41
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'recurrence-stats-'));
42
+ totemDir = path.join(tmpDir, '.totem');
43
+ fs.mkdirSync(totemDir, { recursive: true });
44
+ // Run from tmpDir so totemDir resolves under it.
45
+ process.chdir(tmpDir);
46
+ mockFetchPr.mockReset();
47
+ mockFetchReviewComments.mockReset();
48
+ mockGhFetchAndParse.mockReset();
49
+ });
50
+ afterEach(() => {
51
+ // Restore cwd to a known location so test cleanup can rm tmpDir.
52
+ process.chdir(os.tmpdir());
53
+ fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
54
+ });
55
+ // ─── Test data factories ───────────────────────────────
56
+ function makeReviewComment(overrides) {
57
+ return {
58
+ id: overrides.id,
59
+ author: overrides.prAuthor ?? 'coderabbitai[bot]',
60
+ body: overrides.body,
61
+ path: overrides.filePath ?? 'src/handler.ts',
62
+ diffHunk: `@@ -1,3 +${overrides.line ?? 42},3 @@`,
63
+ inReplyToId: undefined,
64
+ createdAt: '2026-04-01T00:00:00.000Z',
65
+ };
66
+ }
67
+ function loadStats() {
68
+ const raw = fs.readFileSync(path.join(totemDir, 'recurrence-stats.json'), 'utf-8');
69
+ return JSON.parse(raw);
70
+ }
71
+ // ─── Tests ─────────────────────────────────────────────
72
+ describe('runRecurrenceStats', () => {
73
+ it('collapses path/line variants of the same finding to one signature', async () => {
74
+ mockGhFetchAndParse.mockReturnValue([
75
+ { number: 100, mergedAt: '2026-04-01T00:00:00.000Z' },
76
+ { number: 101, mergedAt: '2026-04-02T00:00:00.000Z' },
77
+ { number: 102, mergedAt: '2026-04-03T00:00:00.000Z' },
78
+ ]);
79
+ // Same finding text, different files / lines / fenced code blocks
80
+ mockFetchPr.mockImplementation((num) => ({
81
+ number: num,
82
+ title: `PR ${num}`,
83
+ body: '',
84
+ state: 'merged',
85
+ comments: [],
86
+ reviews: [],
87
+ }));
88
+ mockFetchReviewComments.mockImplementation((num) => {
89
+ const variants = [
90
+ 'Avoid using `any` in packages/cli/src/foo.ts:42 — prefer `unknown`.',
91
+ 'Avoid using `any` in packages/core/src/bar.ts:99 — prefer `unknown`.',
92
+ 'Avoid using `any` in src/baz.ts:7 — prefer `unknown`.',
93
+ ];
94
+ const idx = num - 100;
95
+ return [makeReviewComment({ id: 1000 + num, body: variants[idx], line: 42 + idx })];
96
+ });
97
+ await runRecurrenceStats({ threshold: 1, historyDepth: 5, yes: true });
98
+ const stats = loadStats();
99
+ // Threshold 1 → all clusters surface; should be exactly one cluster
100
+ const allPatterns = [...stats.patterns, ...stats.coveredPatterns];
101
+ expect(allPatterns.length).toBe(1);
102
+ expect(allPatterns[0].occurrences).toBe(3);
103
+ });
104
+ it('marks cluster as `mixed` when same signature spans multiple bots', async () => {
105
+ mockGhFetchAndParse.mockReturnValue([{ number: 200, mergedAt: '2026-04-01T00:00:00.000Z' }]);
106
+ mockFetchPr.mockReturnValue({
107
+ number: 200,
108
+ title: 't',
109
+ body: '',
110
+ state: 'merged',
111
+ comments: [],
112
+ reviews: [],
113
+ });
114
+ mockFetchReviewComments.mockReturnValue([
115
+ makeReviewComment({
116
+ id: 1,
117
+ prAuthor: 'coderabbitai[bot]',
118
+ body: 'Avoid using `any` — prefer `unknown`.',
119
+ }),
120
+ makeReviewComment({
121
+ id: 2,
122
+ prAuthor: 'gemini-code-assist[bot]',
123
+ body: 'Avoid using `any` — prefer `unknown`.',
124
+ }),
125
+ ]);
126
+ await runRecurrenceStats({ threshold: 1, historyDepth: 5, yes: true });
127
+ const stats = loadStats();
128
+ const allPatterns = [...stats.patterns, ...stats.coveredPatterns];
129
+ expect(allPatterns.length).toBe(1);
130
+ expect(allPatterns[0].tool).toBe('mixed');
131
+ // Two distinct findings with same signature
132
+ expect(allPatterns[0].occurrences).toBe(2);
133
+ });
134
+ it('counts occurrences across distinct findings, not distinct PRs', async () => {
135
+ mockGhFetchAndParse.mockReturnValue([
136
+ { number: 300, mergedAt: '2026-04-01T00:00:00.000Z' },
137
+ { number: 301, mergedAt: '2026-04-02T00:00:00.000Z' },
138
+ ]);
139
+ mockFetchPr.mockReturnValue({
140
+ number: 0,
141
+ title: 't',
142
+ body: '',
143
+ state: 'merged',
144
+ comments: [],
145
+ reviews: [],
146
+ });
147
+ // PR 300 has 3 distinct findings with the same body, PR 301 has 1.
148
+ // Total occurrences: 4. Distinct PRs: 2.
149
+ mockFetchReviewComments.mockImplementation((num) => {
150
+ if (num === 300) {
151
+ return [
152
+ makeReviewComment({ id: 1, body: 'Empty catch block.', filePath: 'a.ts', line: 1 }),
153
+ makeReviewComment({ id: 2, body: 'Empty catch block.', filePath: 'b.ts', line: 2 }),
154
+ makeReviewComment({ id: 3, body: 'Empty catch block.', filePath: 'c.ts', line: 3 }),
155
+ ];
156
+ }
157
+ return [makeReviewComment({ id: 4, body: 'Empty catch block.', filePath: 'd.ts', line: 4 })];
158
+ });
159
+ await runRecurrenceStats({ threshold: 1, historyDepth: 5, yes: true });
160
+ const stats = loadStats();
161
+ const allPatterns = [...stats.patterns, ...stats.coveredPatterns];
162
+ expect(allPatterns.length).toBe(1);
163
+ expect(allPatterns[0].occurrences).toBe(4);
164
+ expect(allPatterns[0].prs).toEqual(['300', '301']);
165
+ });
166
+ it('dedupes + sorts prs ascending numerically', async () => {
167
+ mockGhFetchAndParse.mockReturnValue([
168
+ { number: 1500, mergedAt: '2026-04-03T00:00:00.000Z' },
169
+ { number: 200, mergedAt: '2026-04-02T00:00:00.000Z' },
170
+ { number: 80, mergedAt: '2026-04-01T00:00:00.000Z' },
171
+ ]);
172
+ mockFetchPr.mockReturnValue({
173
+ number: 0,
174
+ title: 't',
175
+ body: '',
176
+ state: 'merged',
177
+ comments: [],
178
+ reviews: [],
179
+ });
180
+ mockFetchReviewComments.mockImplementation((num) => [
181
+ makeReviewComment({ id: num, body: 'Same finding text everywhere.', filePath: 'x.ts' }),
182
+ ]);
183
+ await runRecurrenceStats({ threshold: 1, historyDepth: 5, yes: true });
184
+ const stats = loadStats();
185
+ const allPatterns = [...stats.patterns, ...stats.coveredPatterns];
186
+ expect(allPatterns.length).toBe(1);
187
+ // 80 < 200 < 1500 numerically (NOT lexically — '1500' < '200' < '80' lex)
188
+ expect(allPatterns[0].prs).toEqual(['80', '200', '1500']);
189
+ });
190
+ it('excludes patterns below threshold from headline patterns', async () => {
191
+ mockGhFetchAndParse.mockReturnValue([
192
+ { number: 400, mergedAt: '2026-04-01T00:00:00.000Z' },
193
+ { number: 401, mergedAt: '2026-04-02T00:00:00.000Z' },
194
+ ]);
195
+ mockFetchPr.mockReturnValue({
196
+ number: 0,
197
+ title: 't',
198
+ body: '',
199
+ state: 'merged',
200
+ comments: [],
201
+ reviews: [],
202
+ });
203
+ mockFetchReviewComments.mockImplementation((num) => [
204
+ makeReviewComment({ id: num, body: `Distinct issue ${num}.`, filePath: 'x.ts' }),
205
+ ]);
206
+ // threshold=5 — neither cluster has enough occurrences (each is 1)
207
+ await runRecurrenceStats({ threshold: 5, historyDepth: 5, yes: true });
208
+ const stats = loadStats();
209
+ expect(stats.patterns.length).toBe(0);
210
+ });
211
+ it('routes patterns matching an existing rule to coveredPatterns', async () => {
212
+ // Seed compiled-rules.json with a rule whose message overlaps the
213
+ // finding text well enough to clear Jaccard >= 0.6.
214
+ const rule = {
215
+ lessonHash: 'abc123',
216
+ lessonHeading: 'Avoid any',
217
+ message: 'Avoid using any type — prefer unknown',
218
+ pattern: 'any',
219
+ engine: 'regex',
220
+ severity: 'warning',
221
+ category: 'style',
222
+ fileGlobs: ['**/*.ts'],
223
+ status: 'active',
224
+ compiledAt: '2026-04-01T00:00:00.000Z',
225
+ };
226
+ fs.writeFileSync(path.join(totemDir, 'compiled-rules.json'), JSON.stringify({ version: 1, rules: [rule], nonCompilable: [] }));
227
+ mockGhFetchAndParse.mockReturnValue([{ number: 500, mergedAt: '2026-04-01T00:00:00.000Z' }]);
228
+ mockFetchPr.mockReturnValue({
229
+ number: 500,
230
+ title: 't',
231
+ body: '',
232
+ state: 'merged',
233
+ comments: [],
234
+ reviews: [],
235
+ });
236
+ mockFetchReviewComments.mockReturnValue([
237
+ makeReviewComment({
238
+ id: 5001,
239
+ body: 'Avoid using any type — prefer unknown.',
240
+ }),
241
+ ]);
242
+ await runRecurrenceStats({ threshold: 1, historyDepth: 5, yes: true });
243
+ const stats = loadStats();
244
+ expect(stats.patterns.length).toBe(0);
245
+ expect(stats.coveredPatterns.length).toBe(1);
246
+ expect(stats.coveredPatterns[0].tool).toBe('coderabbit');
247
+ });
248
+ it('atomic write — temp file is cleaned up after rename', async () => {
249
+ mockGhFetchAndParse.mockReturnValue([{ number: 600, mergedAt: '2026-04-01T00:00:00.000Z' }]);
250
+ mockFetchPr.mockReturnValue({
251
+ number: 600,
252
+ title: 't',
253
+ body: '',
254
+ state: 'merged',
255
+ comments: [],
256
+ reviews: [],
257
+ });
258
+ mockFetchReviewComments.mockReturnValue([makeReviewComment({ id: 6001, body: 'A finding.' })]);
259
+ await runRecurrenceStats({ threshold: 1, historyDepth: 5, yes: true });
260
+ expect(fs.existsSync(path.join(totemDir, 'recurrence-stats.json'))).toBe(true);
261
+ // No .tmp residue under any name (covers the legacy fixed-name path
262
+ // AND the unique PID+timestamp form introduced for mmnto-ai/totem#1729 CR R1).
263
+ const residue = fs.readdirSync(totemDir).filter((f) => f.endsWith('.tmp'));
264
+ expect(residue).toEqual([]);
265
+ });
266
+ it('survives concurrent invocations without temp-file collision', async () => {
267
+ mockGhFetchAndParse.mockReturnValue([{ number: 650, mergedAt: '2026-04-01T00:00:00.000Z' }]);
268
+ mockFetchPr.mockReturnValue({
269
+ number: 650,
270
+ title: 't',
271
+ body: '',
272
+ state: 'merged',
273
+ comments: [],
274
+ reviews: [],
275
+ });
276
+ mockFetchReviewComments.mockReturnValue([
277
+ makeReviewComment({ id: 6501, body: 'Concurrent invocation test finding.' }),
278
+ ]);
279
+ // Two parallel invocations would collide on a fixed `.tmp` path; the
280
+ // PID+epochMs suffix introduced for mmnto-ai/totem#1729 CR R1 keeps
281
+ // them disjoint. Both must resolve and the final file must be valid.
282
+ await Promise.all([
283
+ runRecurrenceStats({ threshold: 1, historyDepth: 5, yes: true }),
284
+ runRecurrenceStats({ threshold: 1, historyDepth: 5, yes: true }),
285
+ ]);
286
+ expect(fs.existsSync(path.join(totemDir, 'recurrence-stats.json'))).toBe(true);
287
+ const stats = loadStats();
288
+ expect(stats.version).toBe(1);
289
+ // No .tmp residue under any name
290
+ const residue = fs.readdirSync(totemDir).filter((f) => f.endsWith('.tmp'));
291
+ expect(residue).toEqual([]);
292
+ });
293
+ it('does not throw when compiled-rules.json is missing', async () => {
294
+ // Make sure rules file does not exist
295
+ const rulesPath = path.join(totemDir, 'compiled-rules.json');
296
+ if (fs.existsSync(rulesPath))
297
+ fs.unlinkSync(rulesPath);
298
+ mockGhFetchAndParse.mockReturnValue([{ number: 700, mergedAt: '2026-04-01T00:00:00.000Z' }]);
299
+ mockFetchPr.mockReturnValue({
300
+ number: 700,
301
+ title: 't',
302
+ body: '',
303
+ state: 'merged',
304
+ comments: [],
305
+ reviews: [],
306
+ });
307
+ mockFetchReviewComments.mockReturnValue([
308
+ makeReviewComment({ id: 7001, body: 'Some finding.' }),
309
+ ]);
310
+ await expect(runRecurrenceStats({ threshold: 1, historyDepth: 5, yes: true })).resolves.toBeUndefined();
311
+ });
312
+ it('folds trap-ledger override events as co-equal findings', async () => {
313
+ // Seed events.ndjson with one override event
314
+ const ledgerDir = path.join(totemDir, 'ledger');
315
+ fs.mkdirSync(ledgerDir, { recursive: true });
316
+ const event = {
317
+ timestamp: '2026-04-01T00:00:00.000Z',
318
+ type: 'override',
319
+ ruleId: 'rule-xyz',
320
+ file: 'src/legacy.ts',
321
+ line: 10,
322
+ justification: 'Legacy code we will refactor later.',
323
+ source: 'shield',
324
+ };
325
+ fs.writeFileSync(path.join(ledgerDir, 'events.ndjson'), JSON.stringify(event) + '\n');
326
+ mockGhFetchAndParse.mockReturnValue([]);
327
+ mockFetchPr.mockReturnValue({
328
+ number: 0,
329
+ title: 't',
330
+ body: '',
331
+ state: 'merged',
332
+ comments: [],
333
+ reviews: [],
334
+ });
335
+ mockFetchReviewComments.mockReturnValue([]);
336
+ await runRecurrenceStats({ threshold: 1, historyDepth: 5, yes: true });
337
+ const stats = loadStats();
338
+ const allPatterns = [...stats.patterns, ...stats.coveredPatterns];
339
+ expect(allPatterns.length).toBe(1);
340
+ expect(allPatterns[0].tool).toBe('override');
341
+ // Override events are not tied to a PR
342
+ expect(allPatterns[0].prs).toEqual([]);
343
+ });
344
+ it('marks cluster as `mixed` when override + bot finding share a signature', async () => {
345
+ const ledgerDir = path.join(totemDir, 'ledger');
346
+ fs.mkdirSync(ledgerDir, { recursive: true });
347
+ // Use a justification that, after normalization, matches the bot finding
348
+ const event = {
349
+ timestamp: '2026-04-01T00:00:00.000Z',
350
+ type: 'override',
351
+ ruleId: 'rule-xyz',
352
+ file: 'src/handler.ts',
353
+ line: 10,
354
+ justification: 'Avoid using any type prefer unknown.',
355
+ source: 'shield',
356
+ };
357
+ fs.writeFileSync(path.join(ledgerDir, 'events.ndjson'), JSON.stringify(event) + '\n');
358
+ mockGhFetchAndParse.mockReturnValue([{ number: 800, mergedAt: '2026-04-01T00:00:00.000Z' }]);
359
+ mockFetchPr.mockReturnValue({
360
+ number: 800,
361
+ title: 't',
362
+ body: '',
363
+ state: 'merged',
364
+ comments: [],
365
+ reviews: [],
366
+ });
367
+ mockFetchReviewComments.mockReturnValue([
368
+ makeReviewComment({ id: 8001, body: 'Avoid using any type prefer unknown.' }),
369
+ ]);
370
+ await runRecurrenceStats({ threshold: 1, historyDepth: 5, yes: true });
371
+ const stats = loadStats();
372
+ const allPatterns = [...stats.patterns, ...stats.coveredPatterns];
373
+ expect(allPatterns.length).toBe(1);
374
+ expect(allPatterns[0].tool).toBe('mixed');
375
+ expect(allPatterns[0].occurrences).toBe(2);
376
+ expect(allPatterns[0].prs).toEqual(['800']);
377
+ });
378
+ });
379
+ //# sourceMappingURL=recurrence-stats.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recurrence-stats.test.js","sourceRoot":"","sources":["../../src/commands/recurrence-stats.test.ts"],"names":[],"mappings":"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"}
@@ -46,6 +46,23 @@ export declare function formatVerdictForDisplay(verdict: ShieldStructuredVerdict
46
46
  * boundary.
47
47
  */
48
48
  export declare function writeReviewedContentHash(cwd: string, totemDir: string, configRoot?: string, extensions?: readonly string[]): Promise<void>;
49
+ /**
50
+ * Record a shield override: append the override event to the Trap Ledger
51
+ * AND stamp the reviewed-content-hash so the push-gate hook unblocks.
52
+ *
53
+ * mmnto-ai/totem#1716: prior to this helper the override branch only wrote the ledger
54
+ * entry; the missing stamp left the contributor stuck behind the push-gate
55
+ * with a tribal-knowledge `git reset --soft HEAD~1 && totem review --staged`
56
+ * workaround. Override is a legitimate completion path (with logged
57
+ * justification) and must produce the same cache state as a passing review.
58
+ */
59
+ export declare function recordShieldOverride(params: {
60
+ override: string;
61
+ cwd: string;
62
+ totemDir: string;
63
+ configRoot?: string;
64
+ sourceExtensions?: readonly string[];
65
+ }): Promise<void>;
49
66
  export type ShieldFormat = 'text' | 'sarif' | 'json';
50
67
  export interface ShieldOptions {
51
68
  raw?: boolean;
@@ -53,6 +70,8 @@ export interface ShieldOptions {
53
70
  model?: string;
54
71
  fresh?: boolean;
55
72
  staged?: boolean;
73
+ /** Explicit ref range for `git diff` (mmnto-ai/totem#1717). Bypasses implicit fallback chain. */
74
+ diff?: string;
56
75
  mode?: 'standard' | 'structural';
57
76
  learn?: boolean;
58
77
  yes?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"shield.d.ts","sourceRoot":"","sources":["../../src/commands/shield.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shield.d.ts","sourceRoot":"","sources":["../../src/commands/shield.ts"],"names":[],"mappings":"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"}
@@ -348,6 +348,30 @@ export async function writeReviewedContentHash(cwd, totemDir, configRoot, extens
348
348
  }
349
349
  }
350
350
  }
351
+ /**
352
+ * Record a shield override: append the override event to the Trap Ledger
353
+ * AND stamp the reviewed-content-hash so the push-gate hook unblocks.
354
+ *
355
+ * mmnto-ai/totem#1716: prior to this helper the override branch only wrote the ledger
356
+ * entry; the missing stamp left the contributor stuck behind the push-gate
357
+ * with a tribal-knowledge `git reset --soft HEAD~1 && totem review --staged`
358
+ * workaround. Override is a legitimate completion path (with logged
359
+ * justification) and must produce the same cache state as a passing review.
360
+ */
361
+ export async function recordShieldOverride(params) {
362
+ const path = await import('node:path');
363
+ const { appendLedgerEvent } = await import('@mmnto/totem');
364
+ const resolvedTotemDir = path.join(params.configRoot ?? params.cwd, params.totemDir);
365
+ appendLedgerEvent(resolvedTotemDir, {
366
+ timestamp: new Date().toISOString(),
367
+ type: 'override',
368
+ ruleId: 'shield-override',
369
+ file: '(shield)',
370
+ justification: params.override,
371
+ source: 'shield',
372
+ }, (msg) => log.dim(DISPLAY_TAG, msg));
373
+ await writeReviewedContentHash(params.cwd, params.totemDir, params.configRoot, params.sourceExtensions);
374
+ }
351
375
  // ─── Deterministic mode (delegates to shared engine) ─
352
376
  // ─── Learn: extract lessons from failed verdict ─────
353
377
  export async function learnFromVerdict(verdictContent, diff, options, config, cwd, configRoot) {
@@ -580,24 +604,23 @@ async function handleVerdictResult(content, diff, options, config, cwd, configRo
580
604
  await writeReviewedContentHash(cwd, config.totemDir, configRoot, config.review.sourceExtensions);
581
605
  }
582
606
  else if (options.override) {
583
- const { appendLedgerEvent } = await import('@mmnto/totem');
584
607
  const criticalFindings = filtered.filter((f) => f.severity === 'CRITICAL');
585
608
  log.warn(DISPLAY_TAG, `SHIELD OVERRIDE APPLIED: ${options.override}`);
586
609
  for (const finding of criticalFindings) {
587
610
  log.warn(DISPLAY_TAG, ` [overridden] ${finding.message}`);
588
611
  }
589
- appendLedgerEvent(resolvedTotemDir, {
590
- timestamp: new Date().toISOString(),
591
- type: 'override',
592
- ruleId: 'shield-override',
593
- file: '(shield)',
594
- justification: options.override,
595
- source: 'shield',
596
- }, (msg) => log.dim(DISPLAY_TAG, msg));
612
+ await recordShieldOverride({
613
+ override: options.override,
614
+ cwd,
615
+ totemDir: config.totemDir,
616
+ configRoot,
617
+ sourceExtensions: config.review.sourceExtensions,
618
+ });
597
619
  // Track overridden findings for exemption engine (only non-exempted findings)
598
620
  const { readLocalExemptions, writeLocalExemptions } = await import('../exemptions/exemption-store.js');
599
621
  const { trackFalsePositives } = await import('../exemptions/exemption-engine.js');
600
622
  const { PROMOTION_THRESHOLD } = await import('../exemptions/exemption-schema.js');
623
+ const { appendLedgerEvent } = await import('@mmnto/totem');
601
624
  const localExemptions = readLocalExemptions(cacheDir, (msg) => log.dim(DISPLAY_TAG, msg));
602
625
  const tracked = trackFalsePositives(criticalFindings, 'shield', localExemptions, shared);
603
626
  for (const msg of tracked.promoted) {
@@ -636,18 +659,14 @@ async function handleVerdictResult(content, diff, options, config, cwd, configRo
636
659
  await writeReviewedContentHash(cwd, config.totemDir, configRoot, config.review.sourceExtensions);
637
660
  }
638
661
  else if (options.override) {
639
- const { appendLedgerEvent } = await import('@mmnto/totem');
640
- const pathMod = await import('node:path');
641
- const resolvedTotemDir = pathMod.join(configRoot ?? cwd, config.totemDir);
642
662
  log.warn(DISPLAY_TAG, `SHIELD OVERRIDE APPLIED: ${options.override}`);
643
- appendLedgerEvent(resolvedTotemDir, {
644
- timestamp: new Date().toISOString(),
645
- type: 'override',
646
- ruleId: 'shield-override',
647
- file: '(shield)',
648
- justification: options.override,
649
- source: 'shield',
650
- }, (msg) => log.dim(DISPLAY_TAG, msg));
663
+ await recordShieldOverride({
664
+ override: options.override,
665
+ cwd,
666
+ totemDir: config.totemDir,
667
+ configRoot,
668
+ sourceExtensions: config.review.sourceExtensions,
669
+ });
651
670
  }
652
671
  else {
653
672
  if (options.learn || config.shieldAutoLearn)