@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,314 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Phase 94-03 -- canonical Brain MCP server-name resolution fence.
6
+ *
7
+ * BSL 1.1. Copyright (c) Mindrian 2026.
8
+ * (Business Source License 1.1; SPDX BUSL-1.1; see LICENSE.)
9
+ *
10
+ * Why this fixture exists.
11
+ *
12
+ * The v1.11.0 QA harness (Lawrence's Dr. Miriam Kaplan persona, 2026-04-28)
13
+ * surfaced a P0 ship-blocker (FIX-2): the Brain knowledge graph is alive
14
+ * (7,353 LazyGraphConcept nodes, 119,706 CO_OCCURS edges, 20+ named PWS
15
+ * frameworks) but unreachable from /mos:* commands. Root cause: plugin
16
+ * command frontmatter declared THREE inconsistent server-name prefixes
17
+ *
18
+ * mcp__neo4j-brain__ (5 commands)
19
+ * mcp__mindrian-brain__ (6 commands -- already canonical)
20
+ * mcp__pinecone-brain__ (3 commands)
21
+ *
22
+ * while .mcp.json declared only `mindrian-os` (the plugin's own MCP
23
+ * router, not the Brain). Result: every Brain-touching slash command
24
+ * silently fell back to the graceful tier_0 path. Decision-traces
25
+ * confirmed routing_source: legacy and brain_md_tier_mode: tier_0
26
+ * on every session.
27
+ *
28
+ * Plan 94-03 selects Option A from the QA handoff (cheapest path):
29
+ * standardize the canonical server name across all 10 affected command
30
+ * frontmatter files on `mindrian-brain`, document the user-side
31
+ * .mcp.json snippet, and let users register their Neo4j MCP under the
32
+ * canonical name. No alias system. No auto-detect. Both deferred to
33
+ * v1.12 per Options B/C.
34
+ *
35
+ * Test map (5 cases, one-to-one with the PLAN <behavior> block).
36
+ *
37
+ * T1 Every commands/*.md allowed-tools list mcp__*brain* reference
38
+ * starts with the canonical prefix `mcp__mindrian-brain__`. We
39
+ * walk the 10 affected files, parse each YAML frontmatter, collect
40
+ * allowed-tools entries, and assert any tool matching /^mcp__\\S+brain\\S*__/
41
+ * starts with `mcp__mindrian-brain__`.
42
+ *
43
+ * T2 Zero `mcp__neo4j-brain__` references remain in any commands/*.md
44
+ * (frontmatter OR body prose). grep -lE pattern.
45
+ *
46
+ * T3 Zero `mcp__pinecone-brain__` references remain in any commands/*.md
47
+ * (frontmatter OR body prose). grep -lE pattern.
48
+ *
49
+ * T4 docs/install/BRAIN-SETUP.md exists and contains the literal
50
+ * substring `mindrian-brain` (canonical name) AND the literal
51
+ * substring `mcpServers` (user-side .mcp.json snippet shape).
52
+ *
53
+ * T5 Optional gate: scripts/frontmatter-schema-validator.cjs (Phase
54
+ * 88.1-07) returns exit 0 against each of the 10 modified command
55
+ * files. The validator is ADVISORY (PostToolUse) -- it always
56
+ * exits 0 regardless of violations -- so this case is structural:
57
+ * prove the validator is invokable against each file without
58
+ * crashing. Skip gracefully (PASS-and-warn) if the validator is
59
+ * absent in this checkout.
60
+ *
61
+ * Canon traceability.
62
+ *
63
+ * Part 7 (Reuse Before Build): Option A is the cheapest sweep -- no
64
+ * new alias system, no auto-detect, no new MCP server entry in the
65
+ * plugin's .mcp.json. We compose existing command frontmatter into
66
+ * a single canonical name.
67
+ *
68
+ * Part 8 (Graph Boundary): the Brain query chokepoint behavior is
69
+ * unchanged. This plan only standardizes the MCP server name passed
70
+ * in. Zero user-data egress added or removed.
71
+ *
72
+ * Registered in lib/memory/run-feynman-tests.cjs.
73
+ */
74
+
75
+ const assert = require('node:assert/strict');
76
+ const fs = require('node:fs');
77
+ const path = require('node:path');
78
+ const { spawnSync } = require('node:child_process');
79
+
80
+ const REPO = path.resolve(__dirname, '..', '..');
81
+ const COMMANDS_DIR = path.join(REPO, 'commands');
82
+ const SETUP_DOC = path.join(REPO, 'docs', 'install', 'BRAIN-SETUP.md');
83
+ const VALIDATOR = path.join(REPO, 'scripts', 'frontmatter-schema-validator.cjs');
84
+
85
+ // The 10 commands declared by the plan as carrying or potentially carrying
86
+ // Brain MCP references. Order mirrors plan frontmatter files_modified.
87
+ const AFFECTED_COMMANDS = [
88
+ 'compare-ventures',
89
+ 'suggest-next',
90
+ 'find-analogies',
91
+ 'brain-derive',
92
+ 'query',
93
+ 'rs-experts',
94
+ 'rs-explain',
95
+ 'rs-fetch',
96
+ 'rs-thesis',
97
+ 'admin',
98
+ ];
99
+
100
+ const CANONICAL_PREFIX = 'mcp__mindrian-brain__';
101
+ const FORBIDDEN_PREFIXES = ['mcp__neo4j-brain__', 'mcp__pinecone-brain__'];
102
+
103
+ // ---------- Fixture helpers ----------
104
+
105
+ function readFile(p) {
106
+ return fs.readFileSync(p, 'utf8');
107
+ }
108
+
109
+ /**
110
+ * Minimal single-key YAML frontmatter parser sufficient for allowed-tools.
111
+ * Returns the array of allowed-tools entries (raw strings, '- ' stripped),
112
+ * or [] if no allowed-tools section is found. Tolerant of:
113
+ * - inline form: `allowed-tools: Bash(node *)`
114
+ * - list form (multi-line dash-prefixed entries)
115
+ * Does NOT honor nested YAML structures or quoted values; the fixture
116
+ * does not need them for this contract.
117
+ */
118
+ function parseAllowedTools(text) {
119
+ const m = text.match(/^---\n([\s\S]*?)\n---/);
120
+ if (!m) return [];
121
+ const fm = m[1];
122
+ const lines = fm.split('\n');
123
+ const tools = [];
124
+ let inList = false;
125
+ for (const raw of lines) {
126
+ const line = raw.replace(/\r$/, '');
127
+ if (/^allowed-tools:\s*$/.test(line)) {
128
+ inList = true;
129
+ continue;
130
+ }
131
+ if (/^allowed-tools:\s*\S/.test(line)) {
132
+ // Inline form: allowed-tools: Bash(node *)
133
+ const inline = line.replace(/^allowed-tools:\s*/, '').trim();
134
+ if (inline) tools.push(inline);
135
+ inList = false;
136
+ continue;
137
+ }
138
+ if (inList) {
139
+ if (/^\s+-\s+/.test(line)) {
140
+ const v = line.replace(/^\s+-\s+/, '').trim();
141
+ if (v) tools.push(v);
142
+ continue;
143
+ }
144
+ // Any non-list-item line ends the allowed-tools block.
145
+ if (/^\S/.test(line)) inList = false;
146
+ }
147
+ }
148
+ return tools;
149
+ }
150
+
151
+ // ---------- Tests ----------
152
+
153
+ let passed = 0;
154
+ let failed = 0;
155
+ const fail = (name, msg) => {
156
+ failed++;
157
+ process.stderr.write('FAIL ' + name + ': ' + msg + '\n');
158
+ };
159
+ const pass = (name) => {
160
+ passed++;
161
+ process.stdout.write('PASS ' + name + '\n');
162
+ };
163
+
164
+ function t1_canonical_prefix_in_frontmatter() {
165
+ const name = 'T1 every brain reference in allowed-tools uses canonical prefix';
166
+ try {
167
+ const violations = [];
168
+ for (const cmd of AFFECTED_COMMANDS) {
169
+ const file = path.join(COMMANDS_DIR, cmd + '.md');
170
+ if (!fs.existsSync(file)) {
171
+ violations.push(cmd + ': file missing');
172
+ continue;
173
+ }
174
+ const txt = readFile(file);
175
+ const tools = parseAllowedTools(txt);
176
+ for (const tool of tools) {
177
+ if (/^mcp__\S+brain\S*__/.test(tool)) {
178
+ if (!tool.startsWith(CANONICAL_PREFIX)) {
179
+ violations.push(cmd + ': non-canonical brain tool ' + tool);
180
+ }
181
+ }
182
+ }
183
+ }
184
+ assert.equal(
185
+ violations.length,
186
+ 0,
187
+ 'Non-canonical brain tools found:\n ' + violations.join('\n ')
188
+ );
189
+ pass(name);
190
+ } catch (e) {
191
+ fail(name, e.message);
192
+ }
193
+ }
194
+
195
+ function t2_no_neo4j_brain_anywhere() {
196
+ const name = 'T2 zero mcp__neo4j-brain__ references in commands/*.md';
197
+ try {
198
+ const matches = [];
199
+ const files = fs.readdirSync(COMMANDS_DIR)
200
+ .filter((f) => f.endsWith('.md'))
201
+ .map((f) => path.join(COMMANDS_DIR, f));
202
+ for (const f of files) {
203
+ const txt = readFile(f);
204
+ if (txt.includes('mcp__neo4j-brain__') || txt.includes('mcp__neo4j-brain ')) {
205
+ matches.push(path.relative(REPO, f));
206
+ }
207
+ }
208
+ assert.equal(
209
+ matches.length,
210
+ 0,
211
+ 'mcp__neo4j-brain__ references must be zero, found in:\n ' + matches.join('\n ')
212
+ );
213
+ pass(name);
214
+ } catch (e) {
215
+ fail(name, e.message);
216
+ }
217
+ }
218
+
219
+ function t3_no_pinecone_brain_anywhere() {
220
+ const name = 'T3 zero mcp__pinecone-brain__ references in commands/*.md';
221
+ try {
222
+ const matches = [];
223
+ const files = fs.readdirSync(COMMANDS_DIR)
224
+ .filter((f) => f.endsWith('.md'))
225
+ .map((f) => path.join(COMMANDS_DIR, f));
226
+ for (const f of files) {
227
+ const txt = readFile(f);
228
+ if (txt.includes('mcp__pinecone-brain__') || txt.includes('mcp__pinecone-brain ')) {
229
+ matches.push(path.relative(REPO, f));
230
+ }
231
+ }
232
+ assert.equal(
233
+ matches.length,
234
+ 0,
235
+ 'mcp__pinecone-brain__ references must be zero, found in:\n ' + matches.join('\n ')
236
+ );
237
+ pass(name);
238
+ } catch (e) {
239
+ fail(name, e.message);
240
+ }
241
+ }
242
+
243
+ function t4_brain_setup_doc_exists() {
244
+ const name = 'T4 docs/install/BRAIN-SETUP.md exists with canonical name + mcpServers snippet';
245
+ try {
246
+ assert.ok(
247
+ fs.existsSync(SETUP_DOC),
248
+ 'docs/install/BRAIN-SETUP.md does not exist'
249
+ );
250
+ const txt = readFile(SETUP_DOC);
251
+ assert.ok(
252
+ txt.includes('mindrian-brain'),
253
+ 'BRAIN-SETUP.md missing canonical name `mindrian-brain`'
254
+ );
255
+ assert.ok(
256
+ txt.includes('mcpServers'),
257
+ 'BRAIN-SETUP.md missing user-side .mcp.json snippet (`mcpServers`)'
258
+ );
259
+ pass(name);
260
+ } catch (e) {
261
+ fail(name, e.message);
262
+ }
263
+ }
264
+
265
+ function t5_frontmatter_validator_runnable() {
266
+ const name = 'T5 frontmatter-schema-validator runnable on each modified file';
267
+ try {
268
+ if (!fs.existsSync(VALIDATOR)) {
269
+ // Skip gracefully -- validator is an optional Phase 88.1-07 artifact.
270
+ process.stdout.write(
271
+ 'SKIP ' + name + ' (validator absent at ' +
272
+ path.relative(REPO, VALIDATOR) + ')\n'
273
+ );
274
+ passed++;
275
+ return;
276
+ }
277
+ // Phase 88.1-07 validator is a PostToolUse hook -- it reads JSON on stdin
278
+ // and emits a JSON response on stdout. Calling it without stdin will
279
+ // either no-op or error gracefully. We assert it does not crash with a
280
+ // non-zero exit on a benign invocation. This is a structural smoke
281
+ // (the validator is invokable in this checkout), not a content gate.
282
+ const res = spawnSync(process.execPath, [VALIDATOR, '--help'], {
283
+ input: '',
284
+ timeout: 5000,
285
+ });
286
+ // Accept exit 0 (help printed) OR exit codes from "missing stdin / not a
287
+ // hook context" branches. The validator is advisory and never blocks;
288
+ // the only failure mode that matters here is a hard crash (e.g. syntax
289
+ // error in the validator source).
290
+ assert.ok(
291
+ res.status === null || res.status === 0 || res.status === 1,
292
+ 'frontmatter-schema-validator crashed with unexpected status ' + res.status +
293
+ ' (signal=' + res.signal + ')\n' +
294
+ 'stderr: ' + (res.stderr ? res.stderr.toString() : '')
295
+ );
296
+ pass(name);
297
+ } catch (e) {
298
+ fail(name, e.message);
299
+ }
300
+ }
301
+
302
+ // ---------- Run ----------
303
+
304
+ t1_canonical_prefix_in_frontmatter();
305
+ t2_no_neo4j_brain_anywhere();
306
+ t3_no_pinecone_brain_anywhere();
307
+ t4_brain_setup_doc_exists();
308
+ t5_frontmatter_validator_runnable();
309
+
310
+ const total = passed + failed;
311
+ process.stdout.write(
312
+ '\nbrain-server-resolution: ' + passed + '/' + total + ' passed\n'
313
+ );
314
+ process.exit(failed === 0 ? 0 : 1);
@@ -0,0 +1,233 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Phase 122-03 -- chain-recommender tests (new suite; registered in the Feynman runner).
6
+ *
7
+ * Registered in lib/memory/run-feynman-tests.cjs TEST_FILES[] and in
8
+ * tests/run-all-122.sh CJS_SUITES (as ../lib/memory/chain-recommender.test.cjs).
9
+ * Run: node lib/memory/chain-recommender.test.cjs
10
+ * Exit 0 on pass; throws (node:assert) on any fail.
11
+ *
12
+ * Five assertion groups (per 122-03-PLAN.md Task 2 <behavior>):
13
+ * 1. recommendFrameworkChain({ problemType:'ill-defined' }) -> non-empty
14
+ * ordered array of framework-name strings (length 1..4); element 0 is the
15
+ * seed (here, the IDP family's first skill resolved through the registry).
16
+ * 2. recommendFrameworkChain({ currentFramework:'Beautiful Question Framework' })
17
+ * -> that framework first; with offline FEEDS_INTO edges supplied via
18
+ * roomState, its successors follow in order.
19
+ * 3. A seed framework with no outgoing FEEDS_INTO edge -> [seed] (degrades
20
+ * cleanly, not a crash).
21
+ * 4. Brain unavailable (monkey-patch brain-client.isAvailable() -> false) ->
22
+ * the recommender still returns a chain (offline FEEDS_INTO walk, or [seed]);
23
+ * never throws, never returns null.
24
+ * 5. Canon Part 8: the recommender source contains NO command literal; any
25
+ * Cypher-looking string it builds contains only $-bound params (framework
26
+ * names / enums) -- no string interpolation of a command, no user-content
27
+ * token. The recommender's return value is framework names only.
28
+ */
29
+
30
+ const assert = require('node:assert/strict');
31
+ const fs = require('node:fs');
32
+ const path = require('node:path');
33
+
34
+ const REPO_ROOT = path.resolve(__dirname, '..', '..');
35
+ const RECOMMENDER_PATH = path.join(REPO_ROOT, 'lib', 'brain', 'chain-recommender.cjs');
36
+ const REGISTRY_PATH = path.join(REPO_ROOT, 'data', 'command-registry.json');
37
+
38
+ const recommender = require('../brain/chain-recommender.cjs');
39
+ const composer = require('../core/framework-chain-composer.cjs');
40
+ const problemTypeRouter = require('../core/problem-type-router.cjs');
41
+ const brainClient = require('../core/brain-client.cjs');
42
+
43
+ const registry = JSON.parse(fs.readFileSync(REGISTRY_PATH, 'utf8'));
44
+
45
+ let passed = 0;
46
+ function test(name, fn) {
47
+ fn();
48
+ process.stdout.write(' ok ' + name + '\n');
49
+ passed += 1;
50
+ }
51
+
52
+ // Derive what the IDP seed framework SHOULD be (so the test is retrofit-proof:
53
+ // it does not hardcode "Beautiful Question Framework", it re-derives it the
54
+ // way the recommender does -- router first skill -> registry first framework).
55
+ function slugToFramework(slug) {
56
+ for (const c of registry.commands) {
57
+ if (!c || typeof c.command !== 'string') continue;
58
+ const parts = c.command.split(':');
59
+ if (parts.length >= 2 && parts.slice(1).join(':') === slug) {
60
+ return (Array.isArray(c.frameworks) && c.frameworks.length > 0) ? c.frameworks[0] : null;
61
+ }
62
+ }
63
+ return null;
64
+ }
65
+ const idpSkills = problemTypeRouter.routeByProblemType('IDP', null).recommended_skills;
66
+ const expectedIdpSeed = slugToFramework(idpSkills[0]);
67
+ assert.ok(expectedIdpSeed, 'the IDP family first skill must resolve to a framework via the registry');
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // 1. seeded from problem-type, walked via FEEDS_INTO; seed is first
71
+ // ---------------------------------------------------------------------------
72
+ test('recommendFrameworkChain({problemType:"ill-defined"}) -> non-empty ordered framework-name array, seed first', function () {
73
+ const chain = recommender.recommendFrameworkChain({ problemType: 'ill-defined' });
74
+ assert.ok(Array.isArray(chain), 'returns an array');
75
+ assert.ok(chain.length >= 1 && chain.length <= 4, 'length 1..4, got ' + chain.length);
76
+ assert.ok(chain.every(function (x) { return typeof x === 'string' && x.length > 0; }), 'all elements are non-empty strings');
77
+ assert.strictEqual(chain[0], expectedIdpSeed, 'element 0 is the IDP seed framework');
78
+ // 'IDP' alias and the canonical token yield the same seed.
79
+ assert.strictEqual(recommender.recommendFrameworkChain({ problemType: 'IDP' })[0], expectedIdpSeed);
80
+ // The other problem-type families seed differently (UDP / WDP).
81
+ const udpSeed = slugToFramework(problemTypeRouter.routeByProblemType('UDP', null).recommended_skills[0]);
82
+ assert.strictEqual(recommender.recommendFrameworkChain({ problemType: 'undefined' })[0], udpSeed);
83
+ });
84
+
85
+ // ---------------------------------------------------------------------------
86
+ // 2. currentFramework first; offline FEEDS_INTO successors follow
87
+ // ---------------------------------------------------------------------------
88
+ test('recommendFrameworkChain({currentFramework:X}) -> X first; supplied FEEDS_INTO edges add successors in order', function () {
89
+ // No offline edges -> degrades to [X].
90
+ const bare = recommender.recommendFrameworkChain({ currentFramework: 'Beautiful Question Framework' });
91
+ assert.strictEqual(bare[0], 'Beautiful Question Framework');
92
+
93
+ // With a pre-parsed FEEDS_INTO edge array (the shape parseFrameworkChainSection
94
+ // returns) -> walk the chain via composer.proposeNextFramework.
95
+ const edges = [
96
+ { from: 'Beautiful Question Framework', to: 'Domain Selection', confidence: 0.9, phase_indicator: null },
97
+ { from: 'Domain Selection', to: 'Jobs to Be Done (JTBD)', confidence: 0.85, phase_indicator: null },
98
+ { from: 'Jobs to Be Done (JTBD)', to: 'PWS Value Proposition', confidence: 0.8, phase_indicator: null },
99
+ { from: 'PWS Value Proposition', to: 'Lean Canvas', confidence: 0.75, phase_indicator: null }, // would be 5th -> truncated
100
+ ];
101
+ const walked = recommender.recommendFrameworkChain({
102
+ currentFramework: 'Beautiful Question Framework',
103
+ roomState: { feedsIntoEdges: edges },
104
+ });
105
+ assert.deepStrictEqual(walked, ['Beautiful Question Framework', 'Domain Selection', 'Jobs to Be Done (JTBD)', 'PWS Value Proposition']);
106
+ assert.ok(walked.length <= 4, 'chain is capped at 4');
107
+
108
+ // The same edges expressed as a BRAIN.md framework_chain_predictions section body.
109
+ const sectionBody =
110
+ 'Beautiful Question Framework FEEDS_INTO Domain Selection (confidence: 0.9)\n' +
111
+ 'Domain Selection FEEDS_INTO Jobs to Be Done (JTBD) (confidence: 0.85)';
112
+ const fromSection = recommender.recommendFrameworkChain({
113
+ currentFramework: 'Beautiful Question Framework',
114
+ roomState: { brainSection: { body: sectionBody } },
115
+ });
116
+ assert.deepStrictEqual(fromSection, ['Beautiful Question Framework', 'Domain Selection', 'Jobs to Be Done (JTBD)']);
117
+ });
118
+
119
+ // ---------------------------------------------------------------------------
120
+ // 3. seed with no outgoing FEEDS_INTO edge -> [seed]
121
+ // ---------------------------------------------------------------------------
122
+ test('a seed with no outgoing FEEDS_INTO edge degrades to [seed] (no crash, never null)', function () {
123
+ const edges = [
124
+ { from: 'Some Other Framework', to: 'Yet Another', confidence: 0.9, phase_indicator: null },
125
+ ];
126
+ const chain = recommender.recommendFrameworkChain({
127
+ currentFramework: 'Beautiful Question Framework',
128
+ roomState: { feedsIntoEdges: edges },
129
+ });
130
+ assert.deepStrictEqual(chain, ['Beautiful Question Framework']);
131
+
132
+ // An edge below the composer noise floor (< 0.5) is not followed either.
133
+ const lowConf = [{ from: 'Beautiful Question Framework', to: 'Domain Selection', confidence: 0.3, phase_indicator: null }];
134
+ assert.deepStrictEqual(
135
+ recommender.recommendFrameworkChain({ currentFramework: 'Beautiful Question Framework', roomState: { feedsIntoEdges: lowConf } }),
136
+ ['Beautiful Question Framework']
137
+ );
138
+
139
+ // Empty / no roomState -> [seed].
140
+ assert.deepStrictEqual(
141
+ recommender.recommendFrameworkChain({ currentFramework: 'Beautiful Question Framework', roomState: {} }),
142
+ ['Beautiful Question Framework']
143
+ );
144
+ assert.deepStrictEqual(
145
+ recommender.recommendFrameworkChain({ currentFramework: 'Beautiful Question Framework' }),
146
+ ['Beautiful Question Framework']
147
+ );
148
+
149
+ // No args at all -> the default seed, length 1.
150
+ const def = recommender.recommendFrameworkChain();
151
+ assert.ok(Array.isArray(def) && def.length >= 1 && typeof def[0] === 'string');
152
+ assert.strictEqual(def[0], recommender.DEFAULT_SEED);
153
+ });
154
+
155
+ // ---------------------------------------------------------------------------
156
+ // 4. Brain unavailable -> still returns a chain; never throws, never null
157
+ // ---------------------------------------------------------------------------
158
+ test('Brain unavailable (isAvailable() -> false) still returns a chain; never throws, never null', function () {
159
+ const orig = brainClient.isAvailable;
160
+ try {
161
+ brainClient.isAvailable = function () { return false; };
162
+ const c1 = recommender.recommendFrameworkChain({ problemType: 'ill-defined' });
163
+ assert.ok(Array.isArray(c1) && c1.length >= 1 && c1.every(function (x) { return typeof x === 'string'; }));
164
+ assert.notStrictEqual(c1, null);
165
+ const edges = [{ from: 'Beautiful Question Framework', to: 'Domain Selection', confidence: 0.9, phase_indicator: null }];
166
+ const c2 = recommender.recommendFrameworkChain({ currentFramework: 'Beautiful Question Framework', roomState: { feedsIntoEdges: edges } });
167
+ assert.deepStrictEqual(c2, ['Beautiful Question Framework', 'Domain Selection']);
168
+ } finally {
169
+ brainClient.isAvailable = orig;
170
+ }
171
+ // And with Brain "available" (the real isAvailable, which is false on a CI box
172
+ // without a key -- so this also exercises the no-key path) it still works.
173
+ const c3 = recommender.recommendFrameworkChain({ currentFramework: 'Beautiful Question Framework' });
174
+ assert.deepStrictEqual(c3, ['Beautiful Question Framework']);
175
+ });
176
+
177
+ // ---------------------------------------------------------------------------
178
+ // 5. Canon Part 8: no command literal; Cypher carries only $-bound params; FW names only
179
+ // ---------------------------------------------------------------------------
180
+ test('Canon Part 8: chain-recommender source has no command literal; any Cypher uses only $-bound params', function () {
181
+ const src = fs.readFileSync(RECOMMENDER_PATH, 'utf8');
182
+ // No /mos: command literal anywhere (the pre-commit / 122-02 grep guard too).
183
+ assert.ok(!/\/mos:/.test(src), 'no /mos: command literal in chain-recommender.cjs');
184
+
185
+ // Any Cypher-looking string (a line containing MATCH ... FEEDS_INTO ...) must
186
+ // bind its inputs as $-params, never interpolate them, and never carry a
187
+ // command token or a user-content token.
188
+ const lines = src.split(/\r?\n/);
189
+ let sawCypher = false;
190
+ for (const line of lines) {
191
+ if (!/FEEDS_INTO/.test(line) || !/MATCH/i.test(line)) continue;
192
+ sawCypher = true;
193
+ assert.ok(/\$seed/.test(line) || /\$[a-zA-Z_]/.test(line), 'FEEDS_INTO Cypher binds inputs via $-params: ' + line.trim());
194
+ assert.ok(!/\$\{/.test(line), 'no template-literal interpolation in the Cypher: ' + line.trim());
195
+ assert.ok(!/\/mos:/.test(line), 'no command literal in the Cypher: ' + line.trim());
196
+ }
197
+ // The exported Cypher constant, if present, must also satisfy this.
198
+ if (typeof recommender.FEEDS_INTO_CYPHER === 'string') {
199
+ assert.ok(/\$seed/.test(recommender.FEEDS_INTO_CYPHER), 'FEEDS_INTO_CYPHER binds $seed');
200
+ assert.ok(!/\$\{/.test(recommender.FEEDS_INTO_CYPHER), 'FEEDS_INTO_CYPHER has no template interpolation');
201
+ assert.ok(!/\/mos:/.test(recommender.FEEDS_INTO_CYPHER), 'FEEDS_INTO_CYPHER has no command literal');
202
+ }
203
+ assert.ok(sawCypher || typeof recommender.FEEDS_INTO_CYPHER === 'string', 'a FEEDS_INTO Cypher template exists (source line or exported constant)');
204
+
205
+ // The return value is framework names only -- never a command string.
206
+ const chain = recommender.recommendFrameworkChain({ problemType: 'ill-defined' });
207
+ assert.ok(chain.every(function (x) { return typeof x === 'string' && !/\/mos:/.test(x); }), 'returned chain is framework names, no command strings');
208
+
209
+ // It reuses framework-chain-composer + problem-type-router (not hand-rolled).
210
+ assert.ok(/framework-chain-composer/.test(src), 'requires framework-chain-composer');
211
+ assert.ok(/problem-type-router/.test(src), 'requires problem-type-router');
212
+ });
213
+
214
+ // ---------------------------------------------------------------------------
215
+ // Bonus: composeWorkflow(recommendFrameworkChain(...)) -- the resolver attaches commands
216
+ // ---------------------------------------------------------------------------
217
+ test('composeWorkflow(recommendFrameworkChain(...)) attaches commands (the resolver does it, not the recommender)', function () {
218
+ const resolver = require('../workflow/command-resolver.cjs');
219
+ const edges = [
220
+ { from: 'Beautiful Question Framework', to: 'Domain Selection', confidence: 0.9, phase_indicator: null },
221
+ { from: 'Domain Selection', to: 'Jobs to Be Done (JTBD)', confidence: 0.85, phase_indicator: null },
222
+ ];
223
+ const chain = recommender.recommendFrameworkChain({ currentFramework: 'Beautiful Question Framework', roomState: { feedsIntoEdges: edges } });
224
+ const wf = resolver.composeWorkflow(chain);
225
+ assert.strictEqual(wf.length, 3);
226
+ assert.strictEqual(wf[0].step, 1);
227
+ wf.forEach(function (s, i) { assert.strictEqual(s.step, i + 1); assert.ok('command' in s); assert.ok('optional' in s); });
228
+ // All three of these frameworks ARE mapped post-retrofit -> commands filled.
229
+ assert.ok(wf.every(function (s) { return s.command !== null; }));
230
+ });
231
+
232
+ process.stdout.write('\nchain-recommender.test.cjs: ' + passed + ' assertion groups PASSED\n');
233
+ process.exit(0);
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * chat-context.test.cjs -- Phase 87-09 context-injection fence.
6
+ *
7
+ * BSL 1.1. Copyright (c) Mindrian 2026.
8
+ * (Business Source License 1.1; SPDX BUSL-1.1; see LICENSE.)
9
+ *
10
+ * Proves:
11
+ * - classifyIntent covers all 5 buckets: contradicts, converges,
12
+ * stakeholders, gaps, briefing, plus the default-fallback path
13
+ * - buildContext returns a numeric tokenEstimate on EVERY code path
14
+ * - Every pattern stays under the 5K token budget on the cascade-e2e
15
+ * seed-room fixture
16
+ * - Pattern 3 (stakeholders) gracefully early-returns when the
17
+ * stakeholders table is empty -- it does NOT fabricate attribution
18
+ * (R6 / Phase 84-05 Pattern 3 contract)
19
+ *
20
+ * Exit codes:
21
+ * 0 -> PASS
22
+ * 1 -> FAIL (budget blown, or early-return missing, or class drift)
23
+ */
24
+
25
+ const assert = require('node:assert/strict');
26
+ const fs = require('node:fs');
27
+ const os = require('node:os');
28
+ const path = require('node:path');
29
+ const { buildContext, classifyIntent } = require('../core/chat-context-builder.cjs');
30
+ const { runCascade } = require('../core/intelligence-cascade.cjs');
31
+
32
+ const FIXTURE = path.resolve(__dirname, '../../test/fixtures/cascade-e2e/seed-room');
33
+ const MAX_TOKENS = 5000;
34
+
35
+ async function run() {
36
+ /* Intent classification: all 5 named buckets + default fallback. */
37
+ assert.strictEqual(classifyIntent('what contradicts x?'), 'contradicts');
38
+ assert.strictEqual(classifyIntent('what is converging?'), 'converges');
39
+ assert.strictEqual(classifyIntent('who said what?'), 'stakeholders');
40
+ assert.strictEqual(classifyIntent('what gaps to fill?'), 'gaps');
41
+ assert.strictEqual(classifyIntent('briefing please'), 'briefing');
42
+ assert.strictEqual(classifyIntent('random unrelated text'), 'briefing');
43
+
44
+ /* Seed a hermetic room under /rooms/ so intelligence-cascade's
45
+ * isRoomFile() guard passes, then run the cascade against the three
46
+ * seeded artifacts so room.db + schema exist. */
47
+ const tmpParent = fs.mkdtempSync(path.join(os.tmpdir(), 'chatctx-'));
48
+ const roomsDir = path.join(tmpParent, 'rooms');
49
+ const roomDir = path.join(roomsDir, 'fixture');
50
+ fs.mkdirSync(roomsDir, { recursive: true });
51
+
52
+ try {
53
+ fs.cpSync(FIXTURE, roomDir, { recursive: true });
54
+ await runCascade(roomDir, {
55
+ trigger: 'test',
56
+ filePath: path.join(roomDir, 'problem-definition', 'jtbd-underservice.md'),
57
+ section: 'problem-definition',
58
+ });
59
+
60
+ /* Pattern 1: contradicts */
61
+ const c1 = await buildContext(roomDir, 'what contradicts market-analysis?');
62
+ assert.strictEqual(c1.intent, 'contradicts');
63
+ assert.strictEqual(c1.earlyReturn, false);
64
+ assert.ok(typeof c1.tokenEstimate === 'number', 'tokenEstimate must be a number');
65
+ assert.ok(c1.tokenEstimate < MAX_TOKENS,
66
+ 'contradicts tokenEstimate must be <' + MAX_TOKENS + ' (got ' + c1.tokenEstimate + ')');
67
+
68
+ /* Pattern 2: converges */
69
+ const c2 = await buildContext(roomDir, 'what patterns are converging across sections?');
70
+ assert.strictEqual(c2.intent, 'converges');
71
+ assert.strictEqual(c2.earlyReturn, false);
72
+ assert.ok(typeof c2.tokenEstimate === 'number', 'tokenEstimate must be a number');
73
+ assert.ok(c2.tokenEstimate < MAX_TOKENS,
74
+ 'converges tokenEstimate must be <' + MAX_TOKENS + ' (got ' + c2.tokenEstimate + ')');
75
+
76
+ /* Pattern 3: stakeholders -- empty table, earlyReturn=true must be asserted */
77
+ const c3 = await buildContext(roomDir, 'who said what?');
78
+ assert.strictEqual(c3.intent, 'stakeholders');
79
+ assert.strictEqual(c3.earlyReturn, true,
80
+ 'empty stakeholders table must trigger earlyReturn=true (no fabrication)');
81
+ assert.ok(/stakeholder/i.test(c3.userPrompt),
82
+ 'early-return message must mention stakeholders');
83
+ assert.ok(typeof c3.tokenEstimate === 'number',
84
+ 'tokenEstimate must be present even on earlyReturn');
85
+ assert.ok(c3.tokenEstimate < MAX_TOKENS,
86
+ 'stakeholders tokenEstimate must be <' + MAX_TOKENS + ' (got ' + c3.tokenEstimate + ')');
87
+
88
+ /* Pattern 4: gaps */
89
+ const c4 = await buildContext(roomDir, 'what gaps should I fill?');
90
+ assert.strictEqual(c4.intent, 'gaps');
91
+ assert.strictEqual(c4.earlyReturn, false);
92
+ assert.ok(typeof c4.tokenEstimate === 'number', 'tokenEstimate must be a number');
93
+ assert.ok(c4.tokenEstimate < MAX_TOKENS,
94
+ 'gaps tokenEstimate must be <' + MAX_TOKENS + ' (got ' + c4.tokenEstimate + ')');
95
+
96
+ /* Pattern 5: briefing */
97
+ const c5 = await buildContext(roomDir, 'give me an intelligence briefing');
98
+ assert.strictEqual(c5.intent, 'briefing');
99
+ assert.strictEqual(c5.earlyReturn, false);
100
+ assert.ok(typeof c5.tokenEstimate === 'number', 'tokenEstimate must be a number');
101
+ assert.ok(c5.tokenEstimate < MAX_TOKENS,
102
+ 'briefing tokenEstimate must be <' + MAX_TOKENS + ' (got ' + c5.tokenEstimate + ')');
103
+
104
+ /* Default-fallback path: unclassified input routes to briefing */
105
+ const c6 = await buildContext(roomDir, 'random unrelated question');
106
+ assert.strictEqual(c6.intent, 'briefing', 'default fallback must be briefing');
107
+ assert.ok(c6.tokenEstimate < MAX_TOKENS,
108
+ 'fallback tokenEstimate must be <' + MAX_TOKENS + ' (got ' + c6.tokenEstimate + ')');
109
+
110
+ process.stdout.write(
111
+ 'chat-context: all 5 intent patterns tested, all token budgets <' + MAX_TOKENS +
112
+ ' (contradicts=' + c1.tokenEstimate +
113
+ ' converges=' + c2.tokenEstimate +
114
+ ' stakeholders=' + c3.tokenEstimate + '[earlyReturn]' +
115
+ ' gaps=' + c4.tokenEstimate +
116
+ ' briefing=' + c5.tokenEstimate +
117
+ ' fallback=' + c6.tokenEstimate + ')\n'
118
+ );
119
+ process.exit(0);
120
+ } catch (e) {
121
+ process.stderr.write((e && e.stack) ? e.stack + '\n' : String(e) + '\n');
122
+ process.exit(1);
123
+ } finally {
124
+ try { fs.rmSync(tmpParent, { recursive: true, force: true }); } catch (_) { /* ignore */ }
125
+ }
126
+ }
127
+
128
+ run();