@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,762 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Copyright (c) 2026 Mindrian. BSL 1.1.
|
|
5
|
+
*
|
|
6
|
+
* Phase 90-06 -- cross-room aggregator (Canon Part 8 four-tripwire)
|
|
7
|
+
* =================================================================
|
|
8
|
+
* Walks ~/MindrianRooms/ via the Phase 83 .rooms/registry.json, reads
|
|
9
|
+
* the per-section quadruple (ROOM + STATE + MINTO + BRAIN) through
|
|
10
|
+
* folder-memory.readQuadruple, and computes structural-only
|
|
11
|
+
* contradictions between the current room and its registered peers.
|
|
12
|
+
*
|
|
13
|
+
* Canon Part 8 (Graph Boundary) is the LOAD-BEARING invariant. The
|
|
14
|
+
* aggregator reads MULTIPLE rooms' bytes. Every output path must carry
|
|
15
|
+
* ONLY:
|
|
16
|
+
* - room slug (structural folder name)
|
|
17
|
+
* - section name (slug-safe short string)
|
|
18
|
+
* - frozen contradiction_type enum
|
|
19
|
+
* - frozen problem_type enum ('UDP'|'IDP'|'WDP')
|
|
20
|
+
* - sha256 hash prefix (governing_thought_hash)
|
|
21
|
+
* - scalar confidence in [0,1]
|
|
22
|
+
* Everything else is redacted.
|
|
23
|
+
*
|
|
24
|
+
* Four tripwires now active in Phase 90:
|
|
25
|
+
* 1. Plan 90-00 schema doc frontmatter SCALAR regex scan
|
|
26
|
+
* 2. Plan 90-01 prompt allowlist outbound Brain query allow-list
|
|
27
|
+
* 3. Plan 90-05 invariants BRAIN.md body regex scan
|
|
28
|
+
* 4. Plan 90-06 aggregator sanitizeDetailScalar + JSON.stringify
|
|
29
|
+
* output audit (LAST LINE OF DEFENSE)
|
|
30
|
+
*
|
|
31
|
+
* Canon Part 8 ENFORCEMENT LAYERS in THIS module (four defense layers):
|
|
32
|
+
* L1. ALLOWED_ROOT: every peer path absolute-resolved and rejected if
|
|
33
|
+
* it does not start with ~/MindrianRooms/ (out_of_scope).
|
|
34
|
+
* L2. GUARDRAIL.md: any room with a top-level GUARDRAIL.md is sealed
|
|
35
|
+
* per Phase 83 contract; skipped with skip_reason 'sealed_room'.
|
|
36
|
+
* L3. Per-room opt-out: ROOM.md frontmatter brain_cross_room:false
|
|
37
|
+
* signals explicit opt-out; skipped with skip_reason 'opt_out'.
|
|
38
|
+
* L4. sanitizeDetailScalar + FORBIDDEN_PATTERNS scan on the final
|
|
39
|
+
* JSON.stringify(result); any hit is redacted and a stderr WARN
|
|
40
|
+
* is emitted (defense in depth -- should never fire).
|
|
41
|
+
*
|
|
42
|
+
* Registry-less fallback: if .rooms/registry.json is missing or
|
|
43
|
+
* malformed, returns {contradictions:[], scanned_rooms:0,
|
|
44
|
+
* skipped_rooms:0, reason:'no_registry'} -- graceful degradation like
|
|
45
|
+
* Phase 88-13 guardian.
|
|
46
|
+
*
|
|
47
|
+
* Pure CJS, node built-ins only, zero new deps. Three-surface safe.
|
|
48
|
+
*
|
|
49
|
+
* License: BSL 1.1.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
const fs = require('node:fs');
|
|
53
|
+
const path = require('node:path');
|
|
54
|
+
const os = require('node:os');
|
|
55
|
+
|
|
56
|
+
const folderMemory = require('./folder-memory.cjs');
|
|
57
|
+
|
|
58
|
+
// ---------- Frozen constants (Canon Part 8 invariants) ----------
|
|
59
|
+
|
|
60
|
+
// ALLOWED_ROOT: canonical ~/MindrianRooms/ absolute path. Overridable
|
|
61
|
+
// via MINDRIAN_ROOMS_ROOT for tests; every code path still calls
|
|
62
|
+
// path.resolve + startsWith against this base.
|
|
63
|
+
const ALLOWED_ROOT = (function () {
|
|
64
|
+
const envRoot = process.env.MINDRIAN_ROOMS_ROOT;
|
|
65
|
+
if (typeof envRoot === 'string' && envRoot.length > 0) {
|
|
66
|
+
return path.resolve(envRoot);
|
|
67
|
+
}
|
|
68
|
+
return path.resolve(os.homedir(), 'MindrianRooms');
|
|
69
|
+
})();
|
|
70
|
+
|
|
71
|
+
// Contradiction type enum (frozen).
|
|
72
|
+
const CONTRADICTION_TYPES = Object.freeze([
|
|
73
|
+
'hash_divergence',
|
|
74
|
+
'framework_contradiction',
|
|
75
|
+
'problem_type_mismatch',
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
// Problem type enum (frozen, mirrors Plan 90-01 classifyProblemType).
|
|
79
|
+
const PROBLEM_TYPE_ENUM = Object.freeze(['UDP', 'IDP', 'WDP']);
|
|
80
|
+
|
|
81
|
+
// FORBIDDEN_PATTERNS: the Canon Part 8 regex set, mirrored byte-for-
|
|
82
|
+
// byte from Plan 90-05 invariants validator FORBIDDEN_PATTERNS + Plan
|
|
83
|
+
// 90-00 schema heuristics. Any string matching any of these patterns
|
|
84
|
+
// must NEVER leak into aggregator output. sanitizeDetailScalar strips
|
|
85
|
+
// them at the detail-object layer; the JSON.stringify audit is the
|
|
86
|
+
// last line of defense.
|
|
87
|
+
const FORBIDDEN_PATTERNS = Object.freeze([
|
|
88
|
+
/@[a-zA-Z0-9._-]+\.[a-zA-Z]{2,}/, // email
|
|
89
|
+
/\$[0-9][\d,.]*[KMB]?\b/, // currency magnitude
|
|
90
|
+
/\b(Lawrence|Jonathan|Nimrod|Oren|Dror)\s+(said|revealed|told|mentioned)\b/i, // quoted-person
|
|
91
|
+
/\bmeeting with\b/i, // meeting fragment
|
|
92
|
+
/\b\d{3}-\d{2}-\d{4}\b/, // SSN-like
|
|
93
|
+
/\b\+?1?[\s.\-]?\(?\d{3}\)?[\s.\-]?\d{3}[\s.\-]?\d{4}\b/, // phone-like
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
const DETAIL_STRING_MAX = 40; // detail_scalar strings must be short & structural
|
|
97
|
+
|
|
98
|
+
// Budget defaults.
|
|
99
|
+
const DEFAULT_MAX_ROOMS = 10;
|
|
100
|
+
const DEFAULT_PER_ROOM_TIMEOUT_MS = 300;
|
|
101
|
+
|
|
102
|
+
// Sections to cross-scan. For now we anchor on ANY shared slug; the
|
|
103
|
+
// scanner walks every section directory in each room.
|
|
104
|
+
// Reserved system slugs we skip (mirrors folder-memory-shared RESERVED).
|
|
105
|
+
const RESERVED_TOPLEVEL = Object.freeze(new Set([
|
|
106
|
+
'.rooms', '.mindrian', '.room-graph', '.snapshots', '.context', '.intelligence',
|
|
107
|
+
'assets', 'team', 'meetings', '_archive',
|
|
108
|
+
]));
|
|
109
|
+
|
|
110
|
+
// ---------- Canon Part 8 L2: GUARDRAIL.md sealed-room detection ----------
|
|
111
|
+
|
|
112
|
+
function isRoomSealed(roomPath) {
|
|
113
|
+
try {
|
|
114
|
+
const gp = path.join(roomPath, 'GUARDRAIL.md');
|
|
115
|
+
return fs.existsSync(gp);
|
|
116
|
+
} catch (_e) {
|
|
117
|
+
// Treat stat failure as sealed (fail-closed on this specific check)
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ---------- Canon Part 8 L3: per-room opt-out (ROOM.md frontmatter) ----------
|
|
123
|
+
|
|
124
|
+
function isRoomOptedOut(roomPath) {
|
|
125
|
+
try {
|
|
126
|
+
const rp = path.join(roomPath, 'ROOM.md');
|
|
127
|
+
const st = fs.statSync(rp);
|
|
128
|
+
if (!st.isFile()) return false;
|
|
129
|
+
// Only read the first 2KB; frontmatter is always at the top.
|
|
130
|
+
const fd = fs.openSync(rp, 'r');
|
|
131
|
+
try {
|
|
132
|
+
const buf = Buffer.alloc(2048);
|
|
133
|
+
const bytes = fs.readSync(fd, buf, 0, 2048, 0);
|
|
134
|
+
const head = buf.slice(0, bytes).toString('utf8');
|
|
135
|
+
// Narrow-dialect scan: 'brain_cross_room: false' on a frontmatter
|
|
136
|
+
// line. Accept true/false as boolean or string.
|
|
137
|
+
if (/^brain_cross_room\s*:\s*false\b/mi.test(head)) return true;
|
|
138
|
+
return false;
|
|
139
|
+
} finally {
|
|
140
|
+
try { fs.closeSync(fd); } catch (_e) { /* best effort */ }
|
|
141
|
+
}
|
|
142
|
+
} catch (_e) {
|
|
143
|
+
return false; // absence of ROOM.md is not an opt-out signal
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ---------- Canon Part 8 L1: absolute-path scope check ----------
|
|
148
|
+
|
|
149
|
+
function isRoomInScope(roomPath) {
|
|
150
|
+
try {
|
|
151
|
+
const resolved = path.resolve(roomPath);
|
|
152
|
+
const base = ALLOWED_ROOT + path.sep;
|
|
153
|
+
if (resolved === ALLOWED_ROOT) return false; // the root itself is not a room
|
|
154
|
+
return resolved.startsWith(base);
|
|
155
|
+
} catch (_e) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ---------- Unreadable detection ----------
|
|
161
|
+
|
|
162
|
+
function isRoomUnreadable(roomPath, slug) {
|
|
163
|
+
// Deterministic override for tests (chmod-0 does not block reads under root)
|
|
164
|
+
const forceUnreadable = process.env.MINDRIAN_XROOM_FORCE_UNREADABLE;
|
|
165
|
+
if (typeof forceUnreadable === 'string' && forceUnreadable.length > 0) {
|
|
166
|
+
const slugs = forceUnreadable.split(',').map(function (s) { return s.trim(); });
|
|
167
|
+
if (slugs.indexOf(slug) !== -1) return true;
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
fs.accessSync(roomPath, fs.constants.R_OK | fs.constants.X_OK);
|
|
171
|
+
// Also try a readdir -- chmod-0 on dir blocks this even if access says OK
|
|
172
|
+
fs.readdirSync(roomPath);
|
|
173
|
+
return false;
|
|
174
|
+
} catch (_e) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ---------- Registry discovery ----------
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* discoverRegisteredRooms() -> {rooms: [{slug, path}], reason?}
|
|
183
|
+
*
|
|
184
|
+
* Reads ~/MindrianRooms/.rooms/registry.json. Returns rooms array with
|
|
185
|
+
* absolute-resolved paths. On missing or malformed registry returns
|
|
186
|
+
* reason:'no_registry' with an empty rooms array (graceful fallback).
|
|
187
|
+
*/
|
|
188
|
+
function discoverRegisteredRooms() {
|
|
189
|
+
const registryPath = path.join(ALLOWED_ROOT, '.rooms', 'registry.json');
|
|
190
|
+
let raw;
|
|
191
|
+
try {
|
|
192
|
+
raw = fs.readFileSync(registryPath, 'utf8');
|
|
193
|
+
} catch (_e) {
|
|
194
|
+
return { rooms: [], reason: 'no_registry' };
|
|
195
|
+
}
|
|
196
|
+
let parsed;
|
|
197
|
+
try {
|
|
198
|
+
parsed = JSON.parse(raw);
|
|
199
|
+
} catch (err) {
|
|
200
|
+
try {
|
|
201
|
+
process.stderr.write(
|
|
202
|
+
'[cross-room-aggregator] registry malformed; graceful fallback: ' +
|
|
203
|
+
(err && err.message ? err.message : 'JSON parse error') + '\n'
|
|
204
|
+
);
|
|
205
|
+
} catch (_e) { /* best effort */ }
|
|
206
|
+
return { rooms: [], reason: 'no_registry' };
|
|
207
|
+
}
|
|
208
|
+
if (!parsed || typeof parsed !== 'object' || !parsed.rooms || typeof parsed.rooms !== 'object') {
|
|
209
|
+
return { rooms: [], reason: 'no_registry' };
|
|
210
|
+
}
|
|
211
|
+
const out = [];
|
|
212
|
+
for (const slug of Object.keys(parsed.rooms)) {
|
|
213
|
+
const entry = parsed.rooms[slug];
|
|
214
|
+
if (!entry || typeof entry !== 'object') continue;
|
|
215
|
+
// Slug-sanitize defensively
|
|
216
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/.test(slug)) continue;
|
|
217
|
+
const relPath = typeof entry.path === 'string' && entry.path.length > 0
|
|
218
|
+
? entry.path
|
|
219
|
+
: slug;
|
|
220
|
+
// Resolve relative to ALLOWED_ROOT (same contract as Phase 83)
|
|
221
|
+
const absPath = path.resolve(ALLOWED_ROOT, relPath);
|
|
222
|
+
out.push({ slug: slug, path: absPath, entry: entry });
|
|
223
|
+
}
|
|
224
|
+
return { rooms: out };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ---------- Canon Part 8 L4: sanitizeDetailScalar ----------
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* sanitizeDetailScalar(obj) -> obj
|
|
231
|
+
*
|
|
232
|
+
* Walks the detail scalar object. Strips any string value longer than
|
|
233
|
+
* DETAIL_STRING_MAX OR matching any FORBIDDEN_PATTERNS regex. Replaces
|
|
234
|
+
* with the literal token '[redacted]'. Non-string primitives pass
|
|
235
|
+
* through unchanged. Nested objects / arrays are rejected (detail
|
|
236
|
+
* scalars are flat by contract); any nested structure is replaced with
|
|
237
|
+
* '[redacted-nested]'.
|
|
238
|
+
*
|
|
239
|
+
* THIS IS THE THIRD LAYER OF DEFENSE for Canon Part 8 inside the
|
|
240
|
+
* aggregator (after ALLOWED_ROOT scope + GUARDRAIL.md + opt-out).
|
|
241
|
+
*/
|
|
242
|
+
function sanitizeDetailScalar(obj) {
|
|
243
|
+
if (!obj || typeof obj !== 'object') return {};
|
|
244
|
+
const out = {};
|
|
245
|
+
for (const k of Object.keys(obj)) {
|
|
246
|
+
const v = obj[k];
|
|
247
|
+
if (v === null || typeof v === 'number' || typeof v === 'boolean') {
|
|
248
|
+
out[k] = v;
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (typeof v === 'string') {
|
|
252
|
+
if (v.length > DETAIL_STRING_MAX) {
|
|
253
|
+
out[k] = '[redacted]';
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
let matched = false;
|
|
257
|
+
for (const re of FORBIDDEN_PATTERNS) {
|
|
258
|
+
if (re.test(v)) { matched = true; break; }
|
|
259
|
+
}
|
|
260
|
+
if (matched) {
|
|
261
|
+
out[k] = '[redacted]';
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
out[k] = v;
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
// Arrays, nested objects, functions -> redact aggressively
|
|
268
|
+
out[k] = '[redacted-nested]';
|
|
269
|
+
}
|
|
270
|
+
return out;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ---------- Structural slug cleaner ----------
|
|
274
|
+
|
|
275
|
+
function safeSlug(raw) {
|
|
276
|
+
if (typeof raw !== 'string') return 'unknown';
|
|
277
|
+
const s = raw.toLowerCase().replace(/[^a-z0-9_-]/g, '-').slice(0, 64);
|
|
278
|
+
return /^[a-z0-9][a-z0-9_-]{0,63}$/.test(s) ? s : 'unknown';
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ---------- Per-section scan (reads quadruple, extracts structural signals) ----------
|
|
282
|
+
|
|
283
|
+
function scanRoomSections(roomPath) {
|
|
284
|
+
const out = [];
|
|
285
|
+
let entries;
|
|
286
|
+
try {
|
|
287
|
+
entries = fs.readdirSync(roomPath);
|
|
288
|
+
} catch (_e) {
|
|
289
|
+
return out;
|
|
290
|
+
}
|
|
291
|
+
for (const name of entries) {
|
|
292
|
+
if (name[0] === '.') continue;
|
|
293
|
+
if (RESERVED_TOPLEVEL.has(name)) continue;
|
|
294
|
+
const abs = path.join(roomPath, name);
|
|
295
|
+
let st;
|
|
296
|
+
try { st = fs.statSync(abs); } catch (_e) { continue; }
|
|
297
|
+
if (!st.isDirectory()) continue;
|
|
298
|
+
// Probe quadruple
|
|
299
|
+
let q;
|
|
300
|
+
try {
|
|
301
|
+
q = folderMemory.readQuadruple(abs);
|
|
302
|
+
} catch (_e) {
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
// Extract structural signals only.
|
|
306
|
+
const brain = q && q.brain;
|
|
307
|
+
const reasoning = q && q.reasoning;
|
|
308
|
+
// Problem type: prefer BRAIN.md frontmatter scalar if present; else
|
|
309
|
+
// derive a lightweight enum from reasoning shape (so peers without
|
|
310
|
+
// BRAIN.md still contribute data). We never read governing_thought
|
|
311
|
+
// text -- only an existence flag.
|
|
312
|
+
let problemType = null;
|
|
313
|
+
if (brain && brain.exists && typeof brain.sections === 'object') {
|
|
314
|
+
// BRAIN.md doesn't store problem_type as a first-class frontmatter
|
|
315
|
+
// field in 90-00 schema, but generators DO emit it. Check via a
|
|
316
|
+
// narrow frontmatter re-read is overkill; we instead sniff the
|
|
317
|
+
// BRAIN.md file for the frontmatter 'problem_type:' scalar.
|
|
318
|
+
const brainMdPath = path.join(abs, 'BRAIN.md');
|
|
319
|
+
problemType = extractProblemTypeFromBrain(brainMdPath);
|
|
320
|
+
}
|
|
321
|
+
// Governing-thought hash: prefer BRAIN.md stored hash (already a
|
|
322
|
+
// sha256 prefix, hashed upstream per Canon Part 8). Fallback: null.
|
|
323
|
+
const gtHash = (brain && typeof brain.governing_thought_hash === 'string')
|
|
324
|
+
? brain.governing_thought_hash
|
|
325
|
+
: null;
|
|
326
|
+
// Framework chain signature: a bounded scalar hash of the Framework
|
|
327
|
+
// Chain Predictions section body (if present). Never the body text.
|
|
328
|
+
const fwSig = (brain && brain.sections && brain.sections.framework_chain_predictions)
|
|
329
|
+
? shortFnv1a(brain.sections.framework_chain_predictions.body || '')
|
|
330
|
+
: null;
|
|
331
|
+
// Section reasoning presence
|
|
332
|
+
const reasoningExists = !!(reasoning && reasoning.exists);
|
|
333
|
+
out.push({
|
|
334
|
+
section: safeSlug(name),
|
|
335
|
+
governing_thought_hash: gtHash,
|
|
336
|
+
problem_type: problemType && PROBLEM_TYPE_ENUM.indexOf(problemType) !== -1 ? problemType : null,
|
|
337
|
+
framework_chain_sig: fwSig,
|
|
338
|
+
reasoning_exists: reasoningExists,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
return out;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* extractProblemTypeFromBrain(brainMdPath) -> 'UDP'|'IDP'|'WDP'|null
|
|
346
|
+
*
|
|
347
|
+
* Narrow frontmatter sniff. Canon Part 8 safe: reads ONLY the frontmatter
|
|
348
|
+
* key 'problem_type' which is a frozen enum by Plan 90-01 contract.
|
|
349
|
+
*/
|
|
350
|
+
function extractProblemTypeFromBrain(brainMdPath) {
|
|
351
|
+
try {
|
|
352
|
+
const st = fs.statSync(brainMdPath);
|
|
353
|
+
if (!st.isFile()) return null;
|
|
354
|
+
const fd = fs.openSync(brainMdPath, 'r');
|
|
355
|
+
try {
|
|
356
|
+
const buf = Buffer.alloc(4096);
|
|
357
|
+
const bytes = fs.readSync(fd, buf, 0, 4096, 0);
|
|
358
|
+
const head = buf.slice(0, bytes).toString('utf8');
|
|
359
|
+
const m = head.match(/^problem_type\s*:\s*([A-Za-z]{3,4})\b/m);
|
|
360
|
+
if (!m) return null;
|
|
361
|
+
const v = m[1].toUpperCase();
|
|
362
|
+
return PROBLEM_TYPE_ENUM.indexOf(v) !== -1 ? v : null;
|
|
363
|
+
} finally {
|
|
364
|
+
try { fs.closeSync(fd); } catch (_e) { /* best effort */ }
|
|
365
|
+
}
|
|
366
|
+
} catch (_e) {
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// ---------- Fast 32-bit scalar hash (zero crypto cost, structural only) ----------
|
|
372
|
+
|
|
373
|
+
function shortFnv1a(str) {
|
|
374
|
+
if (typeof str !== 'string' || str.length === 0) return 'fnv1a:0';
|
|
375
|
+
let h = 0x811c9dc5 >>> 0;
|
|
376
|
+
for (let i = 0; i < str.length; i++) {
|
|
377
|
+
h ^= str.charCodeAt(i);
|
|
378
|
+
h = Math.imul(h, 0x01000193) >>> 0;
|
|
379
|
+
}
|
|
380
|
+
return 'fnv1a:' + h.toString(16);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// ---------- Contradiction computation ----------
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* computeCrossRoomContradictions(currentSlug, currentSections,
|
|
387
|
+
* peerSlug, peerSections) -> [contradictions]
|
|
388
|
+
*
|
|
389
|
+
* Frozen rules, structural-only. Returns array of contradiction records
|
|
390
|
+
* each shaped as: {type, room_a_slug, room_b_slug, section, detail_scalar, confidence}.
|
|
391
|
+
*/
|
|
392
|
+
function computeCrossRoomContradictions(currentSlug, currentSections, peerSlug, peerSections) {
|
|
393
|
+
const out = [];
|
|
394
|
+
if (!Array.isArray(currentSections) || !Array.isArray(peerSections)) return out;
|
|
395
|
+
// Index peer sections by section slug for O(1) lookup
|
|
396
|
+
const peerBySection = new Map();
|
|
397
|
+
for (const ps of peerSections) peerBySection.set(ps.section, ps);
|
|
398
|
+
for (const cs of currentSections) {
|
|
399
|
+
const ps = peerBySection.get(cs.section);
|
|
400
|
+
if (!ps) continue;
|
|
401
|
+
// Rule A: hash_divergence -- both carry hashes and they differ
|
|
402
|
+
if (cs.governing_thought_hash && ps.governing_thought_hash &&
|
|
403
|
+
cs.governing_thought_hash !== ps.governing_thought_hash) {
|
|
404
|
+
out.push({
|
|
405
|
+
type: 'hash_divergence',
|
|
406
|
+
room_a_slug: safeSlug(currentSlug),
|
|
407
|
+
room_b_slug: safeSlug(peerSlug),
|
|
408
|
+
section: cs.section,
|
|
409
|
+
detail_scalar: sanitizeDetailScalar({
|
|
410
|
+
hash_a_prefix: String(cs.governing_thought_hash || '').slice(0, 14), // 'sha256:xxxxxx'
|
|
411
|
+
hash_b_prefix: String(ps.governing_thought_hash || '').slice(0, 14),
|
|
412
|
+
}),
|
|
413
|
+
confidence: 0.75,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
// Rule B: problem_type_mismatch -- both classified, different enums
|
|
417
|
+
if (cs.problem_type && ps.problem_type && cs.problem_type !== ps.problem_type) {
|
|
418
|
+
out.push({
|
|
419
|
+
type: 'problem_type_mismatch',
|
|
420
|
+
room_a_slug: safeSlug(currentSlug),
|
|
421
|
+
room_b_slug: safeSlug(peerSlug),
|
|
422
|
+
section: cs.section,
|
|
423
|
+
detail_scalar: sanitizeDetailScalar({
|
|
424
|
+
problem_type_a: cs.problem_type,
|
|
425
|
+
problem_type_b: ps.problem_type,
|
|
426
|
+
}),
|
|
427
|
+
confidence: 0.8,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
// Rule C: framework_contradiction -- both carry framework chain
|
|
431
|
+
// signatures and they differ
|
|
432
|
+
if (cs.framework_chain_sig && ps.framework_chain_sig &&
|
|
433
|
+
cs.framework_chain_sig !== ps.framework_chain_sig) {
|
|
434
|
+
out.push({
|
|
435
|
+
type: 'framework_contradiction',
|
|
436
|
+
room_a_slug: safeSlug(currentSlug),
|
|
437
|
+
room_b_slug: safeSlug(peerSlug),
|
|
438
|
+
section: cs.section,
|
|
439
|
+
detail_scalar: sanitizeDetailScalar({
|
|
440
|
+
sig_a_prefix: String(cs.framework_chain_sig).slice(0, 16),
|
|
441
|
+
sig_b_prefix: String(ps.framework_chain_sig).slice(0, 16),
|
|
442
|
+
}),
|
|
443
|
+
confidence: 0.6,
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return out;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// ---------- Timeout wrapper ----------
|
|
451
|
+
|
|
452
|
+
function withTimeout(fn, timeoutMs) {
|
|
453
|
+
return new Promise(function (resolve, reject) {
|
|
454
|
+
let settled = false;
|
|
455
|
+
const timer = setTimeout(function () {
|
|
456
|
+
if (settled) return;
|
|
457
|
+
settled = true;
|
|
458
|
+
reject(new Error('timeout'));
|
|
459
|
+
}, Math.max(1, timeoutMs));
|
|
460
|
+
// Execute synchronously inside a microtask so timeout races correctly.
|
|
461
|
+
// Await fn() so promise-returning workloads actually race the timer.
|
|
462
|
+
Promise.resolve().then(async function () {
|
|
463
|
+
let r;
|
|
464
|
+
try {
|
|
465
|
+
r = await fn();
|
|
466
|
+
} catch (err) {
|
|
467
|
+
if (settled) return;
|
|
468
|
+
settled = true;
|
|
469
|
+
clearTimeout(timer);
|
|
470
|
+
reject(err);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
if (settled) return;
|
|
474
|
+
settled = true;
|
|
475
|
+
clearTimeout(timer);
|
|
476
|
+
resolve(r);
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// ---------- Main entry: aggregateContradictions ----------
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* aggregateContradictions(currentRoom, options) ->
|
|
485
|
+
* Promise<{contradictions, scanned_rooms, skipped_rooms, skip_reasons, reason?}>
|
|
486
|
+
*
|
|
487
|
+
* NEVER throws. Every failure mode becomes a structured result.
|
|
488
|
+
* Registry-less fallback: reason:'no_registry', empty contradictions.
|
|
489
|
+
*
|
|
490
|
+
* options:
|
|
491
|
+
* opt_in -- caller signals this is an opt-in per-call
|
|
492
|
+
* max_rooms -- cap scanned peer count (default 10)
|
|
493
|
+
* per_room_timeout_ms -- per-peer scan timeout (default 300)
|
|
494
|
+
*/
|
|
495
|
+
async function aggregateContradictions(currentRoom, options) {
|
|
496
|
+
options = options || {};
|
|
497
|
+
const maxRooms = Number.isFinite(options.max_rooms) && options.max_rooms > 0
|
|
498
|
+
? Math.floor(options.max_rooms)
|
|
499
|
+
: DEFAULT_MAX_ROOMS;
|
|
500
|
+
const perRoomTimeout = Number.isFinite(options.per_room_timeout_ms) && options.per_room_timeout_ms > 0
|
|
501
|
+
? Math.floor(options.per_room_timeout_ms)
|
|
502
|
+
: DEFAULT_PER_ROOM_TIMEOUT_MS;
|
|
503
|
+
|
|
504
|
+
const result = {
|
|
505
|
+
contradictions: [],
|
|
506
|
+
scanned_rooms: 0,
|
|
507
|
+
skipped_rooms: 0,
|
|
508
|
+
skip_reasons: {
|
|
509
|
+
sealed_room: 0,
|
|
510
|
+
opt_out: 0,
|
|
511
|
+
out_of_scope: 0,
|
|
512
|
+
unreadable: 0,
|
|
513
|
+
timeout: 0,
|
|
514
|
+
max_rooms_exceeded: 0,
|
|
515
|
+
self: 0,
|
|
516
|
+
empty: 0,
|
|
517
|
+
},
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
// Input validation: currentRoom must be a string path; treat malformed
|
|
521
|
+
// input as no_registry-style fallback.
|
|
522
|
+
if (typeof currentRoom !== 'string' || currentRoom.length === 0) {
|
|
523
|
+
result.reason = 'no_registry';
|
|
524
|
+
return result;
|
|
525
|
+
}
|
|
526
|
+
const currentRoomAbs = path.resolve(currentRoom);
|
|
527
|
+
const currentSlug = safeSlug(path.basename(currentRoomAbs));
|
|
528
|
+
|
|
529
|
+
// Discover registered rooms.
|
|
530
|
+
const discovery = discoverRegisteredRooms();
|
|
531
|
+
if (discovery.reason === 'no_registry') {
|
|
532
|
+
result.reason = 'no_registry';
|
|
533
|
+
return result;
|
|
534
|
+
}
|
|
535
|
+
const rooms = discovery.rooms;
|
|
536
|
+
|
|
537
|
+
// Scan current room sections ONCE
|
|
538
|
+
let currentSections = [];
|
|
539
|
+
try {
|
|
540
|
+
currentSections = scanRoomSections(currentRoomAbs);
|
|
541
|
+
} catch (_e) {
|
|
542
|
+
currentSections = [];
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Walk peers
|
|
546
|
+
let scannedCount = 0;
|
|
547
|
+
for (const r of rooms) {
|
|
548
|
+
// L1: scope check
|
|
549
|
+
if (!isRoomInScope(r.path)) {
|
|
550
|
+
result.skipped_rooms += 1;
|
|
551
|
+
result.skip_reasons.out_of_scope += 1;
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
// Self-exclusion
|
|
555
|
+
if (path.resolve(r.path) === currentRoomAbs) {
|
|
556
|
+
// Self is not counted as skipped (by convention); separate reason
|
|
557
|
+
// for clarity.
|
|
558
|
+
result.skip_reasons.self += 1;
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
// max_rooms cap
|
|
562
|
+
if (scannedCount >= maxRooms) {
|
|
563
|
+
result.skipped_rooms += 1;
|
|
564
|
+
result.skip_reasons.max_rooms_exceeded += 1;
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
// L2: sealed
|
|
568
|
+
if (isRoomSealed(r.path)) {
|
|
569
|
+
result.skipped_rooms += 1;
|
|
570
|
+
result.skip_reasons.sealed_room += 1;
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
// L3: opt-out
|
|
574
|
+
if (isRoomOptedOut(r.path)) {
|
|
575
|
+
result.skipped_rooms += 1;
|
|
576
|
+
result.skip_reasons.opt_out += 1;
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
// Unreadable
|
|
580
|
+
if (isRoomUnreadable(r.path, r.slug)) {
|
|
581
|
+
result.skipped_rooms += 1;
|
|
582
|
+
result.skip_reasons.unreadable += 1;
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
// Scan (with timeout wrapper)
|
|
586
|
+
let peerSections;
|
|
587
|
+
try {
|
|
588
|
+
// Test hook: force timeout deterministically
|
|
589
|
+
const forceTimeout = process.env.MINDRIAN_XROOM_FORCE_TIMEOUT === '1';
|
|
590
|
+
if (forceTimeout) {
|
|
591
|
+
// Simulate a slow scan that exceeds the timeout
|
|
592
|
+
await withTimeout(function () {
|
|
593
|
+
// Busy-wait would block the microtask; use setTimeout via a
|
|
594
|
+
// separate promise so the timeout fires first.
|
|
595
|
+
return new Promise(function (resolve) {
|
|
596
|
+
setTimeout(function () { resolve([]); }, perRoomTimeout + 50);
|
|
597
|
+
});
|
|
598
|
+
}, perRoomTimeout);
|
|
599
|
+
peerSections = [];
|
|
600
|
+
} else {
|
|
601
|
+
peerSections = await withTimeout(function () {
|
|
602
|
+
return scanRoomSections(r.path);
|
|
603
|
+
}, perRoomTimeout);
|
|
604
|
+
}
|
|
605
|
+
} catch (err) {
|
|
606
|
+
// Any error (timeout or otherwise) -> skipped as timeout
|
|
607
|
+
result.skipped_rooms += 1;
|
|
608
|
+
result.skip_reasons.timeout += 1;
|
|
609
|
+
continue;
|
|
610
|
+
}
|
|
611
|
+
// Compute contradictions vs current room
|
|
612
|
+
try {
|
|
613
|
+
const cs = computeCrossRoomContradictions(
|
|
614
|
+
currentSlug, currentSections, r.slug, peerSections
|
|
615
|
+
);
|
|
616
|
+
for (const c of cs) result.contradictions.push(c);
|
|
617
|
+
} catch (_e) {
|
|
618
|
+
// Never propagate a compute error; count as empty scan
|
|
619
|
+
}
|
|
620
|
+
scannedCount += 1;
|
|
621
|
+
}
|
|
622
|
+
result.scanned_rooms = scannedCount;
|
|
623
|
+
|
|
624
|
+
// Canon Part 8 LAST-LINE-OF-DEFENSE audit:
|
|
625
|
+
// JSON.stringify the final payload, scan against FORBIDDEN_PATTERNS.
|
|
626
|
+
// Any hit triggers redaction of the offending contradiction and a
|
|
627
|
+
// stderr WARN. This should never fire in production; the
|
|
628
|
+
// sanitizeDetailScalar + structural-only compute rules should have
|
|
629
|
+
// caught it already. This is the defense-in-depth layer.
|
|
630
|
+
try {
|
|
631
|
+
const serialized = JSON.stringify(result);
|
|
632
|
+
let anyHit = false;
|
|
633
|
+
for (const re of FORBIDDEN_PATTERNS) {
|
|
634
|
+
if (re.test(serialized)) { anyHit = true; break; }
|
|
635
|
+
}
|
|
636
|
+
if (anyHit) {
|
|
637
|
+
try {
|
|
638
|
+
process.stderr.write(
|
|
639
|
+
'[cross-room-aggregator] canon_part_8 WARN: forbidden pattern matched in output; redacting contradictions\n'
|
|
640
|
+
);
|
|
641
|
+
} catch (_e) { /* best effort */ }
|
|
642
|
+
// Redact every contradiction's detail_scalar aggressively
|
|
643
|
+
const redacted = [];
|
|
644
|
+
for (const c of result.contradictions) {
|
|
645
|
+
let redactedScalar = {};
|
|
646
|
+
try {
|
|
647
|
+
redactedScalar = sanitizeDetailScalar(c.detail_scalar || {});
|
|
648
|
+
// Second-pass: if any string survived, mass-redact it
|
|
649
|
+
for (const k of Object.keys(redactedScalar)) {
|
|
650
|
+
const v = redactedScalar[k];
|
|
651
|
+
if (typeof v === 'string') {
|
|
652
|
+
for (const re2 of FORBIDDEN_PATTERNS) {
|
|
653
|
+
if (re2.test(v)) {
|
|
654
|
+
redactedScalar[k] = '[redacted]';
|
|
655
|
+
break;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
} catch (_e) {
|
|
661
|
+
redactedScalar = { _redacted: true };
|
|
662
|
+
}
|
|
663
|
+
redacted.push({
|
|
664
|
+
type: c.type,
|
|
665
|
+
room_a_slug: safeSlug(c.room_a_slug),
|
|
666
|
+
room_b_slug: safeSlug(c.room_b_slug),
|
|
667
|
+
section: safeSlug(c.section),
|
|
668
|
+
detail_scalar: redactedScalar,
|
|
669
|
+
confidence: (typeof c.confidence === 'number') ? c.confidence : 0,
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
result.contradictions = redacted;
|
|
673
|
+
}
|
|
674
|
+
} catch (_e) {
|
|
675
|
+
// If even the audit failed, return empty contradictions with a
|
|
676
|
+
// redaction marker rather than leak.
|
|
677
|
+
result.contradictions = [];
|
|
678
|
+
result.reason = result.reason || 'canon_part_8_audit_failed';
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
return result;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// ---------- Render helper (deriveSection integration) ----------
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* renderCrossRoomSection(result) -> string
|
|
688
|
+
*
|
|
689
|
+
* Render aggregator result into the BRAIN.md 'Flagged Contradictions
|
|
690
|
+
* (cross-room)' body. Structural slug-based entries only. Max 10 lines;
|
|
691
|
+
* if more, appends "... and N more".
|
|
692
|
+
*/
|
|
693
|
+
function renderCrossRoomSection(result) {
|
|
694
|
+
if (!result || typeof result !== 'object') return '(no signal)';
|
|
695
|
+
if (!Array.isArray(result.contradictions) || result.contradictions.length === 0) {
|
|
696
|
+
if (result.reason === 'no_registry') return '(no signal -- no registry)';
|
|
697
|
+
return '(no signal)';
|
|
698
|
+
}
|
|
699
|
+
const MAX_LINES = 10;
|
|
700
|
+
const lines = [];
|
|
701
|
+
const n = result.contradictions.length;
|
|
702
|
+
const toRender = result.contradictions.slice(0, MAX_LINES);
|
|
703
|
+
for (const c of toRender) {
|
|
704
|
+
// Defensive: sanitize every field at render time too (fifth layer)
|
|
705
|
+
const safeType = CONTRADICTION_TYPES.indexOf(c.type) !== -1 ? c.type : 'unknown';
|
|
706
|
+
const section = safeSlug(c.section);
|
|
707
|
+
const peer = safeSlug(c.room_b_slug);
|
|
708
|
+
// Render ONLY structural fields
|
|
709
|
+
let detailRender = '';
|
|
710
|
+
const ds = sanitizeDetailScalar(c.detail_scalar || {});
|
|
711
|
+
const pairs = [];
|
|
712
|
+
for (const k of Object.keys(ds)) {
|
|
713
|
+
const v = ds[k];
|
|
714
|
+
if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean' || v === null) {
|
|
715
|
+
pairs.push(k + '=' + String(v));
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
detailRender = pairs.join(' ');
|
|
719
|
+
lines.push('- ' + section + ': ' + safeType + ' vs ' + peer + ' (' + detailRender + ')');
|
|
720
|
+
}
|
|
721
|
+
if (n > MAX_LINES) {
|
|
722
|
+
lines.push('... and ' + (n - MAX_LINES) + ' more');
|
|
723
|
+
}
|
|
724
|
+
// Last-line-of-defense body scan
|
|
725
|
+
const body = lines.join('\n');
|
|
726
|
+
for (const re of FORBIDDEN_PATTERNS) {
|
|
727
|
+
if (re.test(body)) {
|
|
728
|
+
try {
|
|
729
|
+
process.stderr.write(
|
|
730
|
+
'[cross-room-aggregator] canon_part_8 render WARN: body matched forbidden regex; emitting placeholder\n'
|
|
731
|
+
);
|
|
732
|
+
} catch (_e) { /* best effort */ }
|
|
733
|
+
return '(no signal -- redacted)';
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
return body;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// ---------- Exports ----------
|
|
740
|
+
|
|
741
|
+
module.exports = {
|
|
742
|
+
aggregateContradictions: aggregateContradictions,
|
|
743
|
+
discoverRegisteredRooms: discoverRegisteredRooms,
|
|
744
|
+
isRoomSealed: isRoomSealed,
|
|
745
|
+
isRoomOptedOut: isRoomOptedOut,
|
|
746
|
+
isRoomInScope: isRoomInScope,
|
|
747
|
+
computeCrossRoomContradictions: computeCrossRoomContradictions,
|
|
748
|
+
sanitizeDetailScalar: sanitizeDetailScalar,
|
|
749
|
+
renderCrossRoomSection: renderCrossRoomSection,
|
|
750
|
+
ALLOWED_ROOT: ALLOWED_ROOT,
|
|
751
|
+
CONTRADICTION_TYPES: CONTRADICTION_TYPES,
|
|
752
|
+
PROBLEM_TYPE_ENUM: PROBLEM_TYPE_ENUM,
|
|
753
|
+
FORBIDDEN_PATTERNS: FORBIDDEN_PATTERNS,
|
|
754
|
+
DETAIL_STRING_MAX: DETAIL_STRING_MAX,
|
|
755
|
+
// Test surface
|
|
756
|
+
_test: Object.freeze({
|
|
757
|
+
scanRoomSections: scanRoomSections,
|
|
758
|
+
extractProblemTypeFromBrain: extractProblemTypeFromBrain,
|
|
759
|
+
shortFnv1a: shortFnv1a,
|
|
760
|
+
safeSlug: safeSlug,
|
|
761
|
+
}),
|
|
762
|
+
};
|