@mindrian_os/install 1.13.0-beta.11
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 +21 -0
- package/.mcp.json +9 -0
- package/CHANGELOG.md +3333 -0
- package/LICENSE +123 -0
- package/README.md +673 -0
- package/agents/brain-query.md +80 -0
- package/agents/framework-runner.md +237 -0
- package/agents/grading.md +188 -0
- package/agents/investor.md +128 -0
- package/agents/larry-extended.md +135 -0
- package/agents/opportunity-scanner.md +91 -0
- package/agents/persona-analyst.md +132 -0
- package/agents/research.md +89 -0
- package/agents/reverse-salient-agent.md +27 -0
- package/bin/cli.js +142 -0
- package/bin/mindrian-mcp-server.cjs +182 -0
- package/bin/mindrian-tools.cjs +765 -0
- package/commands/act.md +439 -0
- package/commands/admin.md +404 -0
- package/commands/analyze-needs.md +42 -0
- package/commands/analyze-systems.md +39 -0
- package/commands/analyze-timing.md +42 -0
- package/commands/auto-explore.md +64 -0
- package/commands/beautiful-question.md +40 -0
- package/commands/brain-derive.md +78 -0
- package/commands/build-knowledge.md +42 -0
- package/commands/build-thesis.md +46 -0
- package/commands/causal.md +234 -0
- package/commands/challenge-assumptions.md +33 -0
- package/commands/compare-ventures.md +83 -0
- package/commands/dashboard.md +110 -0
- package/commands/deep-grade.md +82 -0
- package/commands/diagnose.md +58 -0
- package/commands/diagnostics.md +151 -0
- package/commands/doctor.md +151 -0
- package/commands/dominant-designs.md +40 -0
- package/commands/explain-decision.md +87 -0
- package/commands/explore-domains.md +42 -0
- package/commands/explore-futures.md +40 -0
- package/commands/explore-trends.md +42 -0
- package/commands/export.md +103 -0
- package/commands/file-meeting.md +724 -0
- package/commands/find-analogies.md +188 -0
- package/commands/find-bottlenecks.md +62 -0
- package/commands/find-connections.md +76 -0
- package/commands/funding.md +81 -0
- package/commands/grade.md +203 -0
- package/commands/graph.md +128 -0
- package/commands/hat-briefing.md +125 -0
- package/commands/heal.md +196 -0
- package/commands/help.md +399 -0
- package/commands/hmi-status.md +172 -0
- package/commands/jtbd.md +241 -0
- package/commands/leadership.md +73 -0
- package/commands/lean-canvas.md +40 -0
- package/commands/macro-trends.md +40 -0
- package/commands/map-unknowns.md +40 -0
- package/commands/memory.md +173 -0
- package/commands/models.md +175 -0
- package/commands/mos-reason.md +285 -0
- package/commands/mullins.md +120 -0
- package/commands/new-project.md +481 -0
- package/commands/onboard.md +434 -0
- package/commands/operator.md +149 -0
- package/commands/opportunities.md +144 -0
- package/commands/organize.md +497 -0
- package/commands/persona.md +198 -0
- package/commands/pipeline.md +112 -0
- package/commands/present.md +91 -0
- package/commands/publish.md +201 -0
- package/commands/query.md +124 -0
- package/commands/radar.md +72 -0
- package/commands/reanalyze.md +91 -0
- package/commands/research.md +196 -0
- package/commands/room.md +352 -0
- package/commands/rooms.md +598 -0
- package/commands/root-cause.md +40 -0
- package/commands/rs-experts.md +85 -0
- package/commands/rs-explain.md +100 -0
- package/commands/rs-fetch.md +94 -0
- package/commands/rs-thesis.md +85 -0
- package/commands/scenario-plan.md +40 -0
- package/commands/scheduled-tasks.md +285 -0
- package/commands/score-innovation.md +43 -0
- package/commands/scout.md +239 -0
- package/commands/setup.md +618 -0
- package/commands/snapshot.md +147 -0
- package/commands/speakers.md +84 -0
- package/commands/splash.md +28 -0
- package/commands/status.md +75 -0
- package/commands/structure-argument.md +42 -0
- package/commands/suggest-next.md +80 -0
- package/commands/systems-thinking.md +40 -0
- package/commands/think-hats.md +42 -0
- package/commands/update.md +181 -0
- package/commands/user-needs.md +40 -0
- package/commands/validate.md +40 -0
- package/commands/value-proposition.md +61 -0
- package/commands/vault.md +180 -0
- package/commands/visualize.md +52 -0
- package/commands/whitespace.md +507 -0
- package/commands/wiki.md +69 -0
- package/hooks/hooks.json +381 -0
- package/hooks/run-hook.cmd +64 -0
- package/lib/__init__.py +0 -0
- package/lib/__pycache__/__init__.cpython-312.pyc +0 -0
- package/lib/agents/auto-explore-agent.cjs +1043 -0
- package/lib/agents/reverse-salient-agent.cjs +679 -0
- package/lib/agents/tension-hook-agent.cjs +544 -0
- package/lib/brain/ROOM.md +44 -0
- package/lib/brain/chain-recommender.cjs +301 -0
- package/lib/chat/chat-context.js +185 -0
- package/lib/chat/chat-panel.js +721 -0
- package/lib/chat/fabric-chat.cjs +288 -0
- package/lib/chat/generative-tools.js +219 -0
- package/lib/conversation/ROOM.md +39 -0
- package/lib/conversation/classifier-rules.json +38 -0
- package/lib/conversation/classifier.cjs +264 -0
- package/lib/conversation/operator.cjs +287 -0
- package/lib/copy/115-spec-strings.cjs +55 -0
- package/lib/core/__init__.py +0 -0
- package/lib/core/__nav-stub.cjs +14 -0
- package/lib/core/__pycache__/__init__.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs-math.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs_cache.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs_corpus.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs_hybrid.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs_math.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs_rooms.cpython-312.pyc +0 -0
- package/lib/core/artifact-id.cjs +148 -0
- package/lib/core/asset-ops.cjs +151 -0
- package/lib/core/auto-commit-throttle.cjs +129 -0
- package/lib/core/bearer-token.cjs +199 -0
- package/lib/core/brain-client.cjs +865 -0
- package/lib/core/brain-derivation-prompts.cjs +326 -0
- package/lib/core/brain-derivation-queue.cjs +431 -0
- package/lib/core/brain-derivation.cjs +580 -0
- package/lib/core/brain-md-schema.cjs +528 -0
- package/lib/core/brain-md-staleness.cjs +357 -0
- package/lib/core/brain-response-sanitize.cjs +188 -0
- package/lib/core/bridge-writer.cjs +477 -0
- package/lib/core/chat-context-builder.cjs +253 -0
- package/lib/core/cross-room-aggregator.cjs +762 -0
- package/lib/core/daily-briefing.cjs +438 -0
- package/lib/core/decision-capture.cjs +618 -0
- package/lib/core/deep-links.cjs +82 -0
- package/lib/core/dispatch-optimizer.cjs +354 -0
- package/lib/core/dual-path-detector.cjs +84 -0
- package/lib/core/dual-path-detector.test.cjs +334 -0
- package/lib/core/exports-log.cjs +79 -0
- package/lib/core/feynman-minto-invariants.cjs +605 -0
- package/lib/core/folder-memory-async.cjs +338 -0
- package/lib/core/folder-memory-shared.cjs +890 -0
- package/lib/core/folder-memory.cjs +416 -0
- package/lib/core/framework-chain-composer.cjs +411 -0
- package/lib/core/frontmatter-schemas.cjs +330 -0
- package/lib/core/git-ops.cjs +141 -0
- package/lib/core/graph-ops.cjs +258 -0
- package/lib/core/hat-persistence.cjs +362 -0
- package/lib/core/index.cjs +60 -0
- package/lib/core/integration-registry.cjs +232 -0
- package/lib/core/intelligence-cascade.cjs +661 -0
- package/lib/core/lazygraph-ops.cjs +1057 -0
- package/lib/core/lru-cache.cjs +139 -0
- package/lib/core/mcp-profiles.cjs +182 -0
- package/lib/core/meeting-ops.cjs +54 -0
- package/lib/core/memory-ops.cjs +600 -0
- package/lib/core/migrations/ROOM.md +33 -0
- package/lib/core/migrations/phase-109-nodes-provenance.cjs +339 -0
- package/lib/core/migrations/phase-109-session-focus.cjs +99 -0
- package/lib/core/model-profiles.cjs +246 -0
- package/lib/core/mullins-scaffold.cjs +160 -0
- package/lib/core/nav-dial.cjs +316 -0
- package/lib/core/navigation/ROOM.md +15 -0
- package/lib/core/navigation/explanation.cjs +43 -0
- package/lib/core/navigation/focus.cjs +135 -0
- package/lib/core/navigation/ingestion.cjs +82 -0
- package/lib/core/navigation/insights.cjs +350 -0
- package/lib/core/navigation/memory-events.cjs +118 -0
- package/lib/core/navigation/neighborhood.cjs +78 -0
- package/lib/core/navigation/packet.cjs +182 -0
- package/lib/core/navigation/room-home.cjs +127 -0
- package/lib/core/navigation/transitions.cjs +82 -0
- package/lib/core/navigation-engine-shared.cjs +242 -0
- package/lib/core/navigation-engine.cjs +664 -0
- package/lib/core/navigation.cjs +60 -0
- package/lib/core/nl-graph-queries.cjs +164 -0
- package/lib/core/offer-presenter.cjs +406 -0
- package/lib/core/opportunity-extractor.cjs +183 -0
- package/lib/core/opportunity-ops.cjs +1371 -0
- package/lib/core/persona-ops.cjs +537 -0
- package/lib/core/persona-taxonomy.cjs +190 -0
- package/lib/core/platform-gates.cjs +120 -0
- package/lib/core/platform.cjs +257 -0
- package/lib/core/proactive-intelligence.cjs +528 -0
- package/lib/core/problem-type-router.cjs +315 -0
- package/lib/core/reasoning-ops.cjs +639 -0
- package/lib/core/reverse-salient-persona-suffix.cjs +115 -0
- package/lib/core/room-classifier-strict-mode.cjs +229 -0
- package/lib/core/room-db.cjs +127 -0
- package/lib/core/room-ops-async.cjs +92 -0
- package/lib/core/room-ops-shared.cjs +64 -0
- package/lib/core/room-ops-sync.cjs +70 -0
- package/lib/core/room-ops.cjs +32 -0
- package/lib/core/room-type-detector.cjs +386 -0
- package/lib/core/rs-brain-substrate-prompts.cjs +129 -0
- package/lib/core/rs-brain-substrate.cjs +570 -0
- package/lib/core/rs-breakthrough-scorer.cjs +255 -0
- package/lib/core/rs-canon-violations.cjs +82 -0
- package/lib/core/rs-chain-feeder.cjs +343 -0
- package/lib/core/rs-commercial-assessor.cjs +280 -0
- package/lib/core/rs-differential-scorer.cjs +376 -0
- package/lib/core/rs-domain-analyzer.cjs +385 -0
- package/lib/core/rs-egress-prompts.cjs +113 -0
- package/lib/core/rs-egress-telemetry.cjs +225 -0
- package/lib/core/rs-egress-violations.cjs +53 -0
- package/lib/core/rs-expert-mapper.cjs +467 -0
- package/lib/core/rs-fetcher-academic.cjs +697 -0
- package/lib/core/rs-fetcher-experts.cjs +314 -0
- package/lib/core/rs-fetcher-industry.cjs +731 -0
- package/lib/core/rs-fetcher-patents.cjs +564 -0
- package/lib/core/rs-innovation-classifier.cjs +194 -0
- package/lib/core/rs-mind-map.cjs +656 -0
- package/lib/core/rs-neo4j-writer.cjs +388 -0
- package/lib/core/rs-nl-to-query.cjs +425 -0
- package/lib/core/rs-pinecone-bridge.cjs +303 -0
- package/lib/core/rs-preprocessor.cjs +350 -0
- package/lib/core/rs-query-matrix.cjs +316 -0
- package/lib/core/rs-query-to-text.cjs +438 -0
- package/lib/core/rs-sqlite-mirror.cjs +443 -0
- package/lib/core/rs-thesis-generator.cjs +188 -0
- package/lib/core/rs_cache.py +479 -0
- package/lib/core/rs_corpus.py +468 -0
- package/lib/core/rs_hybrid.py +586 -0
- package/lib/core/rs_math.py +287 -0
- package/lib/core/rs_rooms.py +193 -0
- package/lib/core/scheduled-scanner.cjs +463 -0
- package/lib/core/scratchpad-ops.cjs +201 -0
- package/lib/core/section-8-trace-schema.cjs +138 -0
- package/lib/core/section-registry.cjs +111 -0
- package/lib/core/session-state.cjs +144 -0
- package/lib/core/shallow-doc-parser.cjs +174 -0
- package/lib/core/shallow-doc-parser.test.cjs +226 -0
- package/lib/core/skill-activation-router.cjs +284 -0
- package/lib/core/state-ops.cjs +46 -0
- package/lib/core/statusline-cache.cjs +266 -0
- package/lib/core/token-estimator.cjs +348 -0
- package/lib/core/user-archetype.cjs +239 -0
- package/lib/core/user-md-ops.cjs +524 -0
- package/lib/core/visual-ops.cjs +624 -0
- package/lib/core/write-lock.cjs +149 -0
- package/lib/graph/canvas-graph.js +467 -0
- package/lib/graph/constellation-config.cjs +299 -0
- package/lib/graph/graph-detail-panel.js +165 -0
- package/lib/hmi/ROOM.md +47 -0
- package/lib/hmi/across-session-memory.cjs +604 -0
- package/lib/hmi/cross-room-memory.cjs +575 -0
- package/lib/hmi/decoy-tier.cjs +395 -0
- package/lib/hmi/jtbd-classifier.cjs +219 -0
- package/lib/hmi/jtbd-state.cjs +199 -0
- package/lib/hmi/jtbd-taxonomy.json +392 -0
- package/lib/hmi/selector-dispatcher.cjs +546 -0
- package/lib/hmi/selector-telemetry.cjs +263 -0
- package/lib/hmi/shape-f0-renderer.cjs +139 -0
- package/lib/hmi/shape-f1-fallback.cjs +80 -0
- package/lib/hmi/shape-f1-renderer.cjs +138 -0
- package/lib/hmi/shape-f2-renderer.cjs +132 -0
- package/lib/hmi/shape-f3-renderer.cjs +66 -0
- package/lib/hmi/shape-f4-renderer.cjs +72 -0
- package/lib/hmi/shape-f5-renderer.cjs +155 -0
- package/lib/hmi/shape-f6-plan-review-renderer.cjs +312 -0
- package/lib/hmi/shape-f6-renderer.cjs +144 -0
- package/lib/hmi/shape-g-renderer.cjs +219 -0
- package/lib/hmi/shape-h-renderer.cjs +222 -0
- package/lib/hmi/tier-check.cjs +63 -0
- package/lib/import/PRECONDITIONS.md +41 -0
- package/lib/import/branding.cjs +210 -0
- package/lib/import/branding.test.cjs +235 -0
- package/lib/import/classifications-sync.cjs +104 -0
- package/lib/import/classifications-sync.test.cjs +129 -0
- package/lib/import/enricher.cjs +296 -0
- package/lib/import/enricher.test.cjs +273 -0
- package/lib/import/integration.test.cjs +376 -0
- package/lib/import/manifest.cjs +129 -0
- package/lib/import/manifest.schema.json +185 -0
- package/lib/import/manifest.test.cjs +123 -0
- package/lib/import/meeting-detector.cjs +92 -0
- package/lib/import/meeting-detector.test.cjs +100 -0
- package/lib/import/person-detector.cjs +229 -0
- package/lib/import/person-detector.test.cjs +149 -0
- package/lib/import/report.cjs +186 -0
- package/lib/import/report.test.cjs +186 -0
- package/lib/import/room-md-scaffolder.cjs +49 -0
- package/lib/import/router.cjs +224 -0
- package/lib/import/router.test.cjs +356 -0
- package/lib/import/run-all-tests.cjs +36 -0
- package/lib/import/smoke-test.cjs +213 -0
- package/lib/import/smoke-test.test.cjs +148 -0
- package/lib/import/test-fixtures/collision-vault/preexisting-room/STATE.md +8 -0
- package/lib/import/test-fixtures/collision-vault/preexisting-room/problem-definition/onboarding/onboarding.md +7 -0
- package/lib/import/test-fixtures/collision-vault/source/onboarding.md +5 -0
- package/lib/import/test-fixtures/obsidian-vault/.obsidian/workspace.json +1 -0
- package/lib/import/test-fixtures/obsidian-vault/notes/with-wikilinks.md +4 -0
- package/lib/import/test-fixtures/tiny-vault/notes/2026-01-15-team-sync.md +9 -0
- package/lib/import/test-fixtures/tiny-vault/notes/empty.md +3 -0
- package/lib/import/test-fixtures/tiny-vault/notes/onboarding.md +5 -0
- package/lib/import/test-fixtures/tiny-vault/notes/pricing.md +5 -0
- package/lib/import/test-fixtures/tiny-vault/notes/random.md +4 -0
- package/lib/import/undo.test.cjs +199 -0
- package/lib/import/vault-scanner.cjs +105 -0
- package/lib/import/vault-scanner.test.cjs +67 -0
- package/lib/mcp/app-html/dashboard.html +316 -0
- package/lib/mcp/app-html/graph.html +428 -0
- package/lib/mcp/app-html/mindrian-platform.html +1841 -0
- package/lib/mcp/app-html/wiki.html +383 -0
- package/lib/mcp/app-views.cjs +322 -0
- package/lib/mcp/brain-router.cjs +418 -0
- package/lib/mcp/capability-registry.cjs +62 -0
- package/lib/mcp/larry-context.cjs +46 -0
- package/lib/mcp/larry-server-instructions.md +114 -0
- package/lib/mcp/pipeline-state.cjs +275 -0
- package/lib/mcp/prompts.cjs +302 -0
- package/lib/mcp/resources.cjs +227 -0
- package/lib/mcp/session-catchup.cjs +327 -0
- package/lib/mcp/surface-detect.cjs +75 -0
- package/lib/mcp/tool-router.cjs +1034 -0
- package/lib/memory/aaak-compress.cjs +403 -0
- package/lib/memory/aaak-compress.test.cjs +288 -0
- package/lib/memory/async-artifact-auto-commit.test.cjs +223 -0
- package/lib/memory/bearer-token.test.cjs +315 -0
- package/lib/memory/brain-cache-lru.test.cjs +259 -0
- package/lib/memory/brain-client-query-shape.test.cjs +160 -0
- package/lib/memory/brain-derivation-graceful-degradation.test.cjs +1019 -0
- package/lib/memory/brain-derivation-queue.test.cjs +539 -0
- package/lib/memory/brain-derivation.test.cjs +634 -0
- package/lib/memory/brain-derive-command.test.cjs +534 -0
- package/lib/memory/brain-md-invariants-validator.test.cjs +704 -0
- package/lib/memory/brain-md-schema.test.cjs +467 -0
- package/lib/memory/brain-md-staleness.test.cjs +525 -0
- package/lib/memory/brain-server-resolution.test.cjs +314 -0
- package/lib/memory/chain-recommender.test.cjs +233 -0
- package/lib/memory/chat-context.test.cjs +128 -0
- package/lib/memory/command-registry.test.cjs +220 -0
- package/lib/memory/cross-room-aggregator.test.cjs +909 -0
- package/lib/memory/dashboard-server.test.cjs +256 -0
- package/lib/memory/debouncer-drain-at-prompt.test.cjs +389 -0
- package/lib/memory/decision-capture.test.cjs +632 -0
- package/lib/memory/decision-capture.worker.cjs +70 -0
- package/lib/memory/explain-decision-command.test.cjs +521 -0
- package/lib/memory/explain-decision-footer.test.cjs +316 -0
- package/lib/memory/explored-materials-store.cjs +392 -0
- package/lib/memory/feynman-minto-guardian.test.cjs +736 -0
- package/lib/memory/feynman-minto-invariants.test.cjs +511 -0
- package/lib/memory/feynman-prompts-drift.test.cjs +144 -0
- package/lib/memory/feynman-prompts.cjs +151 -0
- package/lib/memory/feynman-prompts.test.cjs +96 -0
- package/lib/memory/folder-memory-quadruple.test.cjs +548 -0
- package/lib/memory/folder-memory.test.cjs +503 -0
- package/lib/memory/framework-chain-composer.test.cjs +515 -0
- package/lib/memory/frontmatter-schema-validator.test.cjs +290 -0
- package/lib/memory/heal-command.test.cjs +604 -0
- package/lib/memory/index-artifact-transaction.test.cjs +333 -0
- package/lib/memory/lazygraph-rs-discoveries-view.test.cjs +122 -0
- package/lib/memory/mcp-input-validation.test.cjs +240 -0
- package/lib/memory/mcp-server-brain-deps.test.cjs +270 -0
- package/lib/memory/mcp-stack-fallback.test.cjs +433 -0
- package/lib/memory/minto-debouncer.test.cjs +407 -0
- package/lib/memory/minto-debouncer.worker.cjs +46 -0
- package/lib/memory/minto-migration-v88.test.cjs +265 -0
- package/lib/memory/minto-schema-v88.test.cjs +390 -0
- package/lib/memory/mos-status-renderer.test.cjs +631 -0
- package/lib/memory/narrative-schema.cjs +376 -0
- package/lib/memory/narrative-schema.test.cjs +209 -0
- package/lib/memory/nav-dial.test.cjs +414 -0
- package/lib/memory/navigation-engine-core.test.cjs +722 -0
- package/lib/memory/navigation-invariants.test.cjs +483 -0
- package/lib/memory/offer-presenter.test.cjs +554 -0
- package/lib/memory/on-stop-snapshot.test.cjs +404 -0
- package/lib/memory/pending-tension-store.cjs +373 -0
- package/lib/memory/post-compact-reinjection.test.cjs +854 -0
- package/lib/memory/post-write-triple.test.cjs +317 -0
- package/lib/memory/pre-compact-snapshot.test.cjs +495 -0
- package/lib/memory/problem-type-router.test.cjs +656 -0
- package/lib/memory/query-efficiency-telemetry.test.cjs +370 -0
- package/lib/memory/recompile-room-references.test.cjs +392 -0
- package/lib/memory/recompile-room-references.worker.cjs +42 -0
- package/lib/memory/record-decision-dual-write.test.cjs +454 -0
- package/lib/memory/room-classifier-strict-mode.test.cjs +417 -0
- package/lib/memory/room-minto-hook.test.cjs +398 -0
- package/lib/memory/rs-discovery-engine.test.cjs +323 -0
- package/lib/memory/run-feynman-tests.cjs +1247 -0
- package/lib/memory/security-trifecta.test.cjs +312 -0
- package/lib/memory/session-start-brain-staleness.test.cjs +363 -0
- package/lib/memory/session-start-triple-injection.test.cjs +514 -0
- package/lib/memory/sessionstart-banner-formatter.cjs +318 -0
- package/lib/memory/sessionstart-minto-banner.test.cjs +373 -0
- package/lib/memory/skill-activation-router.test.cjs +419 -0
- package/lib/memory/stamp-artifact-write.test.cjs +304 -0
- package/lib/memory/statusline-active-room.test.cjs +315 -0
- package/lib/memory/statusline-minto-segment.test.cjs +292 -0
- package/lib/memory/sync-async-entry-points.test.cjs +204 -0
- package/lib/memory/test-bridge-writer-enhanced.cjs +452 -0
- package/lib/memory/test-rs-brain-substrate-shape.cjs +529 -0
- package/lib/memory/test-rs-brain-substrate.cjs +636 -0
- package/lib/memory/test-rs-breakthrough-scorer.cjs +375 -0
- package/lib/memory/test-rs-canon-violations.cjs +218 -0
- package/lib/memory/test-rs-chain-feeder-core.cjs +344 -0
- package/lib/memory/test-rs-chain-feeder-skill-spawn.cjs +297 -0
- package/lib/memory/test-rs-commercial-assessor.cjs +385 -0
- package/lib/memory/test-rs-differential-scorer.cjs +480 -0
- package/lib/memory/test-rs-discovery-engine.cjs +603 -0
- package/lib/memory/test-rs-domain-analyzer.cjs +492 -0
- package/lib/memory/test-rs-egress-primitives.cjs +420 -0
- package/lib/memory/test-rs-expert-mapper.cjs +547 -0
- package/lib/memory/test-rs-explain-command.cjs +443 -0
- package/lib/memory/test-rs-fetcher-academic.cjs +848 -0
- package/lib/memory/test-rs-fetcher-experts.cjs +496 -0
- package/lib/memory/test-rs-fetcher-industry.cjs +702 -0
- package/lib/memory/test-rs-fetcher-patents.cjs +674 -0
- package/lib/memory/test-rs-innovation-classifier.cjs +301 -0
- package/lib/memory/test-rs-mind-map.cjs +646 -0
- package/lib/memory/test-rs-neo4j-writer.cjs +518 -0
- package/lib/memory/test-rs-nl-to-query.cjs +449 -0
- package/lib/memory/test-rs-pinecone-bridge.cjs +277 -0
- package/lib/memory/test-rs-preprocessor.cjs +433 -0
- package/lib/memory/test-rs-query-matrix.cjs +391 -0
- package/lib/memory/test-rs-query-to-text.cjs +551 -0
- package/lib/memory/test-rs-sqlite-mirror.cjs +649 -0
- package/lib/memory/test-rs-thesis-generator.cjs +360 -0
- package/lib/memory/triple-context-formatter.cjs +473 -0
- package/lib/memory/triple-context-formatter.test.cjs +442 -0
- package/lib/memory/user-md-persona.test.cjs +565 -0
- package/lib/memory/userpromptsubmit-integration.test.cjs +690 -0
- package/lib/memory/validators/README.md +157 -0
- package/lib/memory/validators/brain-md-invariants.cjs +475 -0
- package/lib/memory/validators/brain-substrate-invariants.cjs +285 -0
- package/lib/memory/validators/external-academic-invariants.cjs +249 -0
- package/lib/memory/validators/external-industry-invariants.cjs +271 -0
- package/lib/memory/validators/external-patents-invariants.cjs +266 -0
- package/lib/memory/validators/minto-invariants.cjs +62 -0
- package/lib/memory/validators/navigation-invariants.cjs +340 -0
- package/lib/memory/validators/queue-health.cjs +95 -0
- package/lib/memory/validators/snapshot-integrity.cjs +129 -0
- package/lib/memory/validators/stale-lifecycle.cjs +116 -0
- package/lib/memory/vault-section-minto-generator-atomic.test.cjs +556 -0
- package/lib/memory/vault-section-minto-generator-atomic.worker.cjs +73 -0
- package/lib/memory/write-lock-atomic.test.cjs +137 -0
- package/lib/memory/write-lock-atomic.worker.cjs +55 -0
- package/lib/parity/check-parity.cjs +83 -0
- package/lib/presentation/presentation-server.cjs +101 -0
- package/lib/presentation/presentation-watcher.cjs +123 -0
- package/lib/quickview/hub-server.cjs +719 -0
- package/lib/quickview/server.cjs +533 -0
- package/lib/render/JTBD-PALETTES.md +145 -0
- package/lib/render/ROOM.md +59 -0
- package/lib/render/render-v2.cjs +486 -0
- package/lib/render/render-v2.test.cjs +267 -0
- package/lib/render/render.cjs +65 -0
- package/lib/state/ROOM.md +46 -0
- package/lib/state/state-md-parser.cjs +215 -0
- package/lib/statusline/ROOM.md +38 -0
- package/lib/statusline/banner-suppression.cjs +50 -0
- package/lib/statusline/surface-detect.cjs +85 -0
- package/lib/update-bootstrap.sh.template +145 -0
- package/lib/vault/frontmatter-schema.cjs +297 -0
- package/lib/vault/room-scanner.cjs +352 -0
- package/lib/vault/wikilink-builder.cjs +231 -0
- package/lib/vault/wikilink-builder.test.cjs +182 -0
- package/lib/wiki/graph-links.cjs +281 -0
- package/lib/wiki/page-renderer.cjs +229 -0
- package/lib/wiki/wiki-chat.cjs +81 -0
- package/lib/wiki/wiki-layout.cjs +1459 -0
- package/lib/wiki/wiki-search.cjs +142 -0
- package/lib/wiki/wiki-server.cjs +678 -0
- package/lib/wiki/wiki-watcher.cjs +105 -0
- package/lib/workflow/ROOM.md +47 -0
- package/lib/workflow/command-resolver.cjs +155 -0
- package/lib/workflow/command-resolver.test.cjs +235 -0
- package/package.json +44 -0
- package/pipelines/analogy/01-decompose.md +80 -0
- package/pipelines/analogy/02-abstract.md +87 -0
- package/pipelines/analogy/03-search.md +135 -0
- package/pipelines/analogy/04-transfer.md +101 -0
- package/pipelines/analogy/05-validate.md +106 -0
- package/pipelines/analogy/CHAIN.md +56 -0
- package/pipelines/discovery/01-explore-domains.md +44 -0
- package/pipelines/discovery/02-think-hats.md +50 -0
- package/pipelines/discovery/03-analyze-needs.md +54 -0
- package/pipelines/discovery/CHAIN.md +37 -0
- package/pipelines/thesis/01-structure-argument.md +45 -0
- package/pipelines/thesis/02-challenge-assumptions.md +48 -0
- package/pipelines/thesis/03-build-thesis.md +54 -0
- package/pipelines/thesis/CHAIN.md +37 -0
- package/references/brain/causal-directives.md +91 -0
- package/references/brain/causal-enrichment.cypher +165 -0
- package/references/brain/command-triggers-schema.md +226 -0
- package/references/brain/graph-architecture.md +317 -0
- package/references/brain/query-patterns.md +460 -0
- package/references/brain/room-hierarchy-schema.md +218 -0
- package/references/brain/schema.md +76 -0
- package/references/capability-radar/capabilities-index.md +241 -0
- package/references/capability-radar/changelog-cache.md +81 -0
- package/references/causal/causal-schema.md +103 -0
- package/references/design/email-template-standard.md +155 -0
- package/references/design/graph-visualization-standard.md +178 -0
- package/references/document-generation.md +179 -0
- package/references/hsi/HSI-TOOLS-REFERENCE.md +222 -0
- package/references/import-config.md +141 -0
- package/references/integrations/detection-patterns.md +101 -0
- package/references/meeting/artifact-template.md +377 -0
- package/references/meeting/cross-meeting-intelligence.md +216 -0
- package/references/meeting/cross-relationship-patterns.md +202 -0
- package/references/meeting/live-join-interface.md +244 -0
- package/references/meeting/section-mapping.md +192 -0
- package/references/meeting/segment-classification.md +258 -0
- package/references/meeting/speaker-profile-template.md +219 -0
- package/references/meeting/summary-template.md +348 -0
- package/references/meeting/transcript-patterns.md +226 -0
- package/references/methodology/analyze-needs.md +135 -0
- package/references/methodology/analyze-systems.md +121 -0
- package/references/methodology/analyze-timing.md +149 -0
- package/references/methodology/beautiful-question.md +109 -0
- package/references/methodology/build-knowledge.md +161 -0
- package/references/methodology/build-thesis.md +237 -0
- package/references/methodology/challenge-assumptions.md +127 -0
- package/references/methodology/diagnose.md +169 -0
- package/references/methodology/dominant-designs.md +212 -0
- package/references/methodology/explore-domains.md +147 -0
- package/references/methodology/explore-futures.md +163 -0
- package/references/methodology/explore-trends.md +129 -0
- package/references/methodology/find-bottlenecks.md +131 -0
- package/references/methodology/grade.md +211 -0
- package/references/methodology/index.md +97 -0
- package/references/methodology/leadership.md +200 -0
- package/references/methodology/lean-canvas.md +116 -0
- package/references/methodology/macro-trends.md +192 -0
- package/references/methodology/map-unknowns.md +137 -0
- package/references/methodology/mullins-7-domains.md +104 -0
- package/references/methodology/problem-types.md +65 -0
- package/references/methodology/root-cause.md +178 -0
- package/references/methodology/sapphire-encoding.md +355 -0
- package/references/methodology/scenario-plan.md +178 -0
- package/references/methodology/score-innovation.md +154 -0
- package/references/methodology/structure-argument.md +158 -0
- package/references/methodology/systems-thinking.md +159 -0
- package/references/methodology/think-hats.md +147 -0
- package/references/methodology/triz-matrix.json +751 -0
- package/references/methodology/triz-principles.md +501 -0
- package/references/methodology/user-needs.md +199 -0
- package/references/methodology/validate.md +163 -0
- package/references/methodology/value-proposition.md +244 -0
- package/references/opportunities/funding-lifecycle.md +103 -0
- package/references/opportunities/grant-api-patterns.md +99 -0
- package/references/opportunities/opportunity-template.md +84 -0
- package/references/personality/assessment-philosophy.md +72 -0
- package/references/personality/lexicon.md +100 -0
- package/references/personality/persona-chains.md +56 -0
- package/references/personality/pws-lexicon-full.md +499 -0
- package/references/personality/voice-dna.md +156 -0
- package/references/personas/hat-perspectives.md +76 -0
- package/references/personas/persona-template.md +63 -0
- package/references/pipeline/act-output-contract.md +88 -0
- package/references/pipeline/chains-index.md +39 -0
- package/references/pws-profile-generation.md +79 -0
- package/references/reasoning/reasoning-schema.md +143 -0
- package/references/reasoning/reasoning-template.md +68 -0
- package/references/reasoning/run-template.md +38 -0
- package/references/research/RESEARCH_14_CLAUDE_CODE_SOURCE_ARCHITECTURE.md +209 -0
- package/references/research/RESEARCH_15_V1.8_OPTIMIZATION_JTBD.md +375 -0
- package/references/research/RESEARCH_16_NATIVE_FIRST_PLUGIN_ARCHITECTURE.md +575 -0
- package/references/research/RESEARCH_17_MCP_UI_FRAMEWORKS.md +272 -0
- package/references/taxonomy/TAXONOMY.md +192 -0
- package/references/templates/MINTO.md +36 -0
- package/references/user-research/2026-04-05-leah-lawrence-session.md +202 -0
- package/references/vault-kit/README.md +35 -0
- package/references/vault-kit/app.json +12 -0
- package/references/vault-kit/appearance.json +12 -0
- package/references/vault-kit/graph.json +35 -0
- package/references/vault-kit/snippets/mindrian-destijl.css +297 -0
- package/references/vault-kit/templates/new-artifact.md +37 -0
- package/references/vault-kit/templates/new-meeting-note.md +35 -0
- package/references/vault-kit/templates/new-team-profile.md +29 -0
- package/references/vault-kit/templates/new-xref.md +35 -0
- package/references/visual/symbol-system.md +151 -0
- package/skills/MOSDeckEngine/SKILL.md +325 -0
- package/skills/brain-connector/SKILL.md +114 -0
- package/skills/context-engine/SKILL.md +147 -0
- package/skills/conversation-mode/SKILL.md +102 -0
- package/skills/larry-personality/SKILL.md +219 -0
- package/skills/larry-personality/framework-chains.md +92 -0
- package/skills/larry-personality/mode-engine.md +185 -0
- package/skills/mullins-scaffold/SKILL.md +61 -0
- package/skills/mullins-scaffold/scaffold.json +146 -0
- package/skills/pws-methodology/SKILL.md +49 -0
- package/skills/room-passive/SKILL.md +165 -0
- package/skills/room-proactive/SKILL.md +250 -0
- package/skills/ui-system/SKILL.md +277 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Phase 94-03 -- canonical Brain MCP server-name resolution fence.
|
|
6
|
+
*
|
|
7
|
+
* BSL 1.1. Copyright (c) Mindrian 2026.
|
|
8
|
+
* (Business Source License 1.1; SPDX BUSL-1.1; see LICENSE.)
|
|
9
|
+
*
|
|
10
|
+
* Why this fixture exists.
|
|
11
|
+
*
|
|
12
|
+
* The v1.11.0 QA harness (Lawrence's Dr. Miriam Kaplan persona, 2026-04-28)
|
|
13
|
+
* surfaced a P0 ship-blocker (FIX-2): the Brain knowledge graph is alive
|
|
14
|
+
* (7,353 LazyGraphConcept nodes, 119,706 CO_OCCURS edges, 20+ named PWS
|
|
15
|
+
* frameworks) but unreachable from /mos:* commands. Root cause: plugin
|
|
16
|
+
* command frontmatter declared THREE inconsistent server-name prefixes
|
|
17
|
+
*
|
|
18
|
+
* mcp__neo4j-brain__ (5 commands)
|
|
19
|
+
* mcp__mindrian-brain__ (6 commands -- already canonical)
|
|
20
|
+
* mcp__pinecone-brain__ (3 commands)
|
|
21
|
+
*
|
|
22
|
+
* while .mcp.json declared only `mindrian-os` (the plugin's own MCP
|
|
23
|
+
* router, not the Brain). Result: every Brain-touching slash command
|
|
24
|
+
* silently fell back to the graceful tier_0 path. Decision-traces
|
|
25
|
+
* confirmed routing_source: legacy and brain_md_tier_mode: tier_0
|
|
26
|
+
* on every session.
|
|
27
|
+
*
|
|
28
|
+
* Plan 94-03 selects Option A from the QA handoff (cheapest path):
|
|
29
|
+
* standardize the canonical server name across all 10 affected command
|
|
30
|
+
* frontmatter files on `mindrian-brain`, document the user-side
|
|
31
|
+
* .mcp.json snippet, and let users register their Neo4j MCP under the
|
|
32
|
+
* canonical name. No alias system. No auto-detect. Both deferred to
|
|
33
|
+
* v1.12 per Options B/C.
|
|
34
|
+
*
|
|
35
|
+
* Test map (5 cases, one-to-one with the PLAN <behavior> block).
|
|
36
|
+
*
|
|
37
|
+
* T1 Every commands/*.md allowed-tools list mcp__*brain* reference
|
|
38
|
+
* starts with the canonical prefix `mcp__mindrian-brain__`. We
|
|
39
|
+
* walk the 10 affected files, parse each YAML frontmatter, collect
|
|
40
|
+
* allowed-tools entries, and assert any tool matching /^mcp__\\S+brain\\S*__/
|
|
41
|
+
* starts with `mcp__mindrian-brain__`.
|
|
42
|
+
*
|
|
43
|
+
* T2 Zero `mcp__neo4j-brain__` references remain in any commands/*.md
|
|
44
|
+
* (frontmatter OR body prose). grep -lE pattern.
|
|
45
|
+
*
|
|
46
|
+
* T3 Zero `mcp__pinecone-brain__` references remain in any commands/*.md
|
|
47
|
+
* (frontmatter OR body prose). grep -lE pattern.
|
|
48
|
+
*
|
|
49
|
+
* T4 docs/install/BRAIN-SETUP.md exists and contains the literal
|
|
50
|
+
* substring `mindrian-brain` (canonical name) AND the literal
|
|
51
|
+
* substring `mcpServers` (user-side .mcp.json snippet shape).
|
|
52
|
+
*
|
|
53
|
+
* T5 Optional gate: scripts/frontmatter-schema-validator.cjs (Phase
|
|
54
|
+
* 88.1-07) returns exit 0 against each of the 10 modified command
|
|
55
|
+
* files. The validator is ADVISORY (PostToolUse) -- it always
|
|
56
|
+
* exits 0 regardless of violations -- so this case is structural:
|
|
57
|
+
* prove the validator is invokable against each file without
|
|
58
|
+
* crashing. Skip gracefully (PASS-and-warn) if the validator is
|
|
59
|
+
* absent in this checkout.
|
|
60
|
+
*
|
|
61
|
+
* Canon traceability.
|
|
62
|
+
*
|
|
63
|
+
* Part 7 (Reuse Before Build): Option A is the cheapest sweep -- no
|
|
64
|
+
* new alias system, no auto-detect, no new MCP server entry in the
|
|
65
|
+
* plugin's .mcp.json. We compose existing command frontmatter into
|
|
66
|
+
* a single canonical name.
|
|
67
|
+
*
|
|
68
|
+
* Part 8 (Graph Boundary): the Brain query chokepoint behavior is
|
|
69
|
+
* unchanged. This plan only standardizes the MCP server name passed
|
|
70
|
+
* in. Zero user-data egress added or removed.
|
|
71
|
+
*
|
|
72
|
+
* Registered in lib/memory/run-feynman-tests.cjs.
|
|
73
|
+
*/
|
|
74
|
+
|
|
75
|
+
const assert = require('node:assert/strict');
|
|
76
|
+
const fs = require('node:fs');
|
|
77
|
+
const path = require('node:path');
|
|
78
|
+
const { spawnSync } = require('node:child_process');
|
|
79
|
+
|
|
80
|
+
const REPO = path.resolve(__dirname, '..', '..');
|
|
81
|
+
const COMMANDS_DIR = path.join(REPO, 'commands');
|
|
82
|
+
const SETUP_DOC = path.join(REPO, 'docs', 'install', 'BRAIN-SETUP.md');
|
|
83
|
+
const VALIDATOR = path.join(REPO, 'scripts', 'frontmatter-schema-validator.cjs');
|
|
84
|
+
|
|
85
|
+
// The 10 commands declared by the plan as carrying or potentially carrying
|
|
86
|
+
// Brain MCP references. Order mirrors plan frontmatter files_modified.
|
|
87
|
+
const AFFECTED_COMMANDS = [
|
|
88
|
+
'compare-ventures',
|
|
89
|
+
'suggest-next',
|
|
90
|
+
'find-analogies',
|
|
91
|
+
'brain-derive',
|
|
92
|
+
'query',
|
|
93
|
+
'rs-experts',
|
|
94
|
+
'rs-explain',
|
|
95
|
+
'rs-fetch',
|
|
96
|
+
'rs-thesis',
|
|
97
|
+
'admin',
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
const CANONICAL_PREFIX = 'mcp__mindrian-brain__';
|
|
101
|
+
const FORBIDDEN_PREFIXES = ['mcp__neo4j-brain__', 'mcp__pinecone-brain__'];
|
|
102
|
+
|
|
103
|
+
// ---------- Fixture helpers ----------
|
|
104
|
+
|
|
105
|
+
function readFile(p) {
|
|
106
|
+
return fs.readFileSync(p, 'utf8');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Minimal single-key YAML frontmatter parser sufficient for allowed-tools.
|
|
111
|
+
* Returns the array of allowed-tools entries (raw strings, '- ' stripped),
|
|
112
|
+
* or [] if no allowed-tools section is found. Tolerant of:
|
|
113
|
+
* - inline form: `allowed-tools: Bash(node *)`
|
|
114
|
+
* - list form (multi-line dash-prefixed entries)
|
|
115
|
+
* Does NOT honor nested YAML structures or quoted values; the fixture
|
|
116
|
+
* does not need them for this contract.
|
|
117
|
+
*/
|
|
118
|
+
function parseAllowedTools(text) {
|
|
119
|
+
const m = text.match(/^---\n([\s\S]*?)\n---/);
|
|
120
|
+
if (!m) return [];
|
|
121
|
+
const fm = m[1];
|
|
122
|
+
const lines = fm.split('\n');
|
|
123
|
+
const tools = [];
|
|
124
|
+
let inList = false;
|
|
125
|
+
for (const raw of lines) {
|
|
126
|
+
const line = raw.replace(/\r$/, '');
|
|
127
|
+
if (/^allowed-tools:\s*$/.test(line)) {
|
|
128
|
+
inList = true;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (/^allowed-tools:\s*\S/.test(line)) {
|
|
132
|
+
// Inline form: allowed-tools: Bash(node *)
|
|
133
|
+
const inline = line.replace(/^allowed-tools:\s*/, '').trim();
|
|
134
|
+
if (inline) tools.push(inline);
|
|
135
|
+
inList = false;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (inList) {
|
|
139
|
+
if (/^\s+-\s+/.test(line)) {
|
|
140
|
+
const v = line.replace(/^\s+-\s+/, '').trim();
|
|
141
|
+
if (v) tools.push(v);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
// Any non-list-item line ends the allowed-tools block.
|
|
145
|
+
if (/^\S/.test(line)) inList = false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return tools;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ---------- Tests ----------
|
|
152
|
+
|
|
153
|
+
let passed = 0;
|
|
154
|
+
let failed = 0;
|
|
155
|
+
const fail = (name, msg) => {
|
|
156
|
+
failed++;
|
|
157
|
+
process.stderr.write('FAIL ' + name + ': ' + msg + '\n');
|
|
158
|
+
};
|
|
159
|
+
const pass = (name) => {
|
|
160
|
+
passed++;
|
|
161
|
+
process.stdout.write('PASS ' + name + '\n');
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
function t1_canonical_prefix_in_frontmatter() {
|
|
165
|
+
const name = 'T1 every brain reference in allowed-tools uses canonical prefix';
|
|
166
|
+
try {
|
|
167
|
+
const violations = [];
|
|
168
|
+
for (const cmd of AFFECTED_COMMANDS) {
|
|
169
|
+
const file = path.join(COMMANDS_DIR, cmd + '.md');
|
|
170
|
+
if (!fs.existsSync(file)) {
|
|
171
|
+
violations.push(cmd + ': file missing');
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const txt = readFile(file);
|
|
175
|
+
const tools = parseAllowedTools(txt);
|
|
176
|
+
for (const tool of tools) {
|
|
177
|
+
if (/^mcp__\S+brain\S*__/.test(tool)) {
|
|
178
|
+
if (!tool.startsWith(CANONICAL_PREFIX)) {
|
|
179
|
+
violations.push(cmd + ': non-canonical brain tool ' + tool);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
assert.equal(
|
|
185
|
+
violations.length,
|
|
186
|
+
0,
|
|
187
|
+
'Non-canonical brain tools found:\n ' + violations.join('\n ')
|
|
188
|
+
);
|
|
189
|
+
pass(name);
|
|
190
|
+
} catch (e) {
|
|
191
|
+
fail(name, e.message);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function t2_no_neo4j_brain_anywhere() {
|
|
196
|
+
const name = 'T2 zero mcp__neo4j-brain__ references in commands/*.md';
|
|
197
|
+
try {
|
|
198
|
+
const matches = [];
|
|
199
|
+
const files = fs.readdirSync(COMMANDS_DIR)
|
|
200
|
+
.filter((f) => f.endsWith('.md'))
|
|
201
|
+
.map((f) => path.join(COMMANDS_DIR, f));
|
|
202
|
+
for (const f of files) {
|
|
203
|
+
const txt = readFile(f);
|
|
204
|
+
if (txt.includes('mcp__neo4j-brain__') || txt.includes('mcp__neo4j-brain ')) {
|
|
205
|
+
matches.push(path.relative(REPO, f));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
assert.equal(
|
|
209
|
+
matches.length,
|
|
210
|
+
0,
|
|
211
|
+
'mcp__neo4j-brain__ references must be zero, found in:\n ' + matches.join('\n ')
|
|
212
|
+
);
|
|
213
|
+
pass(name);
|
|
214
|
+
} catch (e) {
|
|
215
|
+
fail(name, e.message);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function t3_no_pinecone_brain_anywhere() {
|
|
220
|
+
const name = 'T3 zero mcp__pinecone-brain__ references in commands/*.md';
|
|
221
|
+
try {
|
|
222
|
+
const matches = [];
|
|
223
|
+
const files = fs.readdirSync(COMMANDS_DIR)
|
|
224
|
+
.filter((f) => f.endsWith('.md'))
|
|
225
|
+
.map((f) => path.join(COMMANDS_DIR, f));
|
|
226
|
+
for (const f of files) {
|
|
227
|
+
const txt = readFile(f);
|
|
228
|
+
if (txt.includes('mcp__pinecone-brain__') || txt.includes('mcp__pinecone-brain ')) {
|
|
229
|
+
matches.push(path.relative(REPO, f));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
assert.equal(
|
|
233
|
+
matches.length,
|
|
234
|
+
0,
|
|
235
|
+
'mcp__pinecone-brain__ references must be zero, found in:\n ' + matches.join('\n ')
|
|
236
|
+
);
|
|
237
|
+
pass(name);
|
|
238
|
+
} catch (e) {
|
|
239
|
+
fail(name, e.message);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function t4_brain_setup_doc_exists() {
|
|
244
|
+
const name = 'T4 docs/install/BRAIN-SETUP.md exists with canonical name + mcpServers snippet';
|
|
245
|
+
try {
|
|
246
|
+
assert.ok(
|
|
247
|
+
fs.existsSync(SETUP_DOC),
|
|
248
|
+
'docs/install/BRAIN-SETUP.md does not exist'
|
|
249
|
+
);
|
|
250
|
+
const txt = readFile(SETUP_DOC);
|
|
251
|
+
assert.ok(
|
|
252
|
+
txt.includes('mindrian-brain'),
|
|
253
|
+
'BRAIN-SETUP.md missing canonical name `mindrian-brain`'
|
|
254
|
+
);
|
|
255
|
+
assert.ok(
|
|
256
|
+
txt.includes('mcpServers'),
|
|
257
|
+
'BRAIN-SETUP.md missing user-side .mcp.json snippet (`mcpServers`)'
|
|
258
|
+
);
|
|
259
|
+
pass(name);
|
|
260
|
+
} catch (e) {
|
|
261
|
+
fail(name, e.message);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function t5_frontmatter_validator_runnable() {
|
|
266
|
+
const name = 'T5 frontmatter-schema-validator runnable on each modified file';
|
|
267
|
+
try {
|
|
268
|
+
if (!fs.existsSync(VALIDATOR)) {
|
|
269
|
+
// Skip gracefully -- validator is an optional Phase 88.1-07 artifact.
|
|
270
|
+
process.stdout.write(
|
|
271
|
+
'SKIP ' + name + ' (validator absent at ' +
|
|
272
|
+
path.relative(REPO, VALIDATOR) + ')\n'
|
|
273
|
+
);
|
|
274
|
+
passed++;
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
// Phase 88.1-07 validator is a PostToolUse hook -- it reads JSON on stdin
|
|
278
|
+
// and emits a JSON response on stdout. Calling it without stdin will
|
|
279
|
+
// either no-op or error gracefully. We assert it does not crash with a
|
|
280
|
+
// non-zero exit on a benign invocation. This is a structural smoke
|
|
281
|
+
// (the validator is invokable in this checkout), not a content gate.
|
|
282
|
+
const res = spawnSync(process.execPath, [VALIDATOR, '--help'], {
|
|
283
|
+
input: '',
|
|
284
|
+
timeout: 5000,
|
|
285
|
+
});
|
|
286
|
+
// Accept exit 0 (help printed) OR exit codes from "missing stdin / not a
|
|
287
|
+
// hook context" branches. The validator is advisory and never blocks;
|
|
288
|
+
// the only failure mode that matters here is a hard crash (e.g. syntax
|
|
289
|
+
// error in the validator source).
|
|
290
|
+
assert.ok(
|
|
291
|
+
res.status === null || res.status === 0 || res.status === 1,
|
|
292
|
+
'frontmatter-schema-validator crashed with unexpected status ' + res.status +
|
|
293
|
+
' (signal=' + res.signal + ')\n' +
|
|
294
|
+
'stderr: ' + (res.stderr ? res.stderr.toString() : '')
|
|
295
|
+
);
|
|
296
|
+
pass(name);
|
|
297
|
+
} catch (e) {
|
|
298
|
+
fail(name, e.message);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// ---------- Run ----------
|
|
303
|
+
|
|
304
|
+
t1_canonical_prefix_in_frontmatter();
|
|
305
|
+
t2_no_neo4j_brain_anywhere();
|
|
306
|
+
t3_no_pinecone_brain_anywhere();
|
|
307
|
+
t4_brain_setup_doc_exists();
|
|
308
|
+
t5_frontmatter_validator_runnable();
|
|
309
|
+
|
|
310
|
+
const total = passed + failed;
|
|
311
|
+
process.stdout.write(
|
|
312
|
+
'\nbrain-server-resolution: ' + passed + '/' + total + ' passed\n'
|
|
313
|
+
);
|
|
314
|
+
process.exit(failed === 0 ? 0 : 1);
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Phase 122-03 -- chain-recommender tests (new suite; registered in the Feynman runner).
|
|
6
|
+
*
|
|
7
|
+
* Registered in lib/memory/run-feynman-tests.cjs TEST_FILES[] and in
|
|
8
|
+
* tests/run-all-122.sh CJS_SUITES (as ../lib/memory/chain-recommender.test.cjs).
|
|
9
|
+
* Run: node lib/memory/chain-recommender.test.cjs
|
|
10
|
+
* Exit 0 on pass; throws (node:assert) on any fail.
|
|
11
|
+
*
|
|
12
|
+
* Five assertion groups (per 122-03-PLAN.md Task 2 <behavior>):
|
|
13
|
+
* 1. recommendFrameworkChain({ problemType:'ill-defined' }) -> non-empty
|
|
14
|
+
* ordered array of framework-name strings (length 1..4); element 0 is the
|
|
15
|
+
* seed (here, the IDP family's first skill resolved through the registry).
|
|
16
|
+
* 2. recommendFrameworkChain({ currentFramework:'Beautiful Question Framework' })
|
|
17
|
+
* -> that framework first; with offline FEEDS_INTO edges supplied via
|
|
18
|
+
* roomState, its successors follow in order.
|
|
19
|
+
* 3. A seed framework with no outgoing FEEDS_INTO edge -> [seed] (degrades
|
|
20
|
+
* cleanly, not a crash).
|
|
21
|
+
* 4. Brain unavailable (monkey-patch brain-client.isAvailable() -> false) ->
|
|
22
|
+
* the recommender still returns a chain (offline FEEDS_INTO walk, or [seed]);
|
|
23
|
+
* never throws, never returns null.
|
|
24
|
+
* 5. Canon Part 8: the recommender source contains NO command literal; any
|
|
25
|
+
* Cypher-looking string it builds contains only $-bound params (framework
|
|
26
|
+
* names / enums) -- no string interpolation of a command, no user-content
|
|
27
|
+
* token. The recommender's return value is framework names only.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const assert = require('node:assert/strict');
|
|
31
|
+
const fs = require('node:fs');
|
|
32
|
+
const path = require('node:path');
|
|
33
|
+
|
|
34
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
35
|
+
const RECOMMENDER_PATH = path.join(REPO_ROOT, 'lib', 'brain', 'chain-recommender.cjs');
|
|
36
|
+
const REGISTRY_PATH = path.join(REPO_ROOT, 'data', 'command-registry.json');
|
|
37
|
+
|
|
38
|
+
const recommender = require('../brain/chain-recommender.cjs');
|
|
39
|
+
const composer = require('../core/framework-chain-composer.cjs');
|
|
40
|
+
const problemTypeRouter = require('../core/problem-type-router.cjs');
|
|
41
|
+
const brainClient = require('../core/brain-client.cjs');
|
|
42
|
+
|
|
43
|
+
const registry = JSON.parse(fs.readFileSync(REGISTRY_PATH, 'utf8'));
|
|
44
|
+
|
|
45
|
+
let passed = 0;
|
|
46
|
+
function test(name, fn) {
|
|
47
|
+
fn();
|
|
48
|
+
process.stdout.write(' ok ' + name + '\n');
|
|
49
|
+
passed += 1;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Derive what the IDP seed framework SHOULD be (so the test is retrofit-proof:
|
|
53
|
+
// it does not hardcode "Beautiful Question Framework", it re-derives it the
|
|
54
|
+
// way the recommender does -- router first skill -> registry first framework).
|
|
55
|
+
function slugToFramework(slug) {
|
|
56
|
+
for (const c of registry.commands) {
|
|
57
|
+
if (!c || typeof c.command !== 'string') continue;
|
|
58
|
+
const parts = c.command.split(':');
|
|
59
|
+
if (parts.length >= 2 && parts.slice(1).join(':') === slug) {
|
|
60
|
+
return (Array.isArray(c.frameworks) && c.frameworks.length > 0) ? c.frameworks[0] : null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
const idpSkills = problemTypeRouter.routeByProblemType('IDP', null).recommended_skills;
|
|
66
|
+
const expectedIdpSeed = slugToFramework(idpSkills[0]);
|
|
67
|
+
assert.ok(expectedIdpSeed, 'the IDP family first skill must resolve to a framework via the registry');
|
|
68
|
+
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// 1. seeded from problem-type, walked via FEEDS_INTO; seed is first
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
test('recommendFrameworkChain({problemType:"ill-defined"}) -> non-empty ordered framework-name array, seed first', function () {
|
|
73
|
+
const chain = recommender.recommendFrameworkChain({ problemType: 'ill-defined' });
|
|
74
|
+
assert.ok(Array.isArray(chain), 'returns an array');
|
|
75
|
+
assert.ok(chain.length >= 1 && chain.length <= 4, 'length 1..4, got ' + chain.length);
|
|
76
|
+
assert.ok(chain.every(function (x) { return typeof x === 'string' && x.length > 0; }), 'all elements are non-empty strings');
|
|
77
|
+
assert.strictEqual(chain[0], expectedIdpSeed, 'element 0 is the IDP seed framework');
|
|
78
|
+
// 'IDP' alias and the canonical token yield the same seed.
|
|
79
|
+
assert.strictEqual(recommender.recommendFrameworkChain({ problemType: 'IDP' })[0], expectedIdpSeed);
|
|
80
|
+
// The other problem-type families seed differently (UDP / WDP).
|
|
81
|
+
const udpSeed = slugToFramework(problemTypeRouter.routeByProblemType('UDP', null).recommended_skills[0]);
|
|
82
|
+
assert.strictEqual(recommender.recommendFrameworkChain({ problemType: 'undefined' })[0], udpSeed);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// 2. currentFramework first; offline FEEDS_INTO successors follow
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
test('recommendFrameworkChain({currentFramework:X}) -> X first; supplied FEEDS_INTO edges add successors in order', function () {
|
|
89
|
+
// No offline edges -> degrades to [X].
|
|
90
|
+
const bare = recommender.recommendFrameworkChain({ currentFramework: 'Beautiful Question Framework' });
|
|
91
|
+
assert.strictEqual(bare[0], 'Beautiful Question Framework');
|
|
92
|
+
|
|
93
|
+
// With a pre-parsed FEEDS_INTO edge array (the shape parseFrameworkChainSection
|
|
94
|
+
// returns) -> walk the chain via composer.proposeNextFramework.
|
|
95
|
+
const edges = [
|
|
96
|
+
{ from: 'Beautiful Question Framework', to: 'Domain Selection', confidence: 0.9, phase_indicator: null },
|
|
97
|
+
{ from: 'Domain Selection', to: 'Jobs to Be Done (JTBD)', confidence: 0.85, phase_indicator: null },
|
|
98
|
+
{ from: 'Jobs to Be Done (JTBD)', to: 'PWS Value Proposition', confidence: 0.8, phase_indicator: null },
|
|
99
|
+
{ from: 'PWS Value Proposition', to: 'Lean Canvas', confidence: 0.75, phase_indicator: null }, // would be 5th -> truncated
|
|
100
|
+
];
|
|
101
|
+
const walked = recommender.recommendFrameworkChain({
|
|
102
|
+
currentFramework: 'Beautiful Question Framework',
|
|
103
|
+
roomState: { feedsIntoEdges: edges },
|
|
104
|
+
});
|
|
105
|
+
assert.deepStrictEqual(walked, ['Beautiful Question Framework', 'Domain Selection', 'Jobs to Be Done (JTBD)', 'PWS Value Proposition']);
|
|
106
|
+
assert.ok(walked.length <= 4, 'chain is capped at 4');
|
|
107
|
+
|
|
108
|
+
// The same edges expressed as a BRAIN.md framework_chain_predictions section body.
|
|
109
|
+
const sectionBody =
|
|
110
|
+
'Beautiful Question Framework FEEDS_INTO Domain Selection (confidence: 0.9)\n' +
|
|
111
|
+
'Domain Selection FEEDS_INTO Jobs to Be Done (JTBD) (confidence: 0.85)';
|
|
112
|
+
const fromSection = recommender.recommendFrameworkChain({
|
|
113
|
+
currentFramework: 'Beautiful Question Framework',
|
|
114
|
+
roomState: { brainSection: { body: sectionBody } },
|
|
115
|
+
});
|
|
116
|
+
assert.deepStrictEqual(fromSection, ['Beautiful Question Framework', 'Domain Selection', 'Jobs to Be Done (JTBD)']);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
// 3. seed with no outgoing FEEDS_INTO edge -> [seed]
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
test('a seed with no outgoing FEEDS_INTO edge degrades to [seed] (no crash, never null)', function () {
|
|
123
|
+
const edges = [
|
|
124
|
+
{ from: 'Some Other Framework', to: 'Yet Another', confidence: 0.9, phase_indicator: null },
|
|
125
|
+
];
|
|
126
|
+
const chain = recommender.recommendFrameworkChain({
|
|
127
|
+
currentFramework: 'Beautiful Question Framework',
|
|
128
|
+
roomState: { feedsIntoEdges: edges },
|
|
129
|
+
});
|
|
130
|
+
assert.deepStrictEqual(chain, ['Beautiful Question Framework']);
|
|
131
|
+
|
|
132
|
+
// An edge below the composer noise floor (< 0.5) is not followed either.
|
|
133
|
+
const lowConf = [{ from: 'Beautiful Question Framework', to: 'Domain Selection', confidence: 0.3, phase_indicator: null }];
|
|
134
|
+
assert.deepStrictEqual(
|
|
135
|
+
recommender.recommendFrameworkChain({ currentFramework: 'Beautiful Question Framework', roomState: { feedsIntoEdges: lowConf } }),
|
|
136
|
+
['Beautiful Question Framework']
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
// Empty / no roomState -> [seed].
|
|
140
|
+
assert.deepStrictEqual(
|
|
141
|
+
recommender.recommendFrameworkChain({ currentFramework: 'Beautiful Question Framework', roomState: {} }),
|
|
142
|
+
['Beautiful Question Framework']
|
|
143
|
+
);
|
|
144
|
+
assert.deepStrictEqual(
|
|
145
|
+
recommender.recommendFrameworkChain({ currentFramework: 'Beautiful Question Framework' }),
|
|
146
|
+
['Beautiful Question Framework']
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
// No args at all -> the default seed, length 1.
|
|
150
|
+
const def = recommender.recommendFrameworkChain();
|
|
151
|
+
assert.ok(Array.isArray(def) && def.length >= 1 && typeof def[0] === 'string');
|
|
152
|
+
assert.strictEqual(def[0], recommender.DEFAULT_SEED);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
// 4. Brain unavailable -> still returns a chain; never throws, never null
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
test('Brain unavailable (isAvailable() -> false) still returns a chain; never throws, never null', function () {
|
|
159
|
+
const orig = brainClient.isAvailable;
|
|
160
|
+
try {
|
|
161
|
+
brainClient.isAvailable = function () { return false; };
|
|
162
|
+
const c1 = recommender.recommendFrameworkChain({ problemType: 'ill-defined' });
|
|
163
|
+
assert.ok(Array.isArray(c1) && c1.length >= 1 && c1.every(function (x) { return typeof x === 'string'; }));
|
|
164
|
+
assert.notStrictEqual(c1, null);
|
|
165
|
+
const edges = [{ from: 'Beautiful Question Framework', to: 'Domain Selection', confidence: 0.9, phase_indicator: null }];
|
|
166
|
+
const c2 = recommender.recommendFrameworkChain({ currentFramework: 'Beautiful Question Framework', roomState: { feedsIntoEdges: edges } });
|
|
167
|
+
assert.deepStrictEqual(c2, ['Beautiful Question Framework', 'Domain Selection']);
|
|
168
|
+
} finally {
|
|
169
|
+
brainClient.isAvailable = orig;
|
|
170
|
+
}
|
|
171
|
+
// And with Brain "available" (the real isAvailable, which is false on a CI box
|
|
172
|
+
// without a key -- so this also exercises the no-key path) it still works.
|
|
173
|
+
const c3 = recommender.recommendFrameworkChain({ currentFramework: 'Beautiful Question Framework' });
|
|
174
|
+
assert.deepStrictEqual(c3, ['Beautiful Question Framework']);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
// 5. Canon Part 8: no command literal; Cypher carries only $-bound params; FW names only
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
test('Canon Part 8: chain-recommender source has no command literal; any Cypher uses only $-bound params', function () {
|
|
181
|
+
const src = fs.readFileSync(RECOMMENDER_PATH, 'utf8');
|
|
182
|
+
// No /mos: command literal anywhere (the pre-commit / 122-02 grep guard too).
|
|
183
|
+
assert.ok(!/\/mos:/.test(src), 'no /mos: command literal in chain-recommender.cjs');
|
|
184
|
+
|
|
185
|
+
// Any Cypher-looking string (a line containing MATCH ... FEEDS_INTO ...) must
|
|
186
|
+
// bind its inputs as $-params, never interpolate them, and never carry a
|
|
187
|
+
// command token or a user-content token.
|
|
188
|
+
const lines = src.split(/\r?\n/);
|
|
189
|
+
let sawCypher = false;
|
|
190
|
+
for (const line of lines) {
|
|
191
|
+
if (!/FEEDS_INTO/.test(line) || !/MATCH/i.test(line)) continue;
|
|
192
|
+
sawCypher = true;
|
|
193
|
+
assert.ok(/\$seed/.test(line) || /\$[a-zA-Z_]/.test(line), 'FEEDS_INTO Cypher binds inputs via $-params: ' + line.trim());
|
|
194
|
+
assert.ok(!/\$\{/.test(line), 'no template-literal interpolation in the Cypher: ' + line.trim());
|
|
195
|
+
assert.ok(!/\/mos:/.test(line), 'no command literal in the Cypher: ' + line.trim());
|
|
196
|
+
}
|
|
197
|
+
// The exported Cypher constant, if present, must also satisfy this.
|
|
198
|
+
if (typeof recommender.FEEDS_INTO_CYPHER === 'string') {
|
|
199
|
+
assert.ok(/\$seed/.test(recommender.FEEDS_INTO_CYPHER), 'FEEDS_INTO_CYPHER binds $seed');
|
|
200
|
+
assert.ok(!/\$\{/.test(recommender.FEEDS_INTO_CYPHER), 'FEEDS_INTO_CYPHER has no template interpolation');
|
|
201
|
+
assert.ok(!/\/mos:/.test(recommender.FEEDS_INTO_CYPHER), 'FEEDS_INTO_CYPHER has no command literal');
|
|
202
|
+
}
|
|
203
|
+
assert.ok(sawCypher || typeof recommender.FEEDS_INTO_CYPHER === 'string', 'a FEEDS_INTO Cypher template exists (source line or exported constant)');
|
|
204
|
+
|
|
205
|
+
// The return value is framework names only -- never a command string.
|
|
206
|
+
const chain = recommender.recommendFrameworkChain({ problemType: 'ill-defined' });
|
|
207
|
+
assert.ok(chain.every(function (x) { return typeof x === 'string' && !/\/mos:/.test(x); }), 'returned chain is framework names, no command strings');
|
|
208
|
+
|
|
209
|
+
// It reuses framework-chain-composer + problem-type-router (not hand-rolled).
|
|
210
|
+
assert.ok(/framework-chain-composer/.test(src), 'requires framework-chain-composer');
|
|
211
|
+
assert.ok(/problem-type-router/.test(src), 'requires problem-type-router');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
// Bonus: composeWorkflow(recommendFrameworkChain(...)) -- the resolver attaches commands
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
test('composeWorkflow(recommendFrameworkChain(...)) attaches commands (the resolver does it, not the recommender)', function () {
|
|
218
|
+
const resolver = require('../workflow/command-resolver.cjs');
|
|
219
|
+
const edges = [
|
|
220
|
+
{ from: 'Beautiful Question Framework', to: 'Domain Selection', confidence: 0.9, phase_indicator: null },
|
|
221
|
+
{ from: 'Domain Selection', to: 'Jobs to Be Done (JTBD)', confidence: 0.85, phase_indicator: null },
|
|
222
|
+
];
|
|
223
|
+
const chain = recommender.recommendFrameworkChain({ currentFramework: 'Beautiful Question Framework', roomState: { feedsIntoEdges: edges } });
|
|
224
|
+
const wf = resolver.composeWorkflow(chain);
|
|
225
|
+
assert.strictEqual(wf.length, 3);
|
|
226
|
+
assert.strictEqual(wf[0].step, 1);
|
|
227
|
+
wf.forEach(function (s, i) { assert.strictEqual(s.step, i + 1); assert.ok('command' in s); assert.ok('optional' in s); });
|
|
228
|
+
// All three of these frameworks ARE mapped post-retrofit -> commands filled.
|
|
229
|
+
assert.ok(wf.every(function (s) { return s.command !== null; }));
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
process.stdout.write('\nchain-recommender.test.cjs: ' + passed + ' assertion groups PASSED\n');
|
|
233
|
+
process.exit(0);
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* chat-context.test.cjs -- Phase 87-09 context-injection fence.
|
|
6
|
+
*
|
|
7
|
+
* BSL 1.1. Copyright (c) Mindrian 2026.
|
|
8
|
+
* (Business Source License 1.1; SPDX BUSL-1.1; see LICENSE.)
|
|
9
|
+
*
|
|
10
|
+
* Proves:
|
|
11
|
+
* - classifyIntent covers all 5 buckets: contradicts, converges,
|
|
12
|
+
* stakeholders, gaps, briefing, plus the default-fallback path
|
|
13
|
+
* - buildContext returns a numeric tokenEstimate on EVERY code path
|
|
14
|
+
* - Every pattern stays under the 5K token budget on the cascade-e2e
|
|
15
|
+
* seed-room fixture
|
|
16
|
+
* - Pattern 3 (stakeholders) gracefully early-returns when the
|
|
17
|
+
* stakeholders table is empty -- it does NOT fabricate attribution
|
|
18
|
+
* (R6 / Phase 84-05 Pattern 3 contract)
|
|
19
|
+
*
|
|
20
|
+
* Exit codes:
|
|
21
|
+
* 0 -> PASS
|
|
22
|
+
* 1 -> FAIL (budget blown, or early-return missing, or class drift)
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const assert = require('node:assert/strict');
|
|
26
|
+
const fs = require('node:fs');
|
|
27
|
+
const os = require('node:os');
|
|
28
|
+
const path = require('node:path');
|
|
29
|
+
const { buildContext, classifyIntent } = require('../core/chat-context-builder.cjs');
|
|
30
|
+
const { runCascade } = require('../core/intelligence-cascade.cjs');
|
|
31
|
+
|
|
32
|
+
const FIXTURE = path.resolve(__dirname, '../../test/fixtures/cascade-e2e/seed-room');
|
|
33
|
+
const MAX_TOKENS = 5000;
|
|
34
|
+
|
|
35
|
+
async function run() {
|
|
36
|
+
/* Intent classification: all 5 named buckets + default fallback. */
|
|
37
|
+
assert.strictEqual(classifyIntent('what contradicts x?'), 'contradicts');
|
|
38
|
+
assert.strictEqual(classifyIntent('what is converging?'), 'converges');
|
|
39
|
+
assert.strictEqual(classifyIntent('who said what?'), 'stakeholders');
|
|
40
|
+
assert.strictEqual(classifyIntent('what gaps to fill?'), 'gaps');
|
|
41
|
+
assert.strictEqual(classifyIntent('briefing please'), 'briefing');
|
|
42
|
+
assert.strictEqual(classifyIntent('random unrelated text'), 'briefing');
|
|
43
|
+
|
|
44
|
+
/* Seed a hermetic room under /rooms/ so intelligence-cascade's
|
|
45
|
+
* isRoomFile() guard passes, then run the cascade against the three
|
|
46
|
+
* seeded artifacts so room.db + schema exist. */
|
|
47
|
+
const tmpParent = fs.mkdtempSync(path.join(os.tmpdir(), 'chatctx-'));
|
|
48
|
+
const roomsDir = path.join(tmpParent, 'rooms');
|
|
49
|
+
const roomDir = path.join(roomsDir, 'fixture');
|
|
50
|
+
fs.mkdirSync(roomsDir, { recursive: true });
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
fs.cpSync(FIXTURE, roomDir, { recursive: true });
|
|
54
|
+
await runCascade(roomDir, {
|
|
55
|
+
trigger: 'test',
|
|
56
|
+
filePath: path.join(roomDir, 'problem-definition', 'jtbd-underservice.md'),
|
|
57
|
+
section: 'problem-definition',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
/* Pattern 1: contradicts */
|
|
61
|
+
const c1 = await buildContext(roomDir, 'what contradicts market-analysis?');
|
|
62
|
+
assert.strictEqual(c1.intent, 'contradicts');
|
|
63
|
+
assert.strictEqual(c1.earlyReturn, false);
|
|
64
|
+
assert.ok(typeof c1.tokenEstimate === 'number', 'tokenEstimate must be a number');
|
|
65
|
+
assert.ok(c1.tokenEstimate < MAX_TOKENS,
|
|
66
|
+
'contradicts tokenEstimate must be <' + MAX_TOKENS + ' (got ' + c1.tokenEstimate + ')');
|
|
67
|
+
|
|
68
|
+
/* Pattern 2: converges */
|
|
69
|
+
const c2 = await buildContext(roomDir, 'what patterns are converging across sections?');
|
|
70
|
+
assert.strictEqual(c2.intent, 'converges');
|
|
71
|
+
assert.strictEqual(c2.earlyReturn, false);
|
|
72
|
+
assert.ok(typeof c2.tokenEstimate === 'number', 'tokenEstimate must be a number');
|
|
73
|
+
assert.ok(c2.tokenEstimate < MAX_TOKENS,
|
|
74
|
+
'converges tokenEstimate must be <' + MAX_TOKENS + ' (got ' + c2.tokenEstimate + ')');
|
|
75
|
+
|
|
76
|
+
/* Pattern 3: stakeholders -- empty table, earlyReturn=true must be asserted */
|
|
77
|
+
const c3 = await buildContext(roomDir, 'who said what?');
|
|
78
|
+
assert.strictEqual(c3.intent, 'stakeholders');
|
|
79
|
+
assert.strictEqual(c3.earlyReturn, true,
|
|
80
|
+
'empty stakeholders table must trigger earlyReturn=true (no fabrication)');
|
|
81
|
+
assert.ok(/stakeholder/i.test(c3.userPrompt),
|
|
82
|
+
'early-return message must mention stakeholders');
|
|
83
|
+
assert.ok(typeof c3.tokenEstimate === 'number',
|
|
84
|
+
'tokenEstimate must be present even on earlyReturn');
|
|
85
|
+
assert.ok(c3.tokenEstimate < MAX_TOKENS,
|
|
86
|
+
'stakeholders tokenEstimate must be <' + MAX_TOKENS + ' (got ' + c3.tokenEstimate + ')');
|
|
87
|
+
|
|
88
|
+
/* Pattern 4: gaps */
|
|
89
|
+
const c4 = await buildContext(roomDir, 'what gaps should I fill?');
|
|
90
|
+
assert.strictEqual(c4.intent, 'gaps');
|
|
91
|
+
assert.strictEqual(c4.earlyReturn, false);
|
|
92
|
+
assert.ok(typeof c4.tokenEstimate === 'number', 'tokenEstimate must be a number');
|
|
93
|
+
assert.ok(c4.tokenEstimate < MAX_TOKENS,
|
|
94
|
+
'gaps tokenEstimate must be <' + MAX_TOKENS + ' (got ' + c4.tokenEstimate + ')');
|
|
95
|
+
|
|
96
|
+
/* Pattern 5: briefing */
|
|
97
|
+
const c5 = await buildContext(roomDir, 'give me an intelligence briefing');
|
|
98
|
+
assert.strictEqual(c5.intent, 'briefing');
|
|
99
|
+
assert.strictEqual(c5.earlyReturn, false);
|
|
100
|
+
assert.ok(typeof c5.tokenEstimate === 'number', 'tokenEstimate must be a number');
|
|
101
|
+
assert.ok(c5.tokenEstimate < MAX_TOKENS,
|
|
102
|
+
'briefing tokenEstimate must be <' + MAX_TOKENS + ' (got ' + c5.tokenEstimate + ')');
|
|
103
|
+
|
|
104
|
+
/* Default-fallback path: unclassified input routes to briefing */
|
|
105
|
+
const c6 = await buildContext(roomDir, 'random unrelated question');
|
|
106
|
+
assert.strictEqual(c6.intent, 'briefing', 'default fallback must be briefing');
|
|
107
|
+
assert.ok(c6.tokenEstimate < MAX_TOKENS,
|
|
108
|
+
'fallback tokenEstimate must be <' + MAX_TOKENS + ' (got ' + c6.tokenEstimate + ')');
|
|
109
|
+
|
|
110
|
+
process.stdout.write(
|
|
111
|
+
'chat-context: all 5 intent patterns tested, all token budgets <' + MAX_TOKENS +
|
|
112
|
+
' (contradicts=' + c1.tokenEstimate +
|
|
113
|
+
' converges=' + c2.tokenEstimate +
|
|
114
|
+
' stakeholders=' + c3.tokenEstimate + '[earlyReturn]' +
|
|
115
|
+
' gaps=' + c4.tokenEstimate +
|
|
116
|
+
' briefing=' + c5.tokenEstimate +
|
|
117
|
+
' fallback=' + c6.tokenEstimate + ')\n'
|
|
118
|
+
);
|
|
119
|
+
process.exit(0);
|
|
120
|
+
} catch (e) {
|
|
121
|
+
process.stderr.write((e && e.stack) ? e.stack + '\n' : String(e) + '\n');
|
|
122
|
+
process.exit(1);
|
|
123
|
+
} finally {
|
|
124
|
+
try { fs.rmSync(tmpParent, { recursive: true, force: true }); } catch (_) { /* ignore */ }
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
run();
|