@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.
Files changed (597) hide show
  1. package/.claude-plugin/plugin.json +21 -0
  2. package/.mcp.json +9 -0
  3. package/CHANGELOG.md +3333 -0
  4. package/LICENSE +123 -0
  5. package/README.md +673 -0
  6. package/agents/brain-query.md +80 -0
  7. package/agents/framework-runner.md +237 -0
  8. package/agents/grading.md +188 -0
  9. package/agents/investor.md +128 -0
  10. package/agents/larry-extended.md +135 -0
  11. package/agents/opportunity-scanner.md +91 -0
  12. package/agents/persona-analyst.md +132 -0
  13. package/agents/research.md +89 -0
  14. package/agents/reverse-salient-agent.md +27 -0
  15. package/bin/cli.js +142 -0
  16. package/bin/mindrian-mcp-server.cjs +182 -0
  17. package/bin/mindrian-tools.cjs +765 -0
  18. package/commands/act.md +439 -0
  19. package/commands/admin.md +404 -0
  20. package/commands/analyze-needs.md +42 -0
  21. package/commands/analyze-systems.md +39 -0
  22. package/commands/analyze-timing.md +42 -0
  23. package/commands/auto-explore.md +64 -0
  24. package/commands/beautiful-question.md +40 -0
  25. package/commands/brain-derive.md +78 -0
  26. package/commands/build-knowledge.md +42 -0
  27. package/commands/build-thesis.md +46 -0
  28. package/commands/causal.md +234 -0
  29. package/commands/challenge-assumptions.md +33 -0
  30. package/commands/compare-ventures.md +83 -0
  31. package/commands/dashboard.md +110 -0
  32. package/commands/deep-grade.md +82 -0
  33. package/commands/diagnose.md +58 -0
  34. package/commands/diagnostics.md +151 -0
  35. package/commands/doctor.md +151 -0
  36. package/commands/dominant-designs.md +40 -0
  37. package/commands/explain-decision.md +87 -0
  38. package/commands/explore-domains.md +42 -0
  39. package/commands/explore-futures.md +40 -0
  40. package/commands/explore-trends.md +42 -0
  41. package/commands/export.md +103 -0
  42. package/commands/file-meeting.md +724 -0
  43. package/commands/find-analogies.md +188 -0
  44. package/commands/find-bottlenecks.md +62 -0
  45. package/commands/find-connections.md +76 -0
  46. package/commands/funding.md +81 -0
  47. package/commands/grade.md +203 -0
  48. package/commands/graph.md +128 -0
  49. package/commands/hat-briefing.md +125 -0
  50. package/commands/heal.md +196 -0
  51. package/commands/help.md +399 -0
  52. package/commands/hmi-status.md +172 -0
  53. package/commands/jtbd.md +241 -0
  54. package/commands/leadership.md +73 -0
  55. package/commands/lean-canvas.md +40 -0
  56. package/commands/macro-trends.md +40 -0
  57. package/commands/map-unknowns.md +40 -0
  58. package/commands/memory.md +173 -0
  59. package/commands/models.md +175 -0
  60. package/commands/mos-reason.md +285 -0
  61. package/commands/mullins.md +120 -0
  62. package/commands/new-project.md +481 -0
  63. package/commands/onboard.md +434 -0
  64. package/commands/operator.md +149 -0
  65. package/commands/opportunities.md +144 -0
  66. package/commands/organize.md +497 -0
  67. package/commands/persona.md +198 -0
  68. package/commands/pipeline.md +112 -0
  69. package/commands/present.md +91 -0
  70. package/commands/publish.md +201 -0
  71. package/commands/query.md +124 -0
  72. package/commands/radar.md +72 -0
  73. package/commands/reanalyze.md +91 -0
  74. package/commands/research.md +196 -0
  75. package/commands/room.md +352 -0
  76. package/commands/rooms.md +598 -0
  77. package/commands/root-cause.md +40 -0
  78. package/commands/rs-experts.md +85 -0
  79. package/commands/rs-explain.md +100 -0
  80. package/commands/rs-fetch.md +94 -0
  81. package/commands/rs-thesis.md +85 -0
  82. package/commands/scenario-plan.md +40 -0
  83. package/commands/scheduled-tasks.md +285 -0
  84. package/commands/score-innovation.md +43 -0
  85. package/commands/scout.md +239 -0
  86. package/commands/setup.md +618 -0
  87. package/commands/snapshot.md +147 -0
  88. package/commands/speakers.md +84 -0
  89. package/commands/splash.md +28 -0
  90. package/commands/status.md +75 -0
  91. package/commands/structure-argument.md +42 -0
  92. package/commands/suggest-next.md +80 -0
  93. package/commands/systems-thinking.md +40 -0
  94. package/commands/think-hats.md +42 -0
  95. package/commands/update.md +181 -0
  96. package/commands/user-needs.md +40 -0
  97. package/commands/validate.md +40 -0
  98. package/commands/value-proposition.md +61 -0
  99. package/commands/vault.md +180 -0
  100. package/commands/visualize.md +52 -0
  101. package/commands/whitespace.md +507 -0
  102. package/commands/wiki.md +69 -0
  103. package/hooks/hooks.json +381 -0
  104. package/hooks/run-hook.cmd +64 -0
  105. package/lib/__init__.py +0 -0
  106. package/lib/__pycache__/__init__.cpython-312.pyc +0 -0
  107. package/lib/agents/auto-explore-agent.cjs +1043 -0
  108. package/lib/agents/reverse-salient-agent.cjs +679 -0
  109. package/lib/agents/tension-hook-agent.cjs +544 -0
  110. package/lib/brain/ROOM.md +44 -0
  111. package/lib/brain/chain-recommender.cjs +301 -0
  112. package/lib/chat/chat-context.js +185 -0
  113. package/lib/chat/chat-panel.js +721 -0
  114. package/lib/chat/fabric-chat.cjs +288 -0
  115. package/lib/chat/generative-tools.js +219 -0
  116. package/lib/conversation/ROOM.md +39 -0
  117. package/lib/conversation/classifier-rules.json +38 -0
  118. package/lib/conversation/classifier.cjs +264 -0
  119. package/lib/conversation/operator.cjs +287 -0
  120. package/lib/copy/115-spec-strings.cjs +55 -0
  121. package/lib/core/__init__.py +0 -0
  122. package/lib/core/__nav-stub.cjs +14 -0
  123. package/lib/core/__pycache__/__init__.cpython-312.pyc +0 -0
  124. package/lib/core/__pycache__/rs-math.cpython-312.pyc +0 -0
  125. package/lib/core/__pycache__/rs_cache.cpython-312.pyc +0 -0
  126. package/lib/core/__pycache__/rs_corpus.cpython-312.pyc +0 -0
  127. package/lib/core/__pycache__/rs_hybrid.cpython-312.pyc +0 -0
  128. package/lib/core/__pycache__/rs_math.cpython-312.pyc +0 -0
  129. package/lib/core/__pycache__/rs_rooms.cpython-312.pyc +0 -0
  130. package/lib/core/artifact-id.cjs +148 -0
  131. package/lib/core/asset-ops.cjs +151 -0
  132. package/lib/core/auto-commit-throttle.cjs +129 -0
  133. package/lib/core/bearer-token.cjs +199 -0
  134. package/lib/core/brain-client.cjs +865 -0
  135. package/lib/core/brain-derivation-prompts.cjs +326 -0
  136. package/lib/core/brain-derivation-queue.cjs +431 -0
  137. package/lib/core/brain-derivation.cjs +580 -0
  138. package/lib/core/brain-md-schema.cjs +528 -0
  139. package/lib/core/brain-md-staleness.cjs +357 -0
  140. package/lib/core/brain-response-sanitize.cjs +188 -0
  141. package/lib/core/bridge-writer.cjs +477 -0
  142. package/lib/core/chat-context-builder.cjs +253 -0
  143. package/lib/core/cross-room-aggregator.cjs +762 -0
  144. package/lib/core/daily-briefing.cjs +438 -0
  145. package/lib/core/decision-capture.cjs +618 -0
  146. package/lib/core/deep-links.cjs +82 -0
  147. package/lib/core/dispatch-optimizer.cjs +354 -0
  148. package/lib/core/dual-path-detector.cjs +84 -0
  149. package/lib/core/dual-path-detector.test.cjs +334 -0
  150. package/lib/core/exports-log.cjs +79 -0
  151. package/lib/core/feynman-minto-invariants.cjs +605 -0
  152. package/lib/core/folder-memory-async.cjs +338 -0
  153. package/lib/core/folder-memory-shared.cjs +890 -0
  154. package/lib/core/folder-memory.cjs +416 -0
  155. package/lib/core/framework-chain-composer.cjs +411 -0
  156. package/lib/core/frontmatter-schemas.cjs +330 -0
  157. package/lib/core/git-ops.cjs +141 -0
  158. package/lib/core/graph-ops.cjs +258 -0
  159. package/lib/core/hat-persistence.cjs +362 -0
  160. package/lib/core/index.cjs +60 -0
  161. package/lib/core/integration-registry.cjs +232 -0
  162. package/lib/core/intelligence-cascade.cjs +661 -0
  163. package/lib/core/lazygraph-ops.cjs +1057 -0
  164. package/lib/core/lru-cache.cjs +139 -0
  165. package/lib/core/mcp-profiles.cjs +182 -0
  166. package/lib/core/meeting-ops.cjs +54 -0
  167. package/lib/core/memory-ops.cjs +600 -0
  168. package/lib/core/migrations/ROOM.md +33 -0
  169. package/lib/core/migrations/phase-109-nodes-provenance.cjs +339 -0
  170. package/lib/core/migrations/phase-109-session-focus.cjs +99 -0
  171. package/lib/core/model-profiles.cjs +246 -0
  172. package/lib/core/mullins-scaffold.cjs +160 -0
  173. package/lib/core/nav-dial.cjs +316 -0
  174. package/lib/core/navigation/ROOM.md +15 -0
  175. package/lib/core/navigation/explanation.cjs +43 -0
  176. package/lib/core/navigation/focus.cjs +135 -0
  177. package/lib/core/navigation/ingestion.cjs +82 -0
  178. package/lib/core/navigation/insights.cjs +350 -0
  179. package/lib/core/navigation/memory-events.cjs +118 -0
  180. package/lib/core/navigation/neighborhood.cjs +78 -0
  181. package/lib/core/navigation/packet.cjs +182 -0
  182. package/lib/core/navigation/room-home.cjs +127 -0
  183. package/lib/core/navigation/transitions.cjs +82 -0
  184. package/lib/core/navigation-engine-shared.cjs +242 -0
  185. package/lib/core/navigation-engine.cjs +664 -0
  186. package/lib/core/navigation.cjs +60 -0
  187. package/lib/core/nl-graph-queries.cjs +164 -0
  188. package/lib/core/offer-presenter.cjs +406 -0
  189. package/lib/core/opportunity-extractor.cjs +183 -0
  190. package/lib/core/opportunity-ops.cjs +1371 -0
  191. package/lib/core/persona-ops.cjs +537 -0
  192. package/lib/core/persona-taxonomy.cjs +190 -0
  193. package/lib/core/platform-gates.cjs +120 -0
  194. package/lib/core/platform.cjs +257 -0
  195. package/lib/core/proactive-intelligence.cjs +528 -0
  196. package/lib/core/problem-type-router.cjs +315 -0
  197. package/lib/core/reasoning-ops.cjs +639 -0
  198. package/lib/core/reverse-salient-persona-suffix.cjs +115 -0
  199. package/lib/core/room-classifier-strict-mode.cjs +229 -0
  200. package/lib/core/room-db.cjs +127 -0
  201. package/lib/core/room-ops-async.cjs +92 -0
  202. package/lib/core/room-ops-shared.cjs +64 -0
  203. package/lib/core/room-ops-sync.cjs +70 -0
  204. package/lib/core/room-ops.cjs +32 -0
  205. package/lib/core/room-type-detector.cjs +386 -0
  206. package/lib/core/rs-brain-substrate-prompts.cjs +129 -0
  207. package/lib/core/rs-brain-substrate.cjs +570 -0
  208. package/lib/core/rs-breakthrough-scorer.cjs +255 -0
  209. package/lib/core/rs-canon-violations.cjs +82 -0
  210. package/lib/core/rs-chain-feeder.cjs +343 -0
  211. package/lib/core/rs-commercial-assessor.cjs +280 -0
  212. package/lib/core/rs-differential-scorer.cjs +376 -0
  213. package/lib/core/rs-domain-analyzer.cjs +385 -0
  214. package/lib/core/rs-egress-prompts.cjs +113 -0
  215. package/lib/core/rs-egress-telemetry.cjs +225 -0
  216. package/lib/core/rs-egress-violations.cjs +53 -0
  217. package/lib/core/rs-expert-mapper.cjs +467 -0
  218. package/lib/core/rs-fetcher-academic.cjs +697 -0
  219. package/lib/core/rs-fetcher-experts.cjs +314 -0
  220. package/lib/core/rs-fetcher-industry.cjs +731 -0
  221. package/lib/core/rs-fetcher-patents.cjs +564 -0
  222. package/lib/core/rs-innovation-classifier.cjs +194 -0
  223. package/lib/core/rs-mind-map.cjs +656 -0
  224. package/lib/core/rs-neo4j-writer.cjs +388 -0
  225. package/lib/core/rs-nl-to-query.cjs +425 -0
  226. package/lib/core/rs-pinecone-bridge.cjs +303 -0
  227. package/lib/core/rs-preprocessor.cjs +350 -0
  228. package/lib/core/rs-query-matrix.cjs +316 -0
  229. package/lib/core/rs-query-to-text.cjs +438 -0
  230. package/lib/core/rs-sqlite-mirror.cjs +443 -0
  231. package/lib/core/rs-thesis-generator.cjs +188 -0
  232. package/lib/core/rs_cache.py +479 -0
  233. package/lib/core/rs_corpus.py +468 -0
  234. package/lib/core/rs_hybrid.py +586 -0
  235. package/lib/core/rs_math.py +287 -0
  236. package/lib/core/rs_rooms.py +193 -0
  237. package/lib/core/scheduled-scanner.cjs +463 -0
  238. package/lib/core/scratchpad-ops.cjs +201 -0
  239. package/lib/core/section-8-trace-schema.cjs +138 -0
  240. package/lib/core/section-registry.cjs +111 -0
  241. package/lib/core/session-state.cjs +144 -0
  242. package/lib/core/shallow-doc-parser.cjs +174 -0
  243. package/lib/core/shallow-doc-parser.test.cjs +226 -0
  244. package/lib/core/skill-activation-router.cjs +284 -0
  245. package/lib/core/state-ops.cjs +46 -0
  246. package/lib/core/statusline-cache.cjs +266 -0
  247. package/lib/core/token-estimator.cjs +348 -0
  248. package/lib/core/user-archetype.cjs +239 -0
  249. package/lib/core/user-md-ops.cjs +524 -0
  250. package/lib/core/visual-ops.cjs +624 -0
  251. package/lib/core/write-lock.cjs +149 -0
  252. package/lib/graph/canvas-graph.js +467 -0
  253. package/lib/graph/constellation-config.cjs +299 -0
  254. package/lib/graph/graph-detail-panel.js +165 -0
  255. package/lib/hmi/ROOM.md +47 -0
  256. package/lib/hmi/across-session-memory.cjs +604 -0
  257. package/lib/hmi/cross-room-memory.cjs +575 -0
  258. package/lib/hmi/decoy-tier.cjs +395 -0
  259. package/lib/hmi/jtbd-classifier.cjs +219 -0
  260. package/lib/hmi/jtbd-state.cjs +199 -0
  261. package/lib/hmi/jtbd-taxonomy.json +392 -0
  262. package/lib/hmi/selector-dispatcher.cjs +546 -0
  263. package/lib/hmi/selector-telemetry.cjs +263 -0
  264. package/lib/hmi/shape-f0-renderer.cjs +139 -0
  265. package/lib/hmi/shape-f1-fallback.cjs +80 -0
  266. package/lib/hmi/shape-f1-renderer.cjs +138 -0
  267. package/lib/hmi/shape-f2-renderer.cjs +132 -0
  268. package/lib/hmi/shape-f3-renderer.cjs +66 -0
  269. package/lib/hmi/shape-f4-renderer.cjs +72 -0
  270. package/lib/hmi/shape-f5-renderer.cjs +155 -0
  271. package/lib/hmi/shape-f6-plan-review-renderer.cjs +312 -0
  272. package/lib/hmi/shape-f6-renderer.cjs +144 -0
  273. package/lib/hmi/shape-g-renderer.cjs +219 -0
  274. package/lib/hmi/shape-h-renderer.cjs +222 -0
  275. package/lib/hmi/tier-check.cjs +63 -0
  276. package/lib/import/PRECONDITIONS.md +41 -0
  277. package/lib/import/branding.cjs +210 -0
  278. package/lib/import/branding.test.cjs +235 -0
  279. package/lib/import/classifications-sync.cjs +104 -0
  280. package/lib/import/classifications-sync.test.cjs +129 -0
  281. package/lib/import/enricher.cjs +296 -0
  282. package/lib/import/enricher.test.cjs +273 -0
  283. package/lib/import/integration.test.cjs +376 -0
  284. package/lib/import/manifest.cjs +129 -0
  285. package/lib/import/manifest.schema.json +185 -0
  286. package/lib/import/manifest.test.cjs +123 -0
  287. package/lib/import/meeting-detector.cjs +92 -0
  288. package/lib/import/meeting-detector.test.cjs +100 -0
  289. package/lib/import/person-detector.cjs +229 -0
  290. package/lib/import/person-detector.test.cjs +149 -0
  291. package/lib/import/report.cjs +186 -0
  292. package/lib/import/report.test.cjs +186 -0
  293. package/lib/import/room-md-scaffolder.cjs +49 -0
  294. package/lib/import/router.cjs +224 -0
  295. package/lib/import/router.test.cjs +356 -0
  296. package/lib/import/run-all-tests.cjs +36 -0
  297. package/lib/import/smoke-test.cjs +213 -0
  298. package/lib/import/smoke-test.test.cjs +148 -0
  299. package/lib/import/test-fixtures/collision-vault/preexisting-room/STATE.md +8 -0
  300. package/lib/import/test-fixtures/collision-vault/preexisting-room/problem-definition/onboarding/onboarding.md +7 -0
  301. package/lib/import/test-fixtures/collision-vault/source/onboarding.md +5 -0
  302. package/lib/import/test-fixtures/obsidian-vault/.obsidian/workspace.json +1 -0
  303. package/lib/import/test-fixtures/obsidian-vault/notes/with-wikilinks.md +4 -0
  304. package/lib/import/test-fixtures/tiny-vault/notes/2026-01-15-team-sync.md +9 -0
  305. package/lib/import/test-fixtures/tiny-vault/notes/empty.md +3 -0
  306. package/lib/import/test-fixtures/tiny-vault/notes/onboarding.md +5 -0
  307. package/lib/import/test-fixtures/tiny-vault/notes/pricing.md +5 -0
  308. package/lib/import/test-fixtures/tiny-vault/notes/random.md +4 -0
  309. package/lib/import/undo.test.cjs +199 -0
  310. package/lib/import/vault-scanner.cjs +105 -0
  311. package/lib/import/vault-scanner.test.cjs +67 -0
  312. package/lib/mcp/app-html/dashboard.html +316 -0
  313. package/lib/mcp/app-html/graph.html +428 -0
  314. package/lib/mcp/app-html/mindrian-platform.html +1841 -0
  315. package/lib/mcp/app-html/wiki.html +383 -0
  316. package/lib/mcp/app-views.cjs +322 -0
  317. package/lib/mcp/brain-router.cjs +418 -0
  318. package/lib/mcp/capability-registry.cjs +62 -0
  319. package/lib/mcp/larry-context.cjs +46 -0
  320. package/lib/mcp/larry-server-instructions.md +114 -0
  321. package/lib/mcp/pipeline-state.cjs +275 -0
  322. package/lib/mcp/prompts.cjs +302 -0
  323. package/lib/mcp/resources.cjs +227 -0
  324. package/lib/mcp/session-catchup.cjs +327 -0
  325. package/lib/mcp/surface-detect.cjs +75 -0
  326. package/lib/mcp/tool-router.cjs +1034 -0
  327. package/lib/memory/aaak-compress.cjs +403 -0
  328. package/lib/memory/aaak-compress.test.cjs +288 -0
  329. package/lib/memory/async-artifact-auto-commit.test.cjs +223 -0
  330. package/lib/memory/bearer-token.test.cjs +315 -0
  331. package/lib/memory/brain-cache-lru.test.cjs +259 -0
  332. package/lib/memory/brain-client-query-shape.test.cjs +160 -0
  333. package/lib/memory/brain-derivation-graceful-degradation.test.cjs +1019 -0
  334. package/lib/memory/brain-derivation-queue.test.cjs +539 -0
  335. package/lib/memory/brain-derivation.test.cjs +634 -0
  336. package/lib/memory/brain-derive-command.test.cjs +534 -0
  337. package/lib/memory/brain-md-invariants-validator.test.cjs +704 -0
  338. package/lib/memory/brain-md-schema.test.cjs +467 -0
  339. package/lib/memory/brain-md-staleness.test.cjs +525 -0
  340. package/lib/memory/brain-server-resolution.test.cjs +314 -0
  341. package/lib/memory/chain-recommender.test.cjs +233 -0
  342. package/lib/memory/chat-context.test.cjs +128 -0
  343. package/lib/memory/command-registry.test.cjs +220 -0
  344. package/lib/memory/cross-room-aggregator.test.cjs +909 -0
  345. package/lib/memory/dashboard-server.test.cjs +256 -0
  346. package/lib/memory/debouncer-drain-at-prompt.test.cjs +389 -0
  347. package/lib/memory/decision-capture.test.cjs +632 -0
  348. package/lib/memory/decision-capture.worker.cjs +70 -0
  349. package/lib/memory/explain-decision-command.test.cjs +521 -0
  350. package/lib/memory/explain-decision-footer.test.cjs +316 -0
  351. package/lib/memory/explored-materials-store.cjs +392 -0
  352. package/lib/memory/feynman-minto-guardian.test.cjs +736 -0
  353. package/lib/memory/feynman-minto-invariants.test.cjs +511 -0
  354. package/lib/memory/feynman-prompts-drift.test.cjs +144 -0
  355. package/lib/memory/feynman-prompts.cjs +151 -0
  356. package/lib/memory/feynman-prompts.test.cjs +96 -0
  357. package/lib/memory/folder-memory-quadruple.test.cjs +548 -0
  358. package/lib/memory/folder-memory.test.cjs +503 -0
  359. package/lib/memory/framework-chain-composer.test.cjs +515 -0
  360. package/lib/memory/frontmatter-schema-validator.test.cjs +290 -0
  361. package/lib/memory/heal-command.test.cjs +604 -0
  362. package/lib/memory/index-artifact-transaction.test.cjs +333 -0
  363. package/lib/memory/lazygraph-rs-discoveries-view.test.cjs +122 -0
  364. package/lib/memory/mcp-input-validation.test.cjs +240 -0
  365. package/lib/memory/mcp-server-brain-deps.test.cjs +270 -0
  366. package/lib/memory/mcp-stack-fallback.test.cjs +433 -0
  367. package/lib/memory/minto-debouncer.test.cjs +407 -0
  368. package/lib/memory/minto-debouncer.worker.cjs +46 -0
  369. package/lib/memory/minto-migration-v88.test.cjs +265 -0
  370. package/lib/memory/minto-schema-v88.test.cjs +390 -0
  371. package/lib/memory/mos-status-renderer.test.cjs +631 -0
  372. package/lib/memory/narrative-schema.cjs +376 -0
  373. package/lib/memory/narrative-schema.test.cjs +209 -0
  374. package/lib/memory/nav-dial.test.cjs +414 -0
  375. package/lib/memory/navigation-engine-core.test.cjs +722 -0
  376. package/lib/memory/navigation-invariants.test.cjs +483 -0
  377. package/lib/memory/offer-presenter.test.cjs +554 -0
  378. package/lib/memory/on-stop-snapshot.test.cjs +404 -0
  379. package/lib/memory/pending-tension-store.cjs +373 -0
  380. package/lib/memory/post-compact-reinjection.test.cjs +854 -0
  381. package/lib/memory/post-write-triple.test.cjs +317 -0
  382. package/lib/memory/pre-compact-snapshot.test.cjs +495 -0
  383. package/lib/memory/problem-type-router.test.cjs +656 -0
  384. package/lib/memory/query-efficiency-telemetry.test.cjs +370 -0
  385. package/lib/memory/recompile-room-references.test.cjs +392 -0
  386. package/lib/memory/recompile-room-references.worker.cjs +42 -0
  387. package/lib/memory/record-decision-dual-write.test.cjs +454 -0
  388. package/lib/memory/room-classifier-strict-mode.test.cjs +417 -0
  389. package/lib/memory/room-minto-hook.test.cjs +398 -0
  390. package/lib/memory/rs-discovery-engine.test.cjs +323 -0
  391. package/lib/memory/run-feynman-tests.cjs +1247 -0
  392. package/lib/memory/security-trifecta.test.cjs +312 -0
  393. package/lib/memory/session-start-brain-staleness.test.cjs +363 -0
  394. package/lib/memory/session-start-triple-injection.test.cjs +514 -0
  395. package/lib/memory/sessionstart-banner-formatter.cjs +318 -0
  396. package/lib/memory/sessionstart-minto-banner.test.cjs +373 -0
  397. package/lib/memory/skill-activation-router.test.cjs +419 -0
  398. package/lib/memory/stamp-artifact-write.test.cjs +304 -0
  399. package/lib/memory/statusline-active-room.test.cjs +315 -0
  400. package/lib/memory/statusline-minto-segment.test.cjs +292 -0
  401. package/lib/memory/sync-async-entry-points.test.cjs +204 -0
  402. package/lib/memory/test-bridge-writer-enhanced.cjs +452 -0
  403. package/lib/memory/test-rs-brain-substrate-shape.cjs +529 -0
  404. package/lib/memory/test-rs-brain-substrate.cjs +636 -0
  405. package/lib/memory/test-rs-breakthrough-scorer.cjs +375 -0
  406. package/lib/memory/test-rs-canon-violations.cjs +218 -0
  407. package/lib/memory/test-rs-chain-feeder-core.cjs +344 -0
  408. package/lib/memory/test-rs-chain-feeder-skill-spawn.cjs +297 -0
  409. package/lib/memory/test-rs-commercial-assessor.cjs +385 -0
  410. package/lib/memory/test-rs-differential-scorer.cjs +480 -0
  411. package/lib/memory/test-rs-discovery-engine.cjs +603 -0
  412. package/lib/memory/test-rs-domain-analyzer.cjs +492 -0
  413. package/lib/memory/test-rs-egress-primitives.cjs +420 -0
  414. package/lib/memory/test-rs-expert-mapper.cjs +547 -0
  415. package/lib/memory/test-rs-explain-command.cjs +443 -0
  416. package/lib/memory/test-rs-fetcher-academic.cjs +848 -0
  417. package/lib/memory/test-rs-fetcher-experts.cjs +496 -0
  418. package/lib/memory/test-rs-fetcher-industry.cjs +702 -0
  419. package/lib/memory/test-rs-fetcher-patents.cjs +674 -0
  420. package/lib/memory/test-rs-innovation-classifier.cjs +301 -0
  421. package/lib/memory/test-rs-mind-map.cjs +646 -0
  422. package/lib/memory/test-rs-neo4j-writer.cjs +518 -0
  423. package/lib/memory/test-rs-nl-to-query.cjs +449 -0
  424. package/lib/memory/test-rs-pinecone-bridge.cjs +277 -0
  425. package/lib/memory/test-rs-preprocessor.cjs +433 -0
  426. package/lib/memory/test-rs-query-matrix.cjs +391 -0
  427. package/lib/memory/test-rs-query-to-text.cjs +551 -0
  428. package/lib/memory/test-rs-sqlite-mirror.cjs +649 -0
  429. package/lib/memory/test-rs-thesis-generator.cjs +360 -0
  430. package/lib/memory/triple-context-formatter.cjs +473 -0
  431. package/lib/memory/triple-context-formatter.test.cjs +442 -0
  432. package/lib/memory/user-md-persona.test.cjs +565 -0
  433. package/lib/memory/userpromptsubmit-integration.test.cjs +690 -0
  434. package/lib/memory/validators/README.md +157 -0
  435. package/lib/memory/validators/brain-md-invariants.cjs +475 -0
  436. package/lib/memory/validators/brain-substrate-invariants.cjs +285 -0
  437. package/lib/memory/validators/external-academic-invariants.cjs +249 -0
  438. package/lib/memory/validators/external-industry-invariants.cjs +271 -0
  439. package/lib/memory/validators/external-patents-invariants.cjs +266 -0
  440. package/lib/memory/validators/minto-invariants.cjs +62 -0
  441. package/lib/memory/validators/navigation-invariants.cjs +340 -0
  442. package/lib/memory/validators/queue-health.cjs +95 -0
  443. package/lib/memory/validators/snapshot-integrity.cjs +129 -0
  444. package/lib/memory/validators/stale-lifecycle.cjs +116 -0
  445. package/lib/memory/vault-section-minto-generator-atomic.test.cjs +556 -0
  446. package/lib/memory/vault-section-minto-generator-atomic.worker.cjs +73 -0
  447. package/lib/memory/write-lock-atomic.test.cjs +137 -0
  448. package/lib/memory/write-lock-atomic.worker.cjs +55 -0
  449. package/lib/parity/check-parity.cjs +83 -0
  450. package/lib/presentation/presentation-server.cjs +101 -0
  451. package/lib/presentation/presentation-watcher.cjs +123 -0
  452. package/lib/quickview/hub-server.cjs +719 -0
  453. package/lib/quickview/server.cjs +533 -0
  454. package/lib/render/JTBD-PALETTES.md +145 -0
  455. package/lib/render/ROOM.md +59 -0
  456. package/lib/render/render-v2.cjs +486 -0
  457. package/lib/render/render-v2.test.cjs +267 -0
  458. package/lib/render/render.cjs +65 -0
  459. package/lib/state/ROOM.md +46 -0
  460. package/lib/state/state-md-parser.cjs +215 -0
  461. package/lib/statusline/ROOM.md +38 -0
  462. package/lib/statusline/banner-suppression.cjs +50 -0
  463. package/lib/statusline/surface-detect.cjs +85 -0
  464. package/lib/update-bootstrap.sh.template +145 -0
  465. package/lib/vault/frontmatter-schema.cjs +297 -0
  466. package/lib/vault/room-scanner.cjs +352 -0
  467. package/lib/vault/wikilink-builder.cjs +231 -0
  468. package/lib/vault/wikilink-builder.test.cjs +182 -0
  469. package/lib/wiki/graph-links.cjs +281 -0
  470. package/lib/wiki/page-renderer.cjs +229 -0
  471. package/lib/wiki/wiki-chat.cjs +81 -0
  472. package/lib/wiki/wiki-layout.cjs +1459 -0
  473. package/lib/wiki/wiki-search.cjs +142 -0
  474. package/lib/wiki/wiki-server.cjs +678 -0
  475. package/lib/wiki/wiki-watcher.cjs +105 -0
  476. package/lib/workflow/ROOM.md +47 -0
  477. package/lib/workflow/command-resolver.cjs +155 -0
  478. package/lib/workflow/command-resolver.test.cjs +235 -0
  479. package/package.json +44 -0
  480. package/pipelines/analogy/01-decompose.md +80 -0
  481. package/pipelines/analogy/02-abstract.md +87 -0
  482. package/pipelines/analogy/03-search.md +135 -0
  483. package/pipelines/analogy/04-transfer.md +101 -0
  484. package/pipelines/analogy/05-validate.md +106 -0
  485. package/pipelines/analogy/CHAIN.md +56 -0
  486. package/pipelines/discovery/01-explore-domains.md +44 -0
  487. package/pipelines/discovery/02-think-hats.md +50 -0
  488. package/pipelines/discovery/03-analyze-needs.md +54 -0
  489. package/pipelines/discovery/CHAIN.md +37 -0
  490. package/pipelines/thesis/01-structure-argument.md +45 -0
  491. package/pipelines/thesis/02-challenge-assumptions.md +48 -0
  492. package/pipelines/thesis/03-build-thesis.md +54 -0
  493. package/pipelines/thesis/CHAIN.md +37 -0
  494. package/references/brain/causal-directives.md +91 -0
  495. package/references/brain/causal-enrichment.cypher +165 -0
  496. package/references/brain/command-triggers-schema.md +226 -0
  497. package/references/brain/graph-architecture.md +317 -0
  498. package/references/brain/query-patterns.md +460 -0
  499. package/references/brain/room-hierarchy-schema.md +218 -0
  500. package/references/brain/schema.md +76 -0
  501. package/references/capability-radar/capabilities-index.md +241 -0
  502. package/references/capability-radar/changelog-cache.md +81 -0
  503. package/references/causal/causal-schema.md +103 -0
  504. package/references/design/email-template-standard.md +155 -0
  505. package/references/design/graph-visualization-standard.md +178 -0
  506. package/references/document-generation.md +179 -0
  507. package/references/hsi/HSI-TOOLS-REFERENCE.md +222 -0
  508. package/references/import-config.md +141 -0
  509. package/references/integrations/detection-patterns.md +101 -0
  510. package/references/meeting/artifact-template.md +377 -0
  511. package/references/meeting/cross-meeting-intelligence.md +216 -0
  512. package/references/meeting/cross-relationship-patterns.md +202 -0
  513. package/references/meeting/live-join-interface.md +244 -0
  514. package/references/meeting/section-mapping.md +192 -0
  515. package/references/meeting/segment-classification.md +258 -0
  516. package/references/meeting/speaker-profile-template.md +219 -0
  517. package/references/meeting/summary-template.md +348 -0
  518. package/references/meeting/transcript-patterns.md +226 -0
  519. package/references/methodology/analyze-needs.md +135 -0
  520. package/references/methodology/analyze-systems.md +121 -0
  521. package/references/methodology/analyze-timing.md +149 -0
  522. package/references/methodology/beautiful-question.md +109 -0
  523. package/references/methodology/build-knowledge.md +161 -0
  524. package/references/methodology/build-thesis.md +237 -0
  525. package/references/methodology/challenge-assumptions.md +127 -0
  526. package/references/methodology/diagnose.md +169 -0
  527. package/references/methodology/dominant-designs.md +212 -0
  528. package/references/methodology/explore-domains.md +147 -0
  529. package/references/methodology/explore-futures.md +163 -0
  530. package/references/methodology/explore-trends.md +129 -0
  531. package/references/methodology/find-bottlenecks.md +131 -0
  532. package/references/methodology/grade.md +211 -0
  533. package/references/methodology/index.md +97 -0
  534. package/references/methodology/leadership.md +200 -0
  535. package/references/methodology/lean-canvas.md +116 -0
  536. package/references/methodology/macro-trends.md +192 -0
  537. package/references/methodology/map-unknowns.md +137 -0
  538. package/references/methodology/mullins-7-domains.md +104 -0
  539. package/references/methodology/problem-types.md +65 -0
  540. package/references/methodology/root-cause.md +178 -0
  541. package/references/methodology/sapphire-encoding.md +355 -0
  542. package/references/methodology/scenario-plan.md +178 -0
  543. package/references/methodology/score-innovation.md +154 -0
  544. package/references/methodology/structure-argument.md +158 -0
  545. package/references/methodology/systems-thinking.md +159 -0
  546. package/references/methodology/think-hats.md +147 -0
  547. package/references/methodology/triz-matrix.json +751 -0
  548. package/references/methodology/triz-principles.md +501 -0
  549. package/references/methodology/user-needs.md +199 -0
  550. package/references/methodology/validate.md +163 -0
  551. package/references/methodology/value-proposition.md +244 -0
  552. package/references/opportunities/funding-lifecycle.md +103 -0
  553. package/references/opportunities/grant-api-patterns.md +99 -0
  554. package/references/opportunities/opportunity-template.md +84 -0
  555. package/references/personality/assessment-philosophy.md +72 -0
  556. package/references/personality/lexicon.md +100 -0
  557. package/references/personality/persona-chains.md +56 -0
  558. package/references/personality/pws-lexicon-full.md +499 -0
  559. package/references/personality/voice-dna.md +156 -0
  560. package/references/personas/hat-perspectives.md +76 -0
  561. package/references/personas/persona-template.md +63 -0
  562. package/references/pipeline/act-output-contract.md +88 -0
  563. package/references/pipeline/chains-index.md +39 -0
  564. package/references/pws-profile-generation.md +79 -0
  565. package/references/reasoning/reasoning-schema.md +143 -0
  566. package/references/reasoning/reasoning-template.md +68 -0
  567. package/references/reasoning/run-template.md +38 -0
  568. package/references/research/RESEARCH_14_CLAUDE_CODE_SOURCE_ARCHITECTURE.md +209 -0
  569. package/references/research/RESEARCH_15_V1.8_OPTIMIZATION_JTBD.md +375 -0
  570. package/references/research/RESEARCH_16_NATIVE_FIRST_PLUGIN_ARCHITECTURE.md +575 -0
  571. package/references/research/RESEARCH_17_MCP_UI_FRAMEWORKS.md +272 -0
  572. package/references/taxonomy/TAXONOMY.md +192 -0
  573. package/references/templates/MINTO.md +36 -0
  574. package/references/user-research/2026-04-05-leah-lawrence-session.md +202 -0
  575. package/references/vault-kit/README.md +35 -0
  576. package/references/vault-kit/app.json +12 -0
  577. package/references/vault-kit/appearance.json +12 -0
  578. package/references/vault-kit/graph.json +35 -0
  579. package/references/vault-kit/snippets/mindrian-destijl.css +297 -0
  580. package/references/vault-kit/templates/new-artifact.md +37 -0
  581. package/references/vault-kit/templates/new-meeting-note.md +35 -0
  582. package/references/vault-kit/templates/new-team-profile.md +29 -0
  583. package/references/vault-kit/templates/new-xref.md +35 -0
  584. package/references/visual/symbol-system.md +151 -0
  585. package/skills/MOSDeckEngine/SKILL.md +325 -0
  586. package/skills/brain-connector/SKILL.md +114 -0
  587. package/skills/context-engine/SKILL.md +147 -0
  588. package/skills/conversation-mode/SKILL.md +102 -0
  589. package/skills/larry-personality/SKILL.md +219 -0
  590. package/skills/larry-personality/framework-chains.md +92 -0
  591. package/skills/larry-personality/mode-engine.md +185 -0
  592. package/skills/mullins-scaffold/SKILL.md +61 -0
  593. package/skills/mullins-scaffold/scaffold.json +146 -0
  594. package/skills/pws-methodology/SKILL.md +49 -0
  595. package/skills/room-passive/SKILL.md +165 -0
  596. package/skills/room-proactive/SKILL.md +250 -0
  597. 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
+ };