@mmnto/totem 1.23.0 → 1.24.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.
- package/dist/compile-lesson.d.ts +1 -1
- package/dist/compile-manifest.d.ts +14 -1
- package/dist/compile-manifest.d.ts.map +1 -1
- package/dist/compile-manifest.js +30 -1
- package/dist/compile-manifest.js.map +1 -1
- package/dist/compile-manifest.test.js +36 -1
- package/dist/compile-manifest.test.js.map +1 -1
- package/dist/compiler-schema.d.ts +44 -16
- package/dist/compiler-schema.d.ts.map +1 -1
- package/dist/compiler-schema.js +19 -3
- package/dist/compiler-schema.js.map +1 -1
- package/dist/compiler-schema.test.js +23 -1
- package/dist/compiler-schema.test.js.map +1 -1
- package/dist/compiler.d.ts +8 -1
- package/dist/compiler.d.ts.map +1 -1
- package/dist/compiler.js +11 -2
- package/dist/compiler.js.map +1 -1
- package/dist/compiler.test.js +35 -0
- package/dist/compiler.test.js.map +1 -1
- package/dist/config-schema.d.ts +2 -2
- package/dist/config-schema.js +2 -2
- package/dist/first-lint-promote.d.ts +90 -0
- package/dist/first-lint-promote.d.ts.map +1 -0
- package/dist/first-lint-promote.js +103 -0
- package/dist/first-lint-promote.js.map +1 -0
- package/dist/first-lint-promote.test.d.ts +2 -0
- package/dist/first-lint-promote.test.d.ts.map +1 -0
- package/dist/first-lint-promote.test.js +242 -0
- package/dist/first-lint-promote.test.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/pack-discovery.d.ts +1 -1
- package/dist/pack-discovery.js +1 -1
- package/dist/pack-discovery.test.js +21 -21
- package/dist/pack-manifest-writer.d.ts +2 -2
- package/dist/pack-manifest-writer.js +3 -3
- package/dist/pack-manifest-writer.test.js +22 -22
- package/dist/pack-merge.d.ts +1 -1
- package/dist/pack-merge.js +1 -1
- package/dist/rule-engine.js +1 -1
- package/dist/verification-outcomes.d.ts +160 -0
- package/dist/verification-outcomes.d.ts.map +1 -0
- package/dist/verification-outcomes.js +149 -0
- package/dist/verification-outcomes.js.map +1 -0
- package/dist/verification-outcomes.test.d.ts +2 -0
- package/dist/verification-outcomes.test.d.ts.map +1 -0
- package/dist/verification-outcomes.test.js +228 -0
- package/dist/verification-outcomes.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { canonicalStringify } from './compile-manifest.js';
|
|
5
|
+
/**
|
|
6
|
+
* Stage 4 outcome literals serialized into `.totem/verification-outcomes.json`
|
|
7
|
+
* (mmnto-ai/totem#1684, ADR-091 § Bootstrap CI-first). Mirrors the
|
|
8
|
+
* `Stage4Outcome` type from `stage4-verifier.ts` exactly so the storage
|
|
9
|
+
* vocabulary matches the runtime type and we avoid a translation layer that
|
|
10
|
+
* would drift on Stage 4 evolution.
|
|
11
|
+
*
|
|
12
|
+
* The `_OutcomeAlignment` type below enforces the mirror at compile time —
|
|
13
|
+
* if either side adds, drops, or renames a literal, the assertion below
|
|
14
|
+
* fails the build before drift can land in the corpus.
|
|
15
|
+
*/
|
|
16
|
+
export const Stage4OutcomeStored = z.enum([
|
|
17
|
+
'no-matches',
|
|
18
|
+
'out-of-scope',
|
|
19
|
+
'in-scope-bad-example',
|
|
20
|
+
'candidate-debt',
|
|
21
|
+
]);
|
|
22
|
+
const _stage4OutcomeAlignment = true;
|
|
23
|
+
void _stage4OutcomeAlignment;
|
|
24
|
+
/**
|
|
25
|
+
* One verification record per pack-installed rule (keyed by `lessonHash` in
|
|
26
|
+
* the file-level record). Persisted across lint runs so subsequent passes
|
|
27
|
+
* skip re-verification when the rule's content hash matches a recorded
|
|
28
|
+
* outcome.
|
|
29
|
+
*/
|
|
30
|
+
export const VerificationOutcomeEntrySchema = z.object({
|
|
31
|
+
/**
|
|
32
|
+
* `lessonHash` of the rule the outcome was recorded against. Persisted
|
|
33
|
+
* inside the entry as well as serving as the file-level record key so a
|
|
34
|
+
* tampered key (key/value mismatch) can be detected on read.
|
|
35
|
+
*/
|
|
36
|
+
ruleHash: z.string().trim().min(1),
|
|
37
|
+
/** ISO-8601 timestamp of when the verifier produced the outcome. */
|
|
38
|
+
verifiedAt: z.string().datetime(),
|
|
39
|
+
/** The terminal Stage 4 outcome that was recorded. */
|
|
40
|
+
outcome: Stage4OutcomeStored,
|
|
41
|
+
/** Repo-relative paths of baseline-scoped files where the rule fired. */
|
|
42
|
+
baselineMatches: z.array(z.string().trim().min(1)).default([]),
|
|
43
|
+
/** Repo-relative paths of in-scope files where the rule fired. */
|
|
44
|
+
inScopeMatches: z.array(z.string().trim().min(1)).default([]),
|
|
45
|
+
/**
|
|
46
|
+
* In-scope match lines that did not match the `badExample` shape — the
|
|
47
|
+
* Candidate Debt evidence carried forward to the `totem doctor` UX
|
|
48
|
+
* surface in mmnto-ai/totem#1685. Empty unless `outcome === 'candidate-debt'`.
|
|
49
|
+
*/
|
|
50
|
+
candidateDebtLines: z.array(z.string().trim().min(1)).default([]),
|
|
51
|
+
});
|
|
52
|
+
/**
|
|
53
|
+
* The on-disk shape of `.totem/verification-outcomes.json`. Wraps the
|
|
54
|
+
* per-rule record in a versioned envelope so structural changes can break
|
|
55
|
+
* the file forward without silent migration: a loader that sees an unknown
|
|
56
|
+
* `version` treats the file as empty and re-verifies, instead of risking a
|
|
57
|
+
* partial parse against a newer schema.
|
|
58
|
+
*/
|
|
59
|
+
export const VerificationOutcomesFileSchema = z.object({
|
|
60
|
+
version: z.literal(1).default(1),
|
|
61
|
+
outcomes: z.record(z.string(), VerificationOutcomeEntrySchema),
|
|
62
|
+
});
|
|
63
|
+
// ─── Persistence (mmnto-ai/totem#1684 T2) ───────────
|
|
64
|
+
/**
|
|
65
|
+
* Load the per-rule verification outcomes from a `verification-outcomes.json`
|
|
66
|
+
* file. Returns an empty store when the file is missing, contains malformed
|
|
67
|
+
* JSON, or fails schema validation — the corpus self-heals on the next
|
|
68
|
+
* write. The interceptor that wrote the corrupt file (or a future pass with
|
|
69
|
+
* a refreshed schema) will overwrite it on the next promotion run, so the
|
|
70
|
+
* cache state cannot wedge.
|
|
71
|
+
*
|
|
72
|
+
* Invariant: schema-version mismatch is treated as "no outcomes recorded"
|
|
73
|
+
* rather than attempted migration. A v2 file read by a v1 loader returns
|
|
74
|
+
* an empty store and warns; the next write produces a valid v1 file.
|
|
75
|
+
*/
|
|
76
|
+
export function readVerificationOutcomes(filePath, onWarn) {
|
|
77
|
+
if (!fs.existsSync(filePath))
|
|
78
|
+
return {};
|
|
79
|
+
let raw;
|
|
80
|
+
try {
|
|
81
|
+
raw = fs.readFileSync(filePath, 'utf-8'); // totem-context: self-healing on transient read errors per Invariant #10 of #1684 — verification-outcomes.json is per-machine cache; corruption is recoverable on next write
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
onWarn?.(
|
|
85
|
+
// totem-context: see preceding try — corrupt cache state self-heals; next write overwrites
|
|
86
|
+
`Could not read ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
87
|
+
return {};
|
|
88
|
+
}
|
|
89
|
+
let parsed;
|
|
90
|
+
try {
|
|
91
|
+
parsed = JSON.parse(raw); // totem-context: self-healing on malformed JSON per Invariant #10 of #1684 — corrupt cache state self-heals; next write overwrites
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
onWarn?.(
|
|
95
|
+
// totem-context: see preceding try — corrupt cache state self-heals; next write overwrites
|
|
96
|
+
`Malformed JSON in ${filePath}: ${err instanceof Error ? err.message : String(err)}. Treating as empty; next write will overwrite.`);
|
|
97
|
+
return {};
|
|
98
|
+
}
|
|
99
|
+
const result = VerificationOutcomesFileSchema.safeParse(parsed);
|
|
100
|
+
if (!result.success) {
|
|
101
|
+
const issues = result.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; ');
|
|
102
|
+
onWarn?.(`Schema validation failed for ${filePath}: ${issues}. Treating as empty; next write will overwrite.`);
|
|
103
|
+
return {};
|
|
104
|
+
}
|
|
105
|
+
// totem-context: defense-in-depth — schema validates each entry's shape but
|
|
106
|
+
// not that the file-level record key matches the entry's stored ruleHash.
|
|
107
|
+
// A tampered or hand-edited file could memoize an outcome under the wrong
|
|
108
|
+
// hash. Drop those entries (with a warn) so memoization never satisfies a
|
|
109
|
+
// mismatched lookup.
|
|
110
|
+
const aligned = {};
|
|
111
|
+
for (const [key, entry] of Object.entries(result.data.outcomes)) {
|
|
112
|
+
if (entry.ruleHash !== key) {
|
|
113
|
+
onWarn?.(`Key/hash mismatch in ${filePath}: key=${key}, entry.ruleHash=${entry.ruleHash}. Ignoring entry.`);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
aligned[key] = entry;
|
|
117
|
+
}
|
|
118
|
+
return aligned;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Persist the per-rule verification outcomes to disk via temp-file +
|
|
122
|
+
* atomic rename, so a concurrent CI lint pass that interrupts mid-write
|
|
123
|
+
* leaves the prior valid file intact rather than producing a truncated
|
|
124
|
+
* partial-write that the next loader would reject.
|
|
125
|
+
*
|
|
126
|
+
* Output is canonicalized (recursive object-key sort) before serialization
|
|
127
|
+
* so two lint passes that produce the same outcomes write byte-identical
|
|
128
|
+
* files — Invariant #11 in the design doc, which keeps consumer repos
|
|
129
|
+
* from seeing phantom diffs on every CI run.
|
|
130
|
+
*/
|
|
131
|
+
export function writeVerificationOutcomes(filePath, outcomes) {
|
|
132
|
+
const dir = path.dirname(filePath);
|
|
133
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
134
|
+
const file = { version: 1, outcomes };
|
|
135
|
+
const json = `${canonicalStringify(file, 2)}\n`;
|
|
136
|
+
// totem-context: per-write unique temp filename so two concurrent lint
|
|
137
|
+
// processes can't clobber each other's in-flight write and trigger a
|
|
138
|
+
// hard rename failure mid-pass (CR mmnto-ai/totem#1787 R1).
|
|
139
|
+
const tmpPath = `${filePath}.${process.pid}.${Date.now().toString(36)}.tmp`;
|
|
140
|
+
try {
|
|
141
|
+
fs.writeFileSync(tmpPath, json, 'utf-8');
|
|
142
|
+
fs.renameSync(tmpPath, filePath);
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
if (fs.existsSync(tmpPath))
|
|
146
|
+
fs.rmSync(tmpPath, { force: true });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=verification-outcomes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification-outcomes.js","sourceRoot":"","sources":["../src/verification-outcomes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification-outcomes.test.d.ts","sourceRoot":"","sources":["../src/verification-outcomes.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,228 @@
|
|
|
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 } from 'vitest';
|
|
5
|
+
import { cleanTmpDir } from './test-utils.js';
|
|
6
|
+
import { readVerificationOutcomes, Stage4OutcomeStored, VerificationOutcomeEntrySchema, VerificationOutcomesFileSchema, writeVerificationOutcomes, } from './verification-outcomes.js';
|
|
7
|
+
// ─── Stage4OutcomeStored ───────────────────────────
|
|
8
|
+
describe('Stage4OutcomeStored', () => {
|
|
9
|
+
it('accepts each Stage4Outcome literal', () => {
|
|
10
|
+
for (const outcome of [
|
|
11
|
+
'no-matches',
|
|
12
|
+
'out-of-scope',
|
|
13
|
+
'in-scope-bad-example',
|
|
14
|
+
'candidate-debt',
|
|
15
|
+
]) {
|
|
16
|
+
expect(Stage4OutcomeStored.parse(outcome)).toBe(outcome);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
it('rejects an unknown literal', () => {
|
|
20
|
+
expect(() => Stage4OutcomeStored.parse('promoted')).toThrow();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
// ─── VerificationOutcomeEntrySchema ────────────────
|
|
24
|
+
describe('VerificationOutcomeEntrySchema', () => {
|
|
25
|
+
const validEntry = {
|
|
26
|
+
ruleHash: 'abc123',
|
|
27
|
+
verifiedAt: '2026-05-01T12:34:56.000Z',
|
|
28
|
+
outcome: 'in-scope-bad-example',
|
|
29
|
+
baselineMatches: [],
|
|
30
|
+
inScopeMatches: ['src/foo.ts'],
|
|
31
|
+
candidateDebtLines: [],
|
|
32
|
+
};
|
|
33
|
+
it('accepts a fully-populated valid entry', () => {
|
|
34
|
+
const parsed = VerificationOutcomeEntrySchema.parse(validEntry);
|
|
35
|
+
expect(parsed.ruleHash).toBe('abc123');
|
|
36
|
+
expect(parsed.outcome).toBe('in-scope-bad-example');
|
|
37
|
+
expect(parsed.inScopeMatches).toEqual(['src/foo.ts']);
|
|
38
|
+
});
|
|
39
|
+
it('defaults match arrays to empty when omitted', () => {
|
|
40
|
+
const parsed = VerificationOutcomeEntrySchema.parse({
|
|
41
|
+
ruleHash: 'abc123',
|
|
42
|
+
verifiedAt: '2026-05-01T12:34:56.000Z',
|
|
43
|
+
outcome: 'no-matches',
|
|
44
|
+
});
|
|
45
|
+
expect(parsed.baselineMatches).toEqual([]);
|
|
46
|
+
expect(parsed.inScopeMatches).toEqual([]);
|
|
47
|
+
expect(parsed.candidateDebtLines).toEqual([]);
|
|
48
|
+
});
|
|
49
|
+
it('rejects invalid verification-outcomes schema payloads', () => {
|
|
50
|
+
expect(() => VerificationOutcomeEntrySchema.parse({ ...validEntry, ruleHash: '' })).toThrow();
|
|
51
|
+
expect(() => VerificationOutcomeEntrySchema.parse({ ...validEntry, ruleHash: 42 })).toThrow();
|
|
52
|
+
expect(() => VerificationOutcomeEntrySchema.parse({ ...validEntry, verifiedAt: 'not-a-date' })).toThrow();
|
|
53
|
+
expect(() => VerificationOutcomeEntrySchema.parse({ ...validEntry, outcome: 'promoted' })).toThrow();
|
|
54
|
+
expect(() => VerificationOutcomeEntrySchema.parse({ ...validEntry, ruleHash: ' ' })).toThrow();
|
|
55
|
+
expect(() => VerificationOutcomeEntrySchema.parse({ ...validEntry, inScopeMatches: [''] })).toThrow();
|
|
56
|
+
const { ruleHash: _omit, ...missingHash } = validEntry;
|
|
57
|
+
void _omit;
|
|
58
|
+
expect(() => VerificationOutcomeEntrySchema.parse(missingHash)).toThrow();
|
|
59
|
+
});
|
|
60
|
+
it('trims surrounding whitespace from ruleHash', () => {
|
|
61
|
+
const parsed = VerificationOutcomeEntrySchema.parse({ ...validEntry, ruleHash: ' abc123 ' });
|
|
62
|
+
expect(parsed.ruleHash).toBe('abc123');
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
// ─── VerificationOutcomesFileSchema ────────────────
|
|
66
|
+
describe('VerificationOutcomesFileSchema', () => {
|
|
67
|
+
const validEntry = {
|
|
68
|
+
ruleHash: 'abc123',
|
|
69
|
+
verifiedAt: '2026-05-01T12:34:56.000Z',
|
|
70
|
+
outcome: 'in-scope-bad-example',
|
|
71
|
+
baselineMatches: [],
|
|
72
|
+
inScopeMatches: [],
|
|
73
|
+
candidateDebtLines: [],
|
|
74
|
+
};
|
|
75
|
+
it('accepts an empty outcomes record with default version', () => {
|
|
76
|
+
const parsed = VerificationOutcomesFileSchema.parse({ outcomes: {} });
|
|
77
|
+
expect(parsed.version).toBe(1);
|
|
78
|
+
expect(parsed.outcomes).toEqual({});
|
|
79
|
+
});
|
|
80
|
+
it('accepts a populated outcomes record', () => {
|
|
81
|
+
const parsed = VerificationOutcomesFileSchema.parse({
|
|
82
|
+
version: 1,
|
|
83
|
+
outcomes: { abc123: validEntry },
|
|
84
|
+
});
|
|
85
|
+
expect(parsed.outcomes['abc123']?.outcome).toBe('in-scope-bad-example');
|
|
86
|
+
});
|
|
87
|
+
it('rejects a future schema version', () => {
|
|
88
|
+
expect(() => VerificationOutcomesFileSchema.parse({
|
|
89
|
+
version: 2,
|
|
90
|
+
outcomes: { abc123: validEntry },
|
|
91
|
+
})).toThrow();
|
|
92
|
+
});
|
|
93
|
+
it('rejects a malformed nested entry', () => {
|
|
94
|
+
expect(() => VerificationOutcomesFileSchema.parse({
|
|
95
|
+
version: 1,
|
|
96
|
+
outcomes: { abc123: { ...validEntry, outcome: 'promoted' } },
|
|
97
|
+
})).toThrow();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
// ─── Persistence (mmnto-ai/totem#1684 T2) ───────────
|
|
101
|
+
describe('readVerificationOutcomes / writeVerificationOutcomes', () => {
|
|
102
|
+
let tmpDir;
|
|
103
|
+
beforeEach(() => {
|
|
104
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'totem-vouts-'));
|
|
105
|
+
});
|
|
106
|
+
afterEach(() => {
|
|
107
|
+
cleanTmpDir(tmpDir);
|
|
108
|
+
});
|
|
109
|
+
function tmpFile(name = 'verification-outcomes.json') {
|
|
110
|
+
return path.join(tmpDir, name);
|
|
111
|
+
}
|
|
112
|
+
const sampleStore = {
|
|
113
|
+
abc123: {
|
|
114
|
+
ruleHash: 'abc123',
|
|
115
|
+
verifiedAt: '2026-05-01T12:34:56.000Z',
|
|
116
|
+
outcome: 'in-scope-bad-example',
|
|
117
|
+
baselineMatches: [],
|
|
118
|
+
inScopeMatches: ['src/foo.ts', 'src/bar.ts'],
|
|
119
|
+
candidateDebtLines: [],
|
|
120
|
+
},
|
|
121
|
+
def456: {
|
|
122
|
+
ruleHash: 'def456',
|
|
123
|
+
verifiedAt: '2026-05-01T12:35:00.000Z',
|
|
124
|
+
outcome: 'candidate-debt',
|
|
125
|
+
baselineMatches: [],
|
|
126
|
+
inScopeMatches: ['src/baz.ts'],
|
|
127
|
+
candidateDebtLines: ['src/baz.ts:10:something'],
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
it('returns an empty store when the file does not exist', () => {
|
|
131
|
+
const warnings = [];
|
|
132
|
+
const store = readVerificationOutcomes(tmpFile('absent.json'), (m) => warnings.push(m));
|
|
133
|
+
expect(store).toEqual({});
|
|
134
|
+
expect(warnings).toEqual([]);
|
|
135
|
+
});
|
|
136
|
+
it('writes verification outcomes atomically and creates missing directories', () => {
|
|
137
|
+
const filePath = path.join(tmpFile('nested'), 'deep', 'verification-outcomes.json');
|
|
138
|
+
expect(fs.existsSync(path.dirname(filePath))).toBe(false);
|
|
139
|
+
writeVerificationOutcomes(filePath, sampleStore);
|
|
140
|
+
expect(fs.existsSync(filePath)).toBe(true);
|
|
141
|
+
expect(fs.existsSync(`${filePath}.tmp`)).toBe(false);
|
|
142
|
+
const roundTripped = readVerificationOutcomes(filePath);
|
|
143
|
+
expect(roundTripped).toEqual(sampleStore);
|
|
144
|
+
});
|
|
145
|
+
it('round-trips the sample store byte-stably', () => {
|
|
146
|
+
const filePath = tmpFile();
|
|
147
|
+
writeVerificationOutcomes(filePath, sampleStore);
|
|
148
|
+
const firstBytes = fs.readFileSync(filePath, 'utf-8');
|
|
149
|
+
writeVerificationOutcomes(filePath, sampleStore);
|
|
150
|
+
const secondBytes = fs.readFileSync(filePath, 'utf-8');
|
|
151
|
+
expect(secondBytes).toBe(firstBytes);
|
|
152
|
+
});
|
|
153
|
+
it('canonicalizes object key order regardless of insertion order', () => {
|
|
154
|
+
const filePath = tmpFile();
|
|
155
|
+
writeVerificationOutcomes(filePath, sampleStore);
|
|
156
|
+
const firstBytes = fs.readFileSync(filePath, 'utf-8');
|
|
157
|
+
const reordered = {
|
|
158
|
+
def456: sampleStore['def456'],
|
|
159
|
+
abc123: sampleStore['abc123'],
|
|
160
|
+
};
|
|
161
|
+
writeVerificationOutcomes(filePath, reordered);
|
|
162
|
+
const secondBytes = fs.readFileSync(filePath, 'utf-8');
|
|
163
|
+
expect(secondBytes).toBe(firstBytes);
|
|
164
|
+
});
|
|
165
|
+
it('returns an empty store and warns on malformed JSON', () => {
|
|
166
|
+
const filePath = tmpFile();
|
|
167
|
+
fs.writeFileSync(filePath, '{ this is : not json,', 'utf-8');
|
|
168
|
+
const warnings = [];
|
|
169
|
+
const store = readVerificationOutcomes(filePath, (m) => warnings.push(m));
|
|
170
|
+
expect(store).toEqual({});
|
|
171
|
+
expect(warnings.length).toBe(1);
|
|
172
|
+
expect(warnings[0]).toMatch(/Malformed JSON/);
|
|
173
|
+
});
|
|
174
|
+
it('returns an empty store and warns on schema mismatch', () => {
|
|
175
|
+
const filePath = tmpFile();
|
|
176
|
+
fs.writeFileSync(filePath, JSON.stringify({ version: 2, outcomes: {} }), 'utf-8');
|
|
177
|
+
const warnings = [];
|
|
178
|
+
const store = readVerificationOutcomes(filePath, (m) => warnings.push(m));
|
|
179
|
+
expect(store).toEqual({});
|
|
180
|
+
expect(warnings.length).toBe(1);
|
|
181
|
+
expect(warnings[0]).toMatch(/Schema validation failed/);
|
|
182
|
+
});
|
|
183
|
+
it('overwrites a prior corrupt file on next write', () => {
|
|
184
|
+
const filePath = tmpFile();
|
|
185
|
+
fs.writeFileSync(filePath, 'garbage{', 'utf-8');
|
|
186
|
+
expect(readVerificationOutcomes(filePath, () => { })).toEqual({});
|
|
187
|
+
writeVerificationOutcomes(filePath, sampleStore);
|
|
188
|
+
expect(readVerificationOutcomes(filePath)).toEqual(sampleStore);
|
|
189
|
+
});
|
|
190
|
+
it('drops entries whose key does not match their stored ruleHash', () => {
|
|
191
|
+
// Defense in depth: schema validates each entry's shape but not that the
|
|
192
|
+
// file-level key matches the stored ruleHash. A tampered or hand-edited
|
|
193
|
+
// file could memoize an outcome under the wrong hash; the loader filters
|
|
194
|
+
// those out with a warn (CR mmnto-ai/totem#1787 R1).
|
|
195
|
+
const filePath = tmpFile();
|
|
196
|
+
const tampered = {
|
|
197
|
+
version: 1,
|
|
198
|
+
outcomes: {
|
|
199
|
+
// Key "abc123" but entry.ruleHash is "different-hash" — mismatch.
|
|
200
|
+
abc123: {
|
|
201
|
+
ruleHash: 'different-hash',
|
|
202
|
+
verifiedAt: '2026-05-02T00:00:00.000Z',
|
|
203
|
+
outcome: 'in-scope-bad-example',
|
|
204
|
+
baselineMatches: [],
|
|
205
|
+
inScopeMatches: [],
|
|
206
|
+
candidateDebtLines: [],
|
|
207
|
+
},
|
|
208
|
+
// Aligned entry — key matches ruleHash.
|
|
209
|
+
aligned: {
|
|
210
|
+
ruleHash: 'aligned',
|
|
211
|
+
verifiedAt: '2026-05-02T00:00:00.000Z',
|
|
212
|
+
outcome: 'no-matches',
|
|
213
|
+
baselineMatches: [],
|
|
214
|
+
inScopeMatches: [],
|
|
215
|
+
candidateDebtLines: [],
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
fs.writeFileSync(filePath, JSON.stringify(tampered), 'utf-8');
|
|
220
|
+
const warnings = [];
|
|
221
|
+
const store = readVerificationOutcomes(filePath, (m) => warnings.push(m));
|
|
222
|
+
expect(Object.keys(store)).toEqual(['aligned']);
|
|
223
|
+
expect(warnings.length).toBe(1);
|
|
224
|
+
expect(warnings[0]).toMatch(/Key\/hash mismatch/);
|
|
225
|
+
expect(warnings[0]).toMatch(/abc123/);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
//# sourceMappingURL=verification-outcomes.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification-outcomes.test.js","sourceRoot":"","sources":["../src/verification-outcomes.test.ts"],"names":[],"mappings":"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"}
|