@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
|
@@ -93,18 +93,18 @@ describe('loadInstalledPacks: malformed manifest', () => {
|
|
|
93
93
|
version: 1,
|
|
94
94
|
packs: [
|
|
95
95
|
{
|
|
96
|
-
name: '@
|
|
96
|
+
name: '@mmnto/pack-rust',
|
|
97
97
|
resolvedPath: path.resolve('/abs/a'),
|
|
98
98
|
declaredEngineRange: '^1.19.0',
|
|
99
99
|
},
|
|
100
100
|
{
|
|
101
|
-
name: '@
|
|
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 '@
|
|
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: '@
|
|
123
|
-
resolvedPath: 'node_modules/@
|
|
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: '@
|
|
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 '@
|
|
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: '@
|
|
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('@
|
|
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: '@
|
|
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: '@
|
|
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: '@
|
|
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: '@
|
|
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: '@
|
|
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 '@
|
|
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: '@
|
|
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: '@
|
|
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 '@
|
|
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: '@
|
|
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: '@
|
|
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 '@
|
|
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 `@
|
|
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` `@
|
|
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 `@
|
|
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 = '@
|
|
31
|
+
const PACK_NAME_PREFIX = '@mmnto/pack-';
|
|
32
32
|
/**
|
|
33
|
-
* Compute the deduplicated union of `package.json` `@
|
|
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, '@
|
|
77
|
+
writeFixturePack(root, '@mmnto/pack-fake', '^1.19.0');
|
|
78
78
|
const result = resolveInstalledPacks({
|
|
79
79
|
projectRoot: root,
|
|
80
|
-
config: makeConfig(['@
|
|
81
|
-
packageJsonDeps: { '@
|
|
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('@
|
|
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, '@
|
|
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: { '@
|
|
94
|
+
packageJsonDeps: { '@mmnto/pack-orphan-dep': '0.1.0' },
|
|
95
95
|
});
|
|
96
|
-
expect(result.warnings).toEqual([{ name: '@
|
|
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(['@
|
|
103
|
+
config: makeConfig(['@mmnto/pack-orphan-extends']),
|
|
104
104
|
packageJsonDeps: {},
|
|
105
105
|
});
|
|
106
106
|
expect(result.warnings).toEqual([
|
|
107
|
-
{ name: '@
|
|
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, '@
|
|
113
|
+
writeFixturePack(root, '@mmnto/pack-broken', undefined);
|
|
114
114
|
const result = resolveInstalledPacks({
|
|
115
115
|
projectRoot: root,
|
|
116
|
-
config: makeConfig(['@
|
|
117
|
-
packageJsonDeps: { '@
|
|
116
|
+
config: makeConfig(['@mmnto/pack-broken']),
|
|
117
|
+
packageJsonDeps: { '@mmnto/pack-broken': '0.1.0' },
|
|
118
118
|
});
|
|
119
|
-
expect(result.warnings).toEqual([{ name: '@
|
|
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 @
|
|
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, '@
|
|
135
|
-
writeFixturePack(root, '@
|
|
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(['@
|
|
138
|
+
config: makeConfig(['@mmnto/pack-zulu', '@mmnto/pack-alpha']),
|
|
139
139
|
packageJsonDeps: {
|
|
140
|
-
'@
|
|
141
|
-
'@
|
|
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(['@
|
|
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: '@
|
|
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('@
|
|
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();
|
package/dist/pack-merge.d.ts
CHANGED
|
@@ -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 `@
|
|
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
|
*
|
package/dist/pack-merge.js
CHANGED
|
@@ -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 `@
|
|
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
|
*
|
package/dist/rule-engine.js
CHANGED
|
@@ -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., \`@
|
|
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"}
|