@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,344 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Copyright (c) 2026 Mindrian. BSL 1.1.
|
|
6
|
+
* Phase 89.4 Plan 02 -- rs-chain-feeder core fixture suite.
|
|
7
|
+
*
|
|
8
|
+
* 8 scenarios + A1 sweep covering the Wave-2 core module:
|
|
9
|
+
* lib/core/rs-chain-feeder.cjs
|
|
10
|
+
*
|
|
11
|
+
* Test 1 (Brain unreachable graceful degradation): mock brainClient
|
|
12
|
+
* with isAvailable() returning false; lookupUpstream returns
|
|
13
|
+
* {state: ready}; stderr captures warning containing chain-feeder
|
|
14
|
+
* + Brain unreachable.
|
|
15
|
+
*
|
|
16
|
+
* Test 2 (Pause-state with missing upstream): mock brainClient.query()
|
|
17
|
+
* to return records with fresh:false flags; opts.upstreamFreshness
|
|
18
|
+
* Map keyed false; lookupUpstream returns {state: pause,
|
|
19
|
+
* missing_upstream: [...], suggested_action: Run Methodology}.
|
|
20
|
+
*
|
|
21
|
+
* Test 3 (Ready-state with all upstream present): mock brainClient.query()
|
|
22
|
+
* to return records; default freshness map -> all upstream fresh;
|
|
23
|
+
* lookupUpstream returns {state: ready}.
|
|
24
|
+
*
|
|
25
|
+
* Test 4 (emitChainMetadata happy-path shape): structural_transfer + 8 +
|
|
26
|
+
* empty active_context produces recommended_verb in CANONICAL_VERBS,
|
|
27
|
+
* feeds_into === PWS VP, spawn_skill === null.
|
|
28
|
+
*
|
|
29
|
+
* Test 5 (emitChainMetadata feeds_into branch coverage):
|
|
30
|
+
* structural_transfer -> PWS VP, semantic_implementation ->
|
|
31
|
+
* Causal Loop, hybrid -> Navigation Engine.
|
|
32
|
+
*
|
|
33
|
+
* Test 6 (Canon Part 8 adversarial -- lookupUpstream input):
|
|
34
|
+
* problem_type containing 'meeting with' throws
|
|
35
|
+
* ExternalEgressViolation with err.meta.surface ===
|
|
36
|
+
* rs-chain-feeder-lookup-upstream.
|
|
37
|
+
*
|
|
38
|
+
* Test 7 (Canon Part 8 adversarial -- emitChainMetadata active_context):
|
|
39
|
+
* active_context.meta.contact === 'john@acme.com' throws
|
|
40
|
+
* ExternalEgressViolation with err.meta.surface ===
|
|
41
|
+
* rs-chain-feeder-emit-metadata.
|
|
42
|
+
*
|
|
43
|
+
* Test 8 (chokepoint exclusivity): brainClient.query is invoked at least
|
|
44
|
+
* once via injected spy; ZERO direct global.fetch() calls.
|
|
45
|
+
*
|
|
46
|
+
* Test 9 (recommendSkillSpawn STUB shape).
|
|
47
|
+
*
|
|
48
|
+
* A1 sweep: 5 happy-path emitChainMetadata calls produce zero
|
|
49
|
+
* forbidden-pattern hits in the JSON.stringify of returned blocks.
|
|
50
|
+
*
|
|
51
|
+
* Pure CJS, zero npm deps, node built-ins only (assert).
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
const assert = require('node:assert/strict');
|
|
55
|
+
|
|
56
|
+
// ---------- Suite bookkeeping ----------
|
|
57
|
+
|
|
58
|
+
let passed = 0;
|
|
59
|
+
let failed = 0;
|
|
60
|
+
const failures = [];
|
|
61
|
+
const tests = [];
|
|
62
|
+
|
|
63
|
+
// Register an async or sync test under a name. The runner awaits each
|
|
64
|
+
// in series so stderr captures and brainClient spies do not interleave.
|
|
65
|
+
function record(name, fn) {
|
|
66
|
+
tests.push({ name, fn });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function runAll() {
|
|
70
|
+
for (const t of tests) {
|
|
71
|
+
try {
|
|
72
|
+
const maybe = t.fn();
|
|
73
|
+
if (maybe && typeof maybe.then === 'function') await maybe;
|
|
74
|
+
passed++;
|
|
75
|
+
process.stdout.write('PASS ' + t.name + '\n');
|
|
76
|
+
} catch (err) {
|
|
77
|
+
failed++;
|
|
78
|
+
failures.push({ name: t.name, message: err && err.message ? err.message : String(err) });
|
|
79
|
+
process.stdout.write('FAIL ' + t.name + ': ' + (err && err.message ? err.message : String(err)) + '\n');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Stderr capture helper. Returns a thunk that, after fn settles, returns
|
|
85
|
+
// what was written to process.stderr. Restores original write at the end.
|
|
86
|
+
async function captureStderrAsync(fn) {
|
|
87
|
+
const chunks = [];
|
|
88
|
+
const origWrite = process.stderr.write.bind(process.stderr);
|
|
89
|
+
process.stderr.write = function (chunk) {
|
|
90
|
+
chunks.push(typeof chunk === 'string' ? chunk : chunk.toString());
|
|
91
|
+
return true;
|
|
92
|
+
};
|
|
93
|
+
try {
|
|
94
|
+
const value = await fn();
|
|
95
|
+
return { value, stderr: chunks.join('') };
|
|
96
|
+
} finally {
|
|
97
|
+
process.stderr.write = origWrite;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Build a Brain client stub with a configurable query() return + spy.
|
|
102
|
+
function makeBrainClientStub(opts) {
|
|
103
|
+
opts = opts || {};
|
|
104
|
+
const queryReturn = opts.queryReturn !== undefined ? opts.queryReturn : { records: [] };
|
|
105
|
+
const isAvailableValue = opts.isAvailable !== undefined ? opts.isAvailable : true;
|
|
106
|
+
const queryThrows = opts.queryThrows || null;
|
|
107
|
+
const callLog = [];
|
|
108
|
+
const stub = {
|
|
109
|
+
isAvailable: function () { return isAvailableValue; },
|
|
110
|
+
query: async function (cypher) {
|
|
111
|
+
callLog.push({ method: 'query', cypher });
|
|
112
|
+
if (queryThrows) throw queryThrows;
|
|
113
|
+
return queryReturn;
|
|
114
|
+
},
|
|
115
|
+
search: async function () { callLog.push({ method: 'search' }); return null; },
|
|
116
|
+
_callLog: callLog,
|
|
117
|
+
};
|
|
118
|
+
return stub;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ---------- Module under test ----------
|
|
122
|
+
|
|
123
|
+
const mod = require('../core/rs-chain-feeder.cjs');
|
|
124
|
+
const {
|
|
125
|
+
lookupUpstream,
|
|
126
|
+
emitChainMetadata,
|
|
127
|
+
recommendSkillSpawn,
|
|
128
|
+
CANONICAL_VERBS,
|
|
129
|
+
} = mod;
|
|
130
|
+
|
|
131
|
+
// ---------- Tests ----------
|
|
132
|
+
|
|
133
|
+
// Test 1: Brain unreachable graceful degradation
|
|
134
|
+
record('T1 Brain unreachable -> lookupUpstream returns {state: ready} + stderr warning', async function () {
|
|
135
|
+
const brainClient = makeBrainClientStub({ isAvailable: false });
|
|
136
|
+
const captured = await captureStderrAsync(function () {
|
|
137
|
+
return lookupUpstream('IDP', 'opportunity_identified', { brainClient: brainClient });
|
|
138
|
+
});
|
|
139
|
+
assert.deepEqual(captured.value, { state: 'ready' },
|
|
140
|
+
'expected {state: ready}, got ' + JSON.stringify(captured.value));
|
|
141
|
+
assert.ok(captured.stderr.indexOf('chain-feeder') !== -1,
|
|
142
|
+
'expected stderr to contain chain-feeder, got ' + JSON.stringify(captured.stderr));
|
|
143
|
+
assert.ok(captured.stderr.indexOf('Brain unreachable') !== -1,
|
|
144
|
+
'expected stderr to contain Brain unreachable, got ' + JSON.stringify(captured.stderr));
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Test 2: Pause-state with missing upstream
|
|
148
|
+
record('T2 Brain reachable + missing upstream -> {state: pause, missing_upstream, suggested_action}', async function () {
|
|
149
|
+
const brainClient = makeBrainClientStub({
|
|
150
|
+
queryReturn: {
|
|
151
|
+
records: [
|
|
152
|
+
{ name: 'systems-thinking' },
|
|
153
|
+
{ name: 'hsi_semantic_surprise_analysis' },
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
const freshness = new Map();
|
|
158
|
+
freshness.set('systems-thinking', false);
|
|
159
|
+
freshness.set('hsi_semantic_surprise_analysis', false);
|
|
160
|
+
const result = await lookupUpstream('IDP', 'opportunity_identified', {
|
|
161
|
+
brainClient: brainClient,
|
|
162
|
+
upstreamFreshness: freshness,
|
|
163
|
+
});
|
|
164
|
+
assert.equal(result.state, 'pause', 'expected state: pause, got ' + result.state);
|
|
165
|
+
assert.ok(Array.isArray(result.missing_upstream), 'missing_upstream must be array');
|
|
166
|
+
assert.equal(result.missing_upstream.length, 2, 'expected 2 missing_upstream entries');
|
|
167
|
+
assert.ok(result.missing_upstream.indexOf('systems-thinking') !== -1,
|
|
168
|
+
'expected systems-thinking in missing_upstream');
|
|
169
|
+
assert.ok(result.missing_upstream.indexOf('hsi_semantic_surprise_analysis') !== -1,
|
|
170
|
+
'expected hsi_semantic_surprise_analysis in missing_upstream');
|
|
171
|
+
assert.equal(result.suggested_action, 'Run Methodology',
|
|
172
|
+
'expected suggested_action: Run Methodology, got ' + result.suggested_action);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Test 3: Ready-state with all upstream present
|
|
176
|
+
record('T3 Brain reachable + all upstream fresh (no freshness Map) -> {state: ready}', async function () {
|
|
177
|
+
const brainClient = makeBrainClientStub({
|
|
178
|
+
queryReturn: {
|
|
179
|
+
records: [
|
|
180
|
+
{ name: 'systems-thinking' },
|
|
181
|
+
{ name: 'Cynefin' },
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
const result = await lookupUpstream('IDP', 'opportunity_identified', { brainClient: brainClient });
|
|
186
|
+
assert.deepEqual(result, { state: 'ready' },
|
|
187
|
+
'expected {state: ready}, got ' + JSON.stringify(result));
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Test 4: emitChainMetadata happy-path shape
|
|
191
|
+
// Plan 89.4-03 update: spawn_skill assertion broadened from "must be null"
|
|
192
|
+
// (STUB) to "must be string-or-null with /mos: prefix when non-null"
|
|
193
|
+
// (additive-shape invariant). The 89.4-02 stub returned null; the 89.4-03
|
|
194
|
+
// rule-table wires Rule 1 (structural_transfer + score 8 ->
|
|
195
|
+
// /mos:find-analogies). The shape contract (string|null) is preserved.
|
|
196
|
+
record('T4 emitChainMetadata structural_transfer + 8 -> shape with verb in CANONICAL_VERBS + PWS VP + spawn_skill string|null', function () {
|
|
197
|
+
const block = emitChainMetadata('structural_transfer', 8, {});
|
|
198
|
+
assert.ok(block && typeof block === 'object', 'expected object return');
|
|
199
|
+
assert.ok(typeof block.recommended_verb === 'string', 'recommended_verb must be string');
|
|
200
|
+
assert.ok(CANONICAL_VERBS.indexOf(block.recommended_verb) !== -1,
|
|
201
|
+
'recommended_verb must be in CANONICAL_VERBS, got ' + JSON.stringify(block.recommended_verb));
|
|
202
|
+
assert.equal(block.feeds_into, 'PWS VP',
|
|
203
|
+
'feeds_into must be PWS VP for structural_transfer, got ' + JSON.stringify(block.feeds_into));
|
|
204
|
+
assert.ok(block.spawn_skill === null || typeof block.spawn_skill === 'string',
|
|
205
|
+
'spawn_skill must be string|null, got ' + JSON.stringify(block.spawn_skill));
|
|
206
|
+
if (typeof block.spawn_skill === 'string') {
|
|
207
|
+
assert.ok(block.spawn_skill.indexOf('/mos:') === 0,
|
|
208
|
+
'when non-null, spawn_skill must start with /mos:, got ' + JSON.stringify(block.spawn_skill));
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Test 5: feeds_into branch coverage
|
|
213
|
+
record('T5 emitChainMetadata feeds_into branches: structural_transfer -> PWS VP, semantic_implementation -> Causal Loop, hybrid -> Navigation Engine', function () {
|
|
214
|
+
const a = emitChainMetadata('structural_transfer', 8, {});
|
|
215
|
+
assert.equal(a.feeds_into, 'PWS VP', 'structural_transfer -> PWS VP');
|
|
216
|
+
const b = emitChainMetadata('semantic_implementation', 8, {});
|
|
217
|
+
assert.equal(b.feeds_into, 'Causal Loop', 'semantic_implementation -> Causal Loop');
|
|
218
|
+
const c = emitChainMetadata('hybrid', 8, {});
|
|
219
|
+
assert.equal(c.feeds_into, 'Navigation Engine', 'hybrid -> Navigation Engine');
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Test 6: Canon Part 8 adversarial -- forbidden pattern in lookupUpstream input
|
|
223
|
+
record('T6 Canon Part 8 adversarial -- forbidden pattern in problem_type throws ExternalEgressViolation', async function () {
|
|
224
|
+
const brainClient = makeBrainClientStub({});
|
|
225
|
+
let caught = null;
|
|
226
|
+
try {
|
|
227
|
+
await lookupUpstream('IDP-meeting with john', 'opportunity_identified', { brainClient: brainClient });
|
|
228
|
+
} catch (e) {
|
|
229
|
+
caught = e;
|
|
230
|
+
}
|
|
231
|
+
assert.ok(caught !== null, 'expected throw');
|
|
232
|
+
assert.equal(caught.name, 'ExternalEgressViolation',
|
|
233
|
+
'expected ExternalEgressViolation, got ' + caught.name);
|
|
234
|
+
assert.equal(caught.meta.surface, 'rs-chain-feeder-lookup-upstream',
|
|
235
|
+
'expected meta.surface rs-chain-feeder-lookup-upstream, got ' + caught.meta.surface);
|
|
236
|
+
assert.equal(brainClient._callLog.length, 0,
|
|
237
|
+
'brain query must NOT be invoked when audit trips (got ' + brainClient._callLog.length + ' calls)');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// Test 7: Canon Part 8 adversarial -- forbidden pattern nested in active_context
|
|
241
|
+
record('T7 Canon Part 8 adversarial -- email nested in active_context.meta throws ExternalEgressViolation', function () {
|
|
242
|
+
let caught = null;
|
|
243
|
+
try {
|
|
244
|
+
emitChainMetadata('structural_transfer', 8, { meta: { contact: 'john@acme.com' } });
|
|
245
|
+
} catch (e) {
|
|
246
|
+
caught = e;
|
|
247
|
+
}
|
|
248
|
+
assert.ok(caught !== null, 'expected throw');
|
|
249
|
+
assert.equal(caught.name, 'ExternalEgressViolation',
|
|
250
|
+
'expected ExternalEgressViolation, got ' + caught.name);
|
|
251
|
+
assert.equal(caught.meta.surface, 'rs-chain-feeder-emit-metadata',
|
|
252
|
+
'expected meta.surface rs-chain-feeder-emit-metadata, got ' + caught.meta.surface);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Test 8: chokepoint exclusivity -- brainClient.query invoked, no direct fetch
|
|
256
|
+
record('T8 chokepoint exclusivity -- brainClient.query called >=1 time + zero direct global.fetch calls', async function () {
|
|
257
|
+
const fetchCalls = [];
|
|
258
|
+
const originalFetch = global.fetch;
|
|
259
|
+
global.fetch = function () {
|
|
260
|
+
fetchCalls.push(Array.prototype.slice.call(arguments));
|
|
261
|
+
return Promise.resolve(null);
|
|
262
|
+
};
|
|
263
|
+
try {
|
|
264
|
+
const brainClient = makeBrainClientStub({
|
|
265
|
+
queryReturn: { records: [{ name: 'systems-thinking' }] },
|
|
266
|
+
});
|
|
267
|
+
await lookupUpstream('IDP', 'opportunity_identified', { brainClient: brainClient });
|
|
268
|
+
assert.ok(brainClient._callLog.length >= 1,
|
|
269
|
+
'expected brainClient.query called >=1 time, got ' + brainClient._callLog.length);
|
|
270
|
+
assert.equal(brainClient._callLog[0].method, 'query',
|
|
271
|
+
'expected first method call to be query, got ' + brainClient._callLog[0].method);
|
|
272
|
+
assert.equal(fetchCalls.length, 0,
|
|
273
|
+
'expected ZERO direct fetch() calls; got ' + fetchCalls.length);
|
|
274
|
+
} finally {
|
|
275
|
+
if (originalFetch === undefined) delete global.fetch;
|
|
276
|
+
else global.fetch = originalFetch;
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// Test 9: recommendSkillSpawn shape invariant
|
|
281
|
+
// Plan 89.4-03 update: assertion broadened from STUB-specific "spawn_skill
|
|
282
|
+
// null + 89.4-02 reasoning string" to the SHAPE invariant -- result is an
|
|
283
|
+
// object with spawn_skill (string|null), numeric confidence in [0, 1], and
|
|
284
|
+
// non-empty reasoning string. The 89.4-02 stub returned null/0/stub-text;
|
|
285
|
+
// the 89.4-03 rule-table fires Rule 1 here -> /mos:find-analogies / 0.85.
|
|
286
|
+
// Shape is byte-identical across both implementations.
|
|
287
|
+
record('T9 recommendSkillSpawn shape invariant -- {spawn_skill: string|null, confidence: 0..1, reasoning: non-empty string}', function () {
|
|
288
|
+
const result = recommendSkillSpawn('structural_transfer', 8, {});
|
|
289
|
+
assert.ok(result && typeof result === 'object', 'expected object');
|
|
290
|
+
assert.ok(result.spawn_skill === null || typeof result.spawn_skill === 'string',
|
|
291
|
+
'spawn_skill must be string|null, got ' + typeof result.spawn_skill);
|
|
292
|
+
if (typeof result.spawn_skill === 'string') {
|
|
293
|
+
assert.ok(result.spawn_skill.indexOf('/mos:') === 0,
|
|
294
|
+
'when non-null, spawn_skill must start with /mos:, got ' + JSON.stringify(result.spawn_skill));
|
|
295
|
+
}
|
|
296
|
+
assert.equal(typeof result.confidence, 'number', 'confidence must be a number');
|
|
297
|
+
assert.ok(result.confidence >= 0 && result.confidence <= 1,
|
|
298
|
+
'confidence must be in [0, 1], got ' + result.confidence);
|
|
299
|
+
assert.ok(typeof result.reasoning === 'string' && result.reasoning.length > 0,
|
|
300
|
+
'reasoning must be non-empty string');
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// A1 sweep: 5 happy-path emitChainMetadata calls produce zero
|
|
304
|
+
// forbidden-pattern hits in JSON.stringify of returned blocks.
|
|
305
|
+
record('A1 sweep: 5 happy-path emitChainMetadata calls produce zero forbidden-pattern hits in returned blocks', function () {
|
|
306
|
+
const calls = [
|
|
307
|
+
['structural_transfer', 9, {}],
|
|
308
|
+
['semantic_implementation', 7, { industry_signals_count: 3 }],
|
|
309
|
+
['hybrid', 5, { cross_methodology_substrates: ['SAPPhIRE', 'TRIZ'] }],
|
|
310
|
+
['structural_transfer', 4, {}],
|
|
311
|
+
['semantic_implementation', 6, { resolved_experts_count: 8 }],
|
|
312
|
+
];
|
|
313
|
+
const { FORBIDDEN_PATTERNS } = require('../core/rs-egress-prompts.cjs');
|
|
314
|
+
for (const [rs_type, score, ctx] of calls) {
|
|
315
|
+
const block = emitChainMetadata(rs_type, score, ctx);
|
|
316
|
+
const json = JSON.stringify(block);
|
|
317
|
+
for (const re of FORBIDDEN_PATTERNS) {
|
|
318
|
+
assert.ok(!re.test(json),
|
|
319
|
+
'A1 sweep: emitChainMetadata output for (' + rs_type + ', ' + score + ') matched forbidden pattern '
|
|
320
|
+
+ re.source + '; output was ' + json);
|
|
321
|
+
}
|
|
322
|
+
assert.ok(CANONICAL_VERBS.indexOf(block.recommended_verb) !== -1,
|
|
323
|
+
'A1 sweep: recommended_verb ' + JSON.stringify(block.recommended_verb)
|
|
324
|
+
+ ' not in CANONICAL_VERBS for (' + rs_type + ', ' + score + ')');
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// ---------- Suite report ----------
|
|
329
|
+
|
|
330
|
+
(async function main() {
|
|
331
|
+
await runAll();
|
|
332
|
+
const total = passed + failed;
|
|
333
|
+
process.stdout.write('\n');
|
|
334
|
+
if (failed === 0) {
|
|
335
|
+
process.stdout.write('=== 89.4-02 rs-chain-feeder-core suite: ' + passed + '/' + total + ' passed ===\n');
|
|
336
|
+
process.exit(0);
|
|
337
|
+
} else {
|
|
338
|
+
process.stdout.write('=== 89.4-02 rs-chain-feeder-core suite: ' + passed + '/' + total + ' passed (' + failed + ' failed) ===\n');
|
|
339
|
+
for (const f of failures) {
|
|
340
|
+
process.stdout.write(' FAIL ' + f.name + ': ' + f.message + '\n');
|
|
341
|
+
}
|
|
342
|
+
process.exit(1);
|
|
343
|
+
}
|
|
344
|
+
})();
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Copyright (c) 2026 Mindrian. BSL 1.1.
|
|
6
|
+
* Phase 89.4 Plan 03 -- rs-chain-feeder skill-spawn rule table fixture suite.
|
|
7
|
+
*
|
|
8
|
+
* 11 scenarios covering the Wave-3 skill-spawn rule table per kickoff
|
|
9
|
+
* section 6.4 (LOCKED deterministic 5-rule table). Tests are Plan-89.4-03
|
|
10
|
+
* RED-then-GREEN: written against the swapped recommendSkillSpawn body and
|
|
11
|
+
* SKILL_SPAWN_RULES Object.freeze export. Plan 89.4-02 stub MUST fail
|
|
12
|
+
* Tests 1-5 + Test 7 + Test 9 + Test 11 because the stub returns
|
|
13
|
+
* spawn_skill: null for every input.
|
|
14
|
+
*
|
|
15
|
+
* Test 1 (Rule 1 positive): recommendSkillSpawn(structural_transfer, 8, {})
|
|
16
|
+
* returns {spawn_skill: /mos:find-analogies, confidence: 0.85,
|
|
17
|
+
* reasoning includes SAPPhIRE}.
|
|
18
|
+
*
|
|
19
|
+
* Test 2 (Rule 2 positive): recommendSkillSpawn(semantic_implementation,
|
|
20
|
+
* 7, {industry_signals_count: 3}) returns {spawn_skill:
|
|
21
|
+
* /mos:lean-canvas, confidence: 0.80, reasoning includes
|
|
22
|
+
* market signal}.
|
|
23
|
+
*
|
|
24
|
+
* Test 3 (Rule 3 positive): recommendSkillSpawn(hybrid, 6,
|
|
25
|
+
* {cross_methodology_substrates: [JTBD, Causal Loop]}) returns
|
|
26
|
+
* {spawn_skill: /mos:pipeline, confidence: 0.75, reasoning
|
|
27
|
+
* includes cross-methodology}.
|
|
28
|
+
*
|
|
29
|
+
* Test 4 (Rule 4 positive): recommendSkillSpawn(semantic_implementation,
|
|
30
|
+
* 5, {resolved_experts_count: 10, resolved_institutions_count: 3})
|
|
31
|
+
* returns {spawn_skill: /mos:persona, confidence: 0.70,
|
|
32
|
+
* reasoning includes expert network}.
|
|
33
|
+
*
|
|
34
|
+
* Test 5 (Rule 5 positive): recommendSkillSpawn(semantic_implementation,
|
|
35
|
+
* 2, {cynefin: complex}) returns {spawn_skill: /mos:think-hats,
|
|
36
|
+
* confidence: 0.65, reasoning includes perspective diversity}.
|
|
37
|
+
*
|
|
38
|
+
* Test 6 (no-match fallback): recommendSkillSpawn(semantic_implementation,
|
|
39
|
+
* 5, {}) returns {spawn_skill: null, confidence: 0, reasoning:
|
|
40
|
+
* no rule matched}.
|
|
41
|
+
*
|
|
42
|
+
* Test 7 (first-match-wins): recommendSkillSpawn(structural_transfer, 8,
|
|
43
|
+
* {industry_signals_count: 3, cross_methodology_substrates:
|
|
44
|
+
* [A, B]}) returns Rule 1 (find-analogies) NOT Rule 2 NOT Rule 3.
|
|
45
|
+
*
|
|
46
|
+
* Test 8 (Canon Part 8 adversarial): opts.contact carrying an email
|
|
47
|
+
* string throws ExternalEgressViolation with
|
|
48
|
+
* err.meta.surface === rs-chain-feeder-recommend-skill-spawn.
|
|
49
|
+
*
|
|
50
|
+
* Test 9 (integration smoke): emitChainMetadata(structural_transfer, 8,
|
|
51
|
+
* {}) returns object with spawn_skill === /mos:find-analogies,
|
|
52
|
+
* confidence: 0.85, recommended_verb: Bank Opportunity,
|
|
53
|
+
* feeds_into: PWS VP.
|
|
54
|
+
*
|
|
55
|
+
* Test 10 (regression): emitChainMetadata(structural_transfer, 3, {})
|
|
56
|
+
* returns spawn_skill === null (no rule matches; Rule 5 needs
|
|
57
|
+
* cynefin) + recommended_verb === Reformulate (score < 5).
|
|
58
|
+
*
|
|
59
|
+
* Test 11 (frozen rule table): SKILL_SPAWN_RULES is exported as an
|
|
60
|
+
* array; Array.isArray true; length 5; Object.isFrozen true;
|
|
61
|
+
* each rule entry is also frozen.
|
|
62
|
+
*
|
|
63
|
+
* Pure CJS, zero npm deps, node built-ins only (assert).
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
const assert = require('node:assert/strict');
|
|
67
|
+
|
|
68
|
+
// ---------- Suite bookkeeping ----------
|
|
69
|
+
|
|
70
|
+
let passed = 0;
|
|
71
|
+
let failed = 0;
|
|
72
|
+
const failures = [];
|
|
73
|
+
const tests = [];
|
|
74
|
+
|
|
75
|
+
function record(name, fn) {
|
|
76
|
+
tests.push({ name, fn });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function runAll() {
|
|
80
|
+
for (const t of tests) {
|
|
81
|
+
try {
|
|
82
|
+
const maybe = t.fn();
|
|
83
|
+
if (maybe && typeof maybe.then === 'function') await maybe;
|
|
84
|
+
passed++;
|
|
85
|
+
process.stdout.write('PASS ' + t.name + '\n');
|
|
86
|
+
} catch (err) {
|
|
87
|
+
failed++;
|
|
88
|
+
failures.push({ name: t.name, message: err && err.message ? err.message : String(err) });
|
|
89
|
+
process.stdout.write('FAIL ' + t.name + ': ' + (err && err.message ? err.message : String(err)) + '\n');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ---------- Module under test ----------
|
|
95
|
+
|
|
96
|
+
const mod = require('../core/rs-chain-feeder.cjs');
|
|
97
|
+
const {
|
|
98
|
+
recommendSkillSpawn,
|
|
99
|
+
emitChainMetadata,
|
|
100
|
+
SKILL_SPAWN_RULES,
|
|
101
|
+
} = mod;
|
|
102
|
+
|
|
103
|
+
// ---------- Tests ----------
|
|
104
|
+
|
|
105
|
+
// Test 1: Rule 1 positive -- structural_transfer + score 8
|
|
106
|
+
record('T1 Rule 1 -- structural_transfer + score 8 -> /mos:find-analogies (0.85)', function () {
|
|
107
|
+
const result = recommendSkillSpawn('structural_transfer', 8, {});
|
|
108
|
+
assert.ok(result && typeof result === 'object', 'expected object return');
|
|
109
|
+
assert.equal(result.spawn_skill, '/mos:find-analogies',
|
|
110
|
+
'expected /mos:find-analogies, got ' + JSON.stringify(result.spawn_skill));
|
|
111
|
+
assert.equal(result.confidence, 0.85,
|
|
112
|
+
'expected confidence 0.85, got ' + result.confidence);
|
|
113
|
+
assert.ok(typeof result.reasoning === 'string' && result.reasoning.length > 0,
|
|
114
|
+
'reasoning must be non-empty string');
|
|
115
|
+
assert.ok(result.reasoning.indexOf('SAPPhIRE') !== -1 || result.reasoning.indexOf('TRIZ') !== -1
|
|
116
|
+
|| result.reasoning.indexOf('structural') !== -1,
|
|
117
|
+
'reasoning should reference SAPPhIRE / TRIZ / structural; got ' + JSON.stringify(result.reasoning));
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Test 2: Rule 2 positive -- score 7 + industry signals
|
|
121
|
+
record('T2 Rule 2 -- score 7 + industry_signals_count 3 -> /mos:lean-canvas (0.80)', function () {
|
|
122
|
+
const result = recommendSkillSpawn('semantic_implementation', 7, { industry_signals_count: 3 });
|
|
123
|
+
assert.equal(result.spawn_skill, '/mos:lean-canvas',
|
|
124
|
+
'expected /mos:lean-canvas, got ' + JSON.stringify(result.spawn_skill));
|
|
125
|
+
assert.equal(result.confidence, 0.80,
|
|
126
|
+
'expected confidence 0.80, got ' + result.confidence);
|
|
127
|
+
assert.ok(result.reasoning.indexOf('market signal') !== -1
|
|
128
|
+
|| result.reasoning.indexOf('business model') !== -1,
|
|
129
|
+
'reasoning should reference market signal / business model; got ' + JSON.stringify(result.reasoning));
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Test 3: Rule 3 positive -- cross-methodology substrates
|
|
133
|
+
record('T3 Rule 3 -- cross_methodology_substrates length > 1 -> /mos:pipeline (0.75)', function () {
|
|
134
|
+
const result = recommendSkillSpawn('hybrid', 6, {
|
|
135
|
+
cross_methodology_substrates: ['JTBD', 'Causal Loop'],
|
|
136
|
+
});
|
|
137
|
+
assert.equal(result.spawn_skill, '/mos:pipeline',
|
|
138
|
+
'expected /mos:pipeline, got ' + JSON.stringify(result.spawn_skill));
|
|
139
|
+
assert.equal(result.confidence, 0.75,
|
|
140
|
+
'expected confidence 0.75, got ' + result.confidence);
|
|
141
|
+
assert.ok(result.reasoning.indexOf('cross-methodology') !== -1
|
|
142
|
+
|| result.reasoning.indexOf('chained pipeline') !== -1,
|
|
143
|
+
'reasoning should reference cross-methodology / chained pipeline; got '
|
|
144
|
+
+ JSON.stringify(result.reasoning));
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Test 4: Rule 4 positive -- expert network dense
|
|
148
|
+
record('T4 Rule 4 -- experts > 5 + institutions > 1 -> /mos:persona (0.70)', function () {
|
|
149
|
+
const result = recommendSkillSpawn('semantic_implementation', 5, {
|
|
150
|
+
resolved_experts_count: 10,
|
|
151
|
+
resolved_institutions_count: 3,
|
|
152
|
+
});
|
|
153
|
+
assert.equal(result.spawn_skill, '/mos:persona',
|
|
154
|
+
'expected /mos:persona, got ' + JSON.stringify(result.spawn_skill));
|
|
155
|
+
assert.equal(result.confidence, 0.70,
|
|
156
|
+
'expected confidence 0.70, got ' + result.confidence);
|
|
157
|
+
assert.ok(result.reasoning.indexOf('expert network') !== -1
|
|
158
|
+
|| result.reasoning.indexOf('team composition') !== -1,
|
|
159
|
+
'reasoning should reference expert network / team composition; got '
|
|
160
|
+
+ JSON.stringify(result.reasoning));
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Test 5: Rule 5 positive -- low breakthrough + Cynefin complex
|
|
164
|
+
record('T5 Rule 5 -- score 2 + cynefin complex -> /mos:think-hats (0.65)', function () {
|
|
165
|
+
const result = recommendSkillSpawn('semantic_implementation', 2, { cynefin: 'complex' });
|
|
166
|
+
assert.equal(result.spawn_skill, '/mos:think-hats',
|
|
167
|
+
'expected /mos:think-hats, got ' + JSON.stringify(result.spawn_skill));
|
|
168
|
+
assert.equal(result.confidence, 0.65,
|
|
169
|
+
'expected confidence 0.65, got ' + result.confidence);
|
|
170
|
+
assert.ok(result.reasoning.indexOf('perspective diversity') !== -1
|
|
171
|
+
|| result.reasoning.indexOf('Cynefin') !== -1,
|
|
172
|
+
'reasoning should reference perspective diversity / Cynefin; got '
|
|
173
|
+
+ JSON.stringify(result.reasoning));
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Test 6: no-match fallback
|
|
177
|
+
record('T6 no-match -- score 5 + empty opts -> {spawn_skill: null, confidence: 0, reasoning: no rule matched}', function () {
|
|
178
|
+
const result = recommendSkillSpawn('semantic_implementation', 5, {});
|
|
179
|
+
assert.equal(result.spawn_skill, null,
|
|
180
|
+
'expected null spawn_skill, got ' + JSON.stringify(result.spawn_skill));
|
|
181
|
+
assert.equal(result.confidence, 0,
|
|
182
|
+
'expected confidence 0, got ' + result.confidence);
|
|
183
|
+
assert.equal(result.reasoning, 'no rule matched',
|
|
184
|
+
'expected reasoning "no rule matched", got ' + JSON.stringify(result.reasoning));
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Test 7: first-match-wins -- Rule 1 wins over Rule 2 + Rule 3
|
|
188
|
+
record('T7 first-match-wins -- Rule 1 inputs + Rule 2 + Rule 3 inputs -> Rule 1 wins (find-analogies)', function () {
|
|
189
|
+
const result = recommendSkillSpawn('structural_transfer', 8, {
|
|
190
|
+
industry_signals_count: 3,
|
|
191
|
+
cross_methodology_substrates: ['A', 'B'],
|
|
192
|
+
});
|
|
193
|
+
assert.equal(result.spawn_skill, '/mos:find-analogies',
|
|
194
|
+
'first-match-wins violated: expected /mos:find-analogies (Rule 1), got '
|
|
195
|
+
+ JSON.stringify(result.spawn_skill));
|
|
196
|
+
assert.equal(result.confidence, 0.85,
|
|
197
|
+
'expected Rule 1 confidence 0.85, got ' + result.confidence);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Test 8: Canon Part 8 adversarial -- email in opts
|
|
201
|
+
record('T8 Canon Part 8 adversarial -- opts.contact carries email -> ExternalEgressViolation', function () {
|
|
202
|
+
let caught = null;
|
|
203
|
+
try {
|
|
204
|
+
recommendSkillSpawn('structural_transfer', 8, {
|
|
205
|
+
industry_signals_count: 3,
|
|
206
|
+
contact: 'john@acme.com',
|
|
207
|
+
});
|
|
208
|
+
} catch (e) {
|
|
209
|
+
caught = e;
|
|
210
|
+
}
|
|
211
|
+
assert.ok(caught !== null, 'expected throw');
|
|
212
|
+
assert.equal(caught.name, 'ExternalEgressViolation',
|
|
213
|
+
'expected ExternalEgressViolation, got ' + caught.name);
|
|
214
|
+
assert.equal(caught.meta.surface, 'rs-chain-feeder-recommend-skill-spawn',
|
|
215
|
+
'expected meta.surface rs-chain-feeder-recommend-skill-spawn, got '
|
|
216
|
+
+ caught.meta.surface);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Test 9: integration smoke -- rule propagates through emitChainMetadata
|
|
220
|
+
record('T9 integration smoke -- emitChainMetadata(structural_transfer, 8) carries Rule 1 fields', function () {
|
|
221
|
+
const block = emitChainMetadata('structural_transfer', 8, {});
|
|
222
|
+
assert.equal(block.spawn_skill, '/mos:find-analogies',
|
|
223
|
+
'expected spawn_skill /mos:find-analogies in chain metadata, got '
|
|
224
|
+
+ JSON.stringify(block.spawn_skill));
|
|
225
|
+
assert.equal(block.confidence, 0.85,
|
|
226
|
+
'expected confidence 0.85 in chain metadata, got ' + block.confidence);
|
|
227
|
+
assert.equal(block.recommended_verb, 'Bank Opportunity',
|
|
228
|
+
'expected recommended_verb Bank Opportunity, got '
|
|
229
|
+
+ JSON.stringify(block.recommended_verb));
|
|
230
|
+
assert.equal(block.feeds_into, 'PWS VP',
|
|
231
|
+
'expected feeds_into PWS VP, got ' + JSON.stringify(block.feeds_into));
|
|
232
|
+
assert.ok(typeof block.reasoning === 'string' && block.reasoning.length > 0,
|
|
233
|
+
'reasoning must be non-empty string in chain metadata');
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// Test 10: regression -- score 3 + structural_transfer -> no rule matches
|
|
237
|
+
record('T10 regression -- emitChainMetadata(structural_transfer, 3) -> spawn_skill null + Reformulate verb', function () {
|
|
238
|
+
const block = emitChainMetadata('structural_transfer', 3, {});
|
|
239
|
+
assert.equal(block.spawn_skill, null,
|
|
240
|
+
'expected null spawn_skill (no rule matches at score 3), got '
|
|
241
|
+
+ JSON.stringify(block.spawn_skill));
|
|
242
|
+
assert.equal(block.recommended_verb, 'Reformulate',
|
|
243
|
+
'expected recommended_verb Reformulate (score < 5), got '
|
|
244
|
+
+ JSON.stringify(block.recommended_verb));
|
|
245
|
+
assert.equal(block.feeds_into, 'PWS VP',
|
|
246
|
+
'expected feeds_into PWS VP (structural_transfer), got '
|
|
247
|
+
+ JSON.stringify(block.feeds_into));
|
|
248
|
+
assert.equal(block.confidence, 0,
|
|
249
|
+
'expected confidence 0 (no rule matched), got ' + block.confidence);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Test 11: SKILL_SPAWN_RULES is frozen + exported with correct shape
|
|
253
|
+
record('T11 SKILL_SPAWN_RULES exported + frozen + length 5 + each entry frozen', function () {
|
|
254
|
+
assert.ok(Array.isArray(SKILL_SPAWN_RULES),
|
|
255
|
+
'SKILL_SPAWN_RULES must be an array, got ' + typeof SKILL_SPAWN_RULES);
|
|
256
|
+
assert.equal(SKILL_SPAWN_RULES.length, 5,
|
|
257
|
+
'expected 5 rules per kickoff section 6.4, got ' + SKILL_SPAWN_RULES.length);
|
|
258
|
+
assert.equal(Object.isFrozen(SKILL_SPAWN_RULES), true,
|
|
259
|
+
'SKILL_SPAWN_RULES must be Object.freeze\'d');
|
|
260
|
+
for (let i = 0; i < SKILL_SPAWN_RULES.length; i++) {
|
|
261
|
+
const rule = SKILL_SPAWN_RULES[i];
|
|
262
|
+
assert.ok(rule && typeof rule === 'object',
|
|
263
|
+
'rule ' + i + ' must be object');
|
|
264
|
+
assert.equal(typeof rule.match, 'function',
|
|
265
|
+
'rule ' + i + ' must have match() function');
|
|
266
|
+
assert.equal(typeof rule.spawn, 'string',
|
|
267
|
+
'rule ' + i + ' must have spawn string');
|
|
268
|
+
assert.ok(rule.spawn.indexOf('/mos:') === 0,
|
|
269
|
+
'rule ' + i + ' spawn must start with /mos:, got ' + rule.spawn);
|
|
270
|
+
assert.equal(typeof rule.confidence, 'number',
|
|
271
|
+
'rule ' + i + ' must have numeric confidence');
|
|
272
|
+
assert.ok(rule.confidence > 0 && rule.confidence <= 1,
|
|
273
|
+
'rule ' + i + ' confidence must be (0, 1], got ' + rule.confidence);
|
|
274
|
+
assert.equal(typeof rule.reasoning, 'string',
|
|
275
|
+
'rule ' + i + ' must have reasoning string');
|
|
276
|
+
assert.equal(Object.isFrozen(rule), true,
|
|
277
|
+
'rule ' + i + ' entry must also be Object.freeze\'d');
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// ---------- Suite report ----------
|
|
282
|
+
|
|
283
|
+
(async function main() {
|
|
284
|
+
await runAll();
|
|
285
|
+
const total = passed + failed;
|
|
286
|
+
process.stdout.write('\n');
|
|
287
|
+
if (failed === 0) {
|
|
288
|
+
process.stdout.write('=== 89.4-03 rs-chain-feeder-skill-spawn suite: ' + passed + '/' + total + ' passed ===\n');
|
|
289
|
+
process.exit(0);
|
|
290
|
+
} else {
|
|
291
|
+
process.stdout.write('=== 89.4-03 rs-chain-feeder-skill-spawn suite: ' + passed + '/' + total + ' passed (' + failed + ' failed) ===\n');
|
|
292
|
+
for (const f of failures) {
|
|
293
|
+
process.stdout.write(' FAIL ' + f.name + ': ' + f.message + '\n');
|
|
294
|
+
}
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
})();
|