@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,357 @@
1
+ 'use strict';
2
+
3
+ /*
4
+ * Copyright (c) 2026 Mindrian. BSL 1.1.
5
+ *
6
+ * Phase 90-03 Task 1 -- BRAIN.md staleness computation
7
+ * =====================================================
8
+ * LOCAL computation of whether a BRAIN.md file is fresh, stale, or absent.
9
+ * Consumed by scripts/session-start (Phase 90-03 injection block) to decide
10
+ * which sections to enqueue for background regeneration via the Phase 90-02
11
+ * brain-derivation-queue. Never queries Brain content. Never walks the graph.
12
+ * Compares local hashes against the local triple and against the local
13
+ * brain_graph_version that was recorded in BRAIN.md frontmatter at the time
14
+ * the derivation was written.
15
+ *
16
+ * Canon Part 8 invariant (load-bearing):
17
+ * The scan NEVER queries the Brain for user content. It may call:
18
+ * - brain-client.isAvailable() -> boolean (no network on cache hit)
19
+ * - brain-client.schema() -> { brain_graph_version: number, ... }
20
+ * It MUST NOT call:
21
+ * - brain-client.query / search / smartSearch / enrich*
22
+ * Test 13 in brain-md-staleness.test.cjs instruments a mock brain-client
23
+ * and asserts ZERO content queries across 10 sections.
24
+ *
25
+ * Staleness precedence (ordered, first match wins):
26
+ * 1. File missing / non-regular / zero-byte -> absent
27
+ * (recommended_action=enqueue_when_brain_online)
28
+ * 2. Parse-fail (malformed frontmatter) -> stale, stale_reason=parse_failed
29
+ * (recommended_action=enqueue_regen, demoted if Brain offline)
30
+ * 3. governing_thought_hash mismatch vs triple -> stale, stale_reason=governing_thought_changed
31
+ * 4. age_days > STALE_AGE_DAYS -> stale, stale_reason=age_exceeded
32
+ * 5. brain_graph_version < current schema().graph_version -> stale,
33
+ * stale_reason=brain_graph_version_mismatch
34
+ * 6. else -> fresh
35
+ *
36
+ * Brain-offline behavior:
37
+ * - absent + offline: unchanged (recommended_action=enqueue_when_brain_online)
38
+ * - stale + offline: stale persists, recommended_action DEMOTED to
39
+ * enqueue_when_brain_online (we cannot drain without Brain; hold the
40
+ * intent for when it comes back; next drain or next session-start's
41
+ * scan will pick it up)
42
+ * - fresh + offline: unchanged (no action)
43
+ * - brain_graph_version check SKIPPED when offline (schema() unavailable)
44
+ *
45
+ * Env override:
46
+ * BRAIN_STALE_AGE_DAYS=3 claude -- tighter staleness (3-day threshold)
47
+ * BRAIN_STALE_AGE_DAYS=30 claude -- looser (30-day threshold)
48
+ * BRAIN_STALE_AGE_DAYS='abc' -- fallback to 7 (no warning)
49
+ * BRAIN_STALE_AGE_DAYS=0 -- fallback to 7 (positive integer only)
50
+ *
51
+ * Return shape:
52
+ * {
53
+ * exists: boolean,
54
+ * staleness: 'fresh' | 'stale' | 'absent',
55
+ * stale_reason: null | 'governing_thought_changed' | 'age_exceeded'
56
+ * | 'brain_graph_version_mismatch' | 'brain_offline'
57
+ * | 'parse_failed',
58
+ * brain_generated_at: string | null,
59
+ * brain_graph_version: number | null,
60
+ * age_days: number | null,
61
+ * recommended_action: 'none' | 'enqueue_regen' | 'enqueue_when_brain_online' | 'skip'
62
+ * }
63
+ *
64
+ * Three-surface pure: CLI + Desktop MCP + Cowork all share this module.
65
+ * Pure CJS, node built-ins only, zero npm deps.
66
+ */
67
+
68
+ const fs = require('node:fs');
69
+ const path = require('node:path');
70
+ const crypto = require('node:crypto');
71
+
72
+ // ---------- Constants ----------
73
+
74
+ const DEFAULT_STALE_AGE_DAYS = 7;
75
+
76
+ // ---------- Env override ----------
77
+
78
+ function getStaleAgeDays() {
79
+ const raw = process.env.BRAIN_STALE_AGE_DAYS;
80
+ if (raw === undefined || raw === null || raw === '') {
81
+ return DEFAULT_STALE_AGE_DAYS;
82
+ }
83
+ const n = parseInt(raw, 10);
84
+ // Positive integer only; garbage (NaN, 0, negative, non-integer) falls back.
85
+ if (Number.isFinite(n) && n > 0 && String(n) === String(raw).trim()) {
86
+ return n;
87
+ }
88
+ return DEFAULT_STALE_AGE_DAYS;
89
+ }
90
+
91
+ // STALE_AGE_DAYS constant for grep discoverability; env override via getter.
92
+ const STALE_AGE_DAYS = DEFAULT_STALE_AGE_DAYS;
93
+
94
+ // ---------- Narrow-dialect frontmatter parser ----------
95
+ //
96
+ // Scoped to BRAIN.md fields (flat scalars only). Mirrors the same narrow
97
+ // dialect shipped in Phase 90-00 brain-md-schema.cjs. Kept local to this
98
+ // module so we do not cross-import (keeps lib/core dependency graph flat
99
+ // per Phase 88-01 key decision). Parse failure returns { ok: false } and
100
+ // the caller maps to stale_reason='parse_failed'.
101
+
102
+ function unquote(raw) {
103
+ const s = raw.trim();
104
+ if (s.length === 0) return '';
105
+ if (s[0] === '"') {
106
+ if (s.length < 2 || s[s.length - 1] !== '"') return null;
107
+ return s.slice(1, -1);
108
+ }
109
+ if (s[0] === "'") {
110
+ if (s.length < 2 || s[s.length - 1] !== "'") return null;
111
+ return s.slice(1, -1);
112
+ }
113
+ return s;
114
+ }
115
+
116
+ function castScalar(s) {
117
+ if (s === 'null' || s === '~') return null;
118
+ if (s === 'true') return true;
119
+ if (s === 'false') return false;
120
+ if (/^-?\d+$/.test(s)) {
121
+ const n = parseInt(s, 10);
122
+ if (Number.isSafeInteger(n)) return n;
123
+ }
124
+ if (/^-?\d+\.\d+$/.test(s)) {
125
+ const f = parseFloat(s);
126
+ if (Number.isFinite(f)) return f;
127
+ }
128
+ return s;
129
+ }
130
+
131
+ function parseFrontmatter(raw) {
132
+ if (typeof raw !== 'string' || raw.length === 0) {
133
+ return { ok: false, error: 'empty' };
134
+ }
135
+ const fmRe = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
136
+ const m = raw.match(fmRe);
137
+ if (!m) {
138
+ return { ok: false, error: 'missing_delimiters' };
139
+ }
140
+ const src = m[1];
141
+ const lines = src.split(/\r?\n/);
142
+ const root = {};
143
+ for (let i = 0; i < lines.length; i++) {
144
+ const line = lines[i];
145
+ if (line.length === 0 || /^\s*$/.test(line) || /^\s*#/.test(line)) continue;
146
+ const m2 = line.match(/^([A-Za-z_][A-Za-z0-9_-]*):(.*)$/);
147
+ if (!m2) {
148
+ return { ok: false, error: 'unrecognized_line_' + (i + 1) };
149
+ }
150
+ const key = m2[1];
151
+ const rest = m2[2];
152
+ if (rest.trim().length === 0) {
153
+ root[key] = '';
154
+ continue;
155
+ }
156
+ const vStr = unquote(rest);
157
+ if (vStr === null) {
158
+ return { ok: false, error: 'unterminated_quote_' + key };
159
+ }
160
+ root[key] = castScalar(vStr);
161
+ }
162
+ return { ok: true, data: root };
163
+ }
164
+
165
+ // ---------- Hash helper ----------
166
+
167
+ function sha256OfGoverningThought(gt) {
168
+ const s = gt == null ? '' : String(gt);
169
+ return 'sha256:' + crypto.createHash('sha256')
170
+ .update(s.normalize('NFC'))
171
+ .digest('hex');
172
+ }
173
+
174
+ // ---------- Age helper ----------
175
+
176
+ function computeAgeDays(isoTs) {
177
+ if (typeof isoTs !== 'string' || isoTs.length === 0) return null;
178
+ const t = Date.parse(isoTs);
179
+ if (!Number.isFinite(t)) return null;
180
+ const ageMs = Date.now() - t;
181
+ if (ageMs < 0) return 0; // clock-skew guard: future timestamps treated as fresh-now
182
+ return ageMs / (24 * 60 * 60 * 1000);
183
+ }
184
+
185
+ // ---------- Result helpers ----------
186
+
187
+ function emptyResult() {
188
+ return {
189
+ exists: false,
190
+ staleness: 'fresh',
191
+ stale_reason: null,
192
+ brain_generated_at: null,
193
+ brain_graph_version: null,
194
+ age_days: null,
195
+ recommended_action: 'none',
196
+ };
197
+ }
198
+
199
+ // Demote recommended_action when Brain offline. Stale cases become "hold
200
+ // for when Brain comes back"; absent unchanged; fresh unchanged.
201
+ function demoteWhenOffline(result, offline) {
202
+ if (!offline) return result;
203
+ if (result.staleness === 'stale' && result.recommended_action === 'enqueue_regen') {
204
+ result.recommended_action = 'enqueue_when_brain_online';
205
+ }
206
+ return result;
207
+ }
208
+
209
+ // ---------- Main entry ----------
210
+
211
+ /**
212
+ * computeBrainStaleness(sectionPath, triple) -> Promise<result>
213
+ *
214
+ * Never throws. Every failure mode returns a structured result object.
215
+ * The caller (scripts/session-start Phase 90-03 block) uses the result
216
+ * to decide whether to enqueue a regeneration and how to annotate the
217
+ * TRIPLE_CONTEXT block.
218
+ */
219
+ async function computeBrainStaleness(sectionPath, triple) {
220
+ const result = emptyResult();
221
+
222
+ // --- Check Brain availability (non-throwing) ---
223
+ let brainClient;
224
+ let offline = false;
225
+ try {
226
+ brainClient = require('./brain-client.cjs');
227
+ } catch (_e) {
228
+ brainClient = null;
229
+ }
230
+ try {
231
+ if (brainClient && typeof brainClient.isAvailable === 'function') {
232
+ offline = brainClient.isAvailable() !== true;
233
+ } else {
234
+ offline = true;
235
+ }
236
+ } catch (_e) {
237
+ offline = true;
238
+ }
239
+
240
+ // --- Check BRAIN.md existence ---
241
+ const brainMdPath = path.join(sectionPath, 'BRAIN.md');
242
+ let stat;
243
+ try {
244
+ stat = fs.statSync(brainMdPath);
245
+ } catch (_e) {
246
+ // Absent.
247
+ result.exists = false;
248
+ result.staleness = 'absent';
249
+ result.stale_reason = offline ? 'brain_offline' : null;
250
+ result.recommended_action = 'enqueue_when_brain_online';
251
+ return result;
252
+ }
253
+ if (!stat.isFile() || stat.size === 0) {
254
+ result.exists = false;
255
+ result.staleness = 'absent';
256
+ result.stale_reason = offline ? 'brain_offline' : null;
257
+ result.recommended_action = 'enqueue_when_brain_online';
258
+ return result;
259
+ }
260
+
261
+ result.exists = true;
262
+
263
+ // --- Read + parse frontmatter ---
264
+ let raw;
265
+ try {
266
+ raw = fs.readFileSync(brainMdPath, 'utf8');
267
+ } catch (_e) {
268
+ result.staleness = 'stale';
269
+ result.stale_reason = 'parse_failed';
270
+ result.recommended_action = 'enqueue_regen';
271
+ return demoteWhenOffline(result, offline);
272
+ }
273
+
274
+ const parsed = parseFrontmatter(raw);
275
+ if (!parsed.ok) {
276
+ result.staleness = 'stale';
277
+ result.stale_reason = 'parse_failed';
278
+ result.recommended_action = 'enqueue_regen';
279
+ return demoteWhenOffline(result, offline);
280
+ }
281
+ const fm = parsed.data;
282
+
283
+ // Populate metadata.
284
+ result.brain_generated_at = typeof fm.brain_generated_at === 'string'
285
+ ? fm.brain_generated_at
286
+ : null;
287
+ result.brain_graph_version = typeof fm.brain_graph_version === 'number'
288
+ ? fm.brain_graph_version
289
+ : null;
290
+ result.age_days = computeAgeDays(result.brain_generated_at);
291
+
292
+ // --- Precedence 3: governing_thought_hash mismatch vs triple ---
293
+ const tripleReasoning = (triple && triple.reasoning) || {};
294
+ const tripleGt = tripleReasoning.governing_thought;
295
+ const currentHash = sha256OfGoverningThought(tripleGt);
296
+ const recordedHash = typeof fm.governing_thought_hash === 'string'
297
+ ? fm.governing_thought_hash
298
+ : null;
299
+
300
+ if (recordedHash && recordedHash !== currentHash) {
301
+ result.staleness = 'stale';
302
+ result.stale_reason = 'governing_thought_changed';
303
+ result.recommended_action = 'enqueue_regen';
304
+ return demoteWhenOffline(result, offline);
305
+ }
306
+
307
+ // --- Precedence 4: age > STALE_AGE_DAYS ---
308
+ const threshold = getStaleAgeDays();
309
+ if (result.age_days != null && result.age_days > threshold) {
310
+ result.staleness = 'stale';
311
+ result.stale_reason = 'age_exceeded';
312
+ result.recommended_action = 'enqueue_regen';
313
+ return demoteWhenOffline(result, offline);
314
+ }
315
+
316
+ // --- Precedence 5: brain_graph_version mismatch ---
317
+ // Skip when offline; we cannot call schema() without Brain. An offline
318
+ // run keeps the file as 'fresh' for the version axis; the next online
319
+ // session-start will re-evaluate.
320
+ if (!offline && result.brain_graph_version != null && brainClient && typeof brainClient.schema === 'function') {
321
+ let currentSchema = null;
322
+ try {
323
+ currentSchema = await brainClient.schema();
324
+ } catch (_e) {
325
+ currentSchema = null;
326
+ }
327
+ if (currentSchema && typeof currentSchema.brain_graph_version === 'number') {
328
+ if (result.brain_graph_version < currentSchema.brain_graph_version) {
329
+ result.staleness = 'stale';
330
+ result.stale_reason = 'brain_graph_version_mismatch';
331
+ result.recommended_action = 'enqueue_regen';
332
+ return result;
333
+ }
334
+ }
335
+ }
336
+
337
+ // --- Precedence 6: fresh ---
338
+ result.staleness = 'fresh';
339
+ result.stale_reason = null;
340
+ result.recommended_action = 'none';
341
+ return result;
342
+ }
343
+
344
+ // ---------- Exports ----------
345
+
346
+ module.exports = {
347
+ computeBrainStaleness: computeBrainStaleness,
348
+ STALE_AGE_DAYS: STALE_AGE_DAYS,
349
+ DEFAULT_STALE_AGE_DAYS: DEFAULT_STALE_AGE_DAYS,
350
+ // Test surface (not public API)
351
+ _test: Object.freeze({
352
+ sha256OfGoverningThought: sha256OfGoverningThought,
353
+ parseFrontmatter: parseFrontmatter,
354
+ computeAgeDays: computeAgeDays,
355
+ getStaleAgeDays: getStaleAgeDays,
356
+ }),
357
+ };
@@ -0,0 +1,188 @@
1
+ /*
2
+ * Copyright (c) 2026 Mindrian. BSL 1.1.
3
+ *
4
+ * Phase 117-04 -- Brain response sanitizer (SEED-003 A3 implementation).
5
+ *
6
+ * Per SEED-003 A3 spec: PostToolUse hook on Brain MCP tool calls uses
7
+ * hookSpecificOutput.updatedToolOutput to scan + redact accidental
8
+ * user-data echo before the response reaches the model. This is the
9
+ * structural enforcement of Canon Part 8 the canon has been describing
10
+ * as "PR review" (check-brain-boundary.cjs PR gate, listed as "pending"
11
+ * in CANON-PHASE-MAP.md Part 8 row).
12
+ *
13
+ * Phase 90 shipped 5 Canon Part 8 tripwires (schema-leak heuristic scan,
14
+ * deriveSection chokepoint, brain-md-invariants body-text scan,
15
+ * sanitizeDetailScalar in cross-room aggregator, cross-scenario sweep).
16
+ * This module adds the 6th tripwire: response-side scan.
17
+ *
18
+ * v1 ships PII pattern redaction ONLY. Conservative non-allowlist
19
+ * redaction (8+ consecutive non-allowlist tokens) stays DISABLED until
20
+ * Phase 121 telemetry calibrates false-positive rate. Risk T4 mitigated
21
+ * by starting permissive; tighten after empirical observation.
22
+ *
23
+ * Pure CJS, node built-ins only.
24
+ */
25
+ 'use strict';
26
+
27
+ const crypto = require('node:crypto');
28
+
29
+ /**
30
+ * PII_PATTERNS -- regex catalogue per RESEARCH Section 4.6.
31
+ * Each pattern carries a name for telemetry attribution (Phase 121).
32
+ *
33
+ * Patterns (6 total):
34
+ * ssn -- US SSN format XXX-XX-XXXX
35
+ * email -- RFC 5322 simplified
36
+ * phone -- US phone XXX-XXX-XXXX or XXX.XXX.XXXX
37
+ * money -- $X, $X.XX, $XM/K/B suffix
38
+ * iso_date -- YYYY-MM-DD
39
+ * abs_path -- POSIX absolute paths under /home/X or /Users/X
40
+ */
41
+ const PII_PATTERNS = Object.freeze([
42
+ { name: 'ssn', regex: /\b\d{3}-\d{2}-\d{4}\b/g },
43
+ { name: 'email', regex: /\b[\w.+-]+@[\w-]+\.[\w.-]+\b/g },
44
+ { name: 'phone', regex: /\b\d{3}[-.]\d{3}[-.]\d{4}\b/g },
45
+ { name: 'money', regex: /\$\d+(?:\.\d+)?[MmKkBb]?\b/g },
46
+ { name: 'iso_date', regex: /\b\d{4}-\d{2}-\d{2}\b/g },
47
+ { name: 'abs_path', regex: /\/(?:home|Users)\/[\w-]+\/[\w/.-]+/g },
48
+ ]);
49
+
50
+ /**
51
+ * ALLOWLIST -- framework names + section names + methodology verbs + enum scalars.
52
+ * v1 list seeds from RESEARCH Section 4.6 + references/methodology/index.md.
53
+ * Tightened over time as Phase 121 telemetry calibrates.
54
+ *
55
+ * v1 redaction is pattern-only (PII_PATTERNS). The ALLOWLIST is exported
56
+ * for downstream consumers (e.g. Phase 121 telemetry attribution) and is
57
+ * NOT consulted by sanitize() in v1. When Phase 121 calibrates a non-
58
+ * allowlist redaction mode, ALLOWLIST membership becomes the carve-out.
59
+ */
60
+ const ALLOWLIST = Object.freeze(new Set([
61
+ // Framework names (from references/methodology/index.md):
62
+ 'JTBD', 'MECE', 'Six Thinking Hats', 'Beautiful Questions', 'BONO', 'PAEI',
63
+ 'SWOT', 'Porter Five Forces', 'Reverse Salients', 'Hooked Model',
64
+ 'Cynefin', 'OODA', 'BMC', 'STP',
65
+ // Section names:
66
+ 'problem-definition', 'market-analysis', 'solution-design', 'business-model',
67
+ 'competitive-analysis', 'team-execution', 'legal-ip', 'financial-model',
68
+ 'team', 'meetings',
69
+ // Methodology verbs:
70
+ 'decomposition', 'hypothesis', 'cascade', 'whitespace', 'analogy',
71
+ 'contradiction', 'convergence', 'reverse salient', 'cross-domain',
72
+ // Enum scalars:
73
+ 'Tier 0', 'Tier 1', 'Tier 2', 'Mode A', 'Mode B', 'Mode C',
74
+ ]));
75
+
76
+ /**
77
+ * isBrainTool(toolName) -- matcher for PostToolUse hook scope.
78
+ * Per SEED-003 A3: scope is mcp__brain_* tool calls.
79
+ *
80
+ * @param {string} toolName tool name string from PostToolUse stdin
81
+ * @returns {boolean} true iff the tool is a Brain MCP tool
82
+ */
83
+ function isBrainTool(toolName) {
84
+ return typeof toolName === 'string' && toolName.indexOf('mcp__brain_') === 0;
85
+ }
86
+
87
+ /**
88
+ * sanitize(text) -- apply PII pattern redaction with deterministic sha256
89
+ * placeholder. ALLOWLIST entries pass through unchanged in v1 because the
90
+ * v1 redaction engine is pattern-only (regex matches). The ALLOWLIST is
91
+ * for the Phase 121 calibration path.
92
+ *
93
+ * v1 ships PII patterns only. Generic-token redaction (8+ consecutive
94
+ * non-allowlist tokens) is DISABLED until Phase 121 telemetry calibrates.
95
+ *
96
+ * Determinism: same input always produces the same redacted output.
97
+ * The placeholder format is `[REDACTED:<8-hex>]` where the hex is the
98
+ * first 8 chars of sha256(matched-substring).
99
+ *
100
+ * @param {string} text input text (any type; coerced to string)
101
+ * @returns {string} sanitized text
102
+ */
103
+ function sanitize(text) {
104
+ const input = String(text == null ? '' : text);
105
+ if (input.length === 0) return input;
106
+ let result = input;
107
+ for (const pat of PII_PATTERNS) {
108
+ result = result.replace(pat.regex, function(match) {
109
+ const hash = crypto.createHash('sha256').update(match).digest('hex').slice(0, 8);
110
+ return '[REDACTED:' + hash + ']';
111
+ });
112
+ }
113
+ return result;
114
+ }
115
+
116
+ /**
117
+ * sanitizeDetailed(text) -- Phase 117-05 extension. Returns BOTH sanitized
118
+ * text AND a per-pattern redaction count map so the PostToolUse hook can
119
+ * emit one auto_explore_sanitizer_hit telemetry event per matched pattern.
120
+ *
121
+ * Backward-compatible: sanitize() still returns the sanitized string only.
122
+ * This variant is for telemetry callers (brain-response-sanitize-hook.cjs).
123
+ *
124
+ * @param {string} text input text
125
+ * @returns {{text: string, redactions: object}} redactions keyed by pattern name
126
+ */
127
+ function sanitizeDetailed(text) {
128
+ const input = String(text == null ? '' : text);
129
+ const redactions = { ssn: 0, email: 0, phone: 0, money: 0, iso_date: 0, abs_path: 0 };
130
+ if (input.length === 0) return { text: input, redactions: redactions };
131
+ let result = input;
132
+ for (const pat of PII_PATTERNS) {
133
+ result = result.replace(pat.regex, function(match) {
134
+ redactions[pat.name] = (redactions[pat.name] || 0) + 1;
135
+ const hash = crypto.createHash('sha256').update(match).digest('hex').slice(0, 8);
136
+ return '[REDACTED:' + hash + ']';
137
+ });
138
+ }
139
+ return { text: result, redactions: redactions };
140
+ }
141
+
142
+ /**
143
+ * buildEnvelope(toolName, originalToolResponse) -- produce PostToolUse hook
144
+ * envelope per SEED-003 A3 spec. Returns the JSON object to write to stdout.
145
+ *
146
+ * If isBrainTool returns false, returns a passthrough envelope (no
147
+ * updatedToolOutput). Other tools' responses are not modified.
148
+ *
149
+ * Per SEED-003 A3 envelope shape:
150
+ * {
151
+ * continue: true,
152
+ * hookSpecificOutput: {
153
+ * hookEventName: 'PostToolUse',
154
+ * updatedToolOutput: { text: '<sanitized response>' }
155
+ * }
156
+ * }
157
+ *
158
+ * @param {string} toolName tool name from PostToolUse stdin
159
+ * @param {object} originalToolResponse tool_response object (expects .text)
160
+ * @returns {object} envelope ready for JSON.stringify -> stdout
161
+ */
162
+ function buildEnvelope(toolName, originalToolResponse) {
163
+ if (!isBrainTool(toolName)) {
164
+ return { continue: true };
165
+ }
166
+ const text = (originalToolResponse && typeof originalToolResponse.text === 'string')
167
+ ? originalToolResponse.text
168
+ : '';
169
+ const sanitized = sanitize(text);
170
+ return {
171
+ continue: true,
172
+ hookSpecificOutput: {
173
+ hookEventName: 'PostToolUse',
174
+ updatedToolOutput: {
175
+ text: sanitized,
176
+ },
177
+ },
178
+ };
179
+ }
180
+
181
+ module.exports = {
182
+ sanitize,
183
+ sanitizeDetailed,
184
+ buildEnvelope,
185
+ isBrainTool,
186
+ PII_PATTERNS,
187
+ ALLOWLIST,
188
+ };