@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,316 @@
1
+ #!/usr/bin/env node
2
+ /*
3
+ * Copyright (c) 2026 Mindrian. BSL 1.1.
4
+ *
5
+ * Phase 94-09 -- /mos:explain-decision Action Footer fixture tests
6
+ * =================================================================
7
+ * Per QA handoff Section 3 FIX-8: scripts/explain-decision-command.cjs
8
+ * renderTrace() output ends with `---` and no Next: action footer. The
9
+ * 4-zone Action Footer rule in skills/ui-system/SKILL.md (Section 1
10
+ * Zone 4: "NEVER omitted") is violated for /mos:explain-decision.
11
+ *
12
+ * Plan 94-09 appends an Action Footer at the END of the rendered output,
13
+ * AFTER the existing closing `---` separator. The footer offers 3
14
+ * canonical /mos:* commands (status / act / suggest-next) per the QA
15
+ * handoff Section 3 FIX-8 verbatim suggestion.
16
+ *
17
+ * Canon Part 7 (Reuse Before Build): extends scripts/explain-decision-
18
+ * command.cjs renderTrace() output by appending a footer. The 9 trace
19
+ * fields covered by lib/memory/explain-decision-command.test.cjs Tests
20
+ * 1-12 are byte-identical before the footer; the new footer is purely
21
+ * additive at the tail.
22
+ *
23
+ * Canon Part 3 (Tri-Context Decision Gate): the footer offers verbs
24
+ * from the canonical 10-verb vocabulary (status -> Synthesize, act ->
25
+ * Run Methodology, suggest-next -> Free-Text / Spawn Sub-Agent). No
26
+ * new verbs introduced.
27
+ *
28
+ * Test map:
29
+ * T1 footer presence: last 5 lines of stdout contain literal 'Next:'
30
+ * T2 footer content: footer contains at least 2 /mos: command refs
31
+ * T3 non-regression: existing renderTrace prefix output (header +
32
+ * 9 trace fields + closing `---`) remains intact when the footer
33
+ * is appended. Verified by checking that all existing markers
34
+ * (Tier Mode, BRAIN.md signal, Five-Signal Triangulation, Chosen
35
+ * rationale, the closing `---`) are still present and in order.
36
+ * T4 graceful empty path: when the trace is empty (no decisions
37
+ * recorded yet), the footer STILL renders. Stdout still contains
38
+ * 'Next:' substring.
39
+ *
40
+ * Registered in lib/memory/run-feynman-tests.cjs.
41
+ */
42
+
43
+ 'use strict';
44
+
45
+ const assert = require('node:assert/strict');
46
+ const fs = require('node:fs');
47
+ const path = require('node:path');
48
+ const os = require('node:os');
49
+ const { spawnSync } = require('node:child_process');
50
+
51
+ const REPO = path.resolve(__dirname, '..', '..');
52
+ const SCRIPT_PATH = path.join(REPO, 'scripts/explain-decision-command.cjs');
53
+
54
+ // ---------- Test scaffolding ----------
55
+
56
+ let passed = 0;
57
+ let failed = 0;
58
+ function run(name, fn) {
59
+ try {
60
+ fn();
61
+ process.stdout.write('ok ' + name + '\n');
62
+ passed += 1;
63
+ } catch (err) {
64
+ process.stderr.write('FAIL ' + name + '\n');
65
+ process.stderr.write(String(err && err.stack ? err.stack : err) + '\n');
66
+ failed += 1;
67
+ }
68
+ }
69
+
70
+ // ---------- Fixture builders ----------
71
+
72
+ const TMPDIRS = [];
73
+
74
+ process.on('exit', () => {
75
+ for (const d of TMPDIRS) {
76
+ try { fs.rmSync(d, { recursive: true, force: true }); } catch (_) {}
77
+ }
78
+ });
79
+
80
+ function makeRoomsFixture(label) {
81
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), '94-09-' + label + '-'));
82
+ TMPDIRS.push(tmp);
83
+ const root = path.join(tmp, 'MindrianRooms');
84
+ fs.mkdirSync(root, { recursive: true });
85
+ const roomDir = path.join(root, 'fixture-room');
86
+ fs.mkdirSync(roomDir, { recursive: true });
87
+ fs.writeFileSync(
88
+ path.join(roomDir, 'STATE.md'),
89
+ '---\nproject: fixture room\n---\n\n# Fixture Room\n'
90
+ );
91
+ const regDir = path.join(root, '.rooms');
92
+ fs.mkdirSync(regDir, { recursive: true });
93
+ fs.writeFileSync(
94
+ path.join(regDir, 'registry.json'),
95
+ JSON.stringify({
96
+ version: 1,
97
+ active: 'fixture-room',
98
+ rooms: {
99
+ 'fixture-room': { path: 'fixture-room' },
100
+ },
101
+ }, null, 2)
102
+ );
103
+ return { tmp: tmp, root: root, roomDir: roomDir };
104
+ }
105
+
106
+ function writeTraceFile(roomDir, sessionId, traces) {
107
+ const dir = path.join(roomDir, '.mindrian', 'decision-traces');
108
+ fs.mkdirSync(dir, { recursive: true });
109
+ const fp = path.join(dir, sessionId + '.json');
110
+ fs.writeFileSync(
111
+ fp,
112
+ JSON.stringify(
113
+ { version: 1, session_id: sessionId, traces: traces },
114
+ null,
115
+ 2
116
+ )
117
+ );
118
+ return fp;
119
+ }
120
+
121
+ function makeTrace(turn, overrides) {
122
+ const base = {
123
+ turn: turn,
124
+ at: '2026-04-29T12:00:0' + (turn % 10) + '.000Z',
125
+ elapsed_ms: 12,
126
+ brain_md_version: 1,
127
+ brain_md_staleness: 'fresh',
128
+ brain_md_stale_reason: null,
129
+ brain_md_weight_applied: 1.0,
130
+ brain_md_recommended_confidence: 0.85,
131
+ brain_md_recommended_marker_rendered: true,
132
+ brain_md_tier_mode: 'mode_a',
133
+ brain_md_sections_consumed: ['pattern_matches', 'wicked_indicators'],
134
+ icm_scope: { section: 'market-analysis', scope_path: 'fixture-room/market-analysis' },
135
+ sql_signals: { edge_counts: 12, contradictions: 1, convergences: 3 },
136
+ minto_reasoning: {
137
+ governing_thought: 'Customers will pay for clarity',
138
+ reasoning_health_score: 0.7,
139
+ is_stale: false,
140
+ },
141
+ intent_persona: {
142
+ larry_persona: 'Researcher',
143
+ brain_persona: 'Implicit',
144
+ detection_confidence: 0.6,
145
+ },
146
+ chosen_rationale: 'Mode A: BRAIN.md fresh; pattern_matches confidence above 0.7 floor.',
147
+ };
148
+ return Object.assign({}, base, overrides || {});
149
+ }
150
+
151
+ function runCommand(roomDir, args, extraEnv) {
152
+ const env = Object.assign({}, process.env, {
153
+ MINDRIAN_ROOMS_HOME: path.dirname(roomDir),
154
+ MINDRIAN_ROOMS_ROOT: path.dirname(roomDir),
155
+ }, extraEnv || {});
156
+ return spawnSync(process.execPath, [SCRIPT_PATH].concat(args || []), {
157
+ env: env,
158
+ encoding: 'utf8',
159
+ timeout: 5000,
160
+ });
161
+ }
162
+
163
+ // =========================================================
164
+ // Tests T1-T4 (Plan 94-09 Action Footer)
165
+ // =========================================================
166
+
167
+ run('T1 footer presence: last 5 lines of stdout contain literal Next:', () => {
168
+ const fix = makeRoomsFixture('t1');
169
+ writeTraceFile(fix.roomDir, 'sid-t1', [makeTrace(1)]);
170
+ const res = runCommand(fix.roomDir, [], { CLAUDE_SESSION_ID: 'sid-t1' });
171
+ assert.equal(res.status, 0, 'exit 0; stderr: ' + (res.stderr || ''));
172
+ const out = res.stdout || '';
173
+ // Trim trailing whitespace then split into lines.
174
+ const lines = out.replace(/\s+$/, '').split('\n');
175
+ const last5 = lines.slice(-5).join('\n');
176
+ assert.equal(
177
+ last5.indexOf('Next:') !== -1,
178
+ true,
179
+ 'expected "Next:" in last 5 lines; got last 5 lines:\n' + last5 + '\n\nFull output:\n' + out
180
+ );
181
+ });
182
+
183
+ run('T2 footer content: at least 2 /mos: command refs in footer', () => {
184
+ const fix = makeRoomsFixture('t2');
185
+ writeTraceFile(fix.roomDir, 'sid-t2', [makeTrace(1)]);
186
+ const res = runCommand(fix.roomDir, [], { CLAUDE_SESSION_ID: 'sid-t2' });
187
+ assert.equal(res.status, 0, 'exit 0; stderr: ' + (res.stderr || ''));
188
+ const out = res.stdout || '';
189
+ // Locate the Next: marker and count /mos: occurrences AFTER it. The
190
+ // pre-94-09 output may already contain /mos: in advisory paths or
191
+ // in the chosen_rationale body. We isolate the footer by anchoring
192
+ // on the literal 'Next:' substring.
193
+ const idx = out.indexOf('Next:');
194
+ assert.equal(
195
+ idx !== -1,
196
+ true,
197
+ 'expected "Next:" anchor; got:\n' + out
198
+ );
199
+ const footer = out.slice(idx);
200
+ const matches = footer.match(/\/mos:/g) || [];
201
+ assert.equal(
202
+ matches.length >= 2,
203
+ true,
204
+ 'expected at least 2 /mos: commands in footer; found '
205
+ + matches.length + ' in:\n' + footer
206
+ );
207
+ });
208
+
209
+ run('T3 non-regression: existing renderTrace markers preserved before footer', () => {
210
+ const fix = makeRoomsFixture('t3');
211
+ writeTraceFile(fix.roomDir, 'sid-t3', [makeTrace(1)]);
212
+ const res = runCommand(fix.roomDir, [], { CLAUDE_SESSION_ID: 'sid-t3' });
213
+ assert.equal(res.status, 0, 'exit 0; stderr: ' + (res.stderr || ''));
214
+ const out = res.stdout || '';
215
+ // The 9 trace-field markers from lib/memory/explain-decision-command.test.cjs
216
+ // Tests 1-4 must all still appear in the output.
217
+ const requiredMarkers = [
218
+ 'Navigation Engine Decision Trace', // header
219
+ 'Turn 1', // structural turn
220
+ 'Tier Mode', // brain_md_tier_mode label
221
+ 'mode_a', // tier_mode value
222
+ 'BRAIN.md signal', // section header
223
+ 'version:',
224
+ 'staleness:',
225
+ 'stale_reason:',
226
+ 'weight_applied:',
227
+ 'sections_consumed:',
228
+ 'RECOMMENDED marker',
229
+ 'rendered:',
230
+ 'highest_confidence:',
231
+ 'Five-Signal Triangulation',
232
+ 'ICM scope',
233
+ 'SQL signals',
234
+ 'Feynman-MINTO',
235
+ 'BRAIN patterns',
236
+ 'Intent + Persona',
237
+ 'Chosen rationale', // chosen_rationale section
238
+ 'pattern_matches', // brain_md_sections_consumed value
239
+ 'market-analysis', // icm_scope.section value
240
+ 'Customers will pay', // minto_reasoning.governing_thought
241
+ 'Researcher', // intent_persona.larry_persona
242
+ 'Mode A: BRAIN.md fresh', // chosen_rationale verbatim prefix
243
+ ];
244
+ for (const m of requiredMarkers) {
245
+ assert.equal(
246
+ out.indexOf(m) !== -1,
247
+ true,
248
+ 'pre-94-09 marker missing after footer append: "' + m + '"'
249
+ + '\nFull output:\n' + out
250
+ );
251
+ }
252
+ // The closing `---` separator MUST appear BEFORE the Next: footer.
253
+ // Pre-94-09 output ended with `---\n`. Plan 94-09 appends the footer
254
+ // AFTER that `---`, so the order is: ...content... `---` ... `Next:`.
255
+ const sepIdx = out.lastIndexOf('\n---');
256
+ const nextIdx = out.indexOf('Next:');
257
+ assert.equal(
258
+ sepIdx !== -1,
259
+ true,
260
+ 'closing `---` separator missing; got:\n' + out
261
+ );
262
+ assert.equal(
263
+ nextIdx !== -1,
264
+ true,
265
+ '"Next:" footer missing; got:\n' + out
266
+ );
267
+ assert.equal(
268
+ sepIdx < nextIdx,
269
+ true,
270
+ 'closing `---` must precede the Next: footer; sepIdx=' + sepIdx
271
+ + ' nextIdx=' + nextIdx + '\nFull output:\n' + out
272
+ );
273
+ });
274
+
275
+ run('T4 graceful empty path: footer renders when no decisions yet', () => {
276
+ // Empty traces array. Pre-94-09 the script prints the
277
+ // "No decisions recorded for this session." advisory and exits 0
278
+ // with no footer. Plan 94-09 appends the footer to the empty path
279
+ // too, so the user always gets a navigable next step.
280
+ const fix = makeRoomsFixture('t4');
281
+ writeTraceFile(fix.roomDir, 'empty-sid', []);
282
+ const res = runCommand(fix.roomDir, [], { CLAUDE_SESSION_ID: 'empty-sid' });
283
+ assert.equal(res.status, 0, 'exit 0; stderr: ' + (res.stderr || ''));
284
+ const out = res.stdout || '';
285
+ // Empty-path advisory still rendered.
286
+ assert.equal(
287
+ out.indexOf('No decisions recorded') !== -1,
288
+ true,
289
+ 'empty-path advisory missing; got:\n' + out
290
+ );
291
+ // Footer rendered too.
292
+ assert.equal(
293
+ out.indexOf('Next:') !== -1,
294
+ true,
295
+ 'expected "Next:" footer on empty-path output; got:\n' + out
296
+ );
297
+ // At least 2 /mos: command refs after the Next: anchor.
298
+ const idx = out.indexOf('Next:');
299
+ const footer = out.slice(idx);
300
+ const matches = footer.match(/\/mos:/g) || [];
301
+ assert.equal(
302
+ matches.length >= 2,
303
+ true,
304
+ 'expected at least 2 /mos: commands in empty-path footer; found '
305
+ + matches.length + ' in:\n' + footer
306
+ );
307
+ });
308
+
309
+ // ---------- Reporter ----------
310
+
311
+ process.stdout.write('\n');
312
+ process.stdout.write('explain-decision-footer: ' + passed + '/' + (passed + failed) + ' passed\n');
313
+ if (failed > 0) {
314
+ process.exit(1);
315
+ }
316
+ process.exit(0);
@@ -0,0 +1,392 @@
1
+ /*
2
+ * Copyright (c) 2026 Mindrian. BSL 1.1.
3
+ *
4
+ * Phase 117-01 Wave 1 -- explored-materials-store substrate.
5
+ *
6
+ * JSONL append-only state store at ~/.mindrian/explored-materials/<roomSlug>.jsonl.
7
+ * Per CLAUDE.md workspace guard: writes OUTSIDE plugin repo (os.homedir()).
8
+ * Per Canon Part 8: USER_CONTENT_KEY_DENYLIST guards against telemetry leakage.
9
+ *
10
+ * Graph-native HARD RULE (memory feedback_reverse_salient_agent_graph_native.md):
11
+ * 1. NEVER require room-db.cjs (Phase 109 D-06 chokepoint).
12
+ * 2. NEVER require brain-client (Canon Part 8 boundary).
13
+ * 3. NEVER call console.log or process.stdout.write (dual-surface telemetry contract).
14
+ *
15
+ * Pattern: append-only writer + LWW replay (mirrors lib/memory/pending-tension-store.cjs
16
+ * Phase 116-01 verbatim; field substitutions per RESEARCH Section 3 sibling code-clone).
17
+ *
18
+ * DELTA vs Phase 116-01 pending-tension-store.cjs: same 8-export skeleton; field
19
+ * renames (tension_id -> material_id; tension_type -> mtime_seconds; source_node_ids
20
+ * -> relative_file_path). State machine vocabulary differs (queued | in_flight |
21
+ * completed | failed instead of queued | surfaced | resolved | dropped). 2 new
22
+ * exports added (markFailed, sweepStaleInFlight) to handle the in_flight
23
+ * stale-sweep that 116 does not have because tensions have no background-spawn
24
+ * lifecycle.
25
+ *
26
+ * Pure CJS, node built-ins only, zero new runtime dependencies.
27
+ */
28
+ 'use strict';
29
+
30
+ const fs = require('node:fs');
31
+ const path = require('node:path');
32
+ const os = require('node:os');
33
+ const crypto = require('node:crypto');
34
+
35
+ // ---------- Constants ----------
36
+
37
+ const EXPLORED_MATERIALS_DIR = path.join(os.homedir(), '.mindrian', 'explored-materials');
38
+
39
+ const VALID_STATES = Object.freeze(new Set(['queued', 'in_flight', 'completed', 'failed']));
40
+
41
+ const VALID_RESPONSES = Object.freeze(new Set(['EXPLORE', 'SKIP', 'LATER', 'FREE_TEXT']));
42
+
43
+ const VALID_SUPPRESS_REASONS = Object.freeze(new Set([
44
+ 'tier_0',
45
+ 'just_talk',
46
+ 'dispatcher_load_failed',
47
+ 'pickShape_unavailable',
48
+ 'all_pipelines_empty',
49
+ 'brain_baseline_unavailable',
50
+ 'rate_limited',
51
+ 'daily_cap_exceeded',
52
+ 'room_dir_not_writable',
53
+ 'ledger_replay_failed',
54
+ 'hybrid_mode_insufficient_corpus',
55
+ 'sanitizer_blocked',
56
+ 'invalid_args',
57
+ ]));
58
+
59
+ // User-content key denylist (Canon Part 8 -- artifact bodies, titles, transcripts
60
+ // live in the graph, never in this JSONL). Rejection is fail-fast at
61
+ // appendMaterial time so a caller bug never leaks user content to disk.
62
+ // NOTE: relative_file_path IS allowed in the ledger (LOCAL-only state file
63
+ // outside the plugin repo per workspace guard), but MUST be hashed to
64
+ // file_path_sha256 BEFORE any memory_event payload write or telemetry mirror.
65
+ const USER_CONTENT_KEY_DENYLIST = Object.freeze(new Set([
66
+ 'body_text',
67
+ 'source_title',
68
+ 'target_title',
69
+ 'artifact_body',
70
+ 'note_content',
71
+ 'transcript_content',
72
+ 'meeting_summary',
73
+ 'cv_content',
74
+ 'file_content',
75
+ ]));
76
+
77
+ const MATERIAL_ID_LEN = 32;
78
+ const STALE_IN_FLIGHT_MS_DEFAULT = 300000; // 5 minutes per RESEARCH scenario 7
79
+
80
+ // ---------- Material id (deterministic, second-precision) ----------
81
+
82
+ /**
83
+ * Compute the deterministic material id for a (roomDir, relativeFilePath, mtimeMs)
84
+ * tuple. Per RESEARCH Section 4.5: mtime is FLOORED to seconds for cross-fs
85
+ * portability (FAT32, NFS, etc. lack ms-precision; risk T1).
86
+ *
87
+ * @param {string} roomDir absolute path to room (with .room-root)
88
+ * @param {string} relativeFilePath path relative to roomDir
89
+ * @param {number} mtimeMs file mtime in ms-epoch
90
+ * @returns {string} 32-char hex sha256 prefix
91
+ */
92
+ function computeMaterialId(roomDir, relativeFilePath, mtimeMs) {
93
+ const seconds = Math.floor(Number(mtimeMs) / 1000);
94
+ const basis = String(roomDir) + '|' + String(relativeFilePath) + '|' + String(seconds);
95
+ return crypto.createHash('sha256').update(basis).digest('hex').slice(0, MATERIAL_ID_LEN);
96
+ }
97
+
98
+ // ---------- JSONL path resolution (workspace-guard-clean) ----------
99
+
100
+ /**
101
+ * Resolve the JSONL path for a given room slug. Always returns a path under
102
+ * os.homedir() per CLAUDE.md workspace guard (OUTSIDE the plugin repo).
103
+ *
104
+ * @param {string} roomSlug
105
+ * @returns {string} absolute path
106
+ */
107
+ function jsonlPath(roomSlug) {
108
+ const safeSlug = encodeURIComponent(String(roomSlug || 'default-room'));
109
+ return path.join(EXPLORED_MATERIALS_DIR, safeSlug + '.jsonl');
110
+ }
111
+
112
+ // ---------- Internal helpers ----------
113
+
114
+ function isPlainObject(v) {
115
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
116
+ }
117
+
118
+ function isHex32(v) {
119
+ return typeof v === 'string' && /^[0-9a-f]{32}$/.test(v);
120
+ }
121
+
122
+ function violatesUserContentBoundary(entry) {
123
+ for (const k of Object.keys(entry)) {
124
+ if (USER_CONTENT_KEY_DENYLIST.has(k)) return k;
125
+ }
126
+ return null;
127
+ }
128
+
129
+ // ---------- Validation ----------
130
+
131
+ /**
132
+ * Validate the shape of an entry. Returns { ok: true } on success, or
133
+ * { ok: false, reason, offending_key? } on rejection.
134
+ *
135
+ * Canon Part 8 enforcement: any USER_CONTENT_KEY_DENYLIST key in the entry
136
+ * triggers a canon_part_8_violation rejection (fail-fast before disk write).
137
+ *
138
+ * @param {object} entry
139
+ * @returns {{ok: true} | {ok: false, reason: string, offending_key?: string}}
140
+ */
141
+ function validateEntryShape(entry) {
142
+ if (!isPlainObject(entry)) {
143
+ return { ok: false, reason: 'entry_not_object' };
144
+ }
145
+ // Canon Part 8 boundary -- check FIRST so a payload with denylist keys is
146
+ // rejected even if other shape checks would also fail.
147
+ const violator = violatesUserContentBoundary(entry);
148
+ if (violator) {
149
+ return { ok: false, reason: 'canon_part_8_violation', offending_key: violator };
150
+ }
151
+ if (!isHex32(entry.material_id)) {
152
+ return { ok: false, reason: 'invalid_material_id' };
153
+ }
154
+ if (!VALID_STATES.has(entry.state)) {
155
+ return { ok: false, reason: 'invalid_state' };
156
+ }
157
+ if (!Number.isInteger(entry.mtime_seconds) || entry.mtime_seconds < 0) {
158
+ return { ok: false, reason: 'invalid_mtime_seconds' };
159
+ }
160
+ if (!Number.isInteger(entry.fired_at) || entry.fired_at < 0) {
161
+ return { ok: false, reason: 'invalid_fired_at' };
162
+ }
163
+ return { ok: true };
164
+ }
165
+
166
+ // ---------- Public API ----------
167
+
168
+ /**
169
+ * Append a material entry to the room's JSONL file. Creates the directory if
170
+ * missing. Validates shape + Canon Part 8 boundary; rejects user-content
171
+ * fields.
172
+ *
173
+ * @param {string} roomSlug
174
+ * @param {object} entry conforming to RESEARCH Section 4.5 schema
175
+ * @returns {{ok: true, path: string} | {ok: false, reason: string, offending_key?: string}}
176
+ */
177
+ function appendMaterial(roomSlug, entry) {
178
+ const validation = validateEntryShape(entry);
179
+ if (!validation.ok) return validation;
180
+ try {
181
+ fs.mkdirSync(EXPLORED_MATERIALS_DIR, { recursive: true });
182
+ const file = jsonlPath(roomSlug);
183
+ fs.appendFileSync(file, JSON.stringify(entry) + '\n');
184
+ return { ok: true, path: file };
185
+ } catch (err) {
186
+ const msg = (err && err.message) ? String(err.message) : 'append_failed';
187
+ return { ok: false, reason: msg.slice(0, 80) };
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Read all materials for a room, applying LWW replay (last entry per
193
+ * material_id wins). Returns [] when the file is missing. Tolerates corrupt
194
+ * lines by skipping them silently.
195
+ *
196
+ * @param {string} roomSlug
197
+ * @returns {Array<object>}
198
+ */
199
+ function readMaterials(roomSlug) {
200
+ const file = jsonlPath(roomSlug);
201
+ if (!fs.existsSync(file)) return [];
202
+ let raw;
203
+ try {
204
+ raw = fs.readFileSync(file, 'utf8');
205
+ } catch (_e) {
206
+ return [];
207
+ }
208
+ const lines = raw.split('\n').filter(Boolean);
209
+ const byId = new Map();
210
+ for (const line of lines) {
211
+ let entry;
212
+ try {
213
+ entry = JSON.parse(line);
214
+ } catch (_e) {
215
+ continue; // corrupt line tolerated per RESEARCH Section 4.5 LWW replay tolerance
216
+ }
217
+ if (!isPlainObject(entry) || !isHex32(entry.material_id)) continue;
218
+ byId.set(entry.material_id, entry);
219
+ }
220
+ return Array.from(byId.values());
221
+ }
222
+
223
+ /**
224
+ * Find the latest entry for a single material_id. Returns null when missing.
225
+ * Sorts by fired_at descending so the most-recent entry wins regardless of
226
+ * append order.
227
+ *
228
+ * @param {string} roomSlug
229
+ * @param {string} materialId 32-char hex
230
+ * @returns {object|null}
231
+ */
232
+ function findLatest(roomSlug, materialId) {
233
+ if (!isHex32(materialId)) return null;
234
+ const file = jsonlPath(roomSlug);
235
+ if (!fs.existsSync(file)) return null;
236
+ let raw;
237
+ try {
238
+ raw = fs.readFileSync(file, 'utf8');
239
+ } catch (_e) {
240
+ return null;
241
+ }
242
+ const lines = raw.split('\n').filter(Boolean);
243
+ let latest = null;
244
+ for (const line of lines) {
245
+ let entry;
246
+ try {
247
+ entry = JSON.parse(line);
248
+ } catch (_e) {
249
+ continue;
250
+ }
251
+ if (!isPlainObject(entry)) continue;
252
+ if (entry.material_id !== materialId) continue;
253
+ if (latest === null) {
254
+ latest = entry;
255
+ continue;
256
+ }
257
+ const a = Number.isInteger(entry.fired_at) ? entry.fired_at : 0;
258
+ const b = Number.isInteger(latest.fired_at) ? latest.fired_at : 0;
259
+ if (a >= b) latest = entry;
260
+ }
261
+ return latest;
262
+ }
263
+
264
+ /**
265
+ * Mark a material as completed. Appends a transition entry where state is
266
+ * 'completed' and finding_count is set from fields.
267
+ *
268
+ * @param {string} roomSlug
269
+ * @param {string} materialId
270
+ * @param {object} fields { finding_count: number }
271
+ * @returns {{ok: true} | {ok: false, reason: string}}
272
+ */
273
+ function markCompleted(roomSlug, materialId, fields) {
274
+ const prev = findLatest(roomSlug, materialId);
275
+ if (!prev) return { ok: false, reason: 'material_not_found' };
276
+ const fc = (fields && Number.isInteger(fields.finding_count)) ? fields.finding_count : null;
277
+ const next = Object.assign({}, prev, {
278
+ state: 'completed',
279
+ finding_count: fc,
280
+ in_flight_since: null,
281
+ });
282
+ const r = appendMaterial(roomSlug, next);
283
+ if (!r.ok) return r;
284
+ return { ok: true };
285
+ }
286
+
287
+ /**
288
+ * Mark a material as failed with a suppression reason. If the material is
289
+ * not yet in the ledger, append a fresh 'failed' entry (suppression on the
290
+ * very first detection event).
291
+ *
292
+ * @param {string} roomSlug
293
+ * @param {string} materialId
294
+ * @param {string} suppressReason one of VALID_SUPPRESS_REASONS
295
+ * @returns {{ok: true} | {ok: false, reason: string}}
296
+ */
297
+ function markFailed(roomSlug, materialId, suppressReason) {
298
+ if (!VALID_SUPPRESS_REASONS.has(suppressReason)) {
299
+ return { ok: false, reason: 'invalid_suppress_reason' };
300
+ }
301
+ if (!isHex32(materialId)) {
302
+ return { ok: false, reason: 'invalid_material_id' };
303
+ }
304
+ const prev = findLatest(roomSlug, materialId);
305
+ let next;
306
+ if (prev) {
307
+ next = Object.assign({}, prev, {
308
+ state: 'failed',
309
+ suppress_reason: suppressReason,
310
+ in_flight_since: null,
311
+ });
312
+ } else {
313
+ // Suppression on first event -- caller has not yet appended a queued entry.
314
+ next = {
315
+ material_id: materialId,
316
+ file_path_sha256: null,
317
+ relative_file_path: null,
318
+ mtime_seconds: 0,
319
+ fired_at: Date.now(),
320
+ state: 'failed',
321
+ finding_count: null,
322
+ surfaced: false,
323
+ user_response: null,
324
+ responded_at: null,
325
+ suppress_reason: suppressReason,
326
+ in_flight_since: null,
327
+ };
328
+ }
329
+ const r = appendMaterial(roomSlug, next);
330
+ if (!r.ok) return r;
331
+ return { ok: true };
332
+ }
333
+
334
+ /**
335
+ * Sweep stale in_flight entries. Per RESEARCH scenario 7 (5min stale
336
+ * recovery): an entry with state==='in_flight' AND
337
+ * (now - in_flight_since) > staleMs is transitioned to 'failed' with
338
+ * suppress_reason='rate_limited' (benign reason; the spawn child likely
339
+ * crashed or timed out, and the next detection of the same material_id
340
+ * will be free to fire fresh).
341
+ *
342
+ * Idempotent: re-running on the same JSONL is a no-op when no entries are
343
+ * stale (terminal states 'completed' and 'failed' are excluded from the scan).
344
+ *
345
+ * Sync. Never throws. On any error returns { ok: true, swept: 0 } so the
346
+ * SessionStart hook can never be derailed by a sweep failure.
347
+ *
348
+ * @param {string} roomSlug
349
+ * @param {object} opts { staleMs: number }
350
+ * @returns {{ok: true, swept: number}}
351
+ */
352
+ function sweepStaleInFlight(roomSlug, opts) {
353
+ try {
354
+ const staleMs = (opts && Number.isInteger(opts.staleMs) && opts.staleMs > 0)
355
+ ? opts.staleMs
356
+ : STALE_IN_FLIGHT_MS_DEFAULT;
357
+ const now = Date.now();
358
+ const entries = readMaterials(roomSlug);
359
+ let swept = 0;
360
+ for (const e of entries) {
361
+ if (!isPlainObject(e)) continue;
362
+ if (e.state !== 'in_flight') continue;
363
+ if (!Number.isInteger(e.in_flight_since)) continue;
364
+ if ((now - e.in_flight_since) <= staleMs) continue;
365
+ const r = markFailed(roomSlug, e.material_id, 'rate_limited');
366
+ if (r && r.ok) swept += 1;
367
+ }
368
+ return { ok: true, swept: swept };
369
+ } catch (_e) {
370
+ return { ok: true, swept: 0 };
371
+ }
372
+ }
373
+
374
+ // ---------- Module exports ----------
375
+
376
+ module.exports = {
377
+ computeMaterialId,
378
+ jsonlPath,
379
+ validateEntryShape,
380
+ appendMaterial,
381
+ readMaterials,
382
+ findLatest,
383
+ markCompleted,
384
+ markFailed,
385
+ sweepStaleInFlight,
386
+ EXPLORED_MATERIALS_DIR,
387
+ MATERIAL_ID_LEN,
388
+ VALID_STATES,
389
+ VALID_RESPONSES,
390
+ VALID_SUPPRESS_REASONS,
391
+ USER_CONTENT_KEY_DENYLIST,
392
+ };