@mmnto/totem 1.22.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.
Files changed (65) hide show
  1. package/dist/ast-grep-query.d.ts +31 -0
  2. package/dist/ast-grep-query.d.ts.map +1 -1
  3. package/dist/ast-grep-query.js +45 -1
  4. package/dist/ast-grep-query.js.map +1 -1
  5. package/dist/compile-lesson.d.ts +25 -16
  6. package/dist/compile-lesson.d.ts.map +1 -1
  7. package/dist/compile-lesson.js +72 -34
  8. package/dist/compile-lesson.js.map +1 -1
  9. package/dist/compile-lesson.test.js +30 -0
  10. package/dist/compile-lesson.test.js.map +1 -1
  11. package/dist/compile-manifest.d.ts +14 -1
  12. package/dist/compile-manifest.d.ts.map +1 -1
  13. package/dist/compile-manifest.js +30 -1
  14. package/dist/compile-manifest.js.map +1 -1
  15. package/dist/compile-manifest.test.js +36 -1
  16. package/dist/compile-manifest.test.js.map +1 -1
  17. package/dist/compile-smoke-gate.d.ts.map +1 -1
  18. package/dist/compile-smoke-gate.js +25 -12
  19. package/dist/compile-smoke-gate.js.map +1 -1
  20. package/dist/compile-smoke-gate.test.js +39 -0
  21. package/dist/compile-smoke-gate.test.js.map +1 -1
  22. package/dist/compiler-schema.d.ts +44 -16
  23. package/dist/compiler-schema.d.ts.map +1 -1
  24. package/dist/compiler-schema.js +19 -3
  25. package/dist/compiler-schema.js.map +1 -1
  26. package/dist/compiler-schema.test.js +23 -1
  27. package/dist/compiler-schema.test.js.map +1 -1
  28. package/dist/compiler.d.ts +8 -1
  29. package/dist/compiler.d.ts.map +1 -1
  30. package/dist/compiler.js +11 -2
  31. package/dist/compiler.js.map +1 -1
  32. package/dist/compiler.test.js +35 -0
  33. package/dist/compiler.test.js.map +1 -1
  34. package/dist/config-schema.d.ts +2 -2
  35. package/dist/config-schema.js +2 -2
  36. package/dist/first-lint-promote.d.ts +90 -0
  37. package/dist/first-lint-promote.d.ts.map +1 -0
  38. package/dist/first-lint-promote.js +103 -0
  39. package/dist/first-lint-promote.js.map +1 -0
  40. package/dist/first-lint-promote.test.d.ts +2 -0
  41. package/dist/first-lint-promote.test.d.ts.map +1 -0
  42. package/dist/first-lint-promote.test.js +242 -0
  43. package/dist/first-lint-promote.test.js.map +1 -0
  44. package/dist/index.d.ts +5 -1
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +3 -1
  47. package/dist/index.js.map +1 -1
  48. package/dist/pack-discovery.d.ts +1 -1
  49. package/dist/pack-discovery.js +1 -1
  50. package/dist/pack-discovery.test.js +21 -21
  51. package/dist/pack-manifest-writer.d.ts +2 -2
  52. package/dist/pack-manifest-writer.js +3 -3
  53. package/dist/pack-manifest-writer.test.js +22 -22
  54. package/dist/pack-merge.d.ts +1 -1
  55. package/dist/pack-merge.js +1 -1
  56. package/dist/rule-engine.js +1 -1
  57. package/dist/verification-outcomes.d.ts +160 -0
  58. package/dist/verification-outcomes.d.ts.map +1 -0
  59. package/dist/verification-outcomes.js +149 -0
  60. package/dist/verification-outcomes.js.map +1 -0
  61. package/dist/verification-outcomes.test.d.ts +2 -0
  62. package/dist/verification-outcomes.test.d.ts.map +1 -0
  63. package/dist/verification-outcomes.test.js +228 -0
  64. package/dist/verification-outcomes.test.js.map +1 -0
  65. package/package.json +1 -1
@@ -93,18 +93,18 @@ describe('loadInstalledPacks: malformed manifest', () => {
93
93
  version: 1,
94
94
  packs: [
95
95
  {
96
- name: '@totem/pack-rust',
96
+ name: '@mmnto/pack-rust',
97
97
  resolvedPath: path.resolve('/abs/a'),
98
98
  declaredEngineRange: '^1.19.0',
99
99
  },
100
100
  {
101
- name: '@totem/pack-rust',
101
+ name: '@mmnto/pack-rust',
102
102
  resolvedPath: path.resolve('/abs/b'),
103
103
  declaredEngineRange: '^1.19.0',
104
104
  },
105
105
  ],
106
106
  }));
