@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,649 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Copyright (c) 2026 Mindrian. BSL 1.1.
|
|
6
|
+
* Phase 89.3 Plan 02 -- SQLite mirror writer fixture suite.
|
|
7
|
+
*
|
|
8
|
+
* writeDiscovery(pair, opts) is the Tier 0 (Aura-unavailable) writer in the
|
|
9
|
+
* dual-tier output layer. Mirrors the rs-neo4j-writer.cjs (Plan 89.3-01)
|
|
10
|
+
* public API surface byte-for-byte so callers swap backends transparently.
|
|
11
|
+
*
|
|
12
|
+
* Schema parity (per kickoff section 5):
|
|
13
|
+
* Nodes: RSDiscovery + ReverseSalient + Innovation + Paper + Author + Institution
|
|
14
|
+
* Edges: DISCOVERED + DERIVED_FROM + ENABLES + AUTHORED_BY + AFFILIATED_WITH
|
|
15
|
+
*
|
|
16
|
+
* REAL idempotency in SQLite via INSERT OR REPLACE on the id PRIMARY KEY:
|
|
17
|
+
* the 3 core node ids (discovery_id, rs_id, innovation_id) are derived
|
|
18
|
+
* sha256 over {query_concept, doc_concept, classification}. Same pair
|
|
19
|
+
* always produces same ids; INSERT OR REPLACE matches existing rows;
|
|
20
|
+
* SELECT COUNT before vs after re-run = same number.
|
|
21
|
+
*
|
|
22
|
+
* 12 scenarios + A1 sweep:
|
|
23
|
+
* 1 Happy path (minimal pair, no opts.context)
|
|
24
|
+
* 2 REAL idempotency via INSERT OR REPLACE (re-run yields zero new rows)
|
|
25
|
+
* 3 Full happy path with opts.context (papers + experts)
|
|
26
|
+
* 4 All 5 edge types written (DISCOVERED + DERIVED_FROM + ENABLES + AUTHORED_BY + AFFILIATED_WITH)
|
|
27
|
+
* 5 Rollback SQL captured + valid (3 DELETE FROM nodes + edges-first DELETE)
|
|
28
|
+
* 6 Missing classification throws TypeError
|
|
29
|
+
* 7 Missing thesis throws TypeError
|
|
30
|
+
* 8 room.db missing or unwritable throws SQLiteUnreachableError
|
|
31
|
+
* 9 CANON PART 8 adversarial -- forbidden in pair.thesis
|
|
32
|
+
* 10 CANON PART 8 adversarial -- forbidden in pair.bridge_concept
|
|
33
|
+
* 11 CANON PART 8 adversarial -- forbidden in pair.query_concept
|
|
34
|
+
* 12 CANON PART 8 adversarial -- forbidden smuggled via opts.context.experts[0].institution
|
|
35
|
+
*
|
|
36
|
+
* End-of-suite sweep:
|
|
37
|
+
* A1: SELECT every row from the test room.db (nodes + edges) JSON.stringify
|
|
38
|
+
* and scan against FORBIDDEN_PATTERNS. Asserts zero hits across the
|
|
39
|
+
* FINAL state of the database after all 12 scenarios. (Throw scenarios
|
|
40
|
+
* verify zero rows added at the time; A1 verifies no smuggled bytes
|
|
41
|
+
* anywhere in final state.)
|
|
42
|
+
*
|
|
43
|
+
* Pure CJS, zero npm deps, node built-ins only (assert, fs, path, os, crypto).
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
const assert = require('node:assert/strict');
|
|
47
|
+
const fs = require('node:fs');
|
|
48
|
+
const path = require('node:path');
|
|
49
|
+
const os = require('node:os');
|
|
50
|
+
const crypto = require('node:crypto');
|
|
51
|
+
|
|
52
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
53
|
+
|
|
54
|
+
// Source of truth for parity gate.
|
|
55
|
+
const crossRoomAggregator = require('../core/cross-room-aggregator.cjs');
|
|
56
|
+
const FORBIDDEN_PATTERNS = crossRoomAggregator.FORBIDDEN_PATTERNS;
|
|
57
|
+
|
|
58
|
+
// Lazy-required (so RED commit can prove module-not-found).
|
|
59
|
+
const lazygraph = require('../core/lazygraph-ops.cjs');
|
|
60
|
+
|
|
61
|
+
// Track every tmp room dir for cleanup + final A1 sweep target.
|
|
62
|
+
const tmpRooms = [];
|
|
63
|
+
|
|
64
|
+
let passed = 0;
|
|
65
|
+
let failed = 0;
|
|
66
|
+
const failures = [];
|
|
67
|
+
|
|
68
|
+
function resetRequireCache() {
|
|
69
|
+
const targets = [
|
|
70
|
+
'../core/rs-egress-violations.cjs',
|
|
71
|
+
'../core/rs-egress-prompts.cjs',
|
|
72
|
+
'../core/cross-room-aggregator.cjs',
|
|
73
|
+
'../core/rs-sqlite-mirror.cjs',
|
|
74
|
+
];
|
|
75
|
+
for (const t of targets) {
|
|
76
|
+
try {
|
|
77
|
+
const p = require.resolve(t);
|
|
78
|
+
delete require.cache[p];
|
|
79
|
+
} catch (_e) { /* best effort */ }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function runScenario(name, fn) {
|
|
84
|
+
const start = Date.now();
|
|
85
|
+
try {
|
|
86
|
+
await fn();
|
|
87
|
+
passed += 1;
|
|
88
|
+
process.stdout.write(' ok ' + name + ' (' + (Date.now() - start) + 'ms)\n');
|
|
89
|
+
} catch (err) {
|
|
90
|
+
failed += 1;
|
|
91
|
+
failures.push({ name: name, err: err });
|
|
92
|
+
process.stderr.write(' FAIL ' + name + '\n');
|
|
93
|
+
process.stderr.write(' ' + (err && err.stack ? err.stack : err) + '\n');
|
|
94
|
+
} finally {
|
|
95
|
+
resetRequireCache();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ---------- Tmp room helper ----------
|
|
100
|
+
//
|
|
101
|
+
// Creates a fresh tmp room dir at os.tmpdir() with a UUID suffix. The
|
|
102
|
+
// rs-sqlite-mirror writer calls lazygraph.openGraph(roomDir), which
|
|
103
|
+
// creates room/.mindrian/room.db on demand and runs initSchema. We do
|
|
104
|
+
// NOT pre-create room.db here; the writer must handle the creation.
|
|
105
|
+
//
|
|
106
|
+
// Returns {roomDir, cleanup} where cleanup() removes the entire tmp
|
|
107
|
+
// subtree. Caller MUST call cleanup() in finally even on assertion error.
|
|
108
|
+
|
|
109
|
+
function makeTmpRoom() {
|
|
110
|
+
const slug = crypto.randomUUID();
|
|
111
|
+
const roomDir = path.join(os.tmpdir(), 'mos-89-3-02-' + slug);
|
|
112
|
+
fs.mkdirSync(roomDir, { recursive: true });
|
|
113
|
+
tmpRooms.push(roomDir);
|
|
114
|
+
return {
|
|
115
|
+
roomDir: roomDir,
|
|
116
|
+
cleanup: function () {
|
|
117
|
+
try {
|
|
118
|
+
fs.rmSync(roomDir, { recursive: true, force: true });
|
|
119
|
+
} catch (_e) { /* best effort */ }
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// SELECT helpers run against an open connection. Caller responsible for
|
|
125
|
+
// open/close.
|
|
126
|
+
|
|
127
|
+
async function countNodes(roomDir) {
|
|
128
|
+
const handle = await lazygraph.openGraph(roomDir);
|
|
129
|
+
try {
|
|
130
|
+
const row = handle.conn.prepare('SELECT COUNT(*) AS n FROM nodes').get();
|
|
131
|
+
return row.n;
|
|
132
|
+
} finally {
|
|
133
|
+
await lazygraph.closeGraph(handle.db);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function countEdges(roomDir) {
|
|
138
|
+
const handle = await lazygraph.openGraph(roomDir);
|
|
139
|
+
try {
|
|
140
|
+
const row = handle.conn.prepare('SELECT COUNT(*) AS n FROM edges').get();
|
|
141
|
+
return row.n;
|
|
142
|
+
} finally {
|
|
143
|
+
await lazygraph.closeGraph(handle.db);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function selectAllNodes(roomDir) {
|
|
148
|
+
const handle = await lazygraph.openGraph(roomDir);
|
|
149
|
+
try {
|
|
150
|
+
return handle.conn.prepare('SELECT id, type, properties FROM nodes').all();
|
|
151
|
+
} finally {
|
|
152
|
+
await lazygraph.closeGraph(handle.db);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function selectAllEdges(roomDir) {
|
|
157
|
+
const handle = await lazygraph.openGraph(roomDir);
|
|
158
|
+
try {
|
|
159
|
+
return handle.conn.prepare('SELECT source, target, type, properties FROM edges').all();
|
|
160
|
+
} finally {
|
|
161
|
+
await lazygraph.closeGraph(handle.db);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function selectDistinctEdgeTypes(roomDir) {
|
|
166
|
+
const handle = await lazygraph.openGraph(roomDir);
|
|
167
|
+
try {
|
|
168
|
+
return handle.conn.prepare('SELECT DISTINCT type FROM edges').all().map(function (r) { return r.type; });
|
|
169
|
+
} finally {
|
|
170
|
+
await lazygraph.closeGraph(handle.db);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ---------- Fixture builders ----------
|
|
175
|
+
|
|
176
|
+
function makeCleanPair(overrides) {
|
|
177
|
+
const base = {
|
|
178
|
+
query_concept: 'CRISPR delivery',
|
|
179
|
+
doc_concept: 'liposome targeting',
|
|
180
|
+
diff: 0.4,
|
|
181
|
+
lsa: 0.5,
|
|
182
|
+
bert: 0.1,
|
|
183
|
+
passes: true,
|
|
184
|
+
classification: 'structural_transfer',
|
|
185
|
+
bridge_concept: 'cross-domain isomorphism',
|
|
186
|
+
breakthrough: {
|
|
187
|
+
score: 5.9,
|
|
188
|
+
breakdown: { feasibility: 2, market: 1.5, magnitude: 1, advantage: 1.4, impact: 0 },
|
|
189
|
+
dominant_dimension: 'feasibility',
|
|
190
|
+
},
|
|
191
|
+
thesis: 'By applying CRISPR delivery to liposome targeting, achieve feasible novel transfer because isomorphic structure transfers under cross-domain isomorphism.',
|
|
192
|
+
};
|
|
193
|
+
if (overrides) {
|
|
194
|
+
for (const k of Object.keys(overrides)) {
|
|
195
|
+
base[k] = overrides[k];
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return base;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function makeFullContext() {
|
|
202
|
+
return {
|
|
203
|
+
papers: [
|
|
204
|
+
{
|
|
205
|
+
id: 'openalex:W123',
|
|
206
|
+
title: 'CRISPR delivery via liposomes',
|
|
207
|
+
authors: [{ name: 'Smith, Jane', orcid: '0000-0001-2345-6789', institution: 'Stanford' }],
|
|
208
|
+
doi: '10.1038/sample.2026',
|
|
209
|
+
source: 'openalex',
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
experts: [
|
|
213
|
+
{
|
|
214
|
+
name: 'Smith, Jane',
|
|
215
|
+
orcid: '0000-0001-2345-6789',
|
|
216
|
+
institution: 'Stanford',
|
|
217
|
+
paper_count: 1,
|
|
218
|
+
h_index_estimate: 1,
|
|
219
|
+
public_email_or_null: null,
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
room_id: 'test-room',
|
|
223
|
+
domain: 'biotech',
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Final-state sweep target. The single shared room used by Tests 1, 2, 3,
|
|
228
|
+
// 4, 5 (happy paths). Throw scenarios use their own fresh tmp rooms so
|
|
229
|
+
// the assertion zero-rows-added is unambiguous.
|
|
230
|
+
let SHARED_ROOM = null;
|
|
231
|
+
|
|
232
|
+
// ---------- Scenarios ----------
|
|
233
|
+
|
|
234
|
+
async function scenario1HappyPathMinimal() {
|
|
235
|
+
const m = require('../core/rs-sqlite-mirror.cjs');
|
|
236
|
+
const tmp = makeTmpRoom();
|
|
237
|
+
SHARED_ROOM = tmp;
|
|
238
|
+
try {
|
|
239
|
+
const pair = makeCleanPair();
|
|
240
|
+
const result = await m.writeDiscovery(pair, { roomDir: tmp.roomDir });
|
|
241
|
+
|
|
242
|
+
assert.equal(result.wrote_node_count, 3, 'wrote_node_count must be 3, got ' + result.wrote_node_count);
|
|
243
|
+
assert.equal(result.wrote_edge_count, 3, 'wrote_edge_count must be 3, got ' + result.wrote_edge_count);
|
|
244
|
+
assert.equal(result.schema_version, '1.0', 'schema_version must be 1.0');
|
|
245
|
+
assert.ok(typeof result.aura_op_id === 'string' && result.aura_op_id.length > 0, 'aura_op_id must be non-empty string');
|
|
246
|
+
assert.ok(typeof result.rollback_cypher === 'string' && result.rollback_cypher.length > 0, 'rollback_cypher must be non-empty string');
|
|
247
|
+
// Verify SQL DELETE statements (NOT Cypher DETACH DELETE).
|
|
248
|
+
assert.ok(/DELETE FROM nodes/.test(result.rollback_cypher), 'rollback_cypher must contain DELETE FROM nodes');
|
|
249
|
+
assert.ok(/DELETE FROM edges/.test(result.rollback_cypher), 'rollback_cypher must contain DELETE FROM edges');
|
|
250
|
+
// SELECT verification: 3 nodes + 3 edges in the room.
|
|
251
|
+
assert.equal(await countNodes(tmp.roomDir), 3, 'room.db must have 3 nodes after happy path');
|
|
252
|
+
assert.equal(await countEdges(tmp.roomDir), 3, 'room.db must have 3 edges after happy path');
|
|
253
|
+
} catch (err) {
|
|
254
|
+
tmp.cleanup();
|
|
255
|
+
SHARED_ROOM = null;
|
|
256
|
+
throw err;
|
|
257
|
+
}
|
|
258
|
+
// Do NOT cleanup the SHARED_ROOM here -- Tests 2, 3, 4, 5 reuse it.
|
|
259
|
+
// Cleanup happens in main() after A1 sweep.
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async function scenario2RealIdempotencyViaInsertOrReplace() {
|
|
263
|
+
// Reuses SHARED_ROOM from Test 1 (3 nodes + 3 edges already present).
|
|
264
|
+
assert.ok(SHARED_ROOM, 'SHARED_ROOM must be initialized by Test 1');
|
|
265
|
+
const m = require('../core/rs-sqlite-mirror.cjs');
|
|
266
|
+
|
|
267
|
+
// Part A: buildDeterministicIds returns byte-identical ids on repeat calls
|
|
268
|
+
// AND matches the format from Plan 89.3-01 (rsd-/rs-/inn- + 16-hex).
|
|
269
|
+
assert.ok(m._test && m._test.buildDeterministicIds, '_test.buildDeterministicIds must be exposed');
|
|
270
|
+
const pair = makeCleanPair();
|
|
271
|
+
const ids1 = m._test.buildDeterministicIds(pair);
|
|
272
|
+
const ids2 = m._test.buildDeterministicIds(pair);
|
|
273
|
+
assert.equal(ids1.discovery_id, ids2.discovery_id, 'discovery_id must be byte-identical across calls');
|
|
274
|
+
assert.equal(ids1.rs_id, ids2.rs_id, 'rs_id must be byte-identical across calls');
|
|
275
|
+
assert.equal(ids1.innovation_id, ids2.innovation_id, 'innovation_id must be byte-identical across calls');
|
|
276
|
+
assert.ok(/^rsd-[0-9a-f]{16}$/.test(ids1.discovery_id), 'discovery_id format must be rsd-<16-hex>, got ' + ids1.discovery_id);
|
|
277
|
+
assert.ok(/^rs-[0-9a-f]{16}$/.test(ids1.rs_id), 'rs_id format must be rs-<16-hex>, got ' + ids1.rs_id);
|
|
278
|
+
assert.ok(/^inn-[0-9a-f]{16}$/.test(ids1.innovation_id), 'innovation_id format must be inn-<16-hex>, got ' + ids1.innovation_id);
|
|
279
|
+
|
|
280
|
+
// Part B: REAL SQLite idempotency via INSERT OR REPLACE on PRIMARY KEY.
|
|
281
|
+
// Re-run writeDiscovery with same pair on SHARED_ROOM (already has 3 nodes
|
|
282
|
+
// + 3 edges). After re-run: still 3 nodes + 3 edges. wrote_node_count = 0.
|
|
283
|
+
const beforeNodes = await countNodes(SHARED_ROOM.roomDir);
|
|
284
|
+
const beforeEdges = await countEdges(SHARED_ROOM.roomDir);
|
|
285
|
+
assert.equal(beforeNodes, 3, 'before re-run: room.db must have 3 nodes');
|
|
286
|
+
assert.equal(beforeEdges, 3, 'before re-run: room.db must have 3 edges');
|
|
287
|
+
|
|
288
|
+
const result = await m.writeDiscovery(pair, { roomDir: SHARED_ROOM.roomDir });
|
|
289
|
+
|
|
290
|
+
const afterNodes = await countNodes(SHARED_ROOM.roomDir);
|
|
291
|
+
const afterEdges = await countEdges(SHARED_ROOM.roomDir);
|
|
292
|
+
assert.equal(afterNodes, 3, 'AFTER re-run: room.db must STILL have 3 nodes (INSERT OR REPLACE matched on PRIMARY KEY)');
|
|
293
|
+
assert.equal(afterEdges, 3, 'AFTER re-run: room.db must STILL have 3 edges');
|
|
294
|
+
assert.equal(result.wrote_node_count, 0, 'second call wrote_node_count must be 0 (zero new rows), got ' + result.wrote_node_count);
|
|
295
|
+
assert.equal(result.wrote_edge_count, 0, 'second call wrote_edge_count must be 0, got ' + result.wrote_edge_count);
|
|
296
|
+
// aura_op_id is per-op telemetry: random and DIFFERENT across calls.
|
|
297
|
+
// (Compare against fresh writeDiscovery call to prove non-determinism.)
|
|
298
|
+
const result2 = await m.writeDiscovery(pair, { roomDir: SHARED_ROOM.roomDir });
|
|
299
|
+
assert.notEqual(result.aura_op_id, result2.aura_op_id, 'aura_op_id must differ across calls (random per-op telemetry)');
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async function scenario3FullHappyPathWithContext() {
|
|
303
|
+
// Use a FRESH room so the count assertions are unambiguous (Test 1's
|
|
304
|
+
// SHARED_ROOM has minimal pair already; mixing would muddy the count).
|
|
305
|
+
const m = require('../core/rs-sqlite-mirror.cjs');
|
|
306
|
+
const tmp = makeTmpRoom();
|
|
307
|
+
try {
|
|
308
|
+
const pair = makeCleanPair();
|
|
309
|
+
const ctx = makeFullContext();
|
|
310
|
+
const result = await m.writeDiscovery(pair, { roomDir: tmp.roomDir, context: ctx });
|
|
311
|
+
|
|
312
|
+
assert.equal(result.wrote_node_count, 6, 'wrote_node_count must be 6 (3 core + Paper + Author + Institution), got ' + result.wrote_node_count);
|
|
313
|
+
assert.equal(result.wrote_edge_count, 6, 'wrote_edge_count must be 6, got ' + result.wrote_edge_count);
|
|
314
|
+
|
|
315
|
+
// Verify the 6 nodes via SELECT.
|
|
316
|
+
const nodes = await selectAllNodes(tmp.roomDir);
|
|
317
|
+
assert.equal(nodes.length, 6, 'SELECT must return 6 nodes');
|
|
318
|
+
const types = nodes.map(function (n) { return n.type; }).sort();
|
|
319
|
+
// Expected types (sorted): Author, Innovation, Institution, Paper, RSDiscovery, ReverseSalient
|
|
320
|
+
const expectedTypes = ['Author', 'Innovation', 'Institution', 'Paper', 'RSDiscovery', 'ReverseSalient'];
|
|
321
|
+
assert.deepEqual(types, expectedTypes, 'node types must include all 6 expected: ' + JSON.stringify(types));
|
|
322
|
+
} finally {
|
|
323
|
+
// Do NOT cleanup yet -- Test 4 reuses this assertion pattern but on its own room.
|
|
324
|
+
// Final cleanup in main().
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
async function scenario4AllFiveEdgeTypesWritten() {
|
|
329
|
+
const m = require('../core/rs-sqlite-mirror.cjs');
|
|
330
|
+
const tmp = makeTmpRoom();
|
|
331
|
+
try {
|
|
332
|
+
const pair = makeCleanPair();
|
|
333
|
+
const ctx = makeFullContext();
|
|
334
|
+
await m.writeDiscovery(pair, { roomDir: tmp.roomDir, context: ctx });
|
|
335
|
+
|
|
336
|
+
const distinctTypes = await selectDistinctEdgeTypes(tmp.roomDir);
|
|
337
|
+
const expected = ['DISCOVERED', 'DERIVED_FROM', 'ENABLES', 'AUTHORED_BY', 'AFFILIATED_WITH'];
|
|
338
|
+
for (const e of expected) {
|
|
339
|
+
assert.ok(distinctTypes.indexOf(e) !== -1, 'distinct edge types must include ' + e + '; got: ' + JSON.stringify(distinctTypes));
|
|
340
|
+
}
|
|
341
|
+
// Strict: 5 of 5 expected types present.
|
|
342
|
+
let hits = 0;
|
|
343
|
+
for (const e of expected) if (distinctTypes.indexOf(e) !== -1) hits += 1;
|
|
344
|
+
assert.equal(hits, 5, 'all 5 edge types must be present, got ' + hits);
|
|
345
|
+
} finally {
|
|
346
|
+
// No cleanup -- final A1 sweep covers all tmp rooms.
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
async function scenario5RollbackSqlCaptured() {
|
|
351
|
+
const m = require('../core/rs-sqlite-mirror.cjs');
|
|
352
|
+
const tmp = makeTmpRoom();
|
|
353
|
+
try {
|
|
354
|
+
const pair = makeCleanPair();
|
|
355
|
+
const result = await m.writeDiscovery(pair, { roomDir: tmp.roomDir });
|
|
356
|
+
|
|
357
|
+
assert.ok(typeof result.rollback_cypher === 'string', 'rollback_cypher must be a string');
|
|
358
|
+
assert.ok(result.rollback_cypher.length > 0, 'rollback_cypher must be non-empty');
|
|
359
|
+
// Exactly 3 'DELETE FROM nodes WHERE id =' statements.
|
|
360
|
+
const nodeDeleteCount = (result.rollback_cypher.match(/DELETE FROM nodes WHERE id =/g) || []).length;
|
|
361
|
+
assert.equal(nodeDeleteCount, 3, 'rollback_cypher must have exactly 3 DELETE FROM nodes WHERE id = statements, got ' + nodeDeleteCount);
|
|
362
|
+
// At least 3 'DELETE FROM edges WHERE source =' statements.
|
|
363
|
+
const edgeDeleteCount = (result.rollback_cypher.match(/DELETE FROM edges WHERE source =/g) || []).length;
|
|
364
|
+
assert.ok(edgeDeleteCount >= 3, 'rollback_cypher must have >= 3 DELETE FROM edges statements, got ' + edgeDeleteCount);
|
|
365
|
+
// Edges-first ordering: first DELETE FROM edges position must be < first DELETE FROM nodes position.
|
|
366
|
+
const firstEdgeDel = result.rollback_cypher.indexOf('DELETE FROM edges');
|
|
367
|
+
const firstNodeDel = result.rollback_cypher.indexOf('DELETE FROM nodes');
|
|
368
|
+
assert.ok(firstEdgeDel !== -1 && firstNodeDel !== -1, 'both DELETE positions must exist');
|
|
369
|
+
assert.ok(firstEdgeDel < firstNodeDel, 'edges DELETEs must come BEFORE nodes DELETEs (FK safety)');
|
|
370
|
+
} finally {
|
|
371
|
+
// No cleanup -- final A1 sweep.
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
async function scenario6MissingClassificationThrows() {
|
|
376
|
+
const m = require('../core/rs-sqlite-mirror.cjs');
|
|
377
|
+
const tmp = makeTmpRoom();
|
|
378
|
+
try {
|
|
379
|
+
const pair = makeCleanPair({ classification: undefined });
|
|
380
|
+
const beforeN = await countNodes(tmp.roomDir);
|
|
381
|
+
const beforeE = await countEdges(tmp.roomDir);
|
|
382
|
+
let threw = null;
|
|
383
|
+
try {
|
|
384
|
+
await m.writeDiscovery(pair, { roomDir: tmp.roomDir });
|
|
385
|
+
} catch (err) {
|
|
386
|
+
threw = err;
|
|
387
|
+
}
|
|
388
|
+
assert.ok(threw, 'must throw on missing classification');
|
|
389
|
+
assert.ok(threw instanceof TypeError, 'must throw TypeError, got ' + (threw && threw.name));
|
|
390
|
+
assert.ok(/classification/.test(threw.message), 'error must mention classification, got: ' + threw.message);
|
|
391
|
+
const afterN = await countNodes(tmp.roomDir);
|
|
392
|
+
const afterE = await countEdges(tmp.roomDir);
|
|
393
|
+
assert.equal(afterN, beforeN, 'no new nodes added on shape error');
|
|
394
|
+
assert.equal(afterE, beforeE, 'no new edges added on shape error');
|
|
395
|
+
} finally {
|
|
396
|
+
tmp.cleanup();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async function scenario7MissingThesisThrows() {
|
|
401
|
+
const m = require('../core/rs-sqlite-mirror.cjs');
|
|
402
|
+
const tmp = makeTmpRoom();
|
|
403
|
+
try {
|
|
404
|
+
const pair = makeCleanPair({ thesis: undefined });
|
|
405
|
+
const beforeN = await countNodes(tmp.roomDir);
|
|
406
|
+
let threw = null;
|
|
407
|
+
try {
|
|
408
|
+
await m.writeDiscovery(pair, { roomDir: tmp.roomDir });
|
|
409
|
+
} catch (err) {
|
|
410
|
+
threw = err;
|
|
411
|
+
}
|
|
412
|
+
assert.ok(threw, 'must throw on missing thesis');
|
|
413
|
+
assert.ok(threw instanceof TypeError, 'must throw TypeError, got ' + (threw && threw.name));
|
|
414
|
+
assert.ok(/thesis/.test(threw.message), 'error must mention thesis, got: ' + threw.message);
|
|
415
|
+
const afterN = await countNodes(tmp.roomDir);
|
|
416
|
+
assert.equal(afterN, beforeN, 'no new nodes added on shape error');
|
|
417
|
+
} finally {
|
|
418
|
+
tmp.cleanup();
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
async function scenario8RoomDbUnreachableThrows() {
|
|
423
|
+
// CANON: opts.roomDir = path that cannot be created (parent is a file,
|
|
424
|
+
// not a directory). On Linux, creating /dev/null/anything fails because
|
|
425
|
+
// /dev/null is not a directory.
|
|
426
|
+
const m = require('../core/rs-sqlite-mirror.cjs');
|
|
427
|
+
const unwritable = path.join('/dev/null', 'mos-89-3-02-impossible-' + crypto.randomUUID());
|
|
428
|
+
const pair = makeCleanPair();
|
|
429
|
+
let threw = null;
|
|
430
|
+
try {
|
|
431
|
+
await m.writeDiscovery(pair, { roomDir: unwritable });
|
|
432
|
+
} catch (err) {
|
|
433
|
+
threw = err;
|
|
434
|
+
}
|
|
435
|
+
assert.ok(threw, 'must throw on room.db unreachable');
|
|
436
|
+
assert.equal(threw.name, 'SQLiteUnreachableError', 'must throw SQLiteUnreachableError, got ' + (threw && threw.name));
|
|
437
|
+
// Must NOT be ExternalEgressViolation -- this is a tier-dispatch signal.
|
|
438
|
+
assert.notEqual(threw.name, 'ExternalEgressViolation', 'must NOT confuse with ExternalEgressViolation');
|
|
439
|
+
assert.ok(threw.meta, 'SQLiteUnreachableError must carry meta');
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async function scenario9AdvForbiddenInThesis() {
|
|
443
|
+
const m = require('../core/rs-sqlite-mirror.cjs');
|
|
444
|
+
const tmp = makeTmpRoom();
|
|
445
|
+
try {
|
|
446
|
+
// FORBIDDEN_PATTERNS[3] matches /\bmeeting with\b/i.
|
|
447
|
+
const pair = makeCleanPair({
|
|
448
|
+
thesis: 'By applying X to Y, achieve Z because mechanism. Decided in meeting with Genentech.',
|
|
449
|
+
});
|
|
450
|
+
const beforeN = await countNodes(tmp.roomDir);
|
|
451
|
+
let threw = null;
|
|
452
|
+
try {
|
|
453
|
+
await m.writeDiscovery(pair, { roomDir: tmp.roomDir });
|
|
454
|
+
} catch (err) {
|
|
455
|
+
threw = err;
|
|
456
|
+
}
|
|
457
|
+
assert.ok(threw, 'must throw on forbidden in thesis');
|
|
458
|
+
assert.equal(threw.name, 'ExternalEgressViolation', 'must throw ExternalEgressViolation, got ' + (threw && threw.name));
|
|
459
|
+
assert.equal(threw.meta.surface, 'rs-sqlite-mirror', 'meta.surface must be rs-sqlite-mirror');
|
|
460
|
+
assert.ok(threw.meta.matched_pattern, 'meta.matched_pattern must be present');
|
|
461
|
+
const afterN = await countNodes(tmp.roomDir);
|
|
462
|
+
assert.equal(afterN, beforeN, 'no rows added (audit fires before SQLite write)');
|
|
463
|
+
} finally {
|
|
464
|
+
tmp.cleanup();
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
async function scenario10AdvForbiddenInBridgeConcept() {
|
|
469
|
+
const m = require('../core/rs-sqlite-mirror.cjs');
|
|
470
|
+
const tmp = makeTmpRoom();
|
|
471
|
+
try {
|
|
472
|
+
// FORBIDDEN_PATTERNS[2] matches /\b(Lawrence|Jonathan|...)\s+(said|...)\b/i.
|
|
473
|
+
const pair = makeCleanPair({
|
|
474
|
+
bridge_concept: 'Lawrence said this is a cross-domain isomorphism',
|
|
475
|
+
});
|
|
476
|
+
const beforeN = await countNodes(tmp.roomDir);
|
|
477
|
+
let threw = null;
|
|
478
|
+
try {
|
|
479
|
+
await m.writeDiscovery(pair, { roomDir: tmp.roomDir });
|
|
480
|
+
} catch (err) {
|
|
481
|
+
threw = err;
|
|
482
|
+
}
|
|
483
|
+
assert.ok(threw, 'must throw on forbidden in bridge_concept');
|
|
484
|
+
assert.equal(threw.name, 'ExternalEgressViolation', 'must throw ExternalEgressViolation, got ' + (threw && threw.name));
|
|
485
|
+
assert.equal(threw.meta.surface, 'rs-sqlite-mirror');
|
|
486
|
+
const afterN = await countNodes(tmp.roomDir);
|
|
487
|
+
assert.equal(afterN, beforeN, 'no rows added on adversarial input');
|
|
488
|
+
} finally {
|
|
489
|
+
tmp.cleanup();
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
async function scenario11AdvForbiddenInQueryConcept() {
|
|
494
|
+
const m = require('../core/rs-sqlite-mirror.cjs');
|
|
495
|
+
const tmp = makeTmpRoom();
|
|
496
|
+
try {
|
|
497
|
+
// FORBIDDEN_PATTERNS[3] matches /\bmeeting with\b/i.
|
|
498
|
+
const pair = makeCleanPair({
|
|
499
|
+
query_concept: 'shared during meeting with the team',
|
|
500
|
+
});
|
|
501
|
+
const beforeN = await countNodes(tmp.roomDir);
|
|
502
|
+
let threw = null;
|
|
503
|
+
try {
|
|
504
|
+
await m.writeDiscovery(pair, { roomDir: tmp.roomDir });
|
|
505
|
+
} catch (err) {
|
|
506
|
+
threw = err;
|
|
507
|
+
}
|
|
508
|
+
assert.ok(threw, 'must throw on forbidden in query_concept');
|
|
509
|
+
assert.equal(threw.name, 'ExternalEgressViolation', 'must throw ExternalEgressViolation, got ' + (threw && threw.name));
|
|
510
|
+
assert.equal(threw.meta.surface, 'rs-sqlite-mirror');
|
|
511
|
+
const afterN = await countNodes(tmp.roomDir);
|
|
512
|
+
assert.equal(afterN, beforeN, 'no rows added on adversarial input');
|
|
513
|
+
} finally {
|
|
514
|
+
tmp.cleanup();
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
async function scenario12AdvForbiddenSmuggledViaExpertsInstitution() {
|
|
519
|
+
// CANON PART 8 strongest smuggling vector: forbidden bytes inside
|
|
520
|
+
// opts.context.experts[0].institution. The audit covers JSON.stringify
|
|
521
|
+
// of the entire SQL params object, including UNWIND $experts arrays.
|
|
522
|
+
const m = require('../core/rs-sqlite-mirror.cjs');
|
|
523
|
+
const tmp = makeTmpRoom();
|
|
524
|
+
try {
|
|
525
|
+
const pair = makeCleanPair();
|
|
526
|
+
const ctx = {
|
|
527
|
+
papers: [],
|
|
528
|
+
experts: [{
|
|
529
|
+
name: 'Smith, Jane',
|
|
530
|
+
orcid: '0000-0001-2345-6789',
|
|
531
|
+
// FORBIDDEN_PATTERNS[3] matches /\bmeeting with\b/i.
|
|
532
|
+
institution: 'noted during meeting with X University',
|
|
533
|
+
paper_count: 1,
|
|
534
|
+
h_index_estimate: 1,
|
|
535
|
+
public_email_or_null: null,
|
|
536
|
+
}],
|
|
537
|
+
room_id: 'test',
|
|
538
|
+
domain: 'biotech',
|
|
539
|
+
};
|
|
540
|
+
const beforeN = await countNodes(tmp.roomDir);
|
|
541
|
+
let threw = null;
|
|
542
|
+
try {
|
|
543
|
+
await m.writeDiscovery(pair, { roomDir: tmp.roomDir, context: ctx });
|
|
544
|
+
} catch (err) {
|
|
545
|
+
threw = err;
|
|
546
|
+
}
|
|
547
|
+
assert.ok(threw, 'must throw on forbidden in experts[].institution');
|
|
548
|
+
assert.equal(threw.name, 'ExternalEgressViolation', 'must throw ExternalEgressViolation, got ' + (threw && threw.name));
|
|
549
|
+
assert.equal(threw.meta.surface, 'rs-sqlite-mirror');
|
|
550
|
+
const afterN = await countNodes(tmp.roomDir);
|
|
551
|
+
assert.equal(afterN, beforeN, 'no rows added on adversarial UNWIND input');
|
|
552
|
+
} finally {
|
|
553
|
+
tmp.cleanup();
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// ---------- A1 end-of-suite sweep ----------
|
|
558
|
+
//
|
|
559
|
+
// SELECT every row from every tmp room.db; JSON.stringify the rows; scan
|
|
560
|
+
// against FORBIDDEN_PATTERNS. Asserts ZERO hits across the FINAL state of
|
|
561
|
+
// every database after all 12 scenarios.
|
|
562
|
+
|
|
563
|
+
async function a1Sweep() {
|
|
564
|
+
let hits = 0;
|
|
565
|
+
for (const roomDir of tmpRooms) {
|
|
566
|
+
if (!fs.existsSync(path.join(roomDir, '.mindrian', 'room.db'))) continue;
|
|
567
|
+
let nodes, edges;
|
|
568
|
+
try {
|
|
569
|
+
nodes = await selectAllNodes(roomDir);
|
|
570
|
+
edges = await selectAllEdges(roomDir);
|
|
571
|
+
} catch (_e) {
|
|
572
|
+
// Room may have been cleaned up; skip.
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
const json = JSON.stringify({ nodes: nodes, edges: edges });
|
|
576
|
+
for (const re of FORBIDDEN_PATTERNS) {
|
|
577
|
+
if (re.test(json)) {
|
|
578
|
+
hits += 1;
|
|
579
|
+
process.stderr.write(
|
|
580
|
+
' A1 HIT roomDir=' + roomDir +
|
|
581
|
+
' pattern=' + re.source + '\n'
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return hits;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// ---------- Cleanup all tmp rooms ----------
|
|
590
|
+
|
|
591
|
+
function cleanupAllTmpRooms() {
|
|
592
|
+
for (const roomDir of tmpRooms) {
|
|
593
|
+
try {
|
|
594
|
+
fs.rmSync(roomDir, { recursive: true, force: true });
|
|
595
|
+
} catch (_e) { /* best effort */ }
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// ---------- Main ----------
|
|
600
|
+
|
|
601
|
+
(async function main() {
|
|
602
|
+
process.stdout.write('=== 89.3-02 rs-sqlite-mirror suite (REPO_ROOT=' +
|
|
603
|
+
REPO_ROOT + ') ===\n');
|
|
604
|
+
|
|
605
|
+
await runScenario('1-happy-minimal', scenario1HappyPathMinimal);
|
|
606
|
+
await runScenario('2-real-idempotency-INSERT-OR-REPLACE', scenario2RealIdempotencyViaInsertOrReplace);
|
|
607
|
+
await runScenario('3-full-happy-with-context', scenario3FullHappyPathWithContext);
|
|
608
|
+
await runScenario('4-all-five-edge-types', scenario4AllFiveEdgeTypesWritten);
|
|
609
|
+
await runScenario('5-rollback-sql-captured', scenario5RollbackSqlCaptured);
|
|
610
|
+
await runScenario('6-missing-classification-throws', scenario6MissingClassificationThrows);
|
|
611
|
+
await runScenario('7-missing-thesis-throws', scenario7MissingThesisThrows);
|
|
612
|
+
await runScenario('8-room-db-unreachable-throws', scenario8RoomDbUnreachableThrows);
|
|
613
|
+
await runScenario('9-adv-CANON-PART-8-thesis', scenario9AdvForbiddenInThesis);
|
|
614
|
+
await runScenario('10-adv-CANON-PART-8-bridge_concept', scenario10AdvForbiddenInBridgeConcept);
|
|
615
|
+
await runScenario('11-adv-CANON-PART-8-query_concept', scenario11AdvForbiddenInQueryConcept);
|
|
616
|
+
await runScenario('12-adv-CANON-PART-8-experts-institution-smuggling', scenario12AdvForbiddenSmuggledViaExpertsInstitution);
|
|
617
|
+
|
|
618
|
+
const sweepHits = await a1Sweep();
|
|
619
|
+
if (sweepHits > 0) {
|
|
620
|
+
failed += 1;
|
|
621
|
+
failures.push({
|
|
622
|
+
name: 'A1-sweep',
|
|
623
|
+
err: new Error('A1 sweep found ' + sweepHits + ' forbidden-pattern hits in room.db final state'),
|
|
624
|
+
});
|
|
625
|
+
process.stderr.write(' FAIL A1-sweep (' + sweepHits + ' hits)\n');
|
|
626
|
+
} else {
|
|
627
|
+
process.stdout.write(' ok A1-sweep (zero forbidden-pattern hits in room.db final state)\n');
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
cleanupAllTmpRooms();
|
|
631
|
+
|
|
632
|
+
if (failed > 0) {
|
|
633
|
+
process.stdout.write('=== 89.3-02 rs-sqlite-mirror suite: ' +
|
|
634
|
+
passed + '/12 passed' +
|
|
635
|
+
' (' + failed + ' failed) ===\n');
|
|
636
|
+
for (const f of failures) {
|
|
637
|
+
process.stderr.write(' FAILURE: ' + f.name + ': ' +
|
|
638
|
+
(f.err && f.err.message ? f.err.message : String(f.err)) + '\n');
|
|
639
|
+
}
|
|
640
|
+
process.exit(1);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
process.stdout.write('=== 89.3-02 rs-sqlite-mirror suite: 12/12 passed ===\n');
|
|
644
|
+
process.exit(0);
|
|
645
|
+
})().catch(function (err) {
|
|
646
|
+
cleanupAllTmpRooms();
|
|
647
|
+
process.stderr.write('FATAL: ' + (err && err.stack ? err.stack : err) + '\n');
|
|
648
|
+
process.exit(2);
|
|
649
|
+
});
|