@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,547 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Copyright (c) 2026 Mindrian. BSL 1.1.
|
|
6
|
+
* Phase 89.3 Plan 04 -- expert mapper fixture suite.
|
|
7
|
+
*
|
|
8
|
+
* mapAuthorsToAura(experts, opts) is the Cypher MATCH + MERGE-and-resolve
|
|
9
|
+
* module that takes Plan 89.2-05 mapExperts output and resolves authors,
|
|
10
|
+
* institutions, and citation edges in the user's own Aura. Tier 1 (Aura
|
|
11
|
+
* connected) runs full Cypher MATCH-then-MERGE; Tier 0 (no Aura) returns
|
|
12
|
+
* a degraded subset from rs-sqlite-mirror's Author + Institution rows in
|
|
13
|
+
* room.db with a friendly note (Claude's Discretion path 3 from CONTEXT.md).
|
|
14
|
+
*
|
|
15
|
+
* Output shape:
|
|
16
|
+
* {resolved_authors, resolved_institutions, citation_edges, missed_count,
|
|
17
|
+
* resolution_quality: 'full'|'degraded', tier: 'aura'|'sqlite', note?}
|
|
18
|
+
*
|
|
19
|
+
* 10 scenarios + A1 sweep:
|
|
20
|
+
* 1 Happy path Tier 1 -- mock Aura returns 5 matched authors
|
|
21
|
+
* 2 Tier 1 with unmatched authors -- MERGEs new nodes (3 MATCH + 2 MERGE = 5 resolved)
|
|
22
|
+
* 3 Tier 0 graceful degradation -- room.db has 3 of 5 authors; 2 missed; degraded note returned
|
|
23
|
+
* 4 Tier dispatch error -- no opts; throws TypeError
|
|
24
|
+
* 5 missed_count accuracy in Tier 1 -- 0 MATCH + 2 MERGE failures = 3 resolved + 2 missed
|
|
25
|
+
* 6 citation_edges populated -- mock returns 2 CO_AUTHORED records
|
|
26
|
+
* 7 Empty experts array returns empty result (early return; no driver/openGraph call)
|
|
27
|
+
* 8 Aura unreachable throws AuraUnreachableError (re-thrown)
|
|
28
|
+
* 9 CANON PART 8 adversarial -- forbidden in expert.name -- pre-Cypher Layer 1 catches
|
|
29
|
+
* 10 CANON PART 8 adversarial -- forbidden in expert.institution -- Layer 1 OR Layer 2 catches
|
|
30
|
+
*
|
|
31
|
+
* End-of-suite sweep:
|
|
32
|
+
* A1: re-scan every captured result object (JSON.stringify) against
|
|
33
|
+
* FORBIDDEN_PATTERNS as cross-scenario Canon Part 8 guarantee. For
|
|
34
|
+
* throw-scenarios, no result captured (audit fired pre-write).
|
|
35
|
+
*
|
|
36
|
+
* Pure CJS, zero npm deps, node built-ins only (assert, fs, path, os, crypto).
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
const assert = require('node:assert/strict');
|
|
40
|
+
const fs = require('node:fs');
|
|
41
|
+
const path = require('node:path');
|
|
42
|
+
const os = require('node:os');
|
|
43
|
+
const crypto = require('node:crypto');
|
|
44
|
+
|
|
45
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
46
|
+
|
|
47
|
+
// Source of truth for parity gate.
|
|
48
|
+
const crossRoomAggregator = require('../core/cross-room-aggregator.cjs');
|
|
49
|
+
const FORBIDDEN_PATTERNS = crossRoomAggregator.FORBIDDEN_PATTERNS;
|
|
50
|
+
|
|
51
|
+
// Lazy-required for tmp room seeding (Test 3 + 7).
|
|
52
|
+
const lazygraph = require('../core/lazygraph-ops.cjs');
|
|
53
|
+
|
|
54
|
+
// Track every tmp room dir for cleanup.
|
|
55
|
+
const tmpRooms = [];
|
|
56
|
+
|
|
57
|
+
// Every result object captured for end-of-suite A1 sweep.
|
|
58
|
+
const capturedResults = [];
|
|
59
|
+
|
|
60
|
+
let passed = 0;
|
|
61
|
+
let failed = 0;
|
|
62
|
+
const failures = [];
|
|
63
|
+
|
|
64
|
+
function resetRequireCache() {
|
|
65
|
+
const targets = [
|
|
66
|
+
'../core/rs-egress-violations.cjs',
|
|
67
|
+
'../core/rs-egress-prompts.cjs',
|
|
68
|
+
'../core/cross-room-aggregator.cjs',
|
|
69
|
+
'../core/rs-neo4j-writer.cjs',
|
|
70
|
+
'../core/rs-expert-mapper.cjs',
|
|
71
|
+
];
|
|
72
|
+
for (const t of targets) {
|
|
73
|
+
try {
|
|
74
|
+
const p = require.resolve(t);
|
|
75
|
+
delete require.cache[p];
|
|
76
|
+
} catch (_e) { /* best effort */ }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function runScenario(name, fn) {
|
|
81
|
+
const start = Date.now();
|
|
82
|
+
try {
|
|
83
|
+
await fn();
|
|
84
|
+
passed += 1;
|
|
85
|
+
process.stdout.write(' ok ' + name + ' (' + (Date.now() - start) + 'ms)\n');
|
|
86
|
+
} catch (err) {
|
|
87
|
+
failed += 1;
|
|
88
|
+
failures.push({ name: name, err: err });
|
|
89
|
+
process.stderr.write(' FAIL ' + name + '\n');
|
|
90
|
+
process.stderr.write(' ' + (err && err.stack ? err.stack : err) + '\n');
|
|
91
|
+
} finally {
|
|
92
|
+
resetRequireCache();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function captureResult(name, result) {
|
|
97
|
+
capturedResults.push({ scenario: name, result: result });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ---------- Tmp room helper ----------
|
|
101
|
+
|
|
102
|
+
function makeTmpRoom() {
|
|
103
|
+
const slug = crypto.randomUUID();
|
|
104
|
+
const roomDir = path.join(os.tmpdir(), 'mos-89-3-04-' + slug);
|
|
105
|
+
fs.mkdirSync(roomDir, { recursive: true });
|
|
106
|
+
tmpRooms.push(roomDir);
|
|
107
|
+
return {
|
|
108
|
+
roomDir: roomDir,
|
|
109
|
+
cleanup: function () {
|
|
110
|
+
try {
|
|
111
|
+
fs.rmSync(roomDir, { recursive: true, force: true });
|
|
112
|
+
} catch (_e) { /* best effort */ }
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Seed the tmp room.db with N Author + Institution rows + AFFILIATED_WITH
|
|
118
|
+
// edges. Mirrors the schema rs-sqlite-mirror writes; the mapper Tier 0
|
|
119
|
+
// path SELECTs from this.
|
|
120
|
+
async function seedRoomDb(roomDir, authors) {
|
|
121
|
+
const handle = await lazygraph.openGraph(roomDir);
|
|
122
|
+
try {
|
|
123
|
+
const insertNode = handle.conn.prepare('INSERT OR REPLACE INTO nodes (id, type, properties) VALUES (?, ?, ?)');
|
|
124
|
+
const insertEdge = handle.conn.prepare('INSERT OR REPLACE INTO edges (source, target, type, properties) VALUES (?, ?, ?, ?)');
|
|
125
|
+
for (const a of authors) {
|
|
126
|
+
const authorKey = a.name + '|' + (a.orcid || '__no_orcid__');
|
|
127
|
+
insertNode.run(authorKey, 'Author', JSON.stringify({
|
|
128
|
+
name: a.name,
|
|
129
|
+
orcid: a.orcid || '',
|
|
130
|
+
h_index_estimate: typeof a.h_index_estimate === 'number' ? a.h_index_estimate : 0,
|
|
131
|
+
}));
|
|
132
|
+
if (a.institution) {
|
|
133
|
+
insertNode.run(a.institution, 'Institution', JSON.stringify({ name: a.institution }));
|
|
134
|
+
insertEdge.run(authorKey, a.institution, 'AFFILIATED_WITH', JSON.stringify({}));
|
|
135
|
+
}
|
|
136
|
+
// Seed a Paper + AUTHORED_BY edge so citation_edges has something to scan.
|
|
137
|
+
if (a.paper_id) {
|
|
138
|
+
insertNode.run(a.paper_id, 'Paper', JSON.stringify({ title: 'Seed Paper', source: 'openalex' }));
|
|
139
|
+
insertEdge.run(a.paper_id, authorKey, 'AUTHORED_BY', JSON.stringify({}));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
} finally {
|
|
143
|
+
await lazygraph.closeGraph(handle.db);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ---------- Mock driver factory ----------
|
|
148
|
+
//
|
|
149
|
+
// Mirrors the neo4j-driver session/run/close API. Each session.run pushes
|
|
150
|
+
// {cypher, params} onto driver.opLog. Returns a Promise that resolves
|
|
151
|
+
// based on opts.matchResults / opts.mergeResults / opts.coAuthorResults
|
|
152
|
+
// arrays (popped per call). opts.simulateConnectionError throws on first
|
|
153
|
+
// session.run. opts.simulateMergeFailureFor: array of author keys whose
|
|
154
|
+
// MERGE call should throw a Neo4j ConstraintError-like error (Test 5).
|
|
155
|
+
|
|
156
|
+
function makeMockDriver(opts) {
|
|
157
|
+
opts = opts || {};
|
|
158
|
+
const matchResults = (opts.matchResults || []).slice();
|
|
159
|
+
const mergeResults = (opts.mergeResults || []).slice();
|
|
160
|
+
const coAuthorResults = opts.coAuthorResults || [];
|
|
161
|
+
const driver = {
|
|
162
|
+
opLog: [],
|
|
163
|
+
runCount: 0,
|
|
164
|
+
sessionsClosed: 0,
|
|
165
|
+
session: function () {
|
|
166
|
+
const drv = this;
|
|
167
|
+
return {
|
|
168
|
+
run: async function (cypher, params) {
|
|
169
|
+
drv.opLog.push({ cypher: cypher, params: params });
|
|
170
|
+
drv.runCount += 1;
|
|
171
|
+
if (opts.simulateConnectionError) {
|
|
172
|
+
throw new Error('connect ECONNREFUSED 127.0.0.1:7687');
|
|
173
|
+
}
|
|
174
|
+
// Route based on cypher content.
|
|
175
|
+
if (/CO_AUTHORED|MATCH \(a1:Author\)/.test(cypher)) {
|
|
176
|
+
return { records: coAuthorResults.map(makeRecord), summary: { counters: {} } };
|
|
177
|
+
}
|
|
178
|
+
if (/^MERGE|^\s*MERGE/.test(cypher)) {
|
|
179
|
+
// MERGE branch.
|
|
180
|
+
if (opts.simulateMergeFailureFor && params && params.key &&
|
|
181
|
+
opts.simulateMergeFailureFor.indexOf(params.key) !== -1) {
|
|
182
|
+
throw new Error('Neo4j.ClientError.Schema.ConstraintValidationFailed: MERGE failed');
|
|
183
|
+
}
|
|
184
|
+
const next = mergeResults.shift();
|
|
185
|
+
return { records: (next || []).map(makeRecord), summary: { counters: { nodesCreated: 1 } } };
|
|
186
|
+
}
|
|
187
|
+
// MATCH branch (default).
|
|
188
|
+
const next = matchResults.shift();
|
|
189
|
+
return { records: (next || []).map(makeRecord), summary: { counters: {} } };
|
|
190
|
+
},
|
|
191
|
+
close: async function () {
|
|
192
|
+
drv.sessionsClosed += 1;
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
return driver;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// neo4j-driver Record shim: get(key) returns the field value.
|
|
201
|
+
function makeRecord(obj) {
|
|
202
|
+
return {
|
|
203
|
+
get: function (key) {
|
|
204
|
+
return obj[key];
|
|
205
|
+
},
|
|
206
|
+
keys: Object.keys(obj),
|
|
207
|
+
_fields: Object.values(obj),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ---------- Fixture builders ----------
|
|
212
|
+
|
|
213
|
+
function makeExpert(overrides) {
|
|
214
|
+
const base = {
|
|
215
|
+
name: 'Smith, Jane',
|
|
216
|
+
orcid: '0000-0001-2345-6789',
|
|
217
|
+
institution: 'Stanford University',
|
|
218
|
+
paper_count: 1,
|
|
219
|
+
h_index_estimate: 1,
|
|
220
|
+
public_email_or_null: null,
|
|
221
|
+
};
|
|
222
|
+
if (overrides) {
|
|
223
|
+
for (const k of Object.keys(overrides)) {
|
|
224
|
+
base[k] = overrides[k];
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return base;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function makeFiveExperts() {
|
|
231
|
+
return [
|
|
232
|
+
makeExpert({ name: 'Smith, Jane', orcid: '0000-0001-1111-1111', institution: 'Stanford', paper_count: 4, h_index_estimate: 3 }),
|
|
233
|
+
makeExpert({ name: 'Alpha, Adam', orcid: '0000-0002-2222-2222', institution: 'MIT', paper_count: 3, h_index_estimate: 2 }),
|
|
234
|
+
makeExpert({ name: 'Beta, Bob', orcid: '0000-0003-3333-3333', institution: 'Harvard', paper_count: 2, h_index_estimate: 2 }),
|
|
235
|
+
makeExpert({ name: 'Gamma, Greg', orcid: '0000-0004-4444-4444', institution: 'Berkeley', paper_count: 1, h_index_estimate: 1 }),
|
|
236
|
+
makeExpert({ name: 'Delta, Dora', orcid: '0000-0005-5555-5555', institution: 'CalTech', paper_count: 1, h_index_estimate: 1 }),
|
|
237
|
+
];
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ---------- Scenarios ----------
|
|
241
|
+
|
|
242
|
+
async function scenario1HappyPathTier1() {
|
|
243
|
+
const m = require('../core/rs-expert-mapper.cjs');
|
|
244
|
+
const experts = makeFiveExperts();
|
|
245
|
+
// Mock returns 1-record MATCH for each expert (5 calls -> 5 single-record results)
|
|
246
|
+
const driver = makeMockDriver({
|
|
247
|
+
matchResults: experts.map(function (e, i) {
|
|
248
|
+
return [{ name: e.name, orcid: e.orcid, aura_node_id: 100 + i }];
|
|
249
|
+
}),
|
|
250
|
+
coAuthorResults: [],
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
const result = await m.mapAuthorsToAura(experts, { tier: 'aura', driver: driver });
|
|
254
|
+
captureResult('1-happy-tier1', result);
|
|
255
|
+
|
|
256
|
+
assert.equal(result.resolved_authors.length, 5, 'expected 5 resolved, got ' + result.resolved_authors.length);
|
|
257
|
+
assert.equal(result.missed_count, 0, 'missed_count must be 0, got ' + result.missed_count);
|
|
258
|
+
assert.equal(result.resolution_quality, 'full', 'resolution_quality must be full, got ' + result.resolution_quality);
|
|
259
|
+
assert.equal(result.tier, 'aura', 'tier must be aura, got ' + result.tier);
|
|
260
|
+
assert.strictEqual(result.note, undefined, 'note must be undefined when full, got ' + JSON.stringify(result.note));
|
|
261
|
+
assert.equal(result.schema_version, '1.0');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async function scenario2Tier1WithUnmatchedMerges() {
|
|
265
|
+
const m = require('../core/rs-expert-mapper.cjs');
|
|
266
|
+
const experts = makeFiveExperts();
|
|
267
|
+
// First 3 experts match; last 2 return zero rows (unmatched -> trigger MERGE)
|
|
268
|
+
const driver = makeMockDriver({
|
|
269
|
+
matchResults: [
|
|
270
|
+
[{ name: experts[0].name, orcid: experts[0].orcid, aura_node_id: 200 }],
|
|
271
|
+
[{ name: experts[1].name, orcid: experts[1].orcid, aura_node_id: 201 }],
|
|
272
|
+
[{ name: experts[2].name, orcid: experts[2].orcid, aura_node_id: 202 }],
|
|
273
|
+
[], // unmatched
|
|
274
|
+
[], // unmatched
|
|
275
|
+
],
|
|
276
|
+
mergeResults: [
|
|
277
|
+
[{ name: experts[3].name, aura_node_id: 203 }],
|
|
278
|
+
[{ name: experts[4].name, aura_node_id: 204 }],
|
|
279
|
+
],
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
const result = await m.mapAuthorsToAura(experts, { tier: 'aura', driver: driver });
|
|
283
|
+
captureResult('2-tier1-unmatched-merge', result);
|
|
284
|
+
|
|
285
|
+
assert.equal(result.resolved_authors.length, 5, 'expected 5 resolved (3 MATCH + 2 MERGE), got ' + result.resolved_authors.length);
|
|
286
|
+
assert.equal(result.missed_count, 0, 'none should be missed; all resolved by MATCH or MERGE');
|
|
287
|
+
// Verify both MATCH and MERGE Cyphers ran.
|
|
288
|
+
const cypherLog = driver.opLog.map(function (o) { return o.cypher; }).join('\n');
|
|
289
|
+
assert.ok(/MATCH/.test(cypherLog), 'MATCH Cypher must have run');
|
|
290
|
+
assert.ok(/MERGE/.test(cypherLog), 'MERGE Cypher must have run for unmatched');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async function scenario3Tier0GracefulDegradation() {
|
|
294
|
+
const m = require('../core/rs-expert-mapper.cjs');
|
|
295
|
+
const tmp = makeTmpRoom();
|
|
296
|
+
try {
|
|
297
|
+
// Seed 3 of the 5 authors into room.db
|
|
298
|
+
await seedRoomDb(tmp.roomDir, [
|
|
299
|
+
{ name: 'Smith, Jane', orcid: '0000-0001-1111-1111', institution: 'Stanford', h_index_estimate: 3, paper_id: 'p1' },
|
|
300
|
+
{ name: 'Alpha, Adam', orcid: '0000-0002-2222-2222', institution: 'MIT', h_index_estimate: 2, paper_id: 'p2' },
|
|
301
|
+
{ name: 'Beta, Bob', orcid: '0000-0003-3333-3333', institution: 'Harvard', h_index_estimate: 2, paper_id: 'p1' },
|
|
302
|
+
]);
|
|
303
|
+
|
|
304
|
+
const experts = makeFiveExperts();
|
|
305
|
+
const result = await m.mapAuthorsToAura(experts, { tier: 'sqlite', roomDir: tmp.roomDir });
|
|
306
|
+
captureResult('3-tier0-degraded', result);
|
|
307
|
+
|
|
308
|
+
assert.equal(result.resolved_authors.length, 3, 'expected 3 resolved from sqlite, got ' + result.resolved_authors.length);
|
|
309
|
+
assert.equal(result.missed_count, 2, 'expected 2 missed (Gamma + Delta absent from db), got ' + result.missed_count);
|
|
310
|
+
assert.equal(result.resolution_quality, 'degraded', 'resolution_quality must be degraded for Tier 0');
|
|
311
|
+
assert.equal(result.tier, 'sqlite');
|
|
312
|
+
assert.ok(typeof result.note === 'string' && result.note.length > 0, 'note must be non-empty for degraded result');
|
|
313
|
+
assert.ok(/Aura/i.test(result.note), 'note should mention Aura, got: ' + result.note);
|
|
314
|
+
assert.ok(/multi-hop/i.test(result.note), 'note should mention multi-hop, got: ' + result.note);
|
|
315
|
+
assert.ok(Array.isArray(result.citation_edges), 'citation_edges must be an array');
|
|
316
|
+
// citation_edges should be best-effort populated from AUTHORED_BY edges (Smith + Beta share paper p1)
|
|
317
|
+
assert.ok(result.citation_edges.length >= 1, 'citation_edges must be populated from shared-paper AUTHORED_BY edges, got ' + result.citation_edges.length);
|
|
318
|
+
} finally {
|
|
319
|
+
tmp.cleanup();
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async function scenario4TierDispatchError() {
|
|
324
|
+
const m = require('../core/rs-expert-mapper.cjs');
|
|
325
|
+
let threw = null;
|
|
326
|
+
try {
|
|
327
|
+
await m.mapAuthorsToAura([{ name: 'Smith' }], {});
|
|
328
|
+
} catch (err) {
|
|
329
|
+
threw = err;
|
|
330
|
+
}
|
|
331
|
+
assert.ok(threw, 'must throw on missing opts.tier/driver/roomDir');
|
|
332
|
+
assert.equal(threw.name, 'TypeError', 'must throw TypeError, got ' + threw.name);
|
|
333
|
+
assert.ok(/opts\.tier OR opts\.driver OR opts\.roomDir/.test(threw.message), 'error message must mention all three options, got: ' + threw.message);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async function scenario5MissedCountAccuracy() {
|
|
337
|
+
const m = require('../core/rs-expert-mapper.cjs');
|
|
338
|
+
const experts = makeFiveExperts();
|
|
339
|
+
// ALL MATCH return zero rows -> all 5 trigger MERGE; simulate 2 MERGE failures
|
|
340
|
+
const driver = makeMockDriver({
|
|
341
|
+
matchResults: [[], [], [], [], []],
|
|
342
|
+
mergeResults: [
|
|
343
|
+
[{ name: experts[0].name, aura_node_id: 300 }],
|
|
344
|
+
[{ name: experts[1].name, aura_node_id: 301 }],
|
|
345
|
+
[{ name: experts[2].name, aura_node_id: 302 }],
|
|
346
|
+
// experts[3] and experts[4] MERGE will fail (simulateMergeFailureFor)
|
|
347
|
+
],
|
|
348
|
+
simulateMergeFailureFor: [
|
|
349
|
+
experts[3].name + '|' + experts[3].orcid,
|
|
350
|
+
experts[4].name + '|' + experts[4].orcid,
|
|
351
|
+
],
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
const result = await m.mapAuthorsToAura(experts, { tier: 'aura', driver: driver });
|
|
355
|
+
captureResult('5-missed-count', result);
|
|
356
|
+
|
|
357
|
+
assert.equal(result.resolved_authors.length, 3, 'expected 3 resolved, got ' + result.resolved_authors.length);
|
|
358
|
+
assert.equal(result.missed_count, 2, 'expected 2 missed (MERGE failures), got ' + result.missed_count);
|
|
359
|
+
assert.equal(result.resolution_quality, 'full', 'still full because tier is aura');
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
async function scenario6CitationEdgesPopulated() {
|
|
363
|
+
const m = require('../core/rs-expert-mapper.cjs');
|
|
364
|
+
const experts = makeFiveExperts().slice(0, 3);
|
|
365
|
+
const driver = makeMockDriver({
|
|
366
|
+
matchResults: experts.map(function (e, i) {
|
|
367
|
+
return [{ name: e.name, orcid: e.orcid, aura_node_id: 400 + i }];
|
|
368
|
+
}),
|
|
369
|
+
coAuthorResults: [
|
|
370
|
+
{ source_author: experts[0].name, target_author: experts[1].name, paper_id: 'openalex:W001' },
|
|
371
|
+
{ source_author: experts[0].name, target_author: experts[2].name, paper_id: 'openalex:W002' },
|
|
372
|
+
],
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
const result = await m.mapAuthorsToAura(experts, { tier: 'aura', driver: driver });
|
|
376
|
+
captureResult('6-citation-edges', result);
|
|
377
|
+
|
|
378
|
+
assert.ok(result.citation_edges.length >= 2, 'citation_edges must have >= 2 records, got ' + result.citation_edges.length);
|
|
379
|
+
for (const edge of result.citation_edges) {
|
|
380
|
+
assert.ok('source_author' in edge, 'edge missing source_author');
|
|
381
|
+
assert.ok('target_author' in edge, 'edge missing target_author');
|
|
382
|
+
assert.ok('paper_id' in edge, 'edge missing paper_id');
|
|
383
|
+
assert.equal(edge.type, 'CO_AUTHORED', 'edge type must be CO_AUTHORED, got ' + edge.type);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async function scenario7EmptyExpertsArray() {
|
|
388
|
+
const m = require('../core/rs-expert-mapper.cjs');
|
|
389
|
+
const tmp = makeTmpRoom();
|
|
390
|
+
try {
|
|
391
|
+
const result = await m.mapAuthorsToAura([], { tier: 'sqlite', roomDir: tmp.roomDir });
|
|
392
|
+
captureResult('7-empty-experts', result);
|
|
393
|
+
|
|
394
|
+
assert.deepEqual(result.resolved_authors, [], 'resolved_authors must be empty');
|
|
395
|
+
assert.deepEqual(result.resolved_institutions, [], 'resolved_institutions must be empty');
|
|
396
|
+
assert.deepEqual(result.citation_edges, [], 'citation_edges must be empty');
|
|
397
|
+
assert.equal(result.missed_count, 0);
|
|
398
|
+
assert.equal(result.resolution_quality, 'degraded', 'still degraded because tier is sqlite');
|
|
399
|
+
assert.equal(result.tier, 'sqlite');
|
|
400
|
+
} finally {
|
|
401
|
+
tmp.cleanup();
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
async function scenario8AuraUnreachable() {
|
|
406
|
+
const m = require('../core/rs-expert-mapper.cjs');
|
|
407
|
+
const experts = makeFiveExperts().slice(0, 1);
|
|
408
|
+
const driver = makeMockDriver({ simulateConnectionError: true });
|
|
409
|
+
|
|
410
|
+
let threw = null;
|
|
411
|
+
try {
|
|
412
|
+
await m.mapAuthorsToAura(experts, { tier: 'aura', driver: driver });
|
|
413
|
+
} catch (err) {
|
|
414
|
+
threw = err;
|
|
415
|
+
}
|
|
416
|
+
assert.ok(threw, 'must throw on Aura connection failure');
|
|
417
|
+
assert.equal(threw.name, 'AuraUnreachableError', 'must throw AuraUnreachableError, got ' + threw.name);
|
|
418
|
+
assert.ok(threw.meta && typeof threw.meta.original === 'string', 'meta.original must carry original message');
|
|
419
|
+
assert.ok(/ECONNREFUSED|connect/i.test(threw.meta.original), 'meta.original must reflect connection failure, got: ' + threw.meta.original);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
async function scenario9AdvForbiddenInName() {
|
|
423
|
+
const m = require('../core/rs-expert-mapper.cjs');
|
|
424
|
+
// FORBIDDEN_PATTERNS[2] matches Lawrence said -- pattern in expert.name
|
|
425
|
+
const poisonedExpert = makeExpert({ name: 'Lawrence said our PI', orcid: null, institution: 'X', paper_count: 1, h_index_estimate: 1 });
|
|
426
|
+
const driver = makeMockDriver({ matchResults: [[]] });
|
|
427
|
+
|
|
428
|
+
let threw = null;
|
|
429
|
+
let result = null;
|
|
430
|
+
try {
|
|
431
|
+
result = await m.mapAuthorsToAura([poisonedExpert], { tier: 'aura', driver: driver });
|
|
432
|
+
} catch (err) {
|
|
433
|
+
threw = err;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
assert.ok(threw, 'must throw on adversarial name (Layer 1 OR Layer 2 catches)');
|
|
437
|
+
assert.equal(threw.name, 'ExternalEgressViolation', 'must throw ExternalEgressViolation, got ' + threw.name);
|
|
438
|
+
assert.ok(threw.meta && typeof threw.meta.surface === 'string', 'meta.surface must be set');
|
|
439
|
+
assert.ok(threw.meta.surface === 'rs-expert-mapper' || threw.meta.surface === 'rs-expert-mapper-output',
|
|
440
|
+
'surface must be one of the 2 expert-mapper tags, got: ' + threw.meta.surface);
|
|
441
|
+
// session.run NEVER called (Layer 1 caught pre-Cypher).
|
|
442
|
+
assert.equal(driver.opLog.length, 0, 'session.run must NOT have run; Layer 1 audit caught pre-Cypher; got opLog.length=' + driver.opLog.length);
|
|
443
|
+
captureResult('9-adv-name-throw', { thrown: threw.name, surface: threw.meta.surface });
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
async function scenario10AdvForbiddenInInstitution() {
|
|
447
|
+
// FORBIDDEN_PATTERNS[3] matches /\bmeeting with\b/i in expert.institution
|
|
448
|
+
// This test verifies BOTH layers are wired (grep for 2 distinct surface
|
|
449
|
+
// strings: 'rs-expert-mapper' AND 'rs-expert-mapper-output' in source).
|
|
450
|
+
const fs2 = require('node:fs');
|
|
451
|
+
const sourcePath = path.join(REPO_ROOT, 'lib', 'core', 'rs-expert-mapper.cjs');
|
|
452
|
+
const source = fs2.readFileSync(sourcePath, 'utf-8');
|
|
453
|
+
assert.ok(/'rs-expert-mapper'/.test(source) || /SURFACE_PRE\s*=\s*'rs-expert-mapper'/.test(source),
|
|
454
|
+
'source must wire SURFACE_PRE = rs-expert-mapper');
|
|
455
|
+
assert.ok(/'rs-expert-mapper-output'/.test(source) || /SURFACE_POST\s*=\s*'rs-expert-mapper-output'/.test(source),
|
|
456
|
+
'source must wire SURFACE_POST = rs-expert-mapper-output');
|
|
457
|
+
|
|
458
|
+
// Now exercise the actual throw path.
|
|
459
|
+
const m = require('../core/rs-expert-mapper.cjs');
|
|
460
|
+
const poisonedExpert = makeExpert({ name: 'Smith, Jane', orcid: '0000-0001-2345-6789', institution: 'meeting with Stanford', paper_count: 1, h_index_estimate: 1 });
|
|
461
|
+
const driver = makeMockDriver({ matchResults: [[]] });
|
|
462
|
+
|
|
463
|
+
let threw = null;
|
|
464
|
+
try {
|
|
465
|
+
await m.mapAuthorsToAura([poisonedExpert], { tier: 'aura', driver: driver });
|
|
466
|
+
} catch (err) {
|
|
467
|
+
threw = err;
|
|
468
|
+
}
|
|
469
|
+
assert.ok(threw, 'must throw on adversarial institution (Layer 1 catches pre-Cypher)');
|
|
470
|
+
assert.equal(threw.name, 'ExternalEgressViolation', 'must throw ExternalEgressViolation, got ' + threw.name);
|
|
471
|
+
assert.ok(threw.meta && /meeting with/i.test(threw.meta.matched_pattern || ''),
|
|
472
|
+
'matched_pattern must reflect meeting with regex, got: ' + (threw.meta && threw.meta.matched_pattern));
|
|
473
|
+
captureResult('10-adv-institution-throw', { thrown: threw.name, surface: threw.meta.surface });
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// ---------- A1 end-of-suite sweep ----------
|
|
477
|
+
|
|
478
|
+
function a1Sweep() {
|
|
479
|
+
let hits = 0;
|
|
480
|
+
for (const rec of capturedResults) {
|
|
481
|
+
let json;
|
|
482
|
+
try {
|
|
483
|
+
json = JSON.stringify(rec.result);
|
|
484
|
+
} catch (_e) {
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
if (typeof json !== 'string') continue;
|
|
488
|
+
for (const re of FORBIDDEN_PATTERNS) {
|
|
489
|
+
if (re.test(json)) {
|
|
490
|
+
hits += 1;
|
|
491
|
+
process.stderr.write(
|
|
492
|
+
' A1 HIT scenario=' + rec.scenario +
|
|
493
|
+
' pattern=' + re.source + '\n'
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
return hits;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// ---------- Main ----------
|
|
502
|
+
|
|
503
|
+
(async function main() {
|
|
504
|
+
process.stdout.write('=== 89.3-04 rs-expert-mapper suite (REPO_ROOT=' + REPO_ROOT + ') ===\n');
|
|
505
|
+
|
|
506
|
+
await runScenario('1-happy-path-tier1', scenario1HappyPathTier1);
|
|
507
|
+
await runScenario('2-tier1-unmatched-MERGE', scenario2Tier1WithUnmatchedMerges);
|
|
508
|
+
await runScenario('3-tier0-graceful-degradation', scenario3Tier0GracefulDegradation);
|
|
509
|
+
await runScenario('4-tier-dispatch-error', scenario4TierDispatchError);
|
|
510
|
+
await runScenario('5-missed-count-accuracy', scenario5MissedCountAccuracy);
|
|
511
|
+
await runScenario('6-citation-edges-populated', scenario6CitationEdgesPopulated);
|
|
512
|
+
await runScenario('7-empty-experts-early-return', scenario7EmptyExpertsArray);
|
|
513
|
+
await runScenario('8-aura-unreachable', scenario8AuraUnreachable);
|
|
514
|
+
await runScenario('9-adv-CANON-PART-8-name', scenario9AdvForbiddenInName);
|
|
515
|
+
await runScenario('10-adv-CANON-PART-8-institution', scenario10AdvForbiddenInInstitution);
|
|
516
|
+
|
|
517
|
+
const sweepHits = a1Sweep();
|
|
518
|
+
if (sweepHits > 0) {
|
|
519
|
+
failed += 1;
|
|
520
|
+
failures.push({
|
|
521
|
+
name: 'A1-sweep',
|
|
522
|
+
err: new Error('A1 sweep found ' + sweepHits + ' forbidden-pattern hits'),
|
|
523
|
+
});
|
|
524
|
+
process.stderr.write(' FAIL A1-sweep (' + sweepHits + ' hits)\n');
|
|
525
|
+
} else {
|
|
526
|
+
process.stdout.write(' ok A1-sweep (zero forbidden-pattern hits)\n');
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Final cleanup of all tmp rooms.
|
|
530
|
+
for (const dir of tmpRooms) {
|
|
531
|
+
try { fs.rmSync(dir, { recursive: true, force: true }); } catch (_e) { /* best effort */ }
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (failed > 0) {
|
|
535
|
+
process.stdout.write('=== 89.3-04 rs-expert-mapper suite: ' + passed + '/10 passed' + ' (' + failed + ' failed) ===\n');
|
|
536
|
+
for (const f of failures) {
|
|
537
|
+
process.stderr.write(' FAILURE: ' + f.name + ': ' + (f.err && f.err.message ? f.err.message : String(f.err)) + '\n');
|
|
538
|
+
}
|
|
539
|
+
process.exit(1);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
process.stdout.write('=== 89.3-04 rs-expert-mapper suite: 10/10 passed ===\n');
|
|
543
|
+
process.exit(0);
|
|
544
|
+
})().catch(function (err) {
|
|
545
|
+
process.stderr.write('FATAL: ' + (err && err.stack ? err.stack : err) + '\n');
|
|
546
|
+
process.exit(2);
|
|
547
|
+
});
|