107
- expect(() => loadInstalledPacks({ projectRoot: tmpRoot, totemDir: '.totem' })).toThrowError(/duplicate pack entry '@totem\/pack-rust'/);
107
+ expect(() => loadInstalledPacks({ projectRoot: tmpRoot, totemDir: '.totem' })).toThrowError(/duplicate pack entry '@mmnto\/pack-rust'/);
108
108
  }
109
109
  finally {
110
110
  fs.rmSync(tmpRoot, { recursive: true, force: true });
@@ -119,8 +119,8 @@ describe('loadInstalledPacks: malformed manifest', () => {
119
119
  version: 1,
120
120
  packs: [
121
121
  {
122
- name: '@totem/pack-relative',
123
- resolvedPath: 'node_modules/@totem/pack-relative',
122
+ name: '@mmnto/pack-relative',
123
+ resolvedPath: 'node_modules/@mmnto/pack-relative',
124
124
  declaredEngineRange: '^1.19.0',
125
125
  },
126
126
  ],
@@ -136,19 +136,19 @@ describe('loadInstalledPacks: peerDependencies engine version mismatch', () => {
136
136
  it('throws structured error naming pack name + declared range + actual engine version', () => {
137
137
  const fakeCallback = () => { };
138
138
  const fakePack = {
139
- name: '@totem/pack-fake',
139
+ name: '@mmnto/pack-fake',
140
140
  resolvedPath: '/fake/path',
141
141
  declaredEngineRange: '^2.0.0',
142
142
  };
143
143
  expect(() => loadInstalledPacks({
144
144
  engineVersion: '1.21.0',
145
145
  inMemoryPacks: [{ pack: fakePack, callback: fakeCallback }],
146
- })).toThrowError(/Pack '@totem\/pack-fake' requires @mmnto\/totem '\^2\.0\.0'.*running engine is 1\.21\.0/);
146
+ })).toThrowError(/Pack '@mmnto\/pack-fake' requires @mmnto\/totem '\^2\.0\.0'.*running engine is 1\.21\.0/);
147
147
  });
148
148
  it('passes when engine version satisfies declared range', () => {
149
149
  const fakeCallback = () => { };
150
150
  const fakePack = {
151
- name: '@totem/pack-fake',
151
+ name: '@mmnto/pack-fake',
152
152
  resolvedPath: '/fake/path',
153
153
  declaredEngineRange: '^1.19.0',
154
154
  };
@@ -157,12 +157,12 @@ describe('loadInstalledPacks: peerDependencies engine version mismatch', () => {
157
157
  inMemoryPacks: [{ pack: fakePack, callback: fakeCallback }],
158
158
  });
159
159
  expect(packs).toHaveLength(1);
160
- expect(packs[0]?.name).toBe('@totem/pack-fake');
160
+ expect(packs[0]?.name).toBe('@mmnto/pack-fake');
161
161
  });
162
162
  it('throws when declared range is invalid semver', () => {
163
163
  const fakeCallback = () => { };
164
164
  const fakePack = {
165
- name: '@totem/pack-fake',
165
+ name: '@mmnto/pack-fake',
166
166
  resolvedPath: '/fake/path',
167
167
  declaredEngineRange: 'not-a-semver-range',
168
168
  };
@@ -185,7 +185,7 @@ describe('loadInstalledPacks: pack callback registration', () => {
185
185
  api.registerLanguage('.rs', 'rust', () => '/fake/tree-sitter-rust.wasm');
186
186
  };
187
187
  const fakePack = {
188
- name: '@totem/pack-rust-architecture',
188
+ name: '@mmnto/pack-rust-architecture',
189
189
  resolvedPath: '/fake/path',
190
190
  declaredEngineRange: '^1.19.0',
191
191
  };
@@ -203,7 +203,7 @@ describe('loadInstalledPacks: pack callback registration', () => {
203
203
  api.registerChunkStrategy('rust-ast', FakeChunker);
204
204
  };
205
205
  const fakePack = {
206
- name: '@totem/pack-rust-architecture',
206
+ name: '@mmnto/pack-rust-architecture',
207
207
  resolvedPath: '/fake/path',
208
208
  declaredEngineRange: '^1.19.0',
209
209
  };
@@ -218,7 +218,7 @@ describe('loadInstalledPacks: pack callback registration', () => {
218
218
  api.registerChunkStrategy('async-strat', FakeChunker);
219
219
  });
220
220
  const fakePack = {
221
- name: '@totem/pack-async',
221
+ name: '@mmnto/pack-async',
222
222
  resolvedPath: '/fake/path',
223
223
  declaredEngineRange: '^1.19.0',
224
224
  };
@@ -242,7 +242,7 @@ describe('loadInstalledPacks: pack callback registration', () => {
242
242
  throw new Error('pack-side bug');
243
243
  };
244
244
  const fakePack = {
245
- name: '@totem/pack-broken',
245
+ name: '@mmnto/pack-broken',
246
246
  resolvedPath: '/fake/path',
247
247
  declaredEngineRange: '^1.19.0',
248
248
  };
@@ -258,7 +258,7 @@ describe('loadInstalledPacks: pack callback registration', () => {
258
258
  }
259
259
  expect(caught).toBeInstanceOf(Error);
260
260
  const outer = caught;
261
- expect(outer.message).toMatch(/Pack '@totem\/pack-broken' registration callback threw/);
261
+ expect(outer.message).toMatch(/Pack '@mmnto\/pack-broken' registration callback threw/);
262
262
  expect(outer.message).toMatch(/must be fixed or removed/);
263
263
  expect(outer.cause).toBeInstanceOf(Error);
264
264
  expect(outer.cause.message).toBe('pack-side bug');
@@ -277,7 +277,7 @@ describe('loadInstalledPacks: pack callback registration', () => {
277
277
  inMemoryPacks: [
278
278
  {
279
279
  pack: {
280
- name: '@totem/pack-a',
280
+ name: '@mmnto/pack-a',
281
281
  resolvedPath: '/a',
282
282
  declaredEngineRange: '^1.19.0',
283
283
  },
@@ -285,7 +285,7 @@ describe('loadInstalledPacks: pack callback registration', () => {
285
285
  },
286
286
  {
287
287
  pack: {
288
- name: '@totem/pack-b',
288
+ name: '@mmnto/pack-b',
289
289
  resolvedPath: '/b',
290
290
  declaredEngineRange: '^1.19.0',
291
291
  },
@@ -298,7 +298,7 @@ describe('loadInstalledPacks: pack callback registration', () => {
298
298
  caught = err;
299
299
  }
300
300
  expect(caught).toBeInstanceOf(Error);
301
- expect(caught.message).toMatch(/Pack '@totem\/pack-b' registration callback threw/);
301
+ expect(caught.message).toMatch(/Pack '@mmnto\/pack-b' registration callback threw/);
302
302
  expect(caught.cause.message).toMatch(/already registered/);
303
303
  });
304
304
  it('two packs registering same extension to different langs: second fails loud', () => {
@@ -315,7 +315,7 @@ describe('loadInstalledPacks: pack callback registration', () => {
315
315
  inMemoryPacks: [
316
316
  {
317
317
  pack: {
318
- name: '@totem/pack-a',
318
+ name: '@mmnto/pack-a',
319
319
  resolvedPath: '/a',
320
320
  declaredEngineRange: '^1.19.0',
321
321
  },
@@ -323,7 +323,7 @@ describe('loadInstalledPacks: pack callback registration', () => {
323
323
  },
324
324
  {
325
325
  pack: {
326
- name: '@totem/pack-b',
326
+ name: '@mmnto/pack-b',
327
327
  resolvedPath: '/b',
328
328
  declaredEngineRange: '^1.19.0',
329
329
  },
@@ -336,7 +336,7 @@ describe('loadInstalledPacks: pack callback registration', () => {
336
336
  caught = err;
337
337
  }
338
338
  expect(caught).toBeInstanceOf(Error);
339
- expect(caught.message).toMatch(/Pack '@totem\/pack-b' registration callback threw/);
339
+ expect(caught.message).toMatch(/Pack '@mmnto\/pack-b' registration callback threw/);
340
340
  expect(caught.cause.message).toMatch(/already registered to language/);
341
341
  });
342
342
  });
@@ -2,7 +2,7 @@
2
2
  * `installed-packs.json` writer (mmnto-ai/totem#1768, Step 4).
3
3
  *
4
4
  * Walks two source surfaces — the consumer's `package.json` dependencies
5
- * for `@totem/pack-*` entries, and the consumer's `totem.config.ts`
5
+ * for `@mmnto/pack-*` entries, and the consumer's `totem.config.ts`
6
6
  * `extends` array — deduplicates, resolves each pack's installed path
7
7
  * via npm/pnpm semantics, and writes `.totem/installed-packs.json` for
8
8
  * `pack-discovery.ts:loadInstalledPacks()` to consume at boot.
@@ -69,7 +69,7 @@ export interface ResolveInstalledPacksInput {
69
69
  readonly resolvePackPath?: (name: string, fromDir: string) => string | undefined;
70
70
  }
71
71
  /**
72
- * Compute the deduplicated union of `package.json` `@totem/pack-*` deps
72
+ * Compute the deduplicated union of `package.json` `@mmnto/pack-*` deps
73
73
  * and `totem.config.ts` `extends` entries. Returns the manifest payload
74
74
  * shape ready for `installed-packs.json` plus per-pack warnings for any
75
75
  * surface mismatch.
@@ -2,7 +2,7 @@
2
2
  * `installed-packs.json` writer (mmnto-ai/totem#1768, Step 4).
3
3
  *
4
4
  * Walks two source surfaces — the consumer's `package.json` dependencies
5
- * for `@totem/pack-*` entries, and the consumer's `totem.config.ts`
5
+ * for `@mmnto/pack-*` entries, and the consumer's `totem.config.ts`
6
6
  * `extends` array — deduplicates, resolves each pack's installed path
7
7
  * via npm/pnpm semantics, and writes `.totem/installed-packs.json` for
8
8
  * `pack-discovery.ts:loadInstalledPacks()` to consume at boot.
@@ -28,9 +28,9 @@ import * as fs from 'node:fs';
28
28
  import { createRequire } from 'node:module';
29
29
  import * as path from 'node:path';
30
30
  // ─── Resolver ───────────────────────────────────────
31
- const PACK_NAME_PREFIX = '@totem/pack-';
31
+ const PACK_NAME_PREFIX = '@mmnto/pack-';
32
32
  /**
33
- * Compute the deduplicated union of `package.json` `@totem/pack-*` deps
33
+ * Compute the deduplicated union of `package.json` `@mmnto/pack-*` deps
34
34
  * and `totem.config.ts` `extends` entries. Returns the manifest payload
35
35
  * shape ready for `installed-packs.json` plus per-pack warnings for any
36
36
  * surface mismatch.
@@ -74,52 +74,52 @@ function makeConfig(extendsList) {
74
74
  describe('resolveInstalledPacks: union of deps + extends', () => {
75
75
  it('resolves a pack present in both deps and extends with no warning', () => {
76
76
  const root = makeTmpRoot();
77
- writeFixturePack(root, '@totem/pack-fake', '^1.19.0');
77
+ writeFixturePack(root, '@mmnto/pack-fake', '^1.19.0');
78
78
  const result = resolveInstalledPacks({
79
79
  projectRoot: root,
80
- config: makeConfig(['@totem/pack-fake']),
81
- packageJsonDeps: { '@totem/pack-fake': '0.1.0' },
80
+ config: makeConfig(['@mmnto/pack-fake']),
81
+ packageJsonDeps: { '@mmnto/pack-fake': '0.1.0' },
82
82
  });
83
83
  expect(result.warnings).toEqual([]);
84
84
  expect(result.resolved).toHaveLength(1);
85
- expect(result.resolved[0]?.name).toBe('@totem/pack-fake');
85
+ expect(result.resolved[0]?.name).toBe('@mmnto/pack-fake');
86
86
  expect(result.resolved[0]?.declaredEngineRange).toBe('^1.19.0');
87
87
  });
88
88
  it('emits dep-only warning when pack is in deps but not extends', () => {
89
89
  const root = makeTmpRoot();
90
- writeFixturePack(root, '@totem/pack-orphan-dep', '^1.19.0');
90
+ writeFixturePack(root, '@mmnto/pack-orphan-dep', '^1.19.0');
91
91
  const result = resolveInstalledPacks({
92
92
  projectRoot: root,
93
93
  config: makeConfig([]),
94
- packageJsonDeps: { '@totem/pack-orphan-dep': '0.1.0' },
94
+ packageJsonDeps: { '@mmnto/pack-orphan-dep': '0.1.0' },
95
95
  });
96
- expect(result.warnings).toEqual([{ name: '@totem/pack-orphan-dep', reason: 'dep-only' }]);
96
+ expect(result.warnings).toEqual([{ name: '@mmnto/pack-orphan-dep', reason: 'dep-only' }]);
97
97
  expect(result.resolved).toEqual([]);
98
98
  });
99
99
  it('emits extends-only warning when pack is in extends but not deps', () => {
100
100
  const root = makeTmpRoot();
101
101
  const result = resolveInstalledPacks({
102
102
  projectRoot: root,
103
- config: makeConfig(['@totem/pack-orphan-extends']),
103
+ config: makeConfig(['@mmnto/pack-orphan-extends']),
104
104
  packageJsonDeps: {},
105
105
  });
106
106
  expect(result.warnings).toEqual([
107
- { name: '@totem/pack-orphan-extends', reason: 'extends-only' },
107
+ { name: '@mmnto/pack-orphan-extends', reason: 'extends-only' },
108
108
  ]);
109
109
  expect(result.resolved).toEqual([]);
110
110
  });
111
111
  it('emits not-a-pack warning when pack lacks peerDependencies[@mmnto/totem]', () => {
112
112
  const root = makeTmpRoot();
113
- writeFixturePack(root, '@totem/pack-broken', undefined);
113
+ writeFixturePack(root, '@mmnto/pack-broken', undefined);
114
114
  const result = resolveInstalledPacks({
115
115
  projectRoot: root,
116
- config: makeConfig(['@totem/pack-broken']),
117
- packageJsonDeps: { '@totem/pack-broken': '0.1.0' },
116
+ config: makeConfig(['@mmnto/pack-broken']),
117
+ packageJsonDeps: { '@mmnto/pack-broken': '0.1.0' },
118
118
  });
119
- expect(result.warnings).toEqual([{ name: '@totem/pack-broken', reason: 'not-a-pack' }]);
119
+ expect(result.warnings).toEqual([{ name: '@mmnto/pack-broken', reason: 'not-a-pack' }]);
120
120
  expect(result.resolved).toEqual([]);
121
121
  });
122
- it('ignores non-pack dependencies (no @totem/pack- prefix)', () => {
122
+ it('ignores non-pack dependencies (no @mmnto/pack- prefix)', () => {
123
123
  const root = makeTmpRoot();
124
124
  const result = resolveInstalledPacks({
125
125
  projectRoot: root,
@@ -131,17 +131,17 @@ describe('resolveInstalledPacks: union of deps + extends', () => {
131
131
  });
132
132
  it('returns sorted output (alphabetical by pack name)', () => {
133
133
  const root = makeTmpRoot();
134
- writeFixturePack(root, '@totem/pack-zulu', '^1.19.0');
135
- writeFixturePack(root, '@totem/pack-alpha', '^1.19.0');
134
+ writeFixturePack(root, '@mmnto/pack-zulu', '^1.19.0');
135
+ writeFixturePack(root, '@mmnto/pack-alpha', '^1.19.0');
136
136
  const result = resolveInstalledPacks({
137
137
  projectRoot: root,
138
- config: makeConfig(['@totem/pack-zulu', '@totem/pack-alpha']),
138
+ config: makeConfig(['@mmnto/pack-zulu', '@mmnto/pack-alpha']),
139
139
  packageJsonDeps: {
140
- '@totem/pack-zulu': '0.1.0',
141
- '@totem/pack-alpha': '0.1.0',
140
+ '@mmnto/pack-zulu': '0.1.0',
141
+ '@mmnto/pack-alpha': '0.1.0',
142
142
  },
143
143
  });
144
- expect(result.resolved.map((p) => p.name)).toEqual(['@totem/pack-alpha', '@totem/pack-zulu']);
144
+ expect(result.resolved.map((p) => p.name)).toEqual(['@mmnto/pack-alpha', '@mmnto/pack-zulu']);
145
145
  });
146
146
  });
147
147
  describe('writeInstalledPacksManifest', () => {
@@ -152,7 +152,7 @@ describe('writeInstalledPacksManifest', () => {
152
152
  version: 1,
153
153
  packs: [
154
154
  {
155
- name: '@totem/pack-fake',
155
+ name: '@mmnto/pack-fake',
156
156
  resolvedPath: '/fake/path',
157
157
  declaredEngineRange: '^1.19.0',
158
158
  },
@@ -164,7 +164,7 @@ describe('writeInstalledPacksManifest', () => {
164
164
  const parsed = JSON.parse(raw);
165
165
  const validation = InstalledPacksManifestSchema.safeParse(parsed);
166
166
  expect(validation.success).toBe(true);
167
- expect(validation.success && validation.data.packs[0]?.name).toBe('@totem/pack-fake');
167
+ expect(validation.success && validation.data.packs[0]?.name).toBe('@mmnto/pack-fake');
168
168
  });
169
169
  it('creates the totemDir if it does not exist', () => {
170
170
  const root = makeTmpRoot();
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * ADR-089 carves out a narrow exception: rules shipped by a pack with
10
10
  * `immutable: true` AND `severity: 'error'` cannot be locally downgraded
11
- * or archived. Security packs like `@totem/pack-agent-security` rely on
11
+ * or archived. Security packs like `@mmnto/pack-agent-security` rely on
12
12
  * this contract to guarantee that enforcement cannot be silently weakened
13
13
  * by a motivated local override.
14
14
  *
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * ADR-089 carves out a narrow exception: rules shipped by a pack with
10
10
  * `immutable: true` AND `severity: 'error'` cannot be locally downgraded
11
- * or archived. Security packs like `@totem/pack-agent-security` rely on
11
+ * or archived. Security packs like `@mmnto/pack-agent-security` rely on
12
12
  * this contract to guarantee that enforcement cannot be silently weakened
13
13
  * by a motivated local override.
14
14
  *
@@ -337,7 +337,7 @@ export async function applyAstRulesToAdditions(ctx, rules, additions, workingDir
337
337
  // intentionally NOT in the message argument here — duplicating it
338
338
  // would produce `[Totem Error] [Totem Error] AST rule ...` at
339
339
  // runtime. See `errors.ts` for the prefix contract.
340
- throw new TotemParseError(`AST rule '${ruleExpectingThisFile.lessonHash}' (${ruleExpectingThisFile.lessonHeading}) is scoped to '${file}' (extension '${ext}') but no Tree-sitter language is registered for that extension`, `Install the pack that provides '${ext}' support (e.g., \`@totem/pack-rust-architecture\` for '.rs'), or correct the rule's fileGlobs to exclude this extension.`);
340
+ throw new TotemParseError(`AST rule '${ruleExpectingThisFile.lessonHash}' (${ruleExpectingThisFile.lessonHeading}) is scoped to '${file}' (extension '${ext}') but no Tree-sitter language is registered for that extension`, `Install the pack that provides '${ext}' support (e.g., \`@mmnto/pack-rust-architecture\` for '.rs'), or correct the rule's fileGlobs to exclude this extension.`);
341
341
  }
342
342
  continue;
343
343
  }
@@ -0,0 +1,160 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Stage 4 outcome literals serialized into `.totem/verification-outcomes.json`
4
+ * (mmnto-ai/totem#1684, ADR-091 § Bootstrap CI-first). Mirrors the
5
+ * `Stage4Outcome` type from `stage4-verifier.ts` exactly so the storage
6
+ * vocabulary matches the runtime type and we avoid a translation layer that
7
+ * would drift on Stage 4 evolution.
8
+ *
9
+ * The `_OutcomeAlignment` type below enforces the mirror at compile time —
10
+ * if either side adds, drops, or renames a literal, the assertion below
11
+ * fails the build before drift can land in the corpus.
12
+ */
13
+ export declare const Stage4OutcomeStored: z.ZodEnum<["no-matches", "out-of-scope", "in-scope-bad-example", "candidate-debt"]>;
14
+ /**
15
+ * One verification record per pack-installed rule (keyed by `lessonHash` in
16
+ * the file-level record). Persisted across lint runs so subsequent passes
17
+ * skip re-verification when the rule's content hash matches a recorded
18
+ * outcome.
19
+ */
20
+ export declare const VerificationOutcomeEntrySchema: z.ZodObject<{
21
+ /**
22
+ * `lessonHash` of the rule the outcome was recorded against. Persisted
23
+ * inside the entry as well as serving as the file-level record key so a
24
+ * tampered key (key/value mismatch) can be detected on read.
25
+ */
26
+ ruleHash: z.ZodString;
27
+ /** ISO-8601 timestamp of when the verifier produced the outcome. */
28
+ verifiedAt: z.ZodString;
29
+ /** The terminal Stage 4 outcome that was recorded. */
30
+ outcome: z.ZodEnum<["no-matches", "out-of-scope", "in-scope-bad-example", "candidate-debt"]>;
31
+ /** Repo-relative paths of baseline-scoped files where the rule fired. */
32
+ baselineMatches: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
33
+ /** Repo-relative paths of in-scope files where the rule fired. */
34
+ inScopeMatches: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
35
+ /**
36
+ * In-scope match lines that did not match the `badExample` shape — the
37
+ * Candidate Debt evidence carried forward to the `totem doctor` UX
38
+ * surface in mmnto-ai/totem#1685. Empty unless `outcome === 'candidate-debt'`.
39
+ */
40
+ candidateDebtLines: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
41
+ }, "strip", z.ZodTypeAny, {
42
+ ruleHash: string;
43
+ outcome: "out-of-scope" | "no-matches" | "in-scope-bad-example" | "candidate-debt";
44
+ baselineMatches: string[];
45
+ inScopeMatches: string[];
46
+ candidateDebtLines: string[];
47
+ verifiedAt: string;
48
+ }, {
49
+ ruleHash: string;
50
+ outcome: "out-of-scope" | "no-matches" | "in-scope-bad-example" | "candidate-debt";
51
+ verifiedAt: string;
52
+ baselineMatches?: string[] | undefined;
53
+ inScopeMatches?: string[] | undefined;
54
+ candidateDebtLines?: string[] | undefined;
55
+ }>;
56
+ /**
57
+ * The on-disk shape of `.totem/verification-outcomes.json`. Wraps the
58
+ * per-rule record in a versioned envelope so structural changes can break
59
+ * the file forward without silent migration: a loader that sees an unknown
60
+ * `version` treats the file as empty and re-verifies, instead of risking a
61
+ * partial parse against a newer schema.
62
+ */
63
+ export declare const VerificationOutcomesFileSchema: z.ZodObject<{
64
+ version: z.ZodDefault<z.ZodLiteral<1>>;
65
+ outcomes: z.ZodRecord<z.ZodString, z.ZodObject<{
66
+ /**
67
+ * `lessonHash` of the rule the outcome was recorded against. Persisted
68
+ * inside the entry as well as serving as the file-level record key so a
69
+ * tampered key (key/value mismatch) can be detected on read.
70
+ */
71
+ ruleHash: z.ZodString;
72
+ /** ISO-8601 timestamp of when the verifier produced the outcome. */
73
+ verifiedAt: z.ZodString;
74
+ /** The terminal Stage 4 outcome that was recorded. */
75
+ outcome: z.ZodEnum<["no-matches", "out-of-scope", "in-scope-bad-example", "candidate-debt"]>;
76
+ /** Repo-relative paths of baseline-scoped files where the rule fired. */
77
+ baselineMatches: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
78
+ /** Repo-relative paths of in-scope files where the rule fired. */
79
+ inScopeMatches: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
80
+ /**
81
+ * In-scope match lines that did not match the `badExample` shape — the
82
+ * Candidate Debt evidence carried forward to the `totem doctor` UX
83
+ * surface in mmnto-ai/totem#1685. Empty unless `outcome === 'candidate-debt'`.
84
+ */
85
+ candidateDebtLines: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
86
+ }, "strip", z.ZodTypeAny, {
87
+ ruleHash: string;
88
+ outcome: "out-of-scope" | "no-matches" | "in-scope-bad-example" | "candidate-debt";
89
+ baselineMatches: string[];
90
+ inScopeMatches: string[];
91
+ candidateDebtLines: string[];
92
+ verifiedAt: string;
93
+ }, {
94
+ ruleHash: string;
95
+ outcome: "out-of-scope" | "no-matches" | "in-scope-bad-example" | "candidate-debt";
96
+ verifiedAt: string;
97
+ baselineMatches?: string[] | undefined;
98
+ inScopeMatches?: string[] | undefined;
99
+ candidateDebtLines?: string[] | undefined;
100
+ }>>;
101
+ }, "strip", z.ZodTypeAny, {
102
+ version: 1;
103
+ outcomes: Record<string, {
104
+ ruleHash: string;
105
+ outcome: "out-of-scope" | "no-matches" | "in-scope-bad-example" | "candidate-debt";
106
+ baselineMatches: string[];
107
+ inScopeMatches: string[];
108
+ candidateDebtLines: string[];
109
+ verifiedAt: string;
110
+ }>;
111
+ }, {
112
+ outcomes: Record<string, {
113
+ ruleHash: string;
114
+ outcome: "out-of-scope" | "no-matches" | "in-scope-bad-example" | "candidate-debt";
115
+ verifiedAt: string;
116
+ baselineMatches?: string[] | undefined;
117
+ inScopeMatches?: string[] | undefined;
118
+ candidateDebtLines?: string[] | undefined;
119
+ }>;
120
+ version?: 1 | undefined;
121
+ }>;
122
+ export type Stage4OutcomeStoredValue = z.infer<typeof Stage4OutcomeStored>;
123
+ export type VerificationOutcomeEntry = z.infer<typeof VerificationOutcomeEntrySchema>;
124
+ export type VerificationOutcomesFile = z.infer<typeof VerificationOutcomesFileSchema>;
125
+ /**
126
+ * Convenience alias for the in-memory mapping of `lessonHash` to its recorded
127
+ * verification outcome. The file-level wrapper holds the same record under
128
+ * `outcomes`; this alias exists so call sites that operate on the mapping
129
+ * directly (the first-lint promotion interceptor in particular) can carry
130
+ * a focused type without re-derivation. Reserved by mmnto-ai/totem#1684 §
131
+ * "Vocabulary alignment" — `VerificationOutcomesStore` is the canonical name
132
+ * for the in-memory mapping.
133
+ */
134
+ export type VerificationOutcomesStore = VerificationOutcomesFile['outcomes'];
135
+ /**
136
+ * Load the per-rule verification outcomes from a `verification-outcomes.json`
137
+ * file. Returns an empty store when the file is missing, contains malformed
138
+ * JSON, or fails schema validation — the corpus self-heals on the next
139
+ * write. The interceptor that wrote the corrupt file (or a future pass with
140
+ * a refreshed schema) will overwrite it on the next promotion run, so the
141
+ * cache state cannot wedge.
142
+ *
143
+ * Invariant: schema-version mismatch is treated as "no outcomes recorded"
144
+ * rather than attempted migration. A v2 file read by a v1 loader returns
145
+ * an empty store and warns; the next write produces a valid v1 file.
146
+ */
147
+ export declare function readVerificationOutcomes(filePath: string, onWarn?: (msg: string) => void): VerificationOutcomesStore;
148
+ /**
149
+ * Persist the per-rule verification outcomes to disk via temp-file +
150
+ * atomic rename, so a concurrent CI lint pass that interrupts mid-write
151
+ * leaves the prior valid file intact rather than producing a truncated
152
+ * partial-write that the next loader would reject.
153
+ *
154
+ * Output is canonicalized (recursive object-key sort) before serialization
155
+ * so two lint passes that produce the same outcomes write byte-identical
156
+ * files — Invariant #11 in the design doc, which keeps consumer repos
157
+ * from seeing phantom diffs on every CI run.
158
+ */
159
+ export declare function writeVerificationOutcomes(filePath: string, outcomes: VerificationOutcomesStore): void;
160
+ //# sourceMappingURL=verification-outcomes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification-outcomes.d.ts","sourceRoot":"","sources":["../src/verification-outcomes.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,qFAK9B,CAAC;AAYH;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B;IACzC;;;;OAIG;;IAEH,oEAAoE;;IAEpE,sDAAsD;;IAEtD,yEAAyE;;IAEzE,kEAAkE;;IAElE;;;;OAIG;;;;;;;;;;;;;;;;EAEH,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,8BAA8B;;;QA7BzC;;;;WAIG;;QAEH,oEAAoE;;QAEpE,sDAAsD;;QAEtD,yEAAyE;;QAEzE,kEAAkE;;QAElE;;;;WAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAcH,CAAC;AAEH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAC3E,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC;AACtF,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC;AAEtF;;;;;;;;GAQG;AACH,MAAM,MAAM,yBAAyB,GAAG,wBAAwB,CAAC,UAAU,CAAC,CAAC;AAI7E;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAC7B,yBAAyB,CA8C3B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,yBAAyB,GAClC,IAAI,CAiBN"}