@mindrian_os/install 1.13.0-beta.16 → 1.13.0-beta.19
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/CHANGELOG.md +36 -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 +4 -1
- 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 +4 -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 +4 -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 +58 -0
- package/commands/mva-option.md +91 -0
- package/commands/new-project.md +4 -0
- package/commands/onboard.md +22 -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 +31 -88
- package/lib/agents/auto-explore-agent.cjs +82 -0
- package/lib/agents/mva/brain-classic-traps.cjs +77 -0
- package/lib/agents/mva/brain-cross-domain.cjs +79 -0
- package/lib/agents/mva/brain-similar-ventures.cjs +93 -0
- package/lib/agents/mva/dashboard-graph-neighborhood.cjs +72 -0
- package/lib/agents/mva/index.cjs +42 -0
- package/lib/agents/mva/six-hats-red-black.cjs +137 -0
- package/lib/agents/mva/tavily-funding-scan.cjs +147 -0
- package/lib/agents/mva/test-all-six-agents.cjs +467 -0
- package/lib/conversation/operator.cjs +64 -0
- package/lib/conversation/operator.test.cjs +160 -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/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/mva-agent-contract.cjs +170 -0
- package/lib/core/mva-agent-contract.test.cjs +169 -0
- package/lib/core/mva-budget.cjs +75 -0
- package/lib/core/mva-budget.test.cjs +68 -0
- package/lib/core/mva-classifier.cjs +370 -0
- package/lib/core/mva-classifier.test.cjs +248 -0
- package/lib/core/mva-deck-builder.cjs +452 -0
- package/lib/core/mva-deck-builder.test.cjs +287 -0
- package/lib/core/mva-detect.smoke.test.cjs +197 -0
- package/lib/core/mva-dispatcher.cjs +110 -0
- package/lib/core/mva-dispatcher.test.cjs +216 -0
- package/lib/core/mva-option-router.cjs +292 -0
- package/lib/core/mva-option-router.test.cjs +483 -0
- package/lib/core/mva-orchestrator.cjs +365 -0
- package/lib/core/mva-orchestrator.test.cjs +908 -0
- package/lib/core/mva-progressive-renderer.cjs +194 -0
- package/lib/core/mva-progressive-renderer.test.cjs +157 -0
- package/lib/core/mva-rule-linter.cjs +213 -0
- package/lib/core/mva-rule-linter.test.cjs +336 -0
- package/lib/core/mva-state.cjs +159 -0
- package/lib/core/mva-telemetry.cjs +58 -0
- package/lib/core/mva-telemetry.test.cjs +196 -0
- package/lib/core/mva-vercel-deploy.cjs +168 -0
- package/lib/core/mva-vercel-deploy.test.cjs +239 -0
- package/lib/core/navigation/dashboard-helpers.cjs +145 -0
- package/lib/core/navigation/edges.cjs +35 -0
- package/lib/core/navigation/memory-events.cjs +126 -0
- package/lib/core/navigation.cjs +11 -0
- package/lib/core/resolve-vercel-key.cjs +107 -0
- package/lib/core/resolve-vercel-key.test.cjs +137 -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/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 +197 -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/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/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/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 +240 -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/mva-pipeline/SKILL.md +129 -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,248 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/*
|
|
3
|
+
* Phase 118-00 Plan 00 -- mva-classifier + mva-state unit tests.
|
|
4
|
+
*
|
|
5
|
+
* 7 tests (per PLAN.md Task 1 behavior block):
|
|
6
|
+
* T1 heuristic positive (3 venture sentences)
|
|
7
|
+
* T2 heuristic negative (3 coding/admin sentences)
|
|
8
|
+
* T3 Hebrew detection -> graceful refusal (per LD1; pre-empts Haiku call)
|
|
9
|
+
* T4 short-circuit on length (< 12 chars OR > 600 chars)
|
|
10
|
+
* T5 cache hit (sha256 keyed; second call returns cached, fetch counter = 0)
|
|
11
|
+
* T6 state I/O round trip (writePending / readPending / markRunning /
|
|
12
|
+
* markComplete / isAlreadyRunning; atomic tmpfile-then-rename)
|
|
13
|
+
* T7 heuristic-only mode when ANTHROPIC_API_KEY absent (fetch counter = 0)
|
|
14
|
+
*
|
|
15
|
+
* Test seam strategy: classifier exposes `_test` namespace for injecting
|
|
16
|
+
* a mock fetch + clearing the in-memory sha256 cache. State module exposes
|
|
17
|
+
* `stateDir` so the test can hijack a tmpdir HOME without touching the
|
|
18
|
+
* real ~/.mindrian/mva. Mirrors the Plan 90-01 / Plan 109-10 seam idiom.
|
|
19
|
+
*
|
|
20
|
+
* Pure CJS, node built-ins only. No mocha, no jest -- mirrors the Phase 109
|
|
21
|
+
* + Phase 124 + Phase 125 in-tree runner pattern.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const assert = require('node:assert/strict');
|
|
25
|
+
const fs = require('node:fs');
|
|
26
|
+
const os = require('node:os');
|
|
27
|
+
const path = require('node:path');
|
|
28
|
+
|
|
29
|
+
const CLASSIFIER_PATH = path.resolve(__dirname, 'mva-classifier.cjs');
|
|
30
|
+
const STATE_PATH = path.resolve(__dirname, 'mva-state.cjs');
|
|
31
|
+
|
|
32
|
+
let passed = 0;
|
|
33
|
+
let failed = 0;
|
|
34
|
+
function run(name, fn) {
|
|
35
|
+
try {
|
|
36
|
+
fn();
|
|
37
|
+
process.stdout.write('ok ' + name + '\n');
|
|
38
|
+
passed += 1;
|
|
39
|
+
} catch (err) {
|
|
40
|
+
process.stderr.write('FAIL ' + name + '\n' + (err && err.stack ? err.stack : String(err)) + '\n');
|
|
41
|
+
failed += 1;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Fresh require each block so the in-memory cache resets between mutating
|
|
46
|
+
// suites. classify keeps a Map at module scope -- a stale entry would mask
|
|
47
|
+
// genuine failures of the no-fetch invariant in T5/T7.
|
|
48
|
+
function freshClassifier() {
|
|
49
|
+
delete require.cache[CLASSIFIER_PATH];
|
|
50
|
+
return require(CLASSIFIER_PATH);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function freshState() {
|
|
54
|
+
delete require.cache[STATE_PATH];
|
|
55
|
+
return require(STATE_PATH);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ---------- T1 heuristic positive ----------
|
|
59
|
+
|
|
60
|
+
run('T1 heuristic positive: 3 venture sentences', () => {
|
|
61
|
+
delete process.env.ANTHROPIC_API_KEY;
|
|
62
|
+
const { classify } = freshClassifier();
|
|
63
|
+
const venture = [
|
|
64
|
+
'I have an idea for a couples finance app',
|
|
65
|
+
'thinking about building a SaaS for dentists',
|
|
66
|
+
'considering launching a platform for nonprofits',
|
|
67
|
+
];
|
|
68
|
+
for (const s of venture) {
|
|
69
|
+
const r = classify(s);
|
|
70
|
+
assert.equal(r.venture, true, 'expected venture=true for: ' + s + ' got ' + JSON.stringify(r));
|
|
71
|
+
assert.ok(r.source === 'heuristic' || r.source === 'heuristic_fallback',
|
|
72
|
+
'expected source heuristic*; got ' + r.source);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// ---------- T2 heuristic negative ----------
|
|
77
|
+
|
|
78
|
+
run('T2 heuristic negative: 3 coding/admin sentences', () => {
|
|
79
|
+
delete process.env.ANTHROPIC_API_KEY;
|
|
80
|
+
const { classify } = freshClassifier();
|
|
81
|
+
const nonVenture = [
|
|
82
|
+
'fix the failing test in foo.test.js',
|
|
83
|
+
'/mos:status now please please',
|
|
84
|
+
'git push origin main right now',
|
|
85
|
+
];
|
|
86
|
+
for (const s of nonVenture) {
|
|
87
|
+
const r = classify(s);
|
|
88
|
+
assert.equal(r.venture, false, 'expected venture=false for: ' + s + ' got ' + JSON.stringify(r));
|
|
89
|
+
assert.ok(r.source === 'heuristic' || r.source === 'language_detect' || r.source === 'length_guard'
|
|
90
|
+
|| r.source === 'heuristic_fallback',
|
|
91
|
+
'unexpected source ' + r.source + ' for ' + s);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// ---------- T3 Hebrew detection (LD1) ----------
|
|
96
|
+
|
|
97
|
+
run('T3 Hebrew detection -> hebrew_unsupported_v1.13.0, no fetch', () => {
|
|
98
|
+
process.env.ANTHROPIC_API_KEY = 'sk-fake-should-never-be-called';
|
|
99
|
+
const mod = freshClassifier();
|
|
100
|
+
let fetchCount = 0;
|
|
101
|
+
mod._test.setFetch(async () => { fetchCount += 1; return { ok: true, json: async () => ({}) }; });
|
|
102
|
+
const r = mod.classify('יש לי רעיון לאפליקציה לזוגות');
|
|
103
|
+
assert.equal(r.venture, false);
|
|
104
|
+
assert.equal(r.reason, 'hebrew_unsupported_v1.13.0');
|
|
105
|
+
assert.equal(r.source, 'language_detect');
|
|
106
|
+
assert.equal(fetchCount, 0, 'Hebrew must short-circuit BEFORE any API call');
|
|
107
|
+
delete process.env.ANTHROPIC_API_KEY;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// ---------- T4 length short-circuit ----------
|
|
111
|
+
|
|
112
|
+
run('T4 length out of range -> short-circuit, no fetch', () => {
|
|
113
|
+
process.env.ANTHROPIC_API_KEY = 'sk-fake';
|
|
114
|
+
const mod = freshClassifier();
|
|
115
|
+
let fetchCount = 0;
|
|
116
|
+
mod._test.setFetch(async () => { fetchCount += 1; return { ok: true, json: async () => ({}) }; });
|
|
117
|
+
// Too short
|
|
118
|
+
let r = mod.classify('');
|
|
119
|
+
assert.equal(r.venture, false);
|
|
120
|
+
assert.equal(r.reason, 'length_out_of_range');
|
|
121
|
+
r = mod.classify('hi');
|
|
122
|
+
assert.equal(r.venture, false);
|
|
123
|
+
assert.equal(r.reason, 'length_out_of_range');
|
|
124
|
+
// Too long
|
|
125
|
+
const longStr = 'x'.repeat(700);
|
|
126
|
+
r = mod.classify(longStr);
|
|
127
|
+
assert.equal(r.venture, false);
|
|
128
|
+
assert.equal(r.reason, 'length_out_of_range');
|
|
129
|
+
assert.equal(fetchCount, 0, 'length-guard must short-circuit BEFORE any API call');
|
|
130
|
+
delete process.env.ANTHROPIC_API_KEY;
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// ---------- T5 cache hit on repeated sha256 ----------
|
|
134
|
+
|
|
135
|
+
run('T5 cache hit: second call returns cached (fetch counter unchanged)', () => {
|
|
136
|
+
// Use ANTHROPIC_API_KEY so the FIRST call may consult fetch; we mock
|
|
137
|
+
// fetch to return a known answer, then assert call 2 does NOT hit fetch.
|
|
138
|
+
process.env.ANTHROPIC_API_KEY = 'sk-fake-for-cache-test';
|
|
139
|
+
const mod = freshClassifier();
|
|
140
|
+
let fetchCount = 0;
|
|
141
|
+
mod._test.setFetch(async () => {
|
|
142
|
+
fetchCount += 1;
|
|
143
|
+
return {
|
|
144
|
+
ok: true,
|
|
145
|
+
json: async () => ({ content: [{ type: 'text', text: 'venture' }] }),
|
|
146
|
+
};
|
|
147
|
+
});
|
|
148
|
+
mod._test.clearCache();
|
|
149
|
+
const s = 'a-very-unique-sentence-not-in-heuristic-bank-1234567890';
|
|
150
|
+
const r1 = mod.classify(s);
|
|
151
|
+
const r2 = mod.classify(s);
|
|
152
|
+
assert.equal(r1.venture, r2.venture, 'cache should return identical result');
|
|
153
|
+
// The heuristic path may have answered without fetch -- that's fine. The
|
|
154
|
+
// contract is: second call MUST NOT add to fetchCount.
|
|
155
|
+
const after1 = fetchCount;
|
|
156
|
+
const r3 = mod.classify(s);
|
|
157
|
+
assert.equal(fetchCount, after1, 'third call must not re-trigger fetch');
|
|
158
|
+
assert.equal(r3.venture, r1.venture);
|
|
159
|
+
delete process.env.ANTHROPIC_API_KEY;
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// ---------- T6 state I/O round trip ----------
|
|
163
|
+
|
|
164
|
+
run('T6 state I/O round trip + atomic write semantics', () => {
|
|
165
|
+
// Isolate state to a tmpdir HOME so we never touch the real ~/.mindrian.
|
|
166
|
+
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'mva-state-test-'));
|
|
167
|
+
const prevHome = process.env.HOME;
|
|
168
|
+
const prevSession = process.env.CLAUDE_SESSION_ID;
|
|
169
|
+
process.env.HOME = tmpHome;
|
|
170
|
+
process.env.CLAUDE_SESSION_ID = 'test-session-118-00';
|
|
171
|
+
try {
|
|
172
|
+
const state = freshState();
|
|
173
|
+
// Clean slate
|
|
174
|
+
assert.equal(state.readPending(), null, 'cold start: readPending returns null');
|
|
175
|
+
assert.equal(state.isAlreadyRunning(), false, 'cold start: not running');
|
|
176
|
+
|
|
177
|
+
const payload = {
|
|
178
|
+
sentence_sha256: 'a'.repeat(64),
|
|
179
|
+
classified_at: Date.now(),
|
|
180
|
+
classifier_source: 'heuristic',
|
|
181
|
+
classifier_confidence: 'high',
|
|
182
|
+
locale: 'en',
|
|
183
|
+
};
|
|
184
|
+
state.writePending(payload);
|
|
185
|
+
const read = state.readPending();
|
|
186
|
+
// writePending initializes pipeline_status='pending' for the dispatcher
|
|
187
|
+
// (Plan 118-01) to read. The original payload fields must all be present
|
|
188
|
+
// and byte-identical; pipeline_status is added by the writer.
|
|
189
|
+
for (const k of Object.keys(payload)) {
|
|
190
|
+
assert.deepEqual(read[k], payload[k],
|
|
191
|
+
'writePending must preserve field ' + k + '; got ' + JSON.stringify(read[k]));
|
|
192
|
+
}
|
|
193
|
+
assert.equal(read.pipeline_status, 'pending',
|
|
194
|
+
'writePending must initialize pipeline_status="pending" for dispatcher');
|
|
195
|
+
|
|
196
|
+
state.markRunning();
|
|
197
|
+
assert.equal(state.isAlreadyRunning(), true);
|
|
198
|
+
|
|
199
|
+
state.markComplete();
|
|
200
|
+
assert.equal(state.isAlreadyRunning(), false);
|
|
201
|
+
|
|
202
|
+
// Verify state dir lives under ~/.mindrian/mva (i.e. respects tmpHome)
|
|
203
|
+
assert.ok(state.stateDir().indexOf(tmpHome) === 0,
|
|
204
|
+
'stateDir should be under tmpHome; got ' + state.stateDir());
|
|
205
|
+
|
|
206
|
+
// Atomic-write source-grep audit: verify the module uses tmp+rename idiom
|
|
207
|
+
const src = fs.readFileSync(STATE_PATH, 'utf8');
|
|
208
|
+
assert.ok(/renameSync/.test(src), 'mva-state.cjs must use fs.renameSync (atomic write)');
|
|
209
|
+
assert.ok(/\.tmp/.test(src) || /tmp/.test(src),
|
|
210
|
+
'mva-state.cjs must write to a tmp path before rename');
|
|
211
|
+
} finally {
|
|
212
|
+
process.env.HOME = prevHome;
|
|
213
|
+
if (prevSession === undefined) delete process.env.CLAUDE_SESSION_ID;
|
|
214
|
+
else process.env.CLAUDE_SESSION_ID = prevSession;
|
|
215
|
+
try { fs.rmSync(tmpHome, { recursive: true, force: true }); } catch (_e) {}
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// ---------- T7 heuristic-only mode when ANTHROPIC_API_KEY unset ----------
|
|
220
|
+
|
|
221
|
+
run('T7 heuristic-only mode: no fetch attempted when key absent', () => {
|
|
222
|
+
delete process.env.ANTHROPIC_API_KEY;
|
|
223
|
+
// Also clear ~/.mindrian.env / .env paths by overriding HOME to a clean
|
|
224
|
+
// tmpdir so the resolver returns not-found deterministically.
|
|
225
|
+
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'mva-no-key-test-'));
|
|
226
|
+
const prevHome = process.env.HOME;
|
|
227
|
+
process.env.HOME = tmpHome;
|
|
228
|
+
try {
|
|
229
|
+
const mod = freshClassifier();
|
|
230
|
+
let fetchCount = 0;
|
|
231
|
+
mod._test.setFetch(async () => { fetchCount += 1; return { ok: true, json: async () => ({}) }; });
|
|
232
|
+
mod._test.clearCache();
|
|
233
|
+
const r = mod.classify('I have an idea for a couples finance app');
|
|
234
|
+
assert.equal(r.venture, true);
|
|
235
|
+
assert.equal(r.source, 'heuristic_fallback',
|
|
236
|
+
'when key absent, source must be heuristic_fallback (medium confidence); got ' + r.source);
|
|
237
|
+
assert.equal(r.confidence, 'medium');
|
|
238
|
+
assert.equal(fetchCount, 0, 'fetch must not be attempted without an API key');
|
|
239
|
+
} finally {
|
|
240
|
+
process.env.HOME = prevHome;
|
|
241
|
+
try { fs.rmSync(tmpHome, { recursive: true, force: true }); } catch (_e) {}
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// ---------- Summary ----------
|
|
246
|
+
|
|
247
|
+
process.stderr.write('\n' + passed + ' passed, ' + failed + ' failed\n');
|
|
248
|
+
process.exit(failed === 0 ? 0 : 1);
|