@mmnto/cli 1.14.11 → 1.14.12
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/commands/compile-prune.test.js +35 -18
- package/dist/commands/compile-prune.test.js.map +1 -1
- package/dist/commands/compile-verbose-trace.test.d.ts +2 -0
- package/dist/commands/compile-verbose-trace.test.d.ts.map +1 -0
- package/dist/commands/compile-verbose-trace.test.js +128 -0
- package/dist/commands/compile-verbose-trace.test.js.map +1 -0
- package/dist/commands/compile.d.ts +33 -8
- package/dist/commands/compile.d.ts.map +1 -1
- package/dist/commands/compile.js +136 -11
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/doctor.d.ts +44 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +147 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/doctor.test.js +342 -2
- package/dist/commands/doctor.test.js.map +1 -1
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +3 -2
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/run-compiled-rules.d.ts.map +1 -1
- package/dist/commands/run-compiled-rules.js +11 -1
- package/dist/commands/run-compiled-rules.js.map +1 -1
- package/dist/commands/run-compiled-rules.test.js +65 -0
- package/dist/commands/run-compiled-rules.test.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
import { pruneStaleNonCompilable, pruneStaleRules } from './compile.js';
|
|
3
|
+
// ─── 4-tuple helpers (mmnto-ai/totem#1481) ───────────
|
|
4
|
+
function entry(title, reasonCode = 'out-of-scope', reason) {
|
|
5
|
+
return reason === undefined ? { title, reasonCode } : { title, reasonCode, reason };
|
|
6
|
+
}
|
|
3
7
|
// ─── Test helpers ────────────────────────────────────
|
|
4
8
|
function makeRule(lessonHash, heading = `Heading for ${lessonHash}`) {
|
|
5
9
|
return {
|
|
@@ -19,51 +23,64 @@ describe('pruneStaleNonCompilable', () => {
|
|
|
19
23
|
});
|
|
20
24
|
it('returns all entries when every hash is still current', () => {
|
|
21
25
|
const map = new Map([
|
|
22
|
-
['abc', 'First lesson'],
|
|
23
|
-
['def', 'Second lesson'],
|
|
26
|
+
['abc', entry('First lesson')],
|
|
27
|
+
['def', entry('Second lesson', 'missing-badexample')],
|
|
24
28
|
]);
|
|
25
29
|
const current = new Set(['abc', 'def']);
|
|
26
30
|
const result = pruneStaleNonCompilable(map, current);
|
|
27
31
|
expect(result.fresh).toEqual([
|
|
28
|
-
{ hash: 'abc', title: 'First lesson' },
|
|
29
|
-
{ hash: 'def', title: 'Second lesson' },
|
|
32
|
+
{ hash: 'abc', title: 'First lesson', reasonCode: 'out-of-scope' },
|
|
33
|
+
{ hash: 'def', title: 'Second lesson', reasonCode: 'missing-badexample' },
|
|
30
34
|
]);
|
|
31
35
|
expect(result.drained).toBe(0);
|
|
32
36
|
});
|
|
33
37
|
it('drops entries whose hashes are no longer present in current lessons', () => {
|
|
34
38
|
const map = new Map([
|
|
35
|
-
['abc', 'Kept lesson'],
|
|
36
|
-
['stale1', 'Removed lesson A'],
|
|
37
|
-
['stale2', 'Removed lesson B'],
|
|
39
|
+
['abc', entry('Kept lesson')],
|
|
40
|
+
['stale1', entry('Removed lesson A')],
|
|
41
|
+
['stale2', entry('Removed lesson B')],
|
|
38
42
|
]);
|
|
39
43
|
const current = new Set(['abc']);
|
|
40
44
|
const result = pruneStaleNonCompilable(map, current);
|
|
41
|
-
expect(result.fresh).toEqual([
|
|
45
|
+
expect(result.fresh).toEqual([
|
|
46
|
+
{ hash: 'abc', title: 'Kept lesson', reasonCode: 'out-of-scope' },
|
|
47
|
+
]);
|
|
42
48
|
expect(result.drained).toBe(2);
|
|
43
49
|
});
|
|
44
50
|
it('drains everything when no hashes are current', () => {
|
|
45
51
|
const map = new Map([
|
|
46
|
-
['stale1', 'Removed A'],
|
|
47
|
-
['stale2', 'Removed B'],
|
|
52
|
+
['stale1', entry('Removed A')],
|
|
53
|
+
['stale2', entry('Removed B')],
|
|
48
54
|
]);
|
|
49
55
|
const current = new Set();
|
|
50
56
|
const result = pruneStaleNonCompilable(map, current);
|
|
51
57
|
expect(result.fresh).toEqual([]);
|
|
52
58
|
expect(result.drained).toBe(2);
|
|
53
59
|
});
|
|
54
|
-
it('preserves
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
const map = new Map([
|
|
58
|
-
|
|
60
|
+
it('preserves reasonCode and reason fields through the prune', () => {
|
|
61
|
+
// mmnto-ai/totem#1481 invariant #8: the 4-tuple must survive the prune
|
|
62
|
+
// intact, not collapse back to a 2-tuple.
|
|
63
|
+
const map = new Map([
|
|
64
|
+
['hash-a', entry('Legacy entry', 'legacy-unknown')],
|
|
65
|
+
['hash-b', entry('Modern entry', 'out-of-scope', 'Architectural principle.')],
|
|
66
|
+
]);
|
|
67
|
+
const current = new Set(['hash-a', 'hash-b']);
|
|
59
68
|
const result = pruneStaleNonCompilable(map, current);
|
|
60
|
-
expect(result.fresh).toEqual([
|
|
69
|
+
expect(result.fresh).toEqual([
|
|
70
|
+
{ hash: 'hash-a', title: 'Legacy entry', reasonCode: 'legacy-unknown' },
|
|
71
|
+
{
|
|
72
|
+
hash: 'hash-b',
|
|
73
|
+
title: 'Modern entry',
|
|
74
|
+
reasonCode: 'out-of-scope',
|
|
75
|
+
reason: 'Architectural principle.',
|
|
76
|
+
},
|
|
77
|
+
]);
|
|
61
78
|
expect(result.drained).toBe(0);
|
|
62
79
|
});
|
|
63
80
|
it('does not mutate the input map', () => {
|
|
64
81
|
const map = new Map([
|
|
65
|
-
['abc', 'Kept'],
|
|
66
|
-
['stale', 'Removed'],
|
|
82
|
+
['abc', entry('Kept')],
|
|
83
|
+
['stale', entry('Removed')],
|
|
67
84
|
]);
|
|
68
85
|
const current = new Set(['abc']);
|
|
69
86
|
pruneStaleNonCompilable(map, current);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile-prune.test.js","sourceRoot":"","sources":["../../src/commands/compile-prune.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"compile-prune.test.js","sourceRoot":"","sources":["../../src/commands/compile-prune.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile-verbose-trace.test.d.ts","sourceRoot":"","sources":["../../src/commands/compile-verbose-trace.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { formatVerboseTraceBlock } from './compile.js';
|
|
3
|
+
// ─── Format verbose trace block (mmnto-ai/totem#1482) ──
|
|
4
|
+
describe('formatVerboseTraceBlock', () => {
|
|
5
|
+
const lesson = { heading: 'No console.log in production', hash: 'abc12345def67890' };
|
|
6
|
+
it('renders a pipeline 1 single-result trace as a header plus result line', () => {
|
|
7
|
+
const trace = [{ layer: 1, action: 'result', outcome: 'compiled' }];
|
|
8
|
+
const block = formatVerboseTraceBlock(lesson, 'compiled', undefined, trace);
|
|
9
|
+
const lines = block.split('\n');
|
|
10
|
+
expect(lines[0]).toBe('lesson-abc12345 "No console.log in production":');
|
|
11
|
+
expect(lines[1]).toBe(' result: compiled');
|
|
12
|
+
expect(lines).toHaveLength(2);
|
|
13
|
+
});
|
|
14
|
+
it('renders a pipeline 2 first-try success with generate + verify + result', () => {
|
|
15
|
+
const trace = [
|
|
16
|
+
{ layer: 3, action: 'generate', outcome: 'attempt-1', patternHash: 'deadbeefcafebabe' },
|
|
17
|
+
{ layer: 3, action: 'verify', outcome: 'MATCH' },
|
|
18
|
+
{ layer: 3, action: 'result', outcome: 'compiled' },
|
|
19
|
+
];
|
|
20
|
+
const block = formatVerboseTraceBlock(lesson, 'compiled', undefined, trace);
|
|
21
|
+
expect(block).toContain('lesson-abc12345');
|
|
22
|
+
expect(block).toContain('Layer 3 (Pipeline 3 (LLM + verify-retry)) -> attempt-1 (patternHash=deadbeefcafebabe)');
|
|
23
|
+
expect(block).toContain('verify on example: MATCH');
|
|
24
|
+
expect(block).toContain('result: compiled');
|
|
25
|
+
});
|
|
26
|
+
it('renders a verify-retry-exhausted trace with retry counters and reasonCode', () => {
|
|
27
|
+
const trace = [
|
|
28
|
+
{ layer: 3, action: 'generate', outcome: 'attempt-1', patternHash: 'a'.repeat(16) },
|
|
29
|
+
{ layer: 3, action: 'verify', outcome: 'example-hit-miss' },
|
|
30
|
+
{ layer: 3, action: 'retry', outcome: 'attempt-2-scheduled' },
|
|
31
|
+
{ layer: 3, action: 'generate', outcome: 'attempt-2', patternHash: 'b'.repeat(16) },
|
|
32
|
+
{ layer: 3, action: 'verify', outcome: 'example-hit-miss' },
|
|
33
|
+
{ layer: 3, action: 'retry', outcome: 'attempt-3-scheduled' },
|
|
34
|
+
{ layer: 3, action: 'generate', outcome: 'attempt-3', patternHash: 'c'.repeat(16) },
|
|
35
|
+
{ layer: 3, action: 'verify', outcome: 'example-hit-miss' },
|
|
36
|
+
{
|
|
37
|
+
layer: 3,
|
|
38
|
+
action: 'result',
|
|
39
|
+
outcome: 'skipped',
|
|
40
|
+
reasonCode: 'verify-retry-exhausted',
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
const block = formatVerboseTraceBlock(lesson, 'skipped', 'verify-retry-exhausted', trace);
|
|
44
|
+
expect(block).toContain('retry 1: attempt-2-scheduled');
|
|
45
|
+
expect(block).toContain('retry 2: attempt-3-scheduled');
|
|
46
|
+
expect(block).toContain('result: skipped (verify-retry-exhausted)');
|
|
47
|
+
// Exactly three generate lines (one per attempt)
|
|
48
|
+
const genCount = (block.match(/Layer 3 \(Pipeline 3 \(LLM \+ verify-retry\)\) -> attempt-/g) ?? []).length;
|
|
49
|
+
expect(genCount).toBe(3);
|
|
50
|
+
});
|
|
51
|
+
it('renders pipeline 3 (layer 2) with the correct pipeline label', () => {
|
|
52
|
+
const trace = [
|
|
53
|
+
{ layer: 2, action: 'generate', outcome: 'produced', patternHash: '0'.repeat(16) },
|
|
54
|
+
{ layer: 2, action: 'verify', outcome: 'passed' },
|
|
55
|
+
{ layer: 2, action: 'result', outcome: 'compiled' },
|
|
56
|
+
];
|
|
57
|
+
const block = formatVerboseTraceBlock(lesson, 'compiled', undefined, trace);
|
|
58
|
+
expect(block).toContain('Layer 2 (Pipeline 2 (example-based)) -> produced');
|
|
59
|
+
});
|
|
60
|
+
it('falls back gracefully when trace is undefined or empty', () => {
|
|
61
|
+
const undefinedBlock = formatVerboseTraceBlock(lesson, 'failed', undefined, undefined);
|
|
62
|
+
expect(undefinedBlock).toContain('(no trace events recorded)');
|
|
63
|
+
expect(undefinedBlock).toContain('result: failed');
|
|
64
|
+
const emptyBlock = formatVerboseTraceBlock(lesson, 'noop', undefined, []);
|
|
65
|
+
expect(emptyBlock).toContain('(no trace events recorded)');
|
|
66
|
+
});
|
|
67
|
+
it('emits a single contiguous multi-line string (no intermediate newlines before content)', () => {
|
|
68
|
+
// Invariant: the caller writes this block via one process.stdout.write
|
|
69
|
+
// call, so the block must already contain its internal newlines and
|
|
70
|
+
// must not start or end with bare whitespace that would collide with
|
|
71
|
+
// the trailing \n the caller appends.
|
|
72
|
+
const trace = [{ layer: 1, action: 'result', outcome: 'compiled' }];
|
|
73
|
+
const block = formatVerboseTraceBlock(lesson, 'compiled', undefined, trace);
|
|
74
|
+
expect(block.startsWith('lesson-')).toBe(true);
|
|
75
|
+
expect(block.endsWith('\n')).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
it('renders a skipped lesson with reasonCode from the terminal event', () => {
|
|
78
|
+
const trace = [
|
|
79
|
+
{
|
|
80
|
+
layer: 3,
|
|
81
|
+
action: 'result',
|
|
82
|
+
outcome: 'skipped',
|
|
83
|
+
reasonCode: 'out-of-scope',
|
|
84
|
+
},
|
|
85
|
+
];
|
|
86
|
+
const block = formatVerboseTraceBlock(lesson, 'skipped', 'out-of-scope', trace);
|
|
87
|
+
expect(block).toContain('result: skipped (out-of-scope)');
|
|
88
|
+
});
|
|
89
|
+
it('tolerates unknown layer numbers via the fallback label', () => {
|
|
90
|
+
const trace = [
|
|
91
|
+
{
|
|
92
|
+
layer: 99,
|
|
93
|
+
action: 'generate',
|
|
94
|
+
outcome: 'produced',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
layer: 99,
|
|
98
|
+
action: 'result',
|
|
99
|
+
outcome: 'compiled',
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
const block = formatVerboseTraceBlock(lesson, 'compiled', undefined, trace);
|
|
103
|
+
expect(block).toContain('Layer 99');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
// ─── Atomic stdout.write invocation ──────────────────
|
|
107
|
+
describe('verbose trace atomic emission', () => {
|
|
108
|
+
it('guarantees the trace block formats as a single string the caller can ship via one stdout.write call', () => {
|
|
109
|
+
// The compile command invokes process.stdout.write(block + '\n') once
|
|
110
|
+
// per lesson so concurrent lessons cannot interleave inside the block.
|
|
111
|
+
// That invariant reduces to: formatVerboseTraceBlock returns a single
|
|
112
|
+
// string. The test below pins the shape so a refactor that splits the
|
|
113
|
+
// render across multiple returns fails loudly.
|
|
114
|
+
const lesson = { heading: 'Atomic test lesson', hash: 'aaaa1111bbbb2222' };
|
|
115
|
+
const trace = [
|
|
116
|
+
{ layer: 3, action: 'generate', outcome: 'attempt-1', patternHash: 'f'.repeat(16) },
|
|
117
|
+
{ layer: 3, action: 'verify', outcome: 'MATCH' },
|
|
118
|
+
{ layer: 3, action: 'result', outcome: 'compiled' },
|
|
119
|
+
];
|
|
120
|
+
const out = formatVerboseTraceBlock(lesson, 'compiled', undefined, trace);
|
|
121
|
+
expect(typeof out).toBe('string');
|
|
122
|
+
// Block must contain every event's outcome; no event gets dropped.
|
|
123
|
+
for (const ev of trace) {
|
|
124
|
+
expect(out).toContain(ev.outcome);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
//# sourceMappingURL=compile-verbose-trace.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile-verbose-trace.test.js","sourceRoot":"","sources":["../../src/commands/compile-verbose-trace.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAI9C,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAEvD,0DAA0D;AAE1D,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,MAAM,MAAM,GAAG,EAAE,OAAO,EAAE,8BAA8B,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC;IAErF,EAAE,CAAC,uEAAuE,EAAE,GAAG,EAAE;QAC/E,MAAM,KAAK,GAAsB,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;QACvF,MAAM,KAAK,GAAG,uBAAuB,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QAC5E,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;QACzE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,GAAG,EAAE;QAChF,MAAM,KAAK,GAAsB;YAC/B,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,kBAAkB,EAAE;YACvF,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE;YAChD,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE;SACpD,CAAC;QACF,MAAM,KAAK,GAAG,uBAAuB,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QAC5E,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CACrB,uFAAuF,CACxF,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC;QACpD,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,GAAG,EAAE;QACnF,MAAM,KAAK,GAAsB;YAC/B,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE;YACnF,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,kBAAkB,EAAE;YAC3D,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE;YAC7D,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE;YACnF,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,kBAAkB,EAAE;YAC3D,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE;YAC7D,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE;YACnF,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,kBAAkB,EAAE;YAC3D;gBACE,KAAK,EAAE,CAAC;gBACR,MAAM,EAAE,QAAQ;gBAChB,OAAO,EAAE,SAAS;gBAClB,UAAU,EAAE,wBAAwB;aACrC;SACF,CAAC;QACF,MAAM,KAAK,GAAG,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,wBAAwB,EAAE,KAAK,CAAC,CAAC;QAC1F,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAC;QACxD,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAC;QACxD,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,0CAA0C,CAAC,CAAC;QACpE,iDAAiD;QACjD,MAAM,QAAQ,GAAG,CACf,KAAK,CAAC,KAAK,CAAC,6DAA6D,CAAC,IAAI,EAAE,CACjF,CAAC,MAAM,CAAC;QACT,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,KAAK,GAAsB;YAC/B,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE;YAClF,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE;YACjD,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE;SACpD,CAAC;QACF,MAAM,KAAK,GAAG,uBAAuB,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QAC5E,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,kDAAkD,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,cAAc,GAAG,uBAAuB,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;QACvF,MAAM,CAAC,cAAc,CAAC,CAAC,SAAS,CAAC,4BAA4B,CAAC,CAAC;QAC/D,MAAM,CAAC,cAAc,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAEnD,MAAM,UAAU,GAAG,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QAC1E,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,4BAA4B,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uFAAuF,EAAE,GAAG,EAAE;QAC/F,uEAAuE;QACvE,oEAAoE;QACpE,qEAAqE;QACrE,sCAAsC;QACtC,MAAM,KAAK,GAAsB,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;QACvF,MAAM,KAAK,GAAG,uBAAuB,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QAC5E,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,KAAK,GAAsB;YAC/B;gBACE,KAAK,EAAE,CAAC;gBACR,MAAM,EAAE,QAAQ;gBAChB,OAAO,EAAE,SAAS;gBAClB,UAAU,EAAE,cAAc;aAC3B;SACF,CAAC;QACF,MAAM,KAAK,GAAG,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,KAAK,CAAC,CAAC;QAChF,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,gCAAgC,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,KAAK,GAAsB;YAC/B;gBACE,KAAK,EAAE,EAAe;gBACtB,MAAM,EAAE,UAAU;gBAClB,OAAO,EAAE,UAAU;aACpB;YACD;gBACE,KAAK,EAAE,EAAe;gBACtB,MAAM,EAAE,QAAQ;gBAChB,OAAO,EAAE,UAAU;aACpB;SACF,CAAC;QACF,MAAM,KAAK,GAAG,uBAAuB,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QAC5E,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,wDAAwD;AAExD,QAAQ,CAAC,+BAA+B,EAAE,GAAG,EAAE;IAC7C,EAAE,CAAC,qGAAqG,EAAE,GAAG,EAAE;QAC7G,sEAAsE;QACtE,uEAAuE;QACvE,sEAAsE;QACtE,sEAAsE;QACtE,+CAA+C;QAC/C,MAAM,MAAM,GAAG,EAAE,OAAO,EAAE,oBAAoB,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC;QAC3E,MAAM,KAAK,GAAsB;YAC/B,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE;YACnF,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE;YAChD,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE;SACpD,CAAC;QACF,MAAM,GAAG,GAAG,uBAAuB,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QAC1E,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAClC,mEAAmE;QACnE,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;YACvB,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CompiledRule, LessonInput } from '@mmnto/totem';
|
|
1
|
+
import type { CompiledRule, LayerTraceEvent, LessonInput, NonCompilableEntry, NonCompilableReasonCode } from '@mmnto/totem';
|
|
2
2
|
/**
|
|
3
3
|
* Terminal outcome of a `--upgrade <hash>` run, returned by `compileCommand`
|
|
4
4
|
* so callers (like `totem doctor --pr` self-healing) can distinguish an actual
|
|
@@ -69,16 +69,18 @@ export declare function buildTelemetryPrefix(contextCounts: {
|
|
|
69
69
|
unknown: number;
|
|
70
70
|
}): string;
|
|
71
71
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
72
|
+
* Value side of the in-memory `nonCompilableMap`. Carries the title plus the
|
|
73
|
+
* machine-readable reasonCode (mmnto-ai/totem#1481) so prune / serialize
|
|
74
|
+
* steps round-trip the full 4-tuple without a lookup.
|
|
74
75
|
*/
|
|
75
|
-
export interface
|
|
76
|
-
hash: string;
|
|
76
|
+
export interface NonCompilableMapValue {
|
|
77
77
|
title: string;
|
|
78
|
+
reasonCode: NonCompilableReasonCode;
|
|
79
|
+
reason?: string;
|
|
78
80
|
}
|
|
79
81
|
/**
|
|
80
82
|
* Filter stale entries from a non-compilable map against the current set of
|
|
81
|
-
* lesson hashes. Returns the fresh tuple
|
|
83
|
+
* lesson hashes. Returns the fresh 4-tuple list and a count of how many
|
|
82
84
|
* entries were drained.
|
|
83
85
|
*
|
|
84
86
|
* Extracted for mmnto/totem#1281 so the no-op compile path can drain stale
|
|
@@ -86,9 +88,14 @@ export interface NonCompilableTuple {
|
|
|
86
88
|
* leaving stale entries stranded on no-op runs (e.g. after a lesson was
|
|
87
89
|
* removed or after a parser-bug fix invalidated old non-compilable hashes).
|
|
88
90
|
* Pure function; does not mutate the input map.
|
|
91
|
+
*
|
|
92
|
+
* mmnto-ai/totem#1481: preserves `reasonCode` and `reason` through the
|
|
93
|
+
* prune so ledger entries stay 4-tuple-shaped on disk. Dropping them back
|
|
94
|
+
* to 2-tuple would silently reintroduce `'legacy-unknown'` on the next
|
|
95
|
+
* load via the Read transform.
|
|
89
96
|
*/
|
|
90
|
-
export declare function pruneStaleNonCompilable(nonCompilableMap: Map<string,
|
|
91
|
-
fresh:
|
|
97
|
+
export declare function pruneStaleNonCompilable(nonCompilableMap: Map<string, NonCompilableMapValue>, currentHashes: Set<string>): {
|
|
98
|
+
fresh: NonCompilableEntry[];
|
|
92
99
|
drained: number;
|
|
93
100
|
};
|
|
94
101
|
/**
|
|
@@ -105,6 +112,24 @@ export declare function pruneStaleRules(rules: readonly CompiledRule[], currentH
|
|
|
105
112
|
fresh: CompiledRule[];
|
|
106
113
|
pruned: number;
|
|
107
114
|
};
|
|
115
|
+
/**
|
|
116
|
+
* Format a lesson's trace array into a single multi-line block for the
|
|
117
|
+
* `--verbose` renderer. Returns a string (no trailing newline — caller
|
|
118
|
+
* controls that). Output shape:
|
|
119
|
+
*
|
|
120
|
+
* lesson-<hash8> "<heading>":
|
|
121
|
+
* Layer <N> (<pipeline label>) -> <outcome> (<patternHash?>)
|
|
122
|
+
* verify on example: <outcome>
|
|
123
|
+
* retry N: scheduled
|
|
124
|
+
* result: <status> (<reasonCode or detail>)
|
|
125
|
+
*
|
|
126
|
+
* The renderer is defensive: malformed / unknown layer numbers render as
|
|
127
|
+
* "(unknown)" rather than throwing.
|
|
128
|
+
*/
|
|
129
|
+
export declare function formatVerboseTraceBlock(lesson: {
|
|
130
|
+
heading: string;
|
|
131
|
+
hash: string;
|
|
132
|
+
}, status: 'compiled' | 'skipped' | 'failed' | 'noop', reasonCode: NonCompilableReasonCode | undefined, trace: readonly LayerTraceEvent[] | undefined): string;
|
|
108
133
|
export interface AutoScaffoldDeps {
|
|
109
134
|
fs: typeof import('node:fs');
|
|
110
135
|
path: typeof import('node:path');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/commands/compile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/commands/compile.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/compile.js
CHANGED
|
@@ -28,7 +28,7 @@ export function buildTelemetryPrefix(contextCounts) {
|
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Filter stale entries from a non-compilable map against the current set of
|
|
31
|
-
* lesson hashes. Returns the fresh tuple
|
|
31
|
+
* lesson hashes. Returns the fresh 4-tuple list and a count of how many
|
|
32
32
|
* entries were drained.
|
|
33
33
|
*
|
|
34
34
|
* Extracted for mmnto/totem#1281 so the no-op compile path can drain stale
|
|
@@ -36,12 +36,24 @@ export function buildTelemetryPrefix(contextCounts) {
|
|
|
36
36
|
* leaving stale entries stranded on no-op runs (e.g. after a lesson was
|
|
37
37
|
* removed or after a parser-bug fix invalidated old non-compilable hashes).
|
|
38
38
|
* Pure function; does not mutate the input map.
|
|
39
|
+
*
|
|
40
|
+
* mmnto-ai/totem#1481: preserves `reasonCode` and `reason` through the
|
|
41
|
+
* prune so ledger entries stay 4-tuple-shaped on disk. Dropping them back
|
|
42
|
+
* to 2-tuple would silently reintroduce `'legacy-unknown'` on the next
|
|
43
|
+
* load via the Read transform.
|
|
39
44
|
*/
|
|
40
45
|
export function pruneStaleNonCompilable(nonCompilableMap, currentHashes) {
|
|
41
46
|
const fresh = [];
|
|
42
|
-
for (const [hash,
|
|
47
|
+
for (const [hash, value] of nonCompilableMap) {
|
|
43
48
|
if (currentHashes.has(hash)) {
|
|
44
|
-
|
|
49
|
+
const entry = {
|
|
50
|
+
hash,
|
|
51
|
+
title: value.title,
|
|
52
|
+
reasonCode: value.reasonCode,
|
|
53
|
+
};
|
|
54
|
+
if (value.reason !== undefined)
|
|
55
|
+
entry.reason = value.reason;
|
|
56
|
+
fresh.push(entry);
|
|
45
57
|
}
|
|
46
58
|
}
|
|
47
59
|
return { fresh, drained: nonCompilableMap.size - fresh.length };
|
|
@@ -60,6 +72,88 @@ export function pruneStaleRules(rules, currentHashes) {
|
|
|
60
72
|
const fresh = rules.filter((r) => currentHashes.has(r.lessonHash));
|
|
61
73
|
return { fresh, pruned: rules.length - fresh.length };
|
|
62
74
|
}
|
|
75
|
+
// ─── Verbose trace renderer (mmnto-ai/totem#1482) ──
|
|
76
|
+
/**
|
|
77
|
+
* Map a numeric layer from a trace event to its pipeline label. Tolerates
|
|
78
|
+
* unknown values so a future ADR-088 phase can introduce new layers without
|
|
79
|
+
* breaking the renderer.
|
|
80
|
+
*/
|
|
81
|
+
function pipelineLabel(layer) {
|
|
82
|
+
switch (layer) {
|
|
83
|
+
case 1:
|
|
84
|
+
return 'Pipeline 1 (manual)';
|
|
85
|
+
case 2:
|
|
86
|
+
return 'Pipeline 2 (example-based)';
|
|
87
|
+
case 3:
|
|
88
|
+
return 'Pipeline 3 (LLM + verify-retry)';
|
|
89
|
+
default:
|
|
90
|
+
return `Layer ${layer}`;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Format a lesson's trace array into a single multi-line block for the
|
|
95
|
+
* `--verbose` renderer. Returns a string (no trailing newline — caller
|
|
96
|
+
* controls that). Output shape:
|
|
97
|
+
*
|
|
98
|
+
* lesson-<hash8> "<heading>":
|
|
99
|
+
* Layer <N> (<pipeline label>) -> <outcome> (<patternHash?>)
|
|
100
|
+
* verify on example: <outcome>
|
|
101
|
+
* retry N: scheduled
|
|
102
|
+
* result: <status> (<reasonCode or detail>)
|
|
103
|
+
*
|
|
104
|
+
* The renderer is defensive: malformed / unknown layer numbers render as
|
|
105
|
+
* "(unknown)" rather than throwing.
|
|
106
|
+
*/
|
|
107
|
+
export function formatVerboseTraceBlock(lesson, status, reasonCode, trace) {
|
|
108
|
+
const lines = [];
|
|
109
|
+
const shortHash = lesson.hash.slice(0, 8);
|
|
110
|
+
lines.push(`lesson-${shortHash} "${lesson.heading}":`);
|
|
111
|
+
if (!trace || trace.length === 0) {
|
|
112
|
+
lines.push(` (no trace events recorded)`);
|
|
113
|
+
const resultSuffix = reasonCode ? ` (${reasonCode})` : '';
|
|
114
|
+
lines.push(' result: ' + status + resultSuffix);
|
|
115
|
+
return lines.join('\n');
|
|
116
|
+
}
|
|
117
|
+
// Separate events into prelude (generate / verify / retry) and terminal
|
|
118
|
+
// (result). The terminal lives on its own line with full framing.
|
|
119
|
+
let retryCounter = 0;
|
|
120
|
+
let sawResult = false;
|
|
121
|
+
for (const ev of trace) {
|
|
122
|
+
const label = pipelineLabel(ev.layer);
|
|
123
|
+
if (ev.action === 'generate') {
|
|
124
|
+
const detail = ev.patternHash ? ` (patternHash=${ev.patternHash})` : '';
|
|
125
|
+
lines.push(` Layer ${ev.layer} (${label}) -> ` + ev.outcome + detail);
|
|
126
|
+
}
|
|
127
|
+
else if (ev.action === 'verify') {
|
|
128
|
+
lines.push(` verify on example: ${ev.outcome}`);
|
|
129
|
+
}
|
|
130
|
+
else if (ev.action === 'retry') {
|
|
131
|
+
retryCounter++;
|
|
132
|
+
lines.push(` retry ${retryCounter}: ${ev.outcome}`);
|
|
133
|
+
}
|
|
134
|
+
else if (ev.action === 'result') {
|
|
135
|
+
const detail = ev.reasonCode ? ` (${ev.reasonCode})` : '';
|
|
136
|
+
lines.push(' result: ' + ev.outcome + detail);
|
|
137
|
+
sawResult = true;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
lines.push(` (unknown) ${String(ev.action)}: ${String(ev.outcome)}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Defense in depth: if the trace somehow never emitted a terminal result
|
|
144
|
+
// event, synthesize one from the caller-supplied `status` so the verbose
|
|
145
|
+
// block always carries a final line. `compileLesson` pushes a result
|
|
146
|
+
// event on every return path, but a future refactor could regress that;
|
|
147
|
+
// this guard keeps the rendered block well-formed regardless. We use
|
|
148
|
+
// `status` directly rather than the last event's outcome because that
|
|
149
|
+
// outcome is an intermediate marker like 'MATCH' or 'attempt-1', not the
|
|
150
|
+
// lesson's final state.
|
|
151
|
+
if (!sawResult) {
|
|
152
|
+
const resultSuffix = reasonCode ? ` (${reasonCode})` : '';
|
|
153
|
+
lines.push(' result: ' + status + resultSuffix);
|
|
154
|
+
}
|
|
155
|
+
return lines.join('\n');
|
|
156
|
+
}
|
|
63
157
|
// ─── Logging helpers ────────────────────────────────
|
|
64
158
|
function logCompiledRule(log, lesson, rule) {
|
|
65
159
|
const engine = rule.engine;
|
|
@@ -316,10 +410,16 @@ export async function compileCommand(options) {
|
|
|
316
410
|
: loadCompiledRulesFile(rulesPath);
|
|
317
411
|
const existingRules = existingFile.rules;
|
|
318
412
|
const existingByHash = new Map(existingRules.map((r) => [r.lessonHash, r]));
|
|
319
|
-
// mmnto/totem#1280: nonCompilable is
|
|
320
|
-
//
|
|
321
|
-
//
|
|
322
|
-
|
|
413
|
+
// mmnto/totem#1280 + mmnto-ai/totem#1481: in-memory nonCompilable is
|
|
414
|
+
// `Map<hash, {title, reasonCode, reason?}>` so every write path carries
|
|
415
|
+
// the full 4-tuple. The schema's Read transform normalizes legacy
|
|
416
|
+
// strings and 2-tuples to the 4-tuple shape (reasonCode:
|
|
417
|
+
// 'legacy-unknown') before we reach this block, so existingFile.
|
|
418
|
+
// nonCompilable is always 4-tuple-shaped here.
|
|
419
|
+
const nonCompilableMap = new Map((existingFile.nonCompilable ?? []).map((entry) => [
|
|
420
|
+
entry.hash,
|
|
421
|
+
{ title: entry.title, reasonCode: entry.reasonCode, reason: entry.reason },
|
|
422
|
+
]));
|
|
323
423
|
// Note: we do NOT delete the --upgrade target from existingByHash here.
|
|
324
424
|
// buildCompiledRule in @mmnto/totem looks up the old entry to preserve
|
|
325
425
|
// metadata (createdAt, audit lineage). Deleting would make the upgraded
|
|
@@ -581,8 +681,15 @@ export async function compileCommand(options) {
|
|
|
581
681
|
continue;
|
|
582
682
|
}
|
|
583
683
|
if (!parsed.compilable) {
|
|
584
|
-
// mmnto/totem#1280: capture title alongside hash for observability
|
|
585
|
-
|
|
684
|
+
// mmnto/totem#1280: capture title alongside hash for observability.
|
|
685
|
+
// mmnto-ai/totem#1481: the cloud worker currently classifies every
|
|
686
|
+
// compilable:false outcome as out-of-scope. Granular cloud-side
|
|
687
|
+
// reasonCodes are out of scope here and track via mmnto/totem#1221.
|
|
688
|
+
nonCompilableMap.set(lesson.hash, {
|
|
689
|
+
title: lesson.heading,
|
|
690
|
+
reasonCode: 'out-of-scope',
|
|
691
|
+
reason: parsed.reason,
|
|
692
|
+
});
|
|
586
693
|
skippedLessons.push({ heading: lesson.heading, reason: parsed.reason });
|
|
587
694
|
skipped++;
|
|
588
695
|
continue;
|
|
@@ -645,6 +752,17 @@ export async function compileCommand(options) {
|
|
|
645
752
|
});
|
|
646
753
|
}));
|
|
647
754
|
for (const { lesson, result } of results) {
|
|
755
|
+
// mmnto-ai/totem#1482: emit the per-lesson layer-trace block when
|
|
756
|
+
// --verbose is active. The whole block ships via one stdout.write
|
|
757
|
+
// call so concurrent lessons cannot interleave their output.
|
|
758
|
+
// Non-trace verbose behavior (skipped-lesson reasons) still fires
|
|
759
|
+
// further down; this renders the structured trace alongside.
|
|
760
|
+
if (options.verbose) {
|
|
761
|
+
const resultTrace = 'trace' in result ? result.trace : undefined;
|
|
762
|
+
const reasonCode = result.status === 'skipped' ? result.reasonCode : undefined;
|
|
763
|
+
const block = formatVerboseTraceBlock(lesson, result.status, reasonCode, resultTrace);
|
|
764
|
+
process.stdout.write(block + '\n');
|
|
765
|
+
}
|
|
648
766
|
// Upgrade targets: remove the stale copy from newRules for any
|
|
649
767
|
// terminal outcome where the rule's state CHANGES (compiled -> new
|
|
650
768
|
// pattern replaces old; skipped -> rule moves to nonCompilable and
|
|
@@ -700,8 +818,15 @@ export async function compileCommand(options) {
|
|
|
700
818
|
logCompiledRule(log, lesson, result.rule);
|
|
701
819
|
break;
|
|
702
820
|
case 'skipped':
|
|
703
|
-
// mmnto/totem#1280: capture
|
|
704
|
-
|
|
821
|
+
// mmnto/totem#1280 + mmnto-ai/totem#1481: capture the full
|
|
822
|
+
// 4-tuple so ledger reads downstream (doctor, telemetry) see
|
|
823
|
+
// a specific reasonCode rather than normalizing to
|
|
824
|
+
// 'legacy-unknown'.
|
|
825
|
+
nonCompilableMap.set(result.hash, {
|
|
826
|
+
title: lesson.heading,
|
|
827
|
+
reasonCode: result.reasonCode,
|
|
828
|
+
reason: result.reason,
|
|
829
|
+
});
|
|
705
830
|
skippedLessons.push({ heading: lesson.heading, reason: result.reason });
|
|
706
831
|
skipped++;
|
|
707
832
|
break;
|