@mindrian_os/install 1.13.0-beta.17 → 1.13.0-beta.21
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/.claude-plugin/plugin.json +1 -1
- package/.mcp.json +6 -1
- package/CHANGELOG.md +31 -0
- package/README.md +51 -56
- package/bin/mindrian-brain-mcp-client.cjs +152 -0
- package/commands/act.md +1 -0
- package/commands/admin.md +1 -0
- package/commands/analyze-needs.md +2 -0
- package/commands/analyze-systems.md +2 -0
- package/commands/analyze-timing.md +2 -0
- package/commands/auto-explore.md +2 -0
- package/commands/beautiful-question.md +2 -0
- package/commands/brain-derive.md +2 -0
- package/commands/build-knowledge.md +2 -0
- package/commands/build-thesis.md +2 -0
- package/commands/causal.md +2 -0
- package/commands/challenge-assumptions.md +2 -0
- package/commands/compare-ventures.md +2 -0
- package/commands/dashboard.md +2 -1
- package/commands/deep-grade.md +2 -0
- package/commands/diagnose.md +21 -1
- package/commands/diagnostics.md +14 -3
- package/commands/doctor.md +6 -2
- package/commands/dogfood-flush.md +92 -0
- package/commands/dominant-designs.md +2 -0
- package/commands/explain-decision.md +2 -0
- package/commands/explore-domains.md +2 -0
- package/commands/explore-futures.md +2 -0
- package/commands/explore-trends.md +2 -0
- package/commands/export.md +1 -0
- package/commands/feynman-timeline-refresh.md +2 -0
- package/commands/file-meeting.md +2 -0
- package/commands/find-analogies.md +1 -0
- package/commands/find-bottlenecks.md +2 -0
- package/commands/find-connections.md +2 -0
- package/commands/funding.md +1 -0
- package/commands/grade.md +2 -0
- package/commands/graph.md +1 -0
- package/commands/hat-briefing.md +1 -0
- package/commands/heal.md +22 -170
- package/commands/help.md +54 -334
- package/commands/hmi-status.md +23 -144
- package/commands/jtbd.md +1 -0
- package/commands/leadership.md +2 -0
- package/commands/lean-canvas.md +2 -0
- package/commands/macro-trends.md +2 -0
- package/commands/map-unknowns.md +2 -0
- package/commands/memory.md +1 -0
- package/commands/models.md +1 -0
- package/commands/mos-reason.md +2 -0
- package/commands/mos.md +139 -0
- package/commands/mullins.md +2 -0
- package/commands/mva-brief.md +2 -0
- package/commands/mva-option.md +2 -0
- package/commands/new-project.md +2 -0
- package/commands/onboard.md +20 -7
- package/commands/operator.md +1 -0
- package/commands/opportunities.md +1 -0
- package/commands/organize.md +22 -469
- package/commands/persona.md +1 -0
- package/commands/pipeline.md +2 -0
- package/commands/present.md +1 -0
- package/commands/publish.md +2 -0
- package/commands/query.md +24 -102
- package/commands/radar.md +2 -0
- package/commands/reanalyze.md +1 -0
- package/commands/research.md +2 -0
- package/commands/room.md +2 -0
- package/commands/rooms.md +1 -0
- package/commands/root-cause.md +2 -0
- package/commands/rs-experts.md +1 -0
- package/commands/rs-explain.md +1 -0
- package/commands/rs-fetch.md +1 -0
- package/commands/rs-thesis.md +1 -0
- package/commands/scenario-plan.md +2 -0
- package/commands/scheduled-tasks.md +1 -0
- package/commands/score-innovation.md +2 -0
- package/commands/scout.md +1 -0
- package/commands/setup.md +2 -0
- package/commands/snapshot.md +2 -0
- package/commands/speakers.md +1 -0
- package/commands/splash.md +5 -2
- package/commands/status.md +1 -0
- package/commands/structure-argument.md +2 -0
- package/commands/suggest-next.md +2 -0
- package/commands/systems-thinking.md +2 -0
- package/commands/think-hats.md +2 -0
- package/commands/update.md +2 -0
- package/commands/user-needs.md +2 -0
- package/commands/validate.md +2 -0
- package/commands/value-proposition.md +2 -0
- package/commands/vault.md +2 -0
- package/commands/visualize.md +24 -29
- package/commands/whitespace.md +2 -1
- package/commands/wiki.md +1 -0
- package/hooks/hooks.json +22 -88
- package/lib/agents/auto-explore-agent.cjs +82 -0
- package/lib/core/breakthrough/canary.cjs +134 -0
- package/lib/core/breakthrough/canary.test.cjs +136 -0
- package/lib/core/breakthrough/detectors.cjs +359 -0
- package/lib/core/breakthrough/detectors.test.cjs +333 -0
- package/lib/core/breakthrough/ethics-fence.cjs +127 -0
- package/lib/core/breakthrough/ethics-fence.test.cjs +178 -0
- package/lib/core/breakthrough/resurfacing.cjs +150 -0
- package/lib/core/breakthrough/resurfacing.test.cjs +233 -0
- package/lib/core/breakthrough/review-queue.cjs +154 -0
- package/lib/core/breakthrough/review-queue.test.cjs +160 -0
- package/lib/core/breakthrough/scanner-d17-d18.test.cjs +229 -0
- package/lib/core/breakthrough/scanner.cjs +426 -0
- package/lib/core/breakthrough/scanner.test.cjs +267 -0
- package/lib/core/breakthrough/schema.cjs +164 -0
- package/lib/core/breakthrough/schema.test.cjs +256 -0
- package/lib/core/breakthrough/scoring.cjs +293 -0
- package/lib/core/breakthrough/scoring.test.cjs +423 -0
- package/lib/core/breakthrough/verb-dispatch.cjs +221 -0
- package/lib/core/breakthrough/verb-dispatch.test.cjs +185 -0
- package/lib/core/breakthrough/voice-scaffold.cjs +247 -0
- package/lib/core/breakthrough/voice-scaffold.test.cjs +251 -0
- package/lib/core/directive-envelope.cjs +175 -0
- package/lib/core/directive-envelope.test.cjs +225 -0
- package/lib/core/doctor/class-m-brain-smoke.cjs +278 -0
- package/lib/core/doctor/class-m-brain-smoke.test.cjs +310 -0
- package/lib/core/first-touch-version-stamper.cjs +113 -0
- package/lib/core/larry-thinness-acknowledgment.cjs +64 -0
- package/lib/core/larry-thinness-acknowledgment.test.cjs +97 -0
- package/lib/core/llm-name-suggester.cjs +194 -0
- package/lib/core/llm-name-suggester.test.cjs +132 -0
- package/lib/core/mcp-profiles.cjs +1 -1
- package/lib/core/migration-snapshot.cjs +172 -0
- package/lib/core/migration-snapshot.test.cjs +174 -0
- package/lib/core/mindrian-brain-shim.test.cjs +214 -0
- package/lib/core/mva-orchestrator.cjs +41 -0
- package/lib/core/mva-telemetry.cjs +31 -143
- package/lib/core/navigation/edges.cjs +35 -0
- package/lib/core/navigation/memory-events.cjs +126 -0
- package/lib/core/room-auto-create.cjs +318 -0
- package/lib/core/room-auto-create.test.cjs +198 -0
- package/lib/core/room-discard-cascade.cjs +225 -0
- package/lib/core/room-discard-cascade.test.cjs +135 -0
- package/lib/core/room-name-validator.cjs +132 -0
- package/lib/core/room-name-validator.test.cjs +156 -0
- package/lib/core/room-naming-selector.cjs +357 -0
- package/lib/core/room-naming-selector.test.cjs +277 -0
- package/lib/core/room-receipt-emit.cjs +63 -0
- package/lib/core/room-skeleton-scaffold.cjs +315 -0
- package/lib/core/room-skeleton-scaffold.test.cjs +291 -0
- package/lib/core/rs-nl-to-query.cjs +1 -1
- package/lib/core/stale-copy-scanner.cjs +190 -0
- package/lib/core/state-aware-router.cjs +78 -0
- package/lib/core/telemetry/schema.cjs +168 -0
- package/lib/core/telemetry/schema.test.cjs +124 -0
- package/lib/core/telemetry/validator.cjs +200 -0
- package/lib/core/telemetry/validator.test.cjs +188 -0
- package/lib/core/telemetry/writer.cjs +141 -0
- package/lib/core/telemetry/writer.test.cjs +331 -0
- package/lib/core/terminal-capability.cjs +88 -0
- package/lib/core/tier0-messaging.cjs +109 -0
- package/lib/core/tier0-messaging.test.cjs +218 -0
- package/lib/core/venture-shape-nudge.cjs +163 -0
- package/lib/core/venture-shape-nudge.test.cjs +161 -0
- package/lib/core/visual-ops.cjs +70 -2
- package/lib/hmi/selector-dispatcher.cjs +90 -1
- package/lib/hmi/shape-f7-breakthrough-renderer.cjs +222 -0
- package/lib/hmi/shape-f7-breakthrough-renderer.test.cjs +233 -0
- package/lib/memory/body-shape-coverage.test.cjs +268 -0
- package/lib/memory/brain-derivation-graceful-degradation.test.cjs +2 -2
- package/lib/memory/doctor-deprecation-surface.test.cjs +185 -0
- package/lib/memory/first-touch-version.test.cjs +198 -0
- package/lib/memory/help-coverage.test.cjs +108 -0
- package/lib/memory/help-renderer.test.cjs +145 -0
- package/lib/memory/mos-status-renderer.test.cjs +2 -2
- package/lib/memory/navigation-engine-core.test.cjs +1 -1
- package/lib/memory/palette-consistency.test.cjs +127 -0
- package/lib/memory/pending-tension-store.cjs +80 -0
- package/lib/memory/render-v2-disposition.test.cjs +199 -0
- package/lib/memory/run-feynman-tests.cjs +223 -0
- package/lib/memory/sessionstart-coordinator.test.cjs +446 -0
- package/lib/memory/skill-vs-code-drift.test.cjs +257 -0
- package/lib/memory/soft-alias.test.cjs +144 -0
- package/lib/memory/stale-copy-scanner.test.cjs +291 -0
- package/lib/memory/state-aware-router.test.cjs +90 -0
- package/lib/memory/statusline-two-row.test.cjs +338 -0
- package/lib/memory/terminal-capability.test.cjs +155 -0
- package/lib/render/ROOM.md +74 -22
- package/lib/sessionstart/budget-compressor.cjs +130 -0
- package/lib/sessionstart/contributor-interface.cjs +134 -0
- package/lib/sessionstart/contributor-isolator.cjs +128 -0
- package/lib/sessionstart/precedence-ladder.cjs +47 -0
- package/lib/statusline/governing-thought-truncator.cjs +45 -0
- package/lib/statusline/two-row-renderer.cjs +186 -0
- package/lib/statusline/version-resolver.cjs +81 -0
- package/package.json +1 -1
- package/references/visual/ROOM.md +55 -0
- package/references/visual/palette.json +54 -0
- package/skills/larry-personality/SKILL.md +34 -0
- package/skills/ui-system/SKILL.md +109 -1
- package/skills/ui-system/rules/dual-palette.md +156 -0
- package/skills/ui-system/rules/glyph-disambiguation.md +171 -0
- package/skills/ui-system/rules/shape-f-zero-and-six.md +169 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Copyright (c) 2026 Mindrian. BSL 1.1.
|
|
6
|
+
*
|
|
7
|
+
* Phase 121.5-02 Task 2 -- skill-vs-code-drift checker test suite
|
|
8
|
+
* =================================================================
|
|
9
|
+
* Tests scripts/check-skill-vs-code-drift.cjs. The checker scans
|
|
10
|
+
* skills/ui-system/SKILL.md against the shipped renderer / glyph / shape
|
|
11
|
+
* code in lib/render/render-v2.cjs and asserts:
|
|
12
|
+
* - Every F.N shape implemented in code is documented in SKILL.md
|
|
13
|
+
* - SKILL.md cross-references Canon Part 3 + Canon Part 10 + the dual
|
|
14
|
+
* palette source (JTBD-PALETTES) + the ODD 4 resolution paragraph
|
|
15
|
+
* - The three required sidecar rules files exist
|
|
16
|
+
*
|
|
17
|
+
* Tests 1, 4, 5, 6, 8 run against the LIVE repo (Task 1 output).
|
|
18
|
+
* Tests 2, 3, 7 use synthetic fixtures via fs.mkdtempSync to exercise the
|
|
19
|
+
* drift-detection paths without touching the real repo.
|
|
20
|
+
*
|
|
21
|
+
* Canon Part 7 invariant: this test asserts the reconciliation lands as
|
|
22
|
+
* data, not as promise. A future drift will fail the checker at PR time,
|
|
23
|
+
* not at audit time.
|
|
24
|
+
*
|
|
25
|
+
* No em-dashes, no emoji (per skills/ui-system/SKILL.md §3).
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const fs = require('fs');
|
|
29
|
+
const path = require('path');
|
|
30
|
+
const os = require('os');
|
|
31
|
+
|
|
32
|
+
const CHECKER_PATH = path.join(__dirname, '..', '..', 'scripts', 'check-skill-vs-code-drift.cjs');
|
|
33
|
+
const REPO_ROOT = path.join(__dirname, '..', '..');
|
|
34
|
+
const REAL_SKILL = path.join(REPO_ROOT, 'skills', 'ui-system', 'SKILL.md');
|
|
35
|
+
const REAL_CODE = path.join(REPO_ROOT, 'lib', 'render', 'render-v2.cjs');
|
|
36
|
+
const REAL_RULES_DIR = path.join(REPO_ROOT, 'skills', 'ui-system', 'rules');
|
|
37
|
+
|
|
38
|
+
let passed = 0;
|
|
39
|
+
let failed = 0;
|
|
40
|
+
const failures = [];
|
|
41
|
+
|
|
42
|
+
function assert(cond, msg) {
|
|
43
|
+
if (cond) {
|
|
44
|
+
passed++;
|
|
45
|
+
} else {
|
|
46
|
+
failed++;
|
|
47
|
+
failures.push(msg);
|
|
48
|
+
console.error(' FAIL: ' + msg);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function test(label, fn) {
|
|
53
|
+
console.log('--- ' + label + ' ---');
|
|
54
|
+
try {
|
|
55
|
+
fn();
|
|
56
|
+
console.log(' passed');
|
|
57
|
+
} catch (e) {
|
|
58
|
+
failed++;
|
|
59
|
+
failures.push(label + ': ' + e.message);
|
|
60
|
+
console.error(' THREW: ' + e.message);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function loadChecker() {
|
|
65
|
+
// Bust require-cache so changes to the checker module surface immediately.
|
|
66
|
+
delete require.cache[require.resolve(CHECKER_PATH)];
|
|
67
|
+
return require(CHECKER_PATH);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function stageFixture(opts) {
|
|
71
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'skill-drift-fixture-'));
|
|
72
|
+
const skillPath = path.join(tmp, 'SKILL.md');
|
|
73
|
+
const codePath = path.join(tmp, 'render-v2.cjs');
|
|
74
|
+
const rulesDir = path.join(tmp, 'rules');
|
|
75
|
+
fs.mkdirSync(rulesDir, { recursive: true });
|
|
76
|
+
fs.writeFileSync(skillPath, opts.skill || '');
|
|
77
|
+
if (typeof opts.code === 'string') fs.writeFileSync(codePath, opts.code);
|
|
78
|
+
if (Array.isArray(opts.rulesFiles)) {
|
|
79
|
+
for (const f of opts.rulesFiles) {
|
|
80
|
+
fs.writeFileSync(path.join(rulesDir, f), 'stub\n');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return { tmp, skillPath, codePath, rulesDir };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ===========================================================================
|
|
87
|
+
// Test 1: clean repo (live files) -> valid:true
|
|
88
|
+
// ===========================================================================
|
|
89
|
+
test('Test 1: clean repo lives at green (Task 1 output)', () => {
|
|
90
|
+
const { check } = loadChecker();
|
|
91
|
+
const result = check();
|
|
92
|
+
assert(result && typeof result === 'object', 'check() returned object');
|
|
93
|
+
assert(result.valid === true,
|
|
94
|
+
'live repo valid=true (got ' + result.valid + '); missing_in_skill=' +
|
|
95
|
+
JSON.stringify(result.missing_in_skill) + ' missing_crossrefs=' +
|
|
96
|
+
JSON.stringify(result.missing_crossrefs) + ' missing_rules=' +
|
|
97
|
+
JSON.stringify(result.missing_rules_files));
|
|
98
|
+
assert(Array.isArray(result.missing_in_skill), 'missing_in_skill is array');
|
|
99
|
+
assert(Array.isArray(result.missing_in_code), 'missing_in_code is array');
|
|
100
|
+
assert(Array.isArray(result.missing_crossrefs), 'missing_crossrefs is array');
|
|
101
|
+
assert(Array.isArray(result.missing_rules_files), 'missing_rules_files is array');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// ===========================================================================
|
|
105
|
+
// Test 2: SKILL.md lists F.7 (bogus); code has no F.7 -> code-missing flagged
|
|
106
|
+
// in missing_in_code (NOT fatal); valid stays true UNLESS other failures.
|
|
107
|
+
// To make the test exercise a FATAL drift, we also strip code F.0 so it
|
|
108
|
+
// appears in SKILL but missing from code -- producing the asymmetric severity
|
|
109
|
+
// behavior: missing_in_code on its own does NOT flip valid. We then verify
|
|
110
|
+
// the inverse (Test 3) where SKILL is missing code shapes -> valid:false.
|
|
111
|
+
// ===========================================================================
|
|
112
|
+
test('Test 2: SKILL with extra F.7 but code lacks it -> missing_in_code flag (non-fatal)', () => {
|
|
113
|
+
const { check } = loadChecker();
|
|
114
|
+
const fixture = stageFixture({
|
|
115
|
+
skill: '# stub\n\nShape F.0 here.\nShape F.1 here.\nShape F.7 bogus.\n\nCanon Part 3\nCanon Part 10\nJTBD-PALETTES\nODD 4 resolution\n',
|
|
116
|
+
code: 'F.0 F.1\n',
|
|
117
|
+
rulesFiles: ['shape-f-zero-and-six.md', 'dual-palette.md', 'glyph-disambiguation.md'],
|
|
118
|
+
});
|
|
119
|
+
const result = check({
|
|
120
|
+
skillPath: fixture.skillPath,
|
|
121
|
+
codePath: fixture.codePath,
|
|
122
|
+
rulesDir: fixture.rulesDir,
|
|
123
|
+
});
|
|
124
|
+
// SKILL has F.0, F.1, F.7 -- code has F.0, F.1. F.7 is in skill but not in code.
|
|
125
|
+
assert(result.missing_in_code.indexOf('F.7') !== -1,
|
|
126
|
+
'F.7 present in SKILL but missing in code (got missing_in_code=' +
|
|
127
|
+
JSON.stringify(result.missing_in_code) + ')');
|
|
128
|
+
// missing_in_code alone does NOT flip valid (asymmetric severity per plan).
|
|
129
|
+
assert(result.valid === true,
|
|
130
|
+
'missing_in_code alone is non-fatal -> valid stays true (got ' + result.valid + ')');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// ===========================================================================
|
|
134
|
+
// Test 3: code implements F.6 but SKILL.md does NOT document it -> valid:false
|
|
135
|
+
// ===========================================================================
|
|
136
|
+
test('Test 3: code has F.6 but SKILL lacks it -> valid:false', () => {
|
|
137
|
+
const { check } = loadChecker();
|
|
138
|
+
const fixture = stageFixture({
|
|
139
|
+
skill: '# stub\n\nShape F.0\nShape F.1\nShape F.2\nShape F.3\nShape F.4\nShape F.5\n\nCanon Part 3\nCanon Part 10\nJTBD-PALETTES\nODD 4 resolution\n',
|
|
140
|
+
code: 'F.0 F.1 F.2 F.3 F.4 F.5 F.6\n',
|
|
141
|
+
rulesFiles: ['shape-f-zero-and-six.md', 'dual-palette.md', 'glyph-disambiguation.md'],
|
|
142
|
+
});
|
|
143
|
+
const result = check({
|
|
144
|
+
skillPath: fixture.skillPath,
|
|
145
|
+
codePath: fixture.codePath,
|
|
146
|
+
rulesDir: fixture.rulesDir,
|
|
147
|
+
});
|
|
148
|
+
assert(result.missing_in_skill.indexOf('F.6') !== -1,
|
|
149
|
+
'F.6 implemented in code but missing from SKILL (got missing_in_skill=' +
|
|
150
|
+
JSON.stringify(result.missing_in_skill) + ')');
|
|
151
|
+
assert(result.valid === false,
|
|
152
|
+
'SKILL missing code-shipped F.6 is FATAL -> valid:false (got ' + result.valid + ')');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// ===========================================================================
|
|
156
|
+
// Test 4: live SKILL.md cross-references lib/render/JTBD-PALETTES.md (grep)
|
|
157
|
+
// ===========================================================================
|
|
158
|
+
test('Test 4: live SKILL.md cross-references JTBD-PALETTES', () => {
|
|
159
|
+
const txt = fs.readFileSync(REAL_SKILL, 'utf8');
|
|
160
|
+
assert(txt.indexOf('JTBD-PALETTES') !== -1,
|
|
161
|
+
'SKILL.md mentions JTBD-PALETTES (the Phase 102 source-of-truth file)');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// ===========================================================================
|
|
165
|
+
// Test 5: live SKILL.md contains ODD 4 resolution paragraph
|
|
166
|
+
// ===========================================================================
|
|
167
|
+
test('Test 5: live SKILL.md contains ODD 4 resolution', () => {
|
|
168
|
+
const txt = fs.readFileSync(REAL_SKILL, 'utf8');
|
|
169
|
+
assert(txt.indexOf('ODD 4 resolution') !== -1,
|
|
170
|
+
'SKILL.md contains ODD 4 resolution paragraph (the glyph + word disambiguation carve-out)');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// ===========================================================================
|
|
174
|
+
// Test 6: live SKILL.md contains Canon Part 3 AND Canon Part 10 cross-refs
|
|
175
|
+
// ===========================================================================
|
|
176
|
+
test('Test 6: live SKILL.md cross-references Canon Part 3 + Part 10', () => {
|
|
177
|
+
const txt = fs.readFileSync(REAL_SKILL, 'utf8');
|
|
178
|
+
assert(txt.indexOf('Canon Part 3') !== -1, 'SKILL.md cross-references Canon Part 3');
|
|
179
|
+
assert(txt.indexOf('Canon Part 10') !== -1, 'SKILL.md cross-references Canon Part 10');
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// ===========================================================================
|
|
183
|
+
// Test 7: three sidecar rules files exist (synthetic fixture w/ NONE missing)
|
|
184
|
+
// vs synthetic fixture missing one -> valid:false
|
|
185
|
+
// ===========================================================================
|
|
186
|
+
test('Test 7: missing sidecar rules file -> valid:false', () => {
|
|
187
|
+
const { check } = loadChecker();
|
|
188
|
+
// Fixture with ALL three rules files -> valid (assuming other checks pass).
|
|
189
|
+
const all = stageFixture({
|
|
190
|
+
skill: '# stub\n\nShape F.0\n\nCanon Part 3\nCanon Part 10\nJTBD-PALETTES\nODD 4 resolution\n',
|
|
191
|
+
code: 'F.0\n',
|
|
192
|
+
rulesFiles: ['shape-f-zero-and-six.md', 'dual-palette.md', 'glyph-disambiguation.md'],
|
|
193
|
+
});
|
|
194
|
+
const ok = check({
|
|
195
|
+
skillPath: all.skillPath,
|
|
196
|
+
codePath: all.codePath,
|
|
197
|
+
rulesDir: all.rulesDir,
|
|
198
|
+
});
|
|
199
|
+
assert(ok.valid === true,
|
|
200
|
+
'all 3 rules files + cross-refs + matched shapes -> valid:true (got ' + ok.valid +
|
|
201
|
+
'; missing_rules_files=' + JSON.stringify(ok.missing_rules_files) + ')');
|
|
202
|
+
// Fixture missing dual-palette.md -> valid:false.
|
|
203
|
+
const missing = stageFixture({
|
|
204
|
+
skill: '# stub\n\nShape F.0\n\nCanon Part 3\nCanon Part 10\nJTBD-PALETTES\nODD 4 resolution\n',
|
|
205
|
+
code: 'F.0\n',
|
|
206
|
+
rulesFiles: ['shape-f-zero-and-six.md', 'glyph-disambiguation.md'],
|
|
207
|
+
});
|
|
208
|
+
const bad = check({
|
|
209
|
+
skillPath: missing.skillPath,
|
|
210
|
+
codePath: missing.codePath,
|
|
211
|
+
rulesDir: missing.rulesDir,
|
|
212
|
+
});
|
|
213
|
+
assert(bad.missing_rules_files.indexOf('dual-palette.md') !== -1,
|
|
214
|
+
'missing dual-palette.md detected (got missing_rules_files=' +
|
|
215
|
+
JSON.stringify(bad.missing_rules_files) + ')');
|
|
216
|
+
assert(bad.valid === false,
|
|
217
|
+
'missing rules file is FATAL -> valid:false (got ' + bad.valid + ')');
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// ===========================================================================
|
|
221
|
+
// Test 8: --json mode emits structured drift report
|
|
222
|
+
// ===========================================================================
|
|
223
|
+
test('Test 8: --json mode emits structured report', () => {
|
|
224
|
+
const { execFileSync } = require('child_process');
|
|
225
|
+
const out = execFileSync('node', [CHECKER_PATH, '--json'], { encoding: 'utf8' });
|
|
226
|
+
let parsed;
|
|
227
|
+
try {
|
|
228
|
+
parsed = JSON.parse(out);
|
|
229
|
+
} catch (e) {
|
|
230
|
+
assert(false, '--json output is parseable JSON; got: ' + out.slice(0, 200));
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
assert(typeof parsed.valid === 'boolean', '--json emits valid:boolean');
|
|
234
|
+
assert(Array.isArray(parsed.missing_in_skill), '--json emits missing_in_skill[]');
|
|
235
|
+
assert(Array.isArray(parsed.missing_in_code), '--json emits missing_in_code[]');
|
|
236
|
+
assert(Array.isArray(parsed.missing_crossrefs), '--json emits missing_crossrefs[]');
|
|
237
|
+
assert(Array.isArray(parsed.missing_rules_files), '--json emits missing_rules_files[]');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// ===========================================================================
|
|
241
|
+
// Summary
|
|
242
|
+
// ===========================================================================
|
|
243
|
+
console.log('');
|
|
244
|
+
console.log('========================================');
|
|
245
|
+
console.log(' skill-vs-code-drift test suite');
|
|
246
|
+
console.log('========================================');
|
|
247
|
+
console.log(' Passed: ' + passed);
|
|
248
|
+
console.log(' Failed: ' + failed);
|
|
249
|
+
if (failed > 0) {
|
|
250
|
+
console.log('');
|
|
251
|
+
console.log(' Failures:');
|
|
252
|
+
for (const f of failures) {
|
|
253
|
+
console.log(' - ' + f);
|
|
254
|
+
}
|
|
255
|
+
process.exit(1);
|
|
256
|
+
}
|
|
257
|
+
process.exit(0);
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* Phase 121.5-08 Plan -- soft-alias-runner unit tests.
|
|
4
|
+
*
|
|
5
|
+
* Behavior contract (per plan):
|
|
6
|
+
* T1: softAliasRun({from, to, args}) returns a JSON envelope with the
|
|
7
|
+
* redirect target, the deprecation note, the args, and ok: true.
|
|
8
|
+
* T2: The deprecation note follows Larry voice: starts with "Deprecated. "
|
|
9
|
+
* and names the new command, and mentions v1.14.0 removal.
|
|
10
|
+
* T3: NO em-dash characters (U+2014) anywhere in the deprecation note.
|
|
11
|
+
* T4: softAliasRun({from, to, args}) returns {redirect, args, ok: true}
|
|
12
|
+
* shape suitable for the LLM to act on.
|
|
13
|
+
* T8a: commands/mos.md exists.
|
|
14
|
+
* T8b: commands/mos.md declares body_shape: E (Action Report).
|
|
15
|
+
* T9: Each of the 5 soft-alias stub commands (heal, query, organize,
|
|
16
|
+
* hmi-status, visualize) carries the "Deprecated" body marker AND
|
|
17
|
+
* names the canonical target.
|
|
18
|
+
* T10a: commands/diagnostics.md carries the "Renamed" rename note + names
|
|
19
|
+
* /mos:fingerprint as the v1.14.0 target.
|
|
20
|
+
* T10b: diagnostics.md frontmatter has renaming_to: fingerprint.
|
|
21
|
+
*
|
|
22
|
+
* No em-dash check covers every soft-alias stub body.
|
|
23
|
+
*
|
|
24
|
+
* Hermetic: reads only the live repo files via fs.readFileSync. No network,
|
|
25
|
+
* no spawn (the runner's emitForLLM uses process.stdout, but we test
|
|
26
|
+
* softAliasRun directly).
|
|
27
|
+
*/
|
|
28
|
+
'use strict';
|
|
29
|
+
|
|
30
|
+
const fs = require('fs');
|
|
31
|
+
const path = require('path');
|
|
32
|
+
|
|
33
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
34
|
+
const RUNNER = require(path.join(REPO_ROOT, 'scripts', 'soft-alias-runner.cjs'));
|
|
35
|
+
|
|
36
|
+
let pass = 0;
|
|
37
|
+
let fail = 0;
|
|
38
|
+
const failures = [];
|
|
39
|
+
|
|
40
|
+
function assert(cond, name, detail) {
|
|
41
|
+
if (cond) {
|
|
42
|
+
pass++;
|
|
43
|
+
console.log('PASS ' + name);
|
|
44
|
+
} else {
|
|
45
|
+
fail++;
|
|
46
|
+
failures.push(name + (detail ? ' -- ' + detail : ''));
|
|
47
|
+
console.log('FAIL ' + name + (detail ? ' -- ' + detail : ''));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// --- T1: redirect target + JSON envelope shape ---
|
|
52
|
+
{
|
|
53
|
+
const r = RUNNER.softAliasRun({ from: 'heal', to: 'doctor --heal-room', args: ['--dry-run'] });
|
|
54
|
+
assert(r && typeof r === 'object', 'T1: returns an object', JSON.stringify(r));
|
|
55
|
+
assert(r.redirect === '/mos:doctor --heal-room', 'T1: redirect is /mos:doctor --heal-room', r.redirect);
|
|
56
|
+
assert(Array.isArray(r.args) && r.args[0] === '--dry-run', 'T1: args list preserved', JSON.stringify(r.args));
|
|
57
|
+
assert(r.ok === true, 'T1: ok=true');
|
|
58
|
+
assert(typeof r.deprecation_note === 'string' && r.deprecation_note.length > 0, 'T1: deprecation_note non-empty');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// --- T2: Larry voice: starts with "Deprecated. " + names new command + v1.14.0 ---
|
|
62
|
+
{
|
|
63
|
+
const r = RUNNER.softAliasRun({ from: 'query', to: 'graph', args: [] });
|
|
64
|
+
assert(r.deprecation_note.indexOf('Deprecated. ') === 0, 'T2: starts with "Deprecated. "', r.deprecation_note);
|
|
65
|
+
assert(r.deprecation_note.indexOf('/mos:graph') >= 0, 'T2: names the new command (/mos:graph)', r.deprecation_note);
|
|
66
|
+
assert(/v1\.14\.0/.test(r.deprecation_note), 'T2: mentions v1.14.0 removal', r.deprecation_note);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// --- T3: NO em-dash anywhere in the note ---
|
|
70
|
+
{
|
|
71
|
+
const samples = [
|
|
72
|
+
RUNNER.softAliasRun({ from: 'heal', to: 'doctor --heal-room', args: [] }),
|
|
73
|
+
RUNNER.softAliasRun({ from: 'query', to: 'graph', args: [] }),
|
|
74
|
+
RUNNER.softAliasRun({ from: 'organize', to: 'rooms organize', args: [] }),
|
|
75
|
+
RUNNER.softAliasRun({ from: 'hmi-status', to: 'doctor --ui-compliance --json', args: [] }),
|
|
76
|
+
RUNNER.softAliasRun({ from: 'visualize', to: 'dashboard --mermaid', args: [] }),
|
|
77
|
+
];
|
|
78
|
+
for (const r of samples) {
|
|
79
|
+
assert(r.deprecation_note.indexOf('—') < 0,
|
|
80
|
+
'T3: no em-dash in note (from=' + (r && r.redirect) + ')',
|
|
81
|
+
r.deprecation_note);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// --- T4: shape is consumable by LLM ---
|
|
86
|
+
{
|
|
87
|
+
const r = RUNNER.softAliasRun({ from: 'organize', to: 'rooms organize', args: ['propose'] });
|
|
88
|
+
const keys = Object.keys(r).sort();
|
|
89
|
+
const required = ['args', 'deprecation_note', 'ok', 'redirect'];
|
|
90
|
+
for (const k of required) {
|
|
91
|
+
assert(keys.indexOf(k) >= 0, 'T4: envelope has key "' + k + '"', JSON.stringify(keys));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// --- T8: commands/mos.md exists + body_shape: E ---
|
|
96
|
+
{
|
|
97
|
+
const mosPath = path.join(REPO_ROOT, 'commands', 'mos.md');
|
|
98
|
+
assert(fs.existsSync(mosPath), 'T8a: commands/mos.md exists', mosPath);
|
|
99
|
+
if (fs.existsSync(mosPath)) {
|
|
100
|
+
const body = fs.readFileSync(mosPath, 'utf8');
|
|
101
|
+
// Accept body_shape: E OR body_shape: E (Action Report) form
|
|
102
|
+
assert(/^body_shape:\s*E(\s|$|\b)/m.test(body), 'T8b: declares body_shape: E', body.slice(0, 400));
|
|
103
|
+
assert(/state-aware router/i.test(body), 'T8c: body mentions "state-aware router"');
|
|
104
|
+
assert(body.indexOf('—') < 0, 'T8d: NO em-dash in commands/mos.md');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// --- T9: 5 soft-alias stubs carry "Deprecated" + name their canonical target ---
|
|
109
|
+
{
|
|
110
|
+
const stubs = [
|
|
111
|
+
{ file: 'commands/heal.md', target: '/mos:doctor --heal-room' },
|
|
112
|
+
{ file: 'commands/query.md', target: '/mos:graph' },
|
|
113
|
+
{ file: 'commands/organize.md', target: '/mos:rooms organize' },
|
|
114
|
+
{ file: 'commands/hmi-status.md', target: '/mos:doctor --ui-compliance' },
|
|
115
|
+
{ file: 'commands/visualize.md', target: '/mos:dashboard --mermaid' },
|
|
116
|
+
];
|
|
117
|
+
for (const s of stubs) {
|
|
118
|
+
const p = path.join(REPO_ROOT, s.file);
|
|
119
|
+
const body = fs.readFileSync(p, 'utf8');
|
|
120
|
+
assert(/Deprecated/.test(body), 'T9: ' + s.file + ' body has "Deprecated"', body.slice(0, 200));
|
|
121
|
+
assert(body.indexOf(s.target) >= 0, 'T9: ' + s.file + ' names target ' + s.target);
|
|
122
|
+
assert(body.indexOf('—') < 0, 'T9: ' + s.file + ' has no em-dash');
|
|
123
|
+
assert(/^deprecated:\s*true/m.test(body), 'T9: ' + s.file + ' frontmatter deprecated: true');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// --- T10: diagnostics.md is a RENAME (not fold) to /mos:fingerprint ---
|
|
128
|
+
{
|
|
129
|
+
const p = path.join(REPO_ROOT, 'commands', 'diagnostics.md');
|
|
130
|
+
const body = fs.readFileSync(p, 'utf8');
|
|
131
|
+
assert(/Renamed/.test(body), 'T10a: diagnostics.md body has "Renamed"');
|
|
132
|
+
assert(body.indexOf('/mos:fingerprint') >= 0, 'T10b: diagnostics.md names /mos:fingerprint');
|
|
133
|
+
assert(/^renaming_to:\s*fingerprint/m.test(body), 'T10c: diagnostics.md frontmatter renaming_to: fingerprint');
|
|
134
|
+
assert(body.indexOf('—') < 0, 'T10d: diagnostics.md has no em-dash');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
console.log('');
|
|
138
|
+
console.log('soft-alias.test.cjs: ' + pass + ' passed, ' + fail + ' failed');
|
|
139
|
+
if (fail > 0) {
|
|
140
|
+
console.log('FAILURES:');
|
|
141
|
+
for (const f of failures) console.log(' - ' + f);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
process.exit(0);
|