@mindrian_os/cli 2.0.0-beta.3 → 2.0.0-beta.33

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 (1829) hide show
  1. package/.claude-plugin/plugin.json +21 -0
  2. package/.mcp.json +14 -0
  3. package/CHANGELOG.md +497 -0
  4. package/agents/analogy-query-fetcher.md +119 -0
  5. package/agents/brain-query.md +91 -0
  6. package/agents/competitor-watch-fetcher.md +94 -0
  7. package/agents/framework-runner.md +258 -0
  8. package/agents/grading.md +216 -0
  9. package/agents/grant-reviewer.md +191 -0
  10. package/agents/investor.md +141 -0
  11. package/agents/larry-extended.md +215 -0
  12. package/agents/meeting-perspective-extractor.md +94 -0
  13. package/agents/opportunity-scanner.md +105 -0
  14. package/agents/persona-analyst.md +201 -0
  15. package/agents/research.md +103 -0
  16. package/agents/reverse-salient-agent.md +42 -0
  17. package/agents/vault-section-reviewer.md +107 -0
  18. package/assets/banner-showcase.html +537 -0
  19. package/assets/fonts/BebasNeue-Regular.ttf +0 -0
  20. package/assets/fonts/Inter-Bold.ttf +0 -0
  21. package/assets/fonts/Inter-Regular.ttf +0 -0
  22. package/assets/logo.svg +10 -0
  23. package/bin/local-chain-recommender.cjs +256 -0
  24. package/bin/mindrian-brain-mcp-client.cjs +319 -0
  25. package/bin/mindrian-mcp-server.cjs +445 -0
  26. package/bin/mindrian-mcp-shim.cjs +100 -0
  27. package/bin/mindrian-tools.cjs +784 -0
  28. package/commands/act.md +529 -0
  29. package/commands/admin.md +439 -0
  30. package/commands/agentshield.md +49 -0
  31. package/commands/analyze-needs.md +78 -0
  32. package/commands/analyze-systems.md +73 -0
  33. package/commands/analyze-timing.md +98 -0
  34. package/commands/auto-explore.md +85 -0
  35. package/commands/beautiful-question.md +76 -0
  36. package/commands/bono.md +152 -0
  37. package/commands/brain-derive.md +137 -0
  38. package/commands/build-knowledge.md +121 -0
  39. package/commands/build-thesis.md +94 -0
  40. package/commands/causal.md +269 -0
  41. package/commands/challenge-assumptions.md +78 -0
  42. package/commands/compare-ventures.md +117 -0
  43. package/commands/correct-reference-now.md +88 -0
  44. package/commands/dashboard.md +144 -0
  45. package/commands/deck.md +117 -0
  46. package/commands/deep-grade.md +214 -0
  47. package/commands/diagnose.md +131 -0
  48. package/commands/diagnostics.md +195 -0
  49. package/commands/dial-memory-refresh.md +113 -0
  50. package/commands/diffusion.md +129 -0
  51. package/commands/discover.md +221 -0
  52. package/commands/doctor.md +271 -0
  53. package/commands/dogfood-flush.md +130 -0
  54. package/commands/dominant-designs.md +76 -0
  55. package/commands/eureka.md +315 -0
  56. package/commands/explain-decision.md +135 -0
  57. package/commands/explore-domains.md +78 -0
  58. package/commands/explore-futures.md +67 -0
  59. package/commands/explore-opportunity.md +172 -0
  60. package/commands/explore-trends.md +78 -0
  61. package/commands/export.md +128 -0
  62. package/commands/feynman-timeline-refresh.md +102 -0
  63. package/commands/file-meeting.md +1195 -0
  64. package/commands/find-analogies.md +369 -0
  65. package/commands/find-bottlenecks.md +108 -0
  66. package/commands/find-connections.md +111 -0
  67. package/commands/funding.md +114 -0
  68. package/commands/futures.md +128 -0
  69. package/commands/grade-grant.md +297 -0
  70. package/commands/grade.md +257 -0
  71. package/commands/graph.md +218 -0
  72. package/commands/hat-briefing.md +163 -0
  73. package/commands/heal.md +72 -0
  74. package/commands/help.md +176 -0
  75. package/commands/hmi-status.md +75 -0
  76. package/commands/ignite.md +229 -0
  77. package/commands/ingest-methodology.md +128 -0
  78. package/commands/intel-pipeline.md +133 -0
  79. package/commands/jtbd.md +262 -0
  80. package/commands/leadership.md +113 -0
  81. package/commands/lean-canvas.md +74 -0
  82. package/commands/macro-trends.md +88 -0
  83. package/commands/map-unknowns.md +76 -0
  84. package/commands/memory-cortex-reach.md +79 -0
  85. package/commands/memory.md +213 -0
  86. package/commands/models.md +200 -0
  87. package/commands/mos-reason.md +489 -0
  88. package/commands/mos.md +162 -0
  89. package/commands/mullins.md +155 -0
  90. package/commands/mva-brief.md +86 -0
  91. package/commands/mva-option.md +119 -0
  92. package/commands/mva-report.md +76 -0
  93. package/commands/new-project.md +633 -0
  94. package/commands/new-surface.md +116 -0
  95. package/commands/onboard.md +489 -0
  96. package/commands/operator.md +168 -0
  97. package/commands/opportunities.md +177 -0
  98. package/commands/organize.md +74 -0
  99. package/commands/persona.md +276 -0
  100. package/commands/pipeline.md +167 -0
  101. package/commands/present.md +125 -0
  102. package/commands/publish.md +227 -0
  103. package/commands/pws-brain.md +154 -0
  104. package/commands/qualify-opportunity.md +113 -0
  105. package/commands/query.md +70 -0
  106. package/commands/radar.md +110 -0
  107. package/commands/reanalyze.md +124 -0
  108. package/commands/research.md +467 -0
  109. package/commands/room.md +385 -0
  110. package/commands/rooms.md +682 -0
  111. package/commands/root-cause.md +77 -0
  112. package/commands/rs-experts.md +153 -0
  113. package/commands/rs-explain.md +134 -0
  114. package/commands/rs-fetch.md +119 -0
  115. package/commands/rs-thesis.md +131 -0
  116. package/commands/scenario-plan.md +80 -0
  117. package/commands/scheduled-tasks.md +308 -0
  118. package/commands/score-innovation.md +78 -0
  119. package/commands/scout.md +379 -0
  120. package/commands/setup.md +658 -0
  121. package/commands/show.md +71 -0
  122. package/commands/skill.md +108 -0
  123. package/commands/snapshot.md +179 -0
  124. package/commands/speakers.md +118 -0
  125. package/commands/splash.md +55 -0
  126. package/commands/stance.md +91 -0
  127. package/commands/status.md +109 -0
  128. package/commands/structure-argument.md +82 -0
  129. package/commands/suggest-next.md +141 -0
  130. package/commands/systems-thinking.md +224 -0
  131. package/commands/think-hats.md +86 -0
  132. package/commands/trending-to-absurd.md +151 -0
  133. package/commands/update.md +256 -0
  134. package/commands/user-needs.md +77 -0
  135. package/commands/validate.md +77 -0
  136. package/commands/value-proposition.md +116 -0
  137. package/commands/vault.md +330 -0
  138. package/commands/visualize.md +71 -0
  139. package/commands/whitespace.md +541 -0
  140. package/commands/wiki.md +96 -0
  141. package/data/ROOM.md +47 -0
  142. package/data/brain-census.generated.json +2617 -0
  143. package/data/brain-orchestration-projection.json +4973 -0
  144. package/data/brain-packet-schema.json +545 -0
  145. package/data/brain-surface-contract.json +32 -0
  146. package/data/capability-ledger.json +391 -0
  147. package/data/capability-map-registry.json +23 -0
  148. package/data/command-registry.json +2433 -0
  149. package/data/connector-coverage-ledger.json +1618 -0
  150. package/data/connector-out-of-spine-allowlist.json +71 -0
  151. package/data/connector-registry.json +3637 -0
  152. package/data/corpus-stats-source.json +11 -0
  153. package/data/corpus-stats.generated.json +10 -0
  154. package/data/cross-domain-analogues.json +15 -0
  155. package/data/deck-aliases.json +6 -0
  156. package/data/deck-styles.json +85 -0
  157. package/data/deployment-surfaces.json +69 -0
  158. package/data/dispatch-framework-map.json +18 -0
  159. package/data/doctor-modules.json +206 -0
  160. package/data/dual-graph-health-baseline.json +12 -0
  161. package/data/eureka-critic-tags.json +32 -0
  162. package/data/first-reward-surfaces.json +66 -0
  163. package/data/first-touch-surfaces.json +60 -0
  164. package/data/flagship-floor-set.json +37 -0
  165. package/data/framework-names.json +121 -0
  166. package/data/grant-rubric-fixtures/early-stage-pre-seed.json +18 -0
  167. package/data/grant-rubric-fixtures/early-stage-round-a.json +18 -0
  168. package/data/grant-rubric-fixtures/early-stage-seed.json +18 -0
  169. package/data/grant-rubric-fixtures/hybrid-seed-incentive.json +18 -0
  170. package/data/grant-rubric-fixtures/rd-fund.json +18 -0
  171. package/data/grant-rubric-fixtures/regional-innovation-incubators.json +18 -0
  172. package/data/grant-rubric-fixtures/technology-incubators.json +18 -0
  173. package/data/grant-rubric-fixtures/tnufa.json +167 -0
  174. package/data/grant-rubric-schema.json +33 -0
  175. package/data/harness-fixtures/converged-room/.context/ROOM.md +13 -0
  176. package/data/harness-fixtures/converged-room/.intelligence/ROOM.md +13 -0
  177. package/data/harness-fixtures/converged-room/.snapshots/ROOM.md +13 -0
  178. package/data/harness-fixtures/converged-room/MINTO.md +19 -0
  179. package/data/harness-fixtures/converged-room/STATE.md +73 -0
  180. package/data/harness-fixtures/converged-room/USER.md +23 -0
  181. package/data/harness-fixtures/converged-room/assets/ROOM.md +13 -0
  182. package/data/harness-fixtures/converged-room/business-model/CONTEXT.md +32 -0
  183. package/data/harness-fixtures/converged-room/business-model/ROOM.md +36 -0
  184. package/data/harness-fixtures/converged-room/competitive-analysis/CONTEXT.md +33 -0
  185. package/data/harness-fixtures/converged-room/competitive-analysis/ROOM.md +35 -0
  186. package/data/harness-fixtures/converged-room/financial-model/CONTEXT.md +30 -0
  187. package/data/harness-fixtures/converged-room/financial-model/ROOM.md +35 -0
  188. package/data/harness-fixtures/converged-room/funding/CONTEXT.md +41 -0
  189. package/data/harness-fixtures/converged-room/funding/ROOM.md +35 -0
  190. package/data/harness-fixtures/converged-room/legal-ip/CONTEXT.md +30 -0
  191. package/data/harness-fixtures/converged-room/legal-ip/ROOM.md +35 -0
  192. package/data/harness-fixtures/converged-room/market-analysis/CONTEXT.md +32 -0
  193. package/data/harness-fixtures/converged-room/market-analysis/ROOM.md +35 -0
  194. package/data/harness-fixtures/converged-room/opportunity-bank/CONTEXT.md +39 -0
  195. package/data/harness-fixtures/converged-room/opportunity-bank/ROOM.md +36 -0
  196. package/data/harness-fixtures/converged-room/problem-definition/CONTEXT.md +32 -0
  197. package/data/harness-fixtures/converged-room/problem-definition/ROOM.md +36 -0
  198. package/data/harness-fixtures/converged-room/references/ROOM.md +13 -0
  199. package/data/harness-fixtures/converged-room/references/SECTION-SCHEMA.md +213 -0
  200. package/data/harness-fixtures/converged-room/references/SUB-SCHEMAS.md +91 -0
  201. package/data/harness-fixtures/converged-room/solution-design/CONTEXT.md +36 -0
  202. package/data/harness-fixtures/converged-room/solution-design/ROOM.md +36 -0
  203. package/data/harness-fixtures/converged-room/strategy/CONTEXT.md +35 -0
  204. package/data/harness-fixtures/converged-room/strategy/ROOM.md +35 -0
  205. package/data/harness-fixtures/converged-room/team/ROOM.md +13 -0
  206. package/data/harness-fixtures/converged-room/team-execution/CONTEXT.md +36 -0
  207. package/data/harness-fixtures/converged-room/team-execution/ROOM.md +35 -0
  208. package/data/harness-manifest.json +74 -0
  209. package/data/harness-policies/CONTEXT.md +55 -0
  210. package/data/harness-policies/_schema.json +42 -0
  211. package/data/harness-policies/contract-parity-larry.json +63 -0
  212. package/data/harness-policies/gate-card-fire.json +13 -0
  213. package/data/harness-policies/gate-graph-derive-health.json +13 -0
  214. package/data/harness-policies/gate-shape-declaration.json +13 -0
  215. package/data/harness-policies/gate-tool-honesty.json +13 -0
  216. package/data/harness-policies/gate-worktree-hygiene.json +13 -0
  217. package/data/harness-policies/memory-write-policy.json +28 -0
  218. package/data/harness-policies/registry-drift.json +13 -0
  219. package/data/harness-policies/release-payload-ceiling.json +13 -0
  220. package/data/harness-policies/voice-backend-noun-free.json +13 -0
  221. package/data/harness-policies/voice-fork-as-card.json +13 -0
  222. package/data/harness-policies/voice-glyph-present.json +13 -0
  223. package/data/harness-policies/voice-hyphens-only.json +13 -0
  224. package/data/help-groups.json +236 -0
  225. package/data/hitl-shape-backfill.json +777 -0
  226. package/data/hitl-shape-declaration-fixtures/invalid-bad-shape.json +5 -0
  227. package/data/hitl-shape-declaration-fixtures/invalid-missing-why.json +4 -0
  228. package/data/hitl-shape-declaration-fixtures/invalid-ranker-contradiction.json +6 -0
  229. package/data/hitl-shape-declaration-fixtures/invalid-skill-gap.json +3 -0
  230. package/data/hitl-shape-declaration-fixtures/valid-multi-stage.json +7 -0
  231. package/data/hitl-shape-declaration-fixtures/valid-single-shape.json +5 -0
  232. package/data/hitl-shape-declaration-fixtures/valid-skill-exempt.json +5 -0
  233. package/data/hitl-shape-declaration-schema.json +34 -0
  234. package/data/hitl-stages-fixtures/bono.json +10 -0
  235. package/data/hitl-stages-fixtures/pipeline-1.json +9 -0
  236. package/data/hitl-stages-fixtures/pipeline-2.json +9 -0
  237. package/data/hitl-stages-fixtures/pipeline-3.json +10 -0
  238. package/data/hitl-stages-fixtures/research.json +10 -0
  239. package/data/hitl-stages-fixtures/rs-convergence.json +10 -0
  240. package/data/hitl-stages-fixtures/rs-family.json +11 -0
  241. package/data/hitl-stages-fixtures/rs-reverse-salient.json +10 -0
  242. package/data/hitl-stages-fixtures/trending-to-absurd.json +11 -0
  243. package/data/hitl-stages-schema.json +26 -0
  244. package/data/mcp-tool-connectors.json +464 -0
  245. package/data/mva-agent-prompts.json +31 -0
  246. package/data/mva-deck-template.html +15 -0
  247. package/data/mva-heuristic-keywords.json +35 -0
  248. package/data/orchestration-command-ledger.json +576 -0
  249. package/data/orchestration-unwired-allowlist.json +6 -0
  250. package/data/portfolio-ahp-matrix.json +16 -0
  251. package/data/publish-needs.json +76 -0
  252. package/data/render-coverage-registry.json +1152 -0
  253. package/data/research-sources.json +58 -0
  254. package/data/roadmap-type-chains.json +28 -0
  255. package/data/room-blueprints.json +156 -0
  256. package/data/subagent-dispatch-grants.json +150 -0
  257. package/hooks/hooks.json +493 -0
  258. package/hooks/run-hook.cmd +64 -0
  259. package/lib/__init__.py +0 -0
  260. package/lib/__pycache__/__init__.cpython-312.pyc +0 -0
  261. package/lib/agents/auto-explore-agent.cjs +1163 -0
  262. package/lib/agents/mva/brain-classic-traps.cjs +55 -0
  263. package/lib/agents/mva/brain-cross-domain.cjs +79 -0
  264. package/lib/agents/mva/brain-similar-ventures.cjs +93 -0
  265. package/lib/agents/mva/dashboard-graph-neighborhood.cjs +72 -0
  266. package/lib/agents/mva/index.cjs +42 -0
  267. package/lib/agents/mva/six-hats-red-black.cjs +137 -0
  268. package/lib/agents/mva/tavily-funding-scan.cjs +147 -0
  269. package/lib/agents/mva/test-all-six-agents.cjs +467 -0
  270. package/lib/agents/reverse-salient-agent.cjs +757 -0
  271. package/lib/agents/tension-hook-agent.cjs +586 -0
  272. package/lib/brain/ROOM.md +44 -0
  273. package/lib/brain/chain-recommender.cjs +784 -0
  274. package/lib/brain/curation-batch.cjs +519 -0
  275. package/lib/brain/framework-chain-slice.cjs +252 -0
  276. package/lib/brain/hypergraph-event-schema.cjs +184 -0
  277. package/lib/chat/chat-context.js +185 -0
  278. package/lib/chat/chat-panel.js +721 -0
  279. package/lib/chat/fabric-chat.cjs +288 -0
  280. package/lib/chat/generative-tools.js +219 -0
  281. package/lib/conversation/ROOM.md +39 -0
  282. package/lib/conversation/classifier-rules.json +38 -0
  283. package/lib/conversation/classifier.cjs +264 -0
  284. package/lib/conversation/operator-epistemic-cap.test.cjs +156 -0
  285. package/lib/conversation/operator.cjs +492 -0
  286. package/lib/conversation/operator.test.cjs +160 -0
  287. package/lib/copy/115-spec-strings.cjs +55 -0
  288. package/lib/core/__init__.py +0 -0
  289. package/lib/core/__nav-stub.cjs +14 -0
  290. package/lib/core/__pycache__/__init__.cpython-312.pyc +0 -0
  291. package/lib/core/__pycache__/rs-math.cpython-312.pyc +0 -0
  292. package/lib/core/__pycache__/rs_cache.cpython-312.pyc +0 -0
  293. package/lib/core/__pycache__/rs_corpus.cpython-312.pyc +0 -0
  294. package/lib/core/__pycache__/rs_corpus_exclude.cpython-312.pyc +0 -0
  295. package/lib/core/__pycache__/rs_hybrid.cpython-312.pyc +0 -0
  296. package/lib/core/__pycache__/rs_math.cpython-312.pyc +0 -0
  297. package/lib/core/__pycache__/rs_rooms.cpython-312.pyc +0 -0
  298. package/lib/core/abstraction-gate.cjs +133 -0
  299. package/lib/core/act-jtbd-blurb.cjs +231 -0
  300. package/lib/core/artifact-id.cjs +148 -0
  301. package/lib/core/asset-ops.cjs +151 -0
  302. package/lib/core/auto-commit-throttle.cjs +129 -0
  303. package/lib/core/bearer-token.cjs +199 -0
  304. package/lib/core/behavioral/observation-schema.cjs +86 -0
  305. package/lib/core/bono/cell-fanout.cjs +305 -0
  306. package/lib/core/bono/debate-composition.cjs +495 -0
  307. package/lib/core/bono/hat-governance.cjs +367 -0
  308. package/lib/core/bono/persona-research.cjs +288 -0
  309. package/lib/core/bono/reviewer-governance.cjs +331 -0
  310. package/lib/core/brain-client.cjs +2621 -0
  311. package/lib/core/brain-derivation-prompts.cjs +331 -0
  312. package/lib/core/brain-derivation-queue.cjs +535 -0
  313. package/lib/core/brain-derivation.cjs +740 -0
  314. package/lib/core/brain-md-schema.cjs +528 -0
  315. package/lib/core/brain-md-staleness.cjs +357 -0
  316. package/lib/core/brain-response-sanitize.cjs +396 -0
  317. package/lib/core/breakthrough/canary.cjs +134 -0
  318. package/lib/core/breakthrough/canary.test.cjs +136 -0
  319. package/lib/core/breakthrough/detectors.cjs +359 -0
  320. package/lib/core/breakthrough/detectors.test.cjs +333 -0
  321. package/lib/core/breakthrough/ethics-fence.cjs +129 -0
  322. package/lib/core/breakthrough/ethics-fence.test.cjs +178 -0
  323. package/lib/core/breakthrough/resurfacing.cjs +150 -0
  324. package/lib/core/breakthrough/resurfacing.test.cjs +233 -0
  325. package/lib/core/breakthrough/review-queue.cjs +168 -0
  326. package/lib/core/breakthrough/review-queue.test.cjs +160 -0
  327. package/lib/core/breakthrough/scanner-d17-d18.test.cjs +229 -0
  328. package/lib/core/breakthrough/scanner.cjs +435 -0
  329. package/lib/core/breakthrough/scanner.test.cjs +267 -0
  330. package/lib/core/breakthrough/schema.cjs +180 -0
  331. package/lib/core/breakthrough/schema.test.cjs +256 -0
  332. package/lib/core/breakthrough/scoring.cjs +293 -0
  333. package/lib/core/breakthrough/scoring.test.cjs +423 -0
  334. package/lib/core/breakthrough/verb-dispatch.cjs +230 -0
  335. package/lib/core/breakthrough/verb-dispatch.test.cjs +185 -0
  336. package/lib/core/breakthrough/voice-scaffold.cjs +249 -0
  337. package/lib/core/breakthrough/voice-scaffold.test.cjs +251 -0
  338. package/lib/core/bridge-writer.cjs +477 -0
  339. package/lib/core/cache-prune.cjs +314 -0
  340. package/lib/core/card-fire-gate.cjs +107 -0
  341. package/lib/core/card-fire-sidechannel.cjs +518 -0
  342. package/lib/core/chain-executor.cjs +939 -0
  343. package/lib/core/chain-retry.cjs +214 -0
  344. package/lib/core/chain-step-dispatcher.cjs +397 -0
  345. package/lib/core/chat-context-builder.cjs +253 -0
  346. package/lib/core/check-plugin-enabled.cjs +145 -0
  347. package/lib/core/close-loop-writer.cjs +492 -0
  348. package/lib/core/command-registration-check.cjs +246 -0
  349. package/lib/core/correlation-label-index.cjs +230 -0
  350. package/lib/core/correlation.cjs +112 -0
  351. package/lib/core/coverage-monitor.cjs +183 -0
  352. package/lib/core/coverage-rollup.cjs +232 -0
  353. package/lib/core/cross-room-aggregator.cjs +1127 -0
  354. package/lib/core/cross-room-store.cjs +292 -0
  355. package/lib/core/cross-room-triggers.cjs +222 -0
  356. package/lib/core/daily-briefing.cjs +438 -0
  357. package/lib/core/decision-axes.cjs +178 -0
  358. package/lib/core/decision-capture.cjs +618 -0
  359. package/lib/core/deck-design-rules.cjs +211 -0
  360. package/lib/core/deep-links.cjs +82 -0
  361. package/lib/core/directive-envelope.cjs +230 -0
  362. package/lib/core/directive-envelope.test.cjs +225 -0
  363. package/lib/core/dispatch-optimizer.cjs +354 -0
  364. package/lib/core/doc-text-extractor.cjs +133 -0
  365. package/lib/core/doctor/capability-ledger-module.cjs +170 -0
  366. package/lib/core/doctor/card-fire-health-module.cjs +159 -0
  367. package/lib/core/doctor/cascade-rooms-active-module.cjs +127 -0
  368. package/lib/core/doctor/cascade-rooms-module.cjs +184 -0
  369. package/lib/core/doctor/class-m-brain-smoke.cjs +479 -0
  370. package/lib/core/doctor/class-m-brain-smoke.test.cjs +448 -0
  371. package/lib/core/doctor/class-s-eureka-smoke.cjs +380 -0
  372. package/lib/core/doctor/deployment-surfaces-module.cjs +408 -0
  373. package/lib/core/doctor/deprecated-usage-module.cjs +155 -0
  374. package/lib/core/doctor/eureka-fts-health-module.cjs +236 -0
  375. package/lib/core/doctor/graph-derive-heal-retrofit-module.cjs +148 -0
  376. package/lib/core/doctor/graph-derive-health-module.cjs +486 -0
  377. package/lib/core/doctor/install-incomplete-module.cjs +320 -0
  378. package/lib/core/doctor/install-state-module.cjs +503 -0
  379. package/lib/core/doctor/mcp-surface-module.cjs +369 -0
  380. package/lib/core/doctor/mode-select-checkpoint-module.cjs +113 -0
  381. package/lib/core/doctor/plugin-enabled-state-module.cjs +86 -0
  382. package/lib/core/doctor/room-graph-density-module.cjs +181 -0
  383. package/lib/core/doctor/room-md-module.cjs +198 -0
  384. package/lib/core/doctor/shared.cjs +555 -0
  385. package/lib/core/doctor/stale-first-touch-copy-module.cjs +83 -0
  386. package/lib/core/doctor/statusline-visibility-module.cjs +295 -0
  387. package/lib/core/doctor/ui-compliance-module.cjs +223 -0
  388. package/lib/core/doctor/umbilical-module.cjs +486 -0
  389. package/lib/core/doctor/verify-surface-module.cjs +82 -0
  390. package/lib/core/doctor/voice-style-log-module.cjs +146 -0
  391. package/lib/core/domain-insight-sweep.cjs +224 -0
  392. package/lib/core/drift-baseline.cjs +362 -0
  393. package/lib/core/drift-runtime-reachability.cjs +283 -0
  394. package/lib/core/dual-path-detector.cjs +84 -0
  395. package/lib/core/dual-path-detector.test.cjs +334 -0
  396. package/lib/core/enrichment-queue.cjs +636 -0
  397. package/lib/core/eureka/ahp-weights.cjs +232 -0
  398. package/lib/core/eureka/analogy-fitness.cjs +326 -0
  399. package/lib/core/eureka/compression-meter.cjs +165 -0
  400. package/lib/core/eureka/embedding-classifier.cjs +226 -0
  401. package/lib/core/eureka/embedding-spine.cjs +563 -0
  402. package/lib/core/eureka/entity-classifier.cjs +232 -0
  403. package/lib/core/eureka/entity-extractor.cjs +356 -0
  404. package/lib/core/eureka/eureka-enable.cjs +228 -0
  405. package/lib/core/eureka/eureka-offer.cjs +225 -0
  406. package/lib/core/eureka/eureka-reach-runner.cjs +430 -0
  407. package/lib/core/eureka/explore-chain.cjs +834 -0
  408. package/lib/core/eureka/explored-artifact.cjs +456 -0
  409. package/lib/core/eureka/fts-index-lifecycle.cjs +265 -0
  410. package/lib/core/eureka/grade-grant-examine.cjs +544 -0
  411. package/lib/core/eureka/grade-grant.cjs +596 -0
  412. package/lib/core/eureka/hybrid-retrieve.cjs +256 -0
  413. package/lib/core/eureka/lateral-engine-adapter.cjs +118 -0
  414. package/lib/core/eureka/lexical-overlap.cjs +98 -0
  415. package/lib/core/eureka/online-pattern-query.cjs +142 -0
  416. package/lib/core/eureka/opportunity-harvest.cjs +752 -0
  417. package/lib/core/eureka/opportunity-statement.cjs +519 -0
  418. package/lib/core/eureka/portfolio-dimensions.cjs +255 -0
  419. package/lib/core/eureka/qualify-opportunity.cjs +607 -0
  420. package/lib/core/eureka/reasoning-mode.cjs +481 -0
  421. package/lib/core/eureka/report-html.cjs +368 -0
  422. package/lib/core/eureka/research-filing.cjs +547 -0
  423. package/lib/core/eureka/room-native-substrate.cjs +343 -0
  424. package/lib/core/eureka/tail-quadrant.cjs +177 -0
  425. package/lib/core/eureka/tri-modal-index.cjs +564 -0
  426. package/lib/core/eureka/vector-store.cjs +414 -0
  427. package/lib/core/eureka-critic.cjs +733 -0
  428. package/lib/core/eureka-deps-resolver.cjs +179 -0
  429. package/lib/core/expert-genesis.cjs +437 -0
  430. package/lib/core/expert-library.cjs +303 -0
  431. package/lib/core/exports-log.cjs +79 -0
  432. package/lib/core/feynman/ROOM.md +25 -0
  433. package/lib/core/feynman/dial-memory-renderer.cjs +279 -0
  434. package/lib/core/feynman/dial-memory-runner.cjs +173 -0
  435. package/lib/core/feynman/feynman-seed-writer.cjs +187 -0
  436. package/lib/core/feynman/timeline-renderer.cjs +197 -0
  437. package/lib/core/feynman/timeline-runner.cjs +281 -0
  438. package/lib/core/feynman-minto-invariants.cjs +609 -0
  439. package/lib/core/findings-wirer.cjs +360 -0
  440. package/lib/core/first-touch-version-stamper.cjs +113 -0
  441. package/lib/core/folder-memory-async.cjs +424 -0
  442. package/lib/core/folder-memory-shared.cjs +890 -0
  443. package/lib/core/folder-memory.cjs +546 -0
  444. package/lib/core/framework-chain-composer.cjs +583 -0
  445. package/lib/core/frontmatter-schemas.cjs +468 -0
  446. package/lib/core/fusion-router.cjs +540 -0
  447. package/lib/core/futures/causal-cue.cjs +91 -0
  448. package/lib/core/futures/orchestrator.cjs +1391 -0
  449. package/lib/core/futures/subsystem-render.cjs +164 -0
  450. package/lib/core/gate-relevance.cjs +278 -0
  451. package/lib/core/git-ops.cjs +141 -0
  452. package/lib/core/graph-backfill.cjs +562 -0
  453. package/lib/core/graph-candidate-producer.cjs +235 -0
  454. package/lib/core/graph-derivation.cjs +546 -0
  455. package/lib/core/graph-derive-classifier.cjs +352 -0
  456. package/lib/core/graph-ops.cjs +274 -0
  457. package/lib/core/graph-refine-loop.cjs +194 -0
  458. package/lib/core/graph-self-heal.cjs +289 -0
  459. package/lib/core/greeting-intent-detector.cjs +201 -0
  460. package/lib/core/grill-engine.cjs +459 -0
  461. package/lib/core/hat-persistence.cjs +274 -0
  462. package/lib/core/hsi-engine.cjs +439 -0
  463. package/lib/core/hsi-lsa.cjs +246 -0
  464. package/lib/core/hsi-spectral.cjs +554 -0
  465. package/lib/core/hsi-to-graph.test.cjs +168 -0
  466. package/lib/core/icm-forest.cjs +703 -0
  467. package/lib/core/ignite-branch-gate.cjs +66 -0
  468. package/lib/core/index.cjs +60 -0
  469. package/lib/core/insight-sensors.cjs +1000 -0
  470. package/lib/core/install-state.cjs +242 -0
  471. package/lib/core/integration-registry.cjs +232 -0
  472. package/lib/core/intel-pipeline.cjs +566 -0
  473. package/lib/core/intelligence-cascade.cjs +852 -0
  474. package/lib/core/issue-tree.cjs +269 -0
  475. package/lib/core/larry-thinness-acknowledgment.cjs +64 -0
  476. package/lib/core/larry-thinness-acknowledgment.test.cjs +97 -0
  477. package/lib/core/lazygraph-ops.cjs +1453 -0
  478. package/lib/core/lens-engine.cjs +434 -0
  479. package/lib/core/leverage-scan.cjs +218 -0
  480. package/lib/core/llm-name-suggester.cjs +194 -0
  481. package/lib/core/llm-name-suggester.test.cjs +132 -0
  482. package/lib/core/lru-cache.cjs +139 -0
  483. package/lib/core/mcp-dep-heal.cjs +475 -0
  484. package/lib/core/mcp-dep-heal.test.cjs +253 -0
  485. package/lib/core/mcp-profiles.cjs +185 -0
  486. package/lib/core/meeting-ops.cjs +54 -0
  487. package/lib/core/memory/governance-candidate-raiser.cjs +205 -0
  488. package/lib/core/memory/governance-candidate.cjs +95 -0
  489. package/lib/core/memory/reconcile-memory-runner.cjs +653 -0
  490. package/lib/core/memory-ops.cjs +641 -0
  491. package/lib/core/meter/gate-density-reader.cjs +112 -0
  492. package/lib/core/meter/transfer-reader.cjs +276 -0
  493. package/lib/core/meter/two-gauge.cjs +245 -0
  494. package/lib/core/methodology-ingest.cjs +346 -0
  495. package/lib/core/metrics/larryreach-loop-health.cjs +142 -0
  496. package/lib/core/migration-snapshot.cjs +287 -0
  497. package/lib/core/migration-snapshot.test.cjs +174 -0
  498. package/lib/core/migrations/ROOM.md +33 -0
  499. package/lib/core/migrations/phase-109-nodes-provenance.cjs +400 -0
  500. package/lib/core/migrations/phase-109-session-focus.cjs +99 -0
  501. package/lib/core/migrations/phase-160-nodes-bitemporal.cjs +168 -0
  502. package/lib/core/migrations/phase-160-nodes-bitemporal.test.cjs +140 -0
  503. package/lib/core/migrations/phase-162-section-nodes.cjs +156 -0
  504. package/lib/core/migrations/phase-222-ranker-weights.cjs +62 -0
  505. package/lib/core/migrations/phase-224-edge-review-status.cjs +87 -0
  506. package/lib/core/mindrian-brain-shim.test.cjs +221 -0
  507. package/lib/core/mode-select-sidechannel.cjs +259 -0
  508. package/lib/core/model-profiles.cjs +246 -0
  509. package/lib/core/mullins-scaffold.cjs +258 -0
  510. package/lib/core/mullins-scaffold.test.cjs +128 -0
  511. package/lib/core/mva-agent-contract.cjs +170 -0
  512. package/lib/core/mva-agent-contract.test.cjs +169 -0
  513. package/lib/core/mva-budget.cjs +75 -0
  514. package/lib/core/mva-budget.test.cjs +68 -0
  515. package/lib/core/mva-classifier.cjs +392 -0
  516. package/lib/core/mva-classifier.test.cjs +248 -0
  517. package/lib/core/mva-deck-builder.cjs +452 -0
  518. package/lib/core/mva-deck-builder.test.cjs +287 -0
  519. package/lib/core/mva-detect.smoke.test.cjs +209 -0
  520. package/lib/core/mva-dispatcher.cjs +110 -0
  521. package/lib/core/mva-dispatcher.test.cjs +216 -0
  522. package/lib/core/mva-option-router.cjs +414 -0
  523. package/lib/core/mva-option-router.test.cjs +483 -0
  524. package/lib/core/mva-orchestrator.cjs +365 -0
  525. package/lib/core/mva-orchestrator.test.cjs +916 -0
  526. package/lib/core/mva-progressive-renderer.cjs +194 -0
  527. package/lib/core/mva-progressive-renderer.test.cjs +157 -0
  528. package/lib/core/mva-rule-linter.cjs +458 -0
  529. package/lib/core/mva-rule-linter.test.cjs +484 -0
  530. package/lib/core/mva-state.cjs +159 -0
  531. package/lib/core/mva-telemetry.cjs +58 -0
  532. package/lib/core/mva-telemetry.test.cjs +196 -0
  533. package/lib/core/mva-vercel-deploy.cjs +168 -0
  534. package/lib/core/mva-vercel-deploy.test.cjs +239 -0
  535. package/lib/core/nav-dial.cjs +440 -0
  536. package/lib/core/navigation/ROOM.md +15 -0
  537. package/lib/core/navigation/abstraction-claim.cjs +185 -0
  538. package/lib/core/navigation/calibration-gate.cjs +284 -0
  539. package/lib/core/navigation/calibration-log.cjs +141 -0
  540. package/lib/core/navigation/confirm-node.cjs +73 -0
  541. package/lib/core/navigation/dashboard-helpers.cjs +145 -0
  542. package/lib/core/navigation/edges.cjs +1150 -0
  543. package/lib/core/navigation/evidence-claim.cjs +186 -0
  544. package/lib/core/navigation/explanation.cjs +48 -0
  545. package/lib/core/navigation/file-evidence-readback.cjs +242 -0
  546. package/lib/core/navigation/focus.cjs +155 -0
  547. package/lib/core/navigation/get-domains-for-trends.cjs +277 -0
  548. package/lib/core/navigation/governance.cjs +101 -0
  549. package/lib/core/navigation/grant-rubric.cjs +218 -0
  550. package/lib/core/navigation/graph-export.cjs +325 -0
  551. package/lib/core/navigation/ingestion.cjs +109 -0
  552. package/lib/core/navigation/insights-stale.test.cjs +127 -0
  553. package/lib/core/navigation/insights.cjs +574 -0
  554. package/lib/core/navigation/lens-nodes.cjs +315 -0
  555. package/lib/core/navigation/memory-artifacts.cjs +437 -0
  556. package/lib/core/navigation/memory-cortex-packet.cjs +274 -0
  557. package/lib/core/navigation/memory-events.cjs +811 -0
  558. package/lib/core/navigation/neighborhood.cjs +79 -0
  559. package/lib/core/navigation/packet.cjs +420 -0
  560. package/lib/core/navigation/planning-artifacts.cjs +219 -0
  561. package/lib/core/navigation/projections.cjs +295 -0
  562. package/lib/core/navigation/ranker-weights.cjs +133 -0
  563. package/lib/core/navigation/reasoning-write.cjs +211 -0
  564. package/lib/core/navigation/reconcile-guard.cjs +98 -0
  565. package/lib/core/navigation/reified-claim.cjs +287 -0
  566. package/lib/core/navigation/research-preflight.cjs +156 -0
  567. package/lib/core/navigation/room-birth.cjs +1213 -0
  568. package/lib/core/navigation/room-context.cjs +380 -0
  569. package/lib/core/navigation/room-home.cjs +142 -0
  570. package/lib/core/navigation/spine-events.cjs +548 -0
  571. package/lib/core/navigation/synthetic-expert.cjs +177 -0
  572. package/lib/core/navigation/transitions.cjs +253 -0
  573. package/lib/core/navigation/typed-claim.cjs +208 -0
  574. package/lib/core/navigation/typed-domain.cjs +249 -0
  575. package/lib/core/navigation/typed-entity.cjs +236 -0
  576. package/lib/core/navigation/typed-frame.cjs +257 -0
  577. package/lib/core/navigation/typed-open-question.cjs +130 -0
  578. package/lib/core/navigation/typed-opportunity.cjs +418 -0
  579. package/lib/core/navigation-engine-offer.cjs +776 -0
  580. package/lib/core/navigation-engine-shared.cjs +256 -0
  581. package/lib/core/navigation-engine.cjs +1696 -0
  582. package/lib/core/navigation.cjs +686 -0
  583. package/lib/core/nl-graph-queries.cjs +164 -0
  584. package/lib/core/node-insert-epistemic.test.cjs +212 -0
  585. package/lib/core/node-insert-overrides.test.cjs +273 -0
  586. package/lib/core/node-insert.cjs +274 -0
  587. package/lib/core/npm-cli-resolve.cjs +151 -0
  588. package/lib/core/npm-cli-resolve.test.cjs +153 -0
  589. package/lib/core/npm-install-lock.cjs +326 -0
  590. package/lib/core/npm-install-lock.test.cjs +379 -0
  591. package/lib/core/numeric/sklearn-stopwords-v1.json +325 -0
  592. package/lib/core/numeric/svd.cjs +377 -0
  593. package/lib/core/numeric/tfidf.cjs +241 -0
  594. package/lib/core/offer-presenter.cjs +406 -0
  595. package/lib/core/opportunity-extractor.cjs +183 -0
  596. package/lib/core/opportunity-ops.cjs +1423 -0
  597. package/lib/core/orchestration-candidate-lift.cjs +284 -0
  598. package/lib/core/part8-egress-guard.cjs +563 -0
  599. package/lib/core/part8-egress-guard.test.cjs +194 -0
  600. package/lib/core/part8-egress-ontology.cjs +174 -0
  601. package/lib/core/part8-egress-ontology.test.cjs +133 -0
  602. package/lib/core/persona-ops.cjs +692 -0
  603. package/lib/core/persona-override.cjs +327 -0
  604. package/lib/core/persona-taxonomy.cjs +328 -0
  605. package/lib/core/pinecone-inference.cjs +176 -0
  606. package/lib/core/pitch-feedback-schemas.cjs +303 -0
  607. package/lib/core/planning/artifact-brain-packet.cjs +212 -0
  608. package/lib/core/planning/reconcile-runner.cjs +347 -0
  609. package/lib/core/platform-gates.cjs +120 -0
  610. package/lib/core/platform.cjs +257 -0
  611. package/lib/core/proactive-intelligence.cjs +528 -0
  612. package/lib/core/problem-type-router.cjs +315 -0
  613. package/lib/core/publish-needs-default-lane.cjs +110 -0
  614. package/lib/core/ralph-loop-gate.cjs +48 -0
  615. package/lib/core/reader/ab-harness.cjs +177 -0
  616. package/lib/core/reader/decide-projection-reader.cjs +358 -0
  617. package/lib/core/reasoning-ops.cjs +639 -0
  618. package/lib/core/recipe-maps.cjs +442 -0
  619. package/lib/core/recovery/case-file.cjs +358 -0
  620. package/lib/core/recovery/controller.cjs +1008 -0
  621. package/lib/core/recovery/dispatcher.cjs +533 -0
  622. package/lib/core/recovery/result-semantics.cjs +488 -0
  623. package/lib/core/recovery/stage-envelope.cjs +338 -0
  624. package/lib/core/refusal-messaging.cjs +569 -0
  625. package/lib/core/refusal-messaging.test.cjs +220 -0
  626. package/lib/core/repo-version.cjs +134 -0
  627. package/lib/core/research-cache.cjs +165 -0
  628. package/lib/core/research-cache.test.cjs +137 -0
  629. package/lib/core/research-context-extractor.cjs +283 -0
  630. package/lib/core/research-corpus.cjs +814 -0
  631. package/lib/core/research-corpus.test.cjs +280 -0
  632. package/lib/core/research-filing-selector.cjs +189 -0
  633. package/lib/core/resolve-active-room.cjs +602 -0
  634. package/lib/core/resolve-brain-key.cjs +273 -0
  635. package/lib/core/resolve-umbilical-target.cjs +288 -0
  636. package/lib/core/resolve-vercel-key.cjs +107 -0
  637. package/lib/core/resolve-vercel-key.test.cjs +137 -0
  638. package/lib/core/reverse-salient-persona-suffix.cjs +115 -0
  639. package/lib/core/room-auto-create.cjs +318 -0
  640. package/lib/core/room-auto-create.test.cjs +198 -0
  641. package/lib/core/room-chooser.cjs +244 -0
  642. package/lib/core/room-classifier-strict-mode.cjs +235 -0
  643. package/lib/core/room-db.cjs +348 -0
  644. package/lib/core/room-discard-cascade.cjs +238 -0
  645. package/lib/core/room-discard-cascade.test.cjs +135 -0
  646. package/lib/core/room-name-validator.cjs +132 -0
  647. package/lib/core/room-name-validator.test.cjs +156 -0
  648. package/lib/core/room-naming-selector.cjs +357 -0
  649. package/lib/core/room-naming-selector.test.cjs +277 -0
  650. package/lib/core/room-open.cjs +259 -0
  651. package/lib/core/room-ops-async.cjs +92 -0
  652. package/lib/core/room-ops-shared.cjs +64 -0
  653. package/lib/core/room-ops-sync.cjs +70 -0
  654. package/lib/core/room-ops.cjs +32 -0
  655. package/lib/core/room-receipt-emit.cjs +63 -0
  656. package/lib/core/room-root.cjs +93 -0
  657. package/lib/core/room-skeleton-scaffold.cjs +669 -0
  658. package/lib/core/room-skeleton-scaffold.test.cjs +296 -0
  659. package/lib/core/room-type-detector.cjs +386 -0
  660. package/lib/core/rs-backend-dispatch.cjs +64 -0
  661. package/lib/core/rs-brain-substrate-prompts.cjs +129 -0
  662. package/lib/core/rs-brain-substrate.cjs +570 -0
  663. package/lib/core/rs-breakthrough-scorer.cjs +255 -0
  664. package/lib/core/rs-canon-violations.cjs +82 -0
  665. package/lib/core/rs-chain-feeder.cjs +381 -0
  666. package/lib/core/rs-commercial-assessor.cjs +280 -0
  667. package/lib/core/rs-corpus-quality-gate.cjs +62 -0
  668. package/lib/core/rs-differential-scorer.cjs +629 -0
  669. package/lib/core/rs-domain-analyzer.cjs +385 -0
  670. package/lib/core/rs-egress-prompts.cjs +113 -0
  671. package/lib/core/rs-egress-telemetry.cjs +225 -0
  672. package/lib/core/rs-egress-violations.cjs +53 -0
  673. package/lib/core/rs-engine.cjs +597 -0
  674. package/lib/core/rs-expert-brain-projection.cjs +280 -0
  675. package/lib/core/rs-expert-mapper.cjs +467 -0
  676. package/lib/core/rs-fetcher-academic.cjs +697 -0
  677. package/lib/core/rs-fetcher-experts.cjs +314 -0
  678. package/lib/core/rs-fetcher-industry.cjs +731 -0
  679. package/lib/core/rs-fetcher-patents.cjs +564 -0
  680. package/lib/core/rs-innovation-classifier.cjs +194 -0
  681. package/lib/core/rs-math.cjs +458 -0
  682. package/lib/core/rs-mind-map.cjs +656 -0
  683. package/lib/core/rs-neo4j-writer.cjs +388 -0
  684. package/lib/core/rs-nl-to-query.cjs +435 -0
  685. package/lib/core/rs-pinecone-bridge.cjs +344 -0
  686. package/lib/core/rs-preprocessor.cjs +350 -0
  687. package/lib/core/rs-query-matrix.cjs +316 -0
  688. package/lib/core/rs-query-to-text.cjs +438 -0
  689. package/lib/core/rs-sqlite-mirror.cjs +495 -0
  690. package/lib/core/rs-thesis-generator.cjs +188 -0
  691. package/lib/core/rs_cache.py +635 -0
  692. package/lib/core/rs_corpus.py +562 -0
  693. package/lib/core/rs_corpus_exclude.py +62 -0
  694. package/lib/core/rs_hybrid.py +589 -0
  695. package/lib/core/rs_math.py +287 -0
  696. package/lib/core/rs_rooms.py +196 -0
  697. package/lib/core/salient-governance.cjs +315 -0
  698. package/lib/core/scheduled-scanner.cjs +658 -0
  699. package/lib/core/scratchpad-ops.cjs +316 -0
  700. package/lib/core/seam-liveness.cjs +191 -0
  701. package/lib/core/seam-liveness.test.cjs +177 -0
  702. package/lib/core/section-8-trace-schema.cjs +138 -0
  703. package/lib/core/section-registry.cjs +167 -0
  704. package/lib/core/security/agentshield-adapters.test.cjs +104 -0
  705. package/lib/core/security/agentshield-run.cjs +343 -0
  706. package/lib/core/security/agentshield-scanner.cjs +314 -0
  707. package/lib/core/security/agentshield-scanner.test.cjs +259 -0
  708. package/lib/core/sensors/hat-scoping-table.cjs +103 -0
  709. package/lib/core/sensors/sensor-circularity.cjs +278 -0
  710. package/lib/core/sensors/sensor-content-relevance.cjs +407 -0
  711. package/lib/core/sensors/sensor-diffusion-adoption.cjs +230 -0
  712. package/lib/core/sensors/sensor-eureka.cjs +208 -0
  713. package/lib/core/sensors/sensor-expert-skill.cjs +293 -0
  714. package/lib/core/sensors/sensor-external-fact.cjs +184 -0
  715. package/lib/core/sensors/sensor-gate-approach.cjs +104 -0
  716. package/lib/core/sensors/sensor-jtbd-reweight.cjs +141 -0
  717. package/lib/core/sensors/sensor-lagging-component.cjs +115 -0
  718. package/lib/core/sensors/sensor-memory-cortex.cjs +111 -0
  719. package/lib/core/sensors/sensor-methodology-decision.cjs +131 -0
  720. package/lib/core/sensors/sensor-opportunity-harvest.cjs +195 -0
  721. package/lib/core/sensors/sensor-perspective-lock.cjs +174 -0
  722. package/lib/core/sensors/sensor-priority.cjs +183 -0
  723. package/lib/core/sensors/sensor-recency.cjs +151 -0
  724. package/lib/core/sensors/sensor-roadmap-type.cjs +421 -0
  725. package/lib/core/sensors/sensor-room-pick.cjs +238 -0
  726. package/lib/core/sensors/sensor-show-share.cjs +215 -0
  727. package/lib/core/sensors/sensor-temporal-blindness.cjs +123 -0
  728. package/lib/core/sensors/sensor-types.cjs +294 -0
  729. package/lib/core/sensors/sensor-url-ingest.cjs +234 -0
  730. package/lib/core/session-binding.cjs +208 -0
  731. package/lib/core/session-presence.cjs +404 -0
  732. package/lib/core/session-register.cjs +138 -0
  733. package/lib/core/session-state.cjs +144 -0
  734. package/lib/core/shallow-doc-parser.cjs +353 -0
  735. package/lib/core/shallow-doc-parser.test.cjs +226 -0
  736. package/lib/core/skill-activation-router.cjs +284 -0
  737. package/lib/core/skillopt-schemas.cjs +509 -0
  738. package/lib/core/stale-copy-scanner.cjs +190 -0
  739. package/lib/core/stance-state.cjs +153 -0
  740. package/lib/core/state-aware-router.cjs +78 -0
  741. package/lib/core/state-ops.cjs +80 -0
  742. package/lib/core/state-version.cjs +350 -0
  743. package/lib/core/statusline-cache.cjs +266 -0
  744. package/lib/core/statusline-liveness-gate.cjs +105 -0
  745. package/lib/core/surface-fence.cjs +127 -0
  746. package/lib/core/synthesizers/comparison-matrix.cjs +82 -0
  747. package/lib/core/synthesizers/convergence-map.cjs +107 -0
  748. package/lib/core/synthesizers/domain-hierarchy.cjs +138 -0
  749. package/lib/core/synthesizers/source-comparison.cjs +56 -0
  750. package/lib/core/synthesizers/tension-map.cjs +119 -0
  751. package/lib/core/synthetic-expert-behavior-gate.cjs +99 -0
  752. package/lib/core/synthetic-expert-construction-gate.cjs +170 -0
  753. package/lib/core/telemetry/schema.cjs +168 -0
  754. package/lib/core/telemetry/schema.test.cjs +124 -0
  755. package/lib/core/telemetry/validator.cjs +200 -0
  756. package/lib/core/telemetry/validator.test.cjs +188 -0
  757. package/lib/core/telemetry/writer.cjs +141 -0
  758. package/lib/core/telemetry/writer.test.cjs +331 -0
  759. package/lib/core/temporal/date-sync-gate.cjs +256 -0
  760. package/lib/core/temporal/date-sync-gate.test.cjs +191 -0
  761. package/lib/core/temporal/dual-stamp.cjs +172 -0
  762. package/lib/core/temporal/dual-stamp.test.cjs +172 -0
  763. package/lib/core/temporal/point-in-time.cjs +76 -0
  764. package/lib/core/temporal/point-in-time.test.cjs +169 -0
  765. package/lib/core/temporal/recency-decay.cjs +119 -0
  766. package/lib/core/temporal/recency-decay.test.cjs +140 -0
  767. package/lib/core/temporal/reference-now.cjs +281 -0
  768. package/lib/core/temporal/reference-now.test.cjs +225 -0
  769. package/lib/core/temporal/resolve-relative-time.cjs +116 -0
  770. package/lib/core/temporal/resolve-relative-time.test.cjs +138 -0
  771. package/lib/core/temporal/supersession.cjs +250 -0
  772. package/lib/core/temporal/supersession.test.cjs +103 -0
  773. package/lib/core/terminal-capability.cjs +88 -0
  774. package/lib/core/token-estimator.cjs +348 -0
  775. package/lib/core/tone-filter.cjs +147 -0
  776. package/lib/core/trending-to-absurd/orchestrator.cjs +341 -0
  777. package/lib/core/trending-to-absurd/stage7-roadmap.cjs +317 -0
  778. package/lib/core/trending-to-absurd/variance.cjs +169 -0
  779. package/lib/core/unknowns/bandit.cjs +332 -0
  780. package/lib/core/unknowns/corpus-adapter.cjs +327 -0
  781. package/lib/core/unknowns/dsp.cjs +356 -0
  782. package/lib/core/unknowns/edge-writer.cjs +211 -0
  783. package/lib/core/unknowns/iface.cjs +212 -0
  784. package/lib/core/unknowns/orchestrator.cjs +375 -0
  785. package/lib/core/unknowns/pattern-miner.cjs +214 -0
  786. package/lib/core/unknowns/rumsfeld-matrix.cjs +155 -0
  787. package/lib/core/unknowns/verdict.cjs +254 -0
  788. package/lib/core/update-path.cjs +57 -0
  789. package/lib/core/url-ingest.cjs +568 -0
  790. package/lib/core/user-archetype.cjs +284 -0
  791. package/lib/core/user-md-ops.cjs +578 -0
  792. package/lib/core/venture-shape-nudge.cjs +178 -0
  793. package/lib/core/venture-shape-nudge.test.cjs +266 -0
  794. package/lib/core/verb-reach-affinity.cjs +299 -0
  795. package/lib/core/visual-ops.cjs +692 -0
  796. package/lib/core/voice-transition-detector.cjs +125 -0
  797. package/lib/core/watched-sources.cjs +237 -0
  798. package/lib/core/write-lock.cjs +149 -0
  799. package/lib/graph/canvas-graph.js +467 -0
  800. package/lib/graph/constellation-config.cjs +299 -0
  801. package/lib/graph/graph-detail-panel.js +165 -0
  802. package/lib/hmi/ROOM.md +47 -0
  803. package/lib/hmi/across-session-memory.cjs +669 -0
  804. package/lib/hmi/agentshield-drift-gate.cjs +142 -0
  805. package/lib/hmi/brain-review-packet.cjs +221 -0
  806. package/lib/hmi/cortex-reach-adapter.cjs +271 -0
  807. package/lib/hmi/cross-room-memory.cjs +618 -0
  808. package/lib/hmi/decoy-tier.cjs +395 -0
  809. package/lib/hmi/depth-state.cjs +164 -0
  810. package/lib/hmi/dial-label-composer.cjs +410 -0
  811. package/lib/hmi/dial-presenter.cjs +508 -0
  812. package/lib/hmi/dial-reach-orchestrator.cjs +377 -0
  813. package/lib/hmi/dial-selector.cjs +239 -0
  814. package/lib/hmi/f1-pick-capture-cli.cjs +131 -0
  815. package/lib/hmi/f3-depth-capture-cli.cjs +77 -0
  816. package/lib/hmi/f4-scope-capture-cli.cjs +86 -0
  817. package/lib/hmi/f8-action-capture-cli.cjs +145 -0
  818. package/lib/hmi/f8-capture.test.cjs +77 -0
  819. package/lib/hmi/f9-ordered-capture-cli.cjs +138 -0
  820. package/lib/hmi/harvest-scope-state.cjs +202 -0
  821. package/lib/hmi/hats-persona-cache.cjs +142 -0
  822. package/lib/hmi/jtbd-classifier.cjs +219 -0
  823. package/lib/hmi/jtbd-state.cjs +263 -0
  824. package/lib/hmi/jtbd-taxonomy.json +402 -0
  825. package/lib/hmi/mindrian-ascii-logo.cjs +96 -0
  826. package/lib/hmi/part8-egress-gate.cjs +149 -0
  827. package/lib/hmi/reach-component-map.json +47 -0
  828. package/lib/hmi/reach-relevance-gate.cjs +147 -0
  829. package/lib/hmi/selector-dispatcher.cjs +1201 -0
  830. package/lib/hmi/selector-dispatcher.test.cjs +141 -0
  831. package/lib/hmi/selector-telemetry.cjs +269 -0
  832. package/lib/hmi/shape-f0-renderer.cjs +145 -0
  833. package/lib/hmi/shape-f1-fallback.cjs +80 -0
  834. package/lib/hmi/shape-f1-renderer.cjs +219 -0
  835. package/lib/hmi/shape-f2-renderer.cjs +132 -0
  836. package/lib/hmi/shape-f3-parity.test.cjs +64 -0
  837. package/lib/hmi/shape-f3-renderer.cjs +66 -0
  838. package/lib/hmi/shape-f4-parity.test.cjs +72 -0
  839. package/lib/hmi/shape-f4-renderer.cjs +72 -0
  840. package/lib/hmi/shape-f5-renderer.cjs +155 -0
  841. package/lib/hmi/shape-f6-plan-review-renderer.cjs +324 -0
  842. package/lib/hmi/shape-f6-renderer.cjs +144 -0
  843. package/lib/hmi/shape-f7-breakthrough-renderer.cjs +234 -0
  844. package/lib/hmi/shape-f7-breakthrough-renderer.test.cjs +233 -0
  845. package/lib/hmi/shape-f8-renderer.cjs +150 -0
  846. package/lib/hmi/shape-f8-renderer.test.cjs +70 -0
  847. package/lib/hmi/shape-f9-renderer.cjs +187 -0
  848. package/lib/hmi/shape-f9-renderer.test.cjs +80 -0
  849. package/lib/hmi/shape-g-renderer.cjs +219 -0
  850. package/lib/hmi/shape-h-renderer.cjs +222 -0
  851. package/lib/hmi/tier-check.cjs +63 -0
  852. package/lib/hmi/turn-text.cjs +268 -0
  853. package/lib/hmi/voice-color-mark.cjs +318 -0
  854. package/lib/hmi/voice-style-log.cjs +276 -0
  855. package/lib/import/PRECONDITIONS.md +41 -0
  856. package/lib/import/branding.cjs +210 -0
  857. package/lib/import/branding.test.cjs +235 -0
  858. package/lib/import/classifications-sync.cjs +104 -0
  859. package/lib/import/classifications-sync.test.cjs +129 -0
  860. package/lib/import/enricher.cjs +296 -0
  861. package/lib/import/enricher.test.cjs +273 -0
  862. package/lib/import/integration.test.cjs +376 -0
  863. package/lib/import/manifest.cjs +129 -0
  864. package/lib/import/manifest.schema.json +185 -0
  865. package/lib/import/manifest.test.cjs +123 -0
  866. package/lib/import/meeting-detector.cjs +92 -0
  867. package/lib/import/meeting-detector.test.cjs +100 -0
  868. package/lib/import/person-detector.cjs +229 -0
  869. package/lib/import/person-detector.test.cjs +149 -0
  870. package/lib/import/report.cjs +186 -0
  871. package/lib/import/report.test.cjs +186 -0
  872. package/lib/import/room-md-scaffolder.cjs +49 -0
  873. package/lib/import/router.cjs +224 -0
  874. package/lib/import/router.test.cjs +356 -0
  875. package/lib/import/run-all-tests.cjs +36 -0
  876. package/lib/import/smoke-test.cjs +213 -0
  877. package/lib/import/smoke-test.test.cjs +148 -0
  878. package/lib/import/undo.test.cjs +199 -0
  879. package/lib/import/vault-scanner.cjs +105 -0
  880. package/lib/import/vault-scanner.test.cjs +67 -0
  881. package/lib/lens-engine/correlation-resolver.cjs +174 -0
  882. package/lib/lens-engine/source-lens-driver.cjs +736 -0
  883. package/lib/mcp/adapter-client.cjs +103 -0
  884. package/lib/mcp/app-html/dashboard.html +316 -0
  885. package/lib/mcp/app-html/graph.html +428 -0
  886. package/lib/mcp/app-html/mindrian-platform.html +1841 -0
  887. package/lib/mcp/app-html/wiki.html +383 -0
  888. package/lib/mcp/app-views.cjs +322 -0
  889. package/lib/mcp/brain-composition-census.cjs +184 -0
  890. package/lib/mcp/brain-router.cjs +551 -0
  891. package/lib/mcp/capability-registry.cjs +62 -0
  892. package/lib/mcp/contract-version.cjs +59 -0
  893. package/lib/mcp/daemon-lifecycle.cjs +324 -0
  894. package/lib/mcp/gate-dedup.cjs +120 -0
  895. package/lib/mcp/gate-ledger.cjs +145 -0
  896. package/lib/mcp/gate-render.cjs +483 -0
  897. package/lib/mcp/hook-adapter-audit.cjs +199 -0
  898. package/lib/mcp/larry-context.cjs +42 -0
  899. package/lib/mcp/mcp-first-flag.cjs +147 -0
  900. package/lib/mcp/no-instructions.test.cjs +270 -0
  901. package/lib/mcp/pipeline-state.cjs +424 -0
  902. package/lib/mcp/prompts.cjs +421 -0
  903. package/lib/mcp/register-core-tools.cjs +78 -0
  904. package/lib/mcp/resources.cjs +359 -0
  905. package/lib/mcp/runtime-instructions.cjs +38 -0
  906. package/lib/mcp/session-catchup.cjs +349 -0
  907. package/lib/mcp/session-registry.cjs +113 -0
  908. package/lib/mcp/session-room.cjs +124 -0
  909. package/lib/mcp/sse-event-bus.cjs +98 -0
  910. package/lib/mcp/stop-gate-handler.cjs +589 -0
  911. package/lib/mcp/surface-detect.cjs +186 -0
  912. package/lib/mcp/tool-router.cjs +2115 -0
  913. package/lib/mcp/tools/chain.cjs +674 -0
  914. package/lib/mcp/tools/claim.cjs +180 -0
  915. package/lib/mcp/tools/context.cjs +92 -0
  916. package/lib/mcp/tools/dual-path.cjs +79 -0
  917. package/lib/mcp/tools/gate.cjs +392 -0
  918. package/lib/mcp/tools/graph-reason.cjs +130 -0
  919. package/lib/mcp/tools/graph.cjs +321 -0
  920. package/lib/mcp/tools/identity.cjs +174 -0
  921. package/lib/mcp/tools/room.cjs +449 -0
  922. package/lib/mcp/tools/sensors.cjs +450 -0
  923. package/lib/mcp/tools/status.cjs +183 -0
  924. package/lib/mcp/tools/stop-gate.cjs +103 -0
  925. package/lib/mcp/tools/views.cjs +382 -0
  926. package/lib/mcp/tree-watcher.cjs +199 -0
  927. package/lib/memory/.mindrian/auto-explore-mat-test-001.json +11 -0
  928. package/lib/memory/aaak-compress.cjs +403 -0
  929. package/lib/memory/aaak-compress.test.cjs +288 -0
  930. package/lib/memory/async-artifact-auto-commit.test.cjs +223 -0
  931. package/lib/memory/bearer-token.test.cjs +315 -0
  932. package/lib/memory/body-shape-coverage.test.cjs +268 -0
  933. package/lib/memory/brain-cache-lru.test.cjs +259 -0
  934. package/lib/memory/brain-client-query-shape.test.cjs +160 -0
  935. package/lib/memory/brain-cypher-chain-slice.test.cjs +368 -0
  936. package/lib/memory/brain-derivation-graceful-degradation.test.cjs +1019 -0
  937. package/lib/memory/brain-derivation-queue.test.cjs +553 -0
  938. package/lib/memory/brain-derivation.test.cjs +639 -0
  939. package/lib/memory/brain-derive-command.test.cjs +534 -0
  940. package/lib/memory/brain-derive-curation.test.cjs +243 -0
  941. package/lib/memory/brain-md-invariants-validator.test.cjs +704 -0
  942. package/lib/memory/brain-md-schema.test.cjs +467 -0
  943. package/lib/memory/brain-md-staleness.test.cjs +525 -0
  944. package/lib/memory/brain-server-resolution.test.cjs +314 -0
  945. package/lib/memory/brain-suggestion-template.test.cjs +200 -0
  946. package/lib/memory/chain-recommender-canonical.test.cjs +208 -0
  947. package/lib/memory/chain-recommender.test.cjs +233 -0
  948. package/lib/memory/chat-context.test.cjs +128 -0
  949. package/lib/memory/command-registry.test.cjs +220 -0
  950. package/lib/memory/connector-registry.test.cjs +94 -0
  951. package/lib/memory/correlation-label-index.test.cjs +119 -0
  952. package/lib/memory/correlation.test.cjs +253 -0
  953. package/lib/memory/cross-room-aggregator.test.cjs +909 -0
  954. package/lib/memory/curation-132-03-dedup-held-rename.test.cjs +251 -0
  955. package/lib/memory/curation-132-05-pseudonymize.test.cjs +205 -0
  956. package/lib/memory/curation-batch.test.cjs +213 -0
  957. package/lib/memory/dashboard-server.test.cjs +256 -0
  958. package/lib/memory/debouncer-drain-at-prompt.test.cjs +389 -0
  959. package/lib/memory/decision-capture.test.cjs +632 -0
  960. package/lib/memory/decision-capture.worker.cjs +70 -0
  961. package/lib/memory/doctor-deprecation-surface.test.cjs +188 -0
  962. package/lib/memory/dual-graph-health.test.cjs +485 -0
  963. package/lib/memory/explain-decision-command.test.cjs +521 -0
  964. package/lib/memory/explain-decision-footer.test.cjs +316 -0
  965. package/lib/memory/explored-materials-store.cjs +392 -0
  966. package/lib/memory/f-selector-ranker.test.cjs +593 -0
  967. package/lib/memory/feynman-minto-guardian.test.cjs +863 -0
  968. package/lib/memory/feynman-minto-invariants.test.cjs +554 -0
  969. package/lib/memory/feynman-prompts-drift.test.cjs +144 -0
  970. package/lib/memory/feynman-prompts.cjs +151 -0
  971. package/lib/memory/feynman-prompts.test.cjs +96 -0
  972. package/lib/memory/first-touch-version.test.cjs +198 -0
  973. package/lib/memory/folder-memory-quadruple.test.cjs +548 -0
  974. package/lib/memory/folder-memory.test.cjs +503 -0
  975. package/lib/memory/framework-chain-composer.test.cjs +549 -0
  976. package/lib/memory/frontmatter-schema-validator.test.cjs +344 -0
  977. package/lib/memory/guardian-onstop-reaches-user.test.cjs +396 -0
  978. package/lib/memory/heal-command.test.cjs +604 -0
  979. package/lib/memory/help-coverage.test.cjs +108 -0
  980. package/lib/memory/help-renderer.test.cjs +145 -0
  981. package/lib/memory/huji-run-one-async-parity.test.cjs +242 -0
  982. package/lib/memory/hypergraph-event-schema.test.cjs +116 -0
  983. package/lib/memory/index-artifact-transaction.test.cjs +333 -0
  984. package/lib/memory/lazygraph-rs-discoveries-view.test.cjs +122 -0
  985. package/lib/memory/local-chain-recommender.test.cjs +172 -0
  986. package/lib/memory/mcp-input-validation.test.cjs +240 -0
  987. package/lib/memory/mcp-server-brain-deps.test.cjs +270 -0
  988. package/lib/memory/mcp-stack-fallback.test.cjs +433 -0
  989. package/lib/memory/minto-debounce-consumer-census.test.cjs +567 -0
  990. package/lib/memory/minto-debouncer.test.cjs +407 -0
  991. package/lib/memory/minto-debouncer.worker.cjs +46 -0
  992. package/lib/memory/minto-migration-v88.test.cjs +265 -0
  993. package/lib/memory/minto-schema-v88.test.cjs +390 -0
  994. package/lib/memory/mos-status-renderer.test.cjs +631 -0
  995. package/lib/memory/narrative-schema.cjs +376 -0
  996. package/lib/memory/narrative-schema.test.cjs +209 -0
  997. package/lib/memory/nav-dial.test.cjs +414 -0
  998. package/lib/memory/navigation-engine-core.test.cjs +712 -0
  999. package/lib/memory/navigation-engine-offer.test.cjs +404 -0
  1000. package/lib/memory/navigation-hook-resolver.test.cjs +177 -0
  1001. package/lib/memory/navigation-invariants.test.cjs +483 -0
  1002. package/lib/memory/navigation-projections.test.cjs +241 -0
  1003. package/lib/memory/navigation-write-edge.test.cjs +206 -0
  1004. package/lib/memory/offer-closer.test.cjs +258 -0
  1005. package/lib/memory/offer-presenter.test.cjs +554 -0
  1006. package/lib/memory/on-stop-snapshot.test.cjs +404 -0
  1007. package/lib/memory/orchestration-projection.test.cjs +706 -0
  1008. package/lib/memory/packet-chain-hint.test.cjs +407 -0
  1009. package/lib/memory/packet-schema-validation.test.cjs +317 -0
  1010. package/lib/memory/palette-consistency.test.cjs +127 -0
  1011. package/lib/memory/pending-tension-store.cjs +453 -0
  1012. package/lib/memory/per-command-jtbd-derivation.test.cjs +130 -0
  1013. package/lib/memory/per-command-teaching.test.cjs +110 -0
  1014. package/lib/memory/post-compact-reinjection.test.cjs +854 -0
  1015. package/lib/memory/post-write-triple.test.cjs +317 -0
  1016. package/lib/memory/pre-compact-snapshot.test.cjs +495 -0
  1017. package/lib/memory/precommit-real-commit.test.cjs +301 -0
  1018. package/lib/memory/problem-type-router.test.cjs +656 -0
  1019. package/lib/memory/query-efficiency-telemetry.test.cjs +612 -0
  1020. package/lib/memory/recompile-room-references.test.cjs +392 -0
  1021. package/lib/memory/recompile-room-references.worker.cjs +42 -0
  1022. package/lib/memory/record-decision-dual-write.test.cjs +454 -0
  1023. package/lib/memory/render-v2-disposition.test.cjs +199 -0
  1024. package/lib/memory/room-classifier-strict-mode.test.cjs +430 -0
  1025. package/lib/memory/room-minto-hook.test.cjs +398 -0
  1026. package/lib/memory/rs-discovery-engine.test.cjs +323 -0
  1027. package/lib/memory/run-feynman-tests.cjs +2101 -0
  1028. package/lib/memory/security-trifecta.test.cjs +329 -0
  1029. package/lib/memory/selector-alias-map.test.cjs +172 -0
  1030. package/lib/memory/selector-decisions.test.cjs +417 -0
  1031. package/lib/memory/selector-miss.test.cjs +290 -0
  1032. package/lib/memory/session-start-brain-staleness.test.cjs +363 -0
  1033. package/lib/memory/session-start-triple-injection.test.cjs +514 -0
  1034. package/lib/memory/sessionstart-banner-formatter.cjs +318 -0
  1035. package/lib/memory/sessionstart-coordinator.test.cjs +446 -0
  1036. package/lib/memory/sessionstart-minto-banner.test.cjs +373 -0
  1037. package/lib/memory/skill-activation-router.test.cjs +465 -0
  1038. package/lib/memory/skill-vs-code-drift.test.cjs +257 -0
  1039. package/lib/memory/soft-alias.test.cjs +144 -0
  1040. package/lib/memory/spine-events-correlation.test.cjs +198 -0
  1041. package/lib/memory/stale-copy-scanner.test.cjs +294 -0
  1042. package/lib/memory/stamp-artifact-write.test.cjs +304 -0
  1043. package/lib/memory/state-aware-router.test.cjs +90 -0
  1044. package/lib/memory/statusline-active-room-write.test.cjs +245 -0
  1045. package/lib/memory/statusline-active-room.test.cjs +315 -0
  1046. package/lib/memory/statusline-minto-segment.test.cjs +292 -0
  1047. package/lib/memory/statusline-two-row.test.cjs +338 -0
  1048. package/lib/memory/suggest-next-workflow.test.cjs +176 -0
  1049. package/lib/memory/sync-async-entry-points.test.cjs +204 -0
  1050. package/lib/memory/terminal-capability.test.cjs +155 -0
  1051. package/lib/memory/test-bridge-writer-enhanced.cjs +452 -0
  1052. package/lib/memory/test-rs-brain-substrate-shape.cjs +529 -0
  1053. package/lib/memory/test-rs-brain-substrate.cjs +636 -0
  1054. package/lib/memory/test-rs-breakthrough-scorer.cjs +375 -0
  1055. package/lib/memory/test-rs-canon-violations.cjs +218 -0
  1056. package/lib/memory/test-rs-chain-feeder-core.cjs +344 -0
  1057. package/lib/memory/test-rs-chain-feeder-skill-spawn.cjs +297 -0
  1058. package/lib/memory/test-rs-commercial-assessor.cjs +385 -0
  1059. package/lib/memory/test-rs-differential-scorer.cjs +480 -0
  1060. package/lib/memory/test-rs-discovery-engine.cjs +648 -0
  1061. package/lib/memory/test-rs-domain-analyzer.cjs +492 -0
  1062. package/lib/memory/test-rs-egress-primitives.cjs +420 -0
  1063. package/lib/memory/test-rs-expert-mapper.cjs +547 -0
  1064. package/lib/memory/test-rs-explain-command.cjs +443 -0
  1065. package/lib/memory/test-rs-fetcher-academic.cjs +848 -0
  1066. package/lib/memory/test-rs-fetcher-experts.cjs +496 -0
  1067. package/lib/memory/test-rs-fetcher-industry.cjs +702 -0
  1068. package/lib/memory/test-rs-fetcher-patents.cjs +674 -0
  1069. package/lib/memory/test-rs-innovation-classifier.cjs +301 -0
  1070. package/lib/memory/test-rs-mind-map.cjs +646 -0
  1071. package/lib/memory/test-rs-neo4j-writer.cjs +518 -0
  1072. package/lib/memory/test-rs-nl-to-query.cjs +449 -0
  1073. package/lib/memory/test-rs-pinecone-bridge.cjs +282 -0
  1074. package/lib/memory/test-rs-preprocessor.cjs +433 -0
  1075. package/lib/memory/test-rs-query-matrix.cjs +391 -0
  1076. package/lib/memory/test-rs-query-to-text.cjs +551 -0
  1077. package/lib/memory/test-rs-sqlite-mirror.cjs +649 -0
  1078. package/lib/memory/test-rs-thesis-generator.cjs +360 -0
  1079. package/lib/memory/triple-context-formatter.cjs +473 -0
  1080. package/lib/memory/triple-context-formatter.test.cjs +442 -0
  1081. package/lib/memory/user-md-persona.test.cjs +565 -0
  1082. package/lib/memory/userpromptsubmit-integration.test.cjs +787 -0
  1083. package/lib/memory/validators/README.md +157 -0
  1084. package/lib/memory/validators/brain-md-invariants.cjs +475 -0
  1085. package/lib/memory/validators/brain-substrate-invariants.cjs +285 -0
  1086. package/lib/memory/validators/external-academic-invariants.cjs +249 -0
  1087. package/lib/memory/validators/external-industry-invariants.cjs +271 -0
  1088. package/lib/memory/validators/external-patents-invariants.cjs +266 -0
  1089. package/lib/memory/validators/minto-invariants.cjs +62 -0
  1090. package/lib/memory/validators/navigation-invariants.cjs +340 -0
  1091. package/lib/memory/validators/queue-health.cjs +95 -0
  1092. package/lib/memory/validators/snapshot-integrity.cjs +129 -0
  1093. package/lib/memory/validators/stale-lifecycle.cjs +116 -0
  1094. package/lib/memory/vault-section-minto-generator-atomic.test.cjs +556 -0
  1095. package/lib/memory/vault-section-minto-generator-atomic.worker.cjs +73 -0
  1096. package/lib/memory/workflow-layer-e2e.test.cjs +262 -0
  1097. package/lib/memory/write-lock-atomic.test.cjs +137 -0
  1098. package/lib/memory/write-lock-atomic.worker.cjs +55 -0
  1099. package/lib/parity/check-parity.cjs +83 -0
  1100. package/lib/presentation/presentation-server.cjs +101 -0
  1101. package/lib/presentation/presentation-watcher.cjs +123 -0
  1102. package/lib/quickview/hub-server.cjs +719 -0
  1103. package/lib/quickview/server.cjs +533 -0
  1104. package/lib/render/JTBD-PALETTES.md +145 -0
  1105. package/lib/render/ROOM.md +111 -0
  1106. package/lib/render/render-v2.cjs +532 -0
  1107. package/lib/render/render-v2.test.cjs +267 -0
  1108. package/lib/render/render.cjs +65 -0
  1109. package/lib/sessionstart/budget-compressor.cjs +130 -0
  1110. package/lib/sessionstart/contributor-interface.cjs +134 -0
  1111. package/lib/sessionstart/contributor-isolator.cjs +128 -0
  1112. package/lib/sessionstart/precedence-ladder.cjs +47 -0
  1113. package/lib/state/ROOM.md +46 -0
  1114. package/lib/state/state-md-parser.cjs +215 -0
  1115. package/lib/statusline/ROOM.md +38 -0
  1116. package/lib/statusline/banner-suppression.cjs +50 -0
  1117. package/lib/statusline/cockpit-renderer.cjs +443 -0
  1118. package/lib/statusline/cockpit-signals.cjs +312 -0
  1119. package/lib/statusline/cockpit-telemetry.cjs +152 -0
  1120. package/lib/statusline/ctx-window.cjs +135 -0
  1121. package/lib/statusline/governing-thought-truncator.cjs +45 -0
  1122. package/lib/statusline/next-move-cache.cjs +235 -0
  1123. package/lib/statusline/ratification-next.cjs +164 -0
  1124. package/lib/statusline/room-health-cache.cjs +200 -0
  1125. package/lib/statusline/surface-detect.cjs +85 -0
  1126. package/lib/statusline/two-row-renderer.cjs +207 -0
  1127. package/lib/statusline/version-resolver.cjs +81 -0
  1128. package/lib/ui/design-system.cjs +19 -0
  1129. package/lib/update-bootstrap.sh.template +145 -0
  1130. package/lib/vault/frontmatter-schema.cjs +297 -0
  1131. package/lib/vault/room-scanner.cjs +365 -0
  1132. package/lib/vault/wikilink-builder.cjs +231 -0
  1133. package/lib/vault/wikilink-builder.test.cjs +182 -0
  1134. package/lib/wiki/briefing.cjs +193 -0
  1135. package/lib/wiki/editor-dist/wiki-editor.css +1 -0
  1136. package/lib/wiki/editor-dist/wiki-editor.js +1327 -0
  1137. package/lib/wiki/graph-links.cjs +281 -0
  1138. package/lib/wiki/page-renderer.cjs +229 -0
  1139. package/lib/wiki/room-home.cjs +353 -0
  1140. package/lib/wiki/wiki-chat.cjs +81 -0
  1141. package/lib/wiki/wiki-export.cjs +364 -0
  1142. package/lib/wiki/wiki-layout.cjs +1143 -0
  1143. package/lib/wiki/wiki-search.cjs +142 -0
  1144. package/lib/wiki/wiki-server.cjs +748 -0
  1145. package/lib/wiki/wiki-watcher.cjs +105 -0
  1146. package/lib/workflow/ROOM.md +47 -0
  1147. package/lib/workflow/chain-source.cjs +179 -0
  1148. package/lib/workflow/command-resolver.cjs +155 -0
  1149. package/lib/workflow/command-resolver.test.cjs +235 -0
  1150. package/lib/workflow/cross-room-umbilical-closer.cjs +364 -0
  1151. package/lib/workflow/dial-close-reach.cjs +405 -0
  1152. package/lib/workflow/f-selector-ranker.cjs +1148 -0
  1153. package/lib/workflow/f1-pick-consumer.cjs +232 -0
  1154. package/lib/workflow/f3-depth-consumer.cjs +98 -0
  1155. package/lib/workflow/f4-scope-consumer.cjs +112 -0
  1156. package/lib/workflow/f8-consumer.test.cjs +82 -0
  1157. package/lib/workflow/f8-fanout-consumer.cjs +208 -0
  1158. package/lib/workflow/f9-consumer.test.cjs +71 -0
  1159. package/lib/workflow/f9-ordered-consumer.cjs +199 -0
  1160. package/lib/workflow/local-chain-recommender.cjs +330 -0
  1161. package/lib/workflow/memory-cascade-f9-adapter.cjs +273 -0
  1162. package/lib/workflow/memory-governance-closer.cjs +384 -0
  1163. package/lib/workflow/offer-closer.cjs +407 -0
  1164. package/lib/workflow/reach-hedge-ranker.cjs +809 -0
  1165. package/lib/workflow/reach-reject-reader.cjs +372 -0
  1166. package/lib/workflow/reconcile-f9-adapter.cjs +299 -0
  1167. package/lib/workflow/selector-decisions.cjs +478 -0
  1168. package/lib/workflow/session-binding-consumer.cjs +249 -0
  1169. package/npm-shrinkwrap.json +1544 -0
  1170. package/output-styles/destijl.md +67 -0
  1171. package/package.json +22 -4
  1172. package/pipelines/PWS_grading/01-deep-grade.md +48 -0
  1173. package/pipelines/PWS_grading/02-mullins.md +50 -0
  1174. package/pipelines/PWS_grading/03-build-thesis.md +60 -0
  1175. package/pipelines/PWS_grading/04-structure-argument.md +77 -0
  1176. package/pipelines/PWS_grading/CHAIN.md +79 -0
  1177. package/pipelines/analogy/01-decompose.md +80 -0
  1178. package/pipelines/analogy/02-abstract.md +87 -0
  1179. package/pipelines/analogy/03-search.md +135 -0
  1180. package/pipelines/analogy/04-transfer.md +101 -0
  1181. package/pipelines/analogy/05-validate.md +106 -0
  1182. package/pipelines/analogy/CHAIN.md +73 -0
  1183. package/pipelines/discovery/01-explore-domains.md +44 -0
  1184. package/pipelines/discovery/02-think-hats.md +50 -0
  1185. package/pipelines/discovery/03-analyze-needs.md +54 -0
  1186. package/pipelines/discovery/CHAIN.md +48 -0
  1187. package/pipelines/thesis/01-structure-argument.md +45 -0
  1188. package/pipelines/thesis/02-challenge-assumptions.md +48 -0
  1189. package/pipelines/thesis/03-build-thesis.md +54 -0
  1190. package/pipelines/thesis/CHAIN.md +48 -0
  1191. package/references/brain/causal-directives.md +91 -0
  1192. package/references/brain/causal-enrichment.cypher +165 -0
  1193. package/references/brain/command-triggers-schema.md +15 -0
  1194. package/references/brain/graph-architecture.md +317 -0
  1195. package/references/brain/query-patterns.md +504 -0
  1196. package/references/brain/room-hierarchy-schema.md +224 -0
  1197. package/references/brain/schema.md +86 -0
  1198. package/references/capability-radar/capabilities-index.md +269 -0
  1199. package/references/capability-radar/changelog-cache.md +95 -0
  1200. package/references/causal/causal-schema.md +103 -0
  1201. package/references/design/email-template-standard.md +169 -0
  1202. package/references/design/graph-visualization-standard.md +178 -0
  1203. package/references/design/newsletter-email-template.html +307 -0
  1204. package/references/document-generation.md +179 -0
  1205. package/references/hsi/HSI-TOOLS-REFERENCE.md +222 -0
  1206. package/references/import-config.md +141 -0
  1207. package/references/integrations/detection-patterns.md +101 -0
  1208. package/references/meeting/artifact-template.md +377 -0
  1209. package/references/meeting/cross-meeting-intelligence.md +216 -0
  1210. package/references/meeting/cross-relationship-patterns.md +202 -0
  1211. package/references/meeting/extraction-perspectives.md +225 -0
  1212. package/references/meeting/filing-protocol.md +222 -0
  1213. package/references/meeting/knowledge-typing.md +242 -0
  1214. package/references/meeting/live-join-interface.md +244 -0
  1215. package/references/meeting/section-mapping.md +192 -0
  1216. package/references/meeting/segment-classification.md +258 -0
  1217. package/references/meeting/speaker-profile-template.md +219 -0
  1218. package/references/meeting/summary-template.md +348 -0
  1219. package/references/meeting/transcript-patterns.md +226 -0
  1220. package/references/methodology/analyze-needs.md +135 -0
  1221. package/references/methodology/analyze-systems.md +121 -0
  1222. package/references/methodology/analyze-timing.md +149 -0
  1223. package/references/methodology/beautiful-question.md +109 -0
  1224. package/references/methodology/build-knowledge.md +161 -0
  1225. package/references/methodology/build-thesis-scored.md +60 -0
  1226. package/references/methodology/build-thesis.md +237 -0
  1227. package/references/methodology/causal-loop-diagrams.md +65 -0
  1228. package/references/methodology/challenge-assumptions.md +127 -0
  1229. package/references/methodology/diagnose.md +169 -0
  1230. package/references/methodology/dominant-designs.md +212 -0
  1231. package/references/methodology/explore-domains.md +147 -0
  1232. package/references/methodology/explore-futures.md +163 -0
  1233. package/references/methodology/explore-trends.md +129 -0
  1234. package/references/methodology/find-bottlenecks.md +131 -0
  1235. package/references/methodology/grade.md +211 -0
  1236. package/references/methodology/huji-stage-a-intake.md +246 -0
  1237. package/references/methodology/index.md +34 -0
  1238. package/references/methodology/leadership.md +200 -0
  1239. package/references/methodology/lean-canvas.md +116 -0
  1240. package/references/methodology/leverage-scan-signatures.md +83 -0
  1241. package/references/methodology/macro-trends.md +192 -0
  1242. package/references/methodology/map-unknowns.md +137 -0
  1243. package/references/methodology/mullins-7-domains.md +104 -0
  1244. package/references/methodology/problem-types.md +65 -0
  1245. package/references/methodology/root-cause.md +178 -0
  1246. package/references/methodology/rubric-huji.md +160 -0
  1247. package/references/methodology/sapphire-encoding.md +355 -0
  1248. package/references/methodology/scenario-plan.md +190 -0
  1249. package/references/methodology/score-innovation.md +154 -0
  1250. package/references/methodology/skillopt-judge-rubric.md +48 -0
  1251. package/references/methodology/skillopt-queries-rubric.md +60 -0
  1252. package/references/methodology/skillopt-refute-rubric.md +59 -0
  1253. package/references/methodology/skillopt-review-rubric.md +72 -0
  1254. package/references/methodology/skillopt-revise-rubric.md +48 -0
  1255. package/references/methodology/structure-argument.md +158 -0
  1256. package/references/methodology/systems-thinking.md +231 -0
  1257. package/references/methodology/think-hats.md +147 -0
  1258. package/references/methodology/triz-matrix.json +751 -0
  1259. package/references/methodology/triz-principles.md +501 -0
  1260. package/references/methodology/user-needs.md +199 -0
  1261. package/references/methodology/validate.md +163 -0
  1262. package/references/methodology/value-proposition.md +244 -0
  1263. package/references/opportunities/funding-lifecycle.md +103 -0
  1264. package/references/opportunities/grant-api-patterns.md +99 -0
  1265. package/references/opportunities/opportunity-template.md +84 -0
  1266. package/references/opportunities/tnufa-rubric.md +81 -0
  1267. package/references/personality/assessment-philosophy.md +72 -0
  1268. package/references/personality/lexicon.md +100 -0
  1269. package/references/personality/persona-chains.md +56 -0
  1270. package/references/personality/pws-lexicon-full.md +499 -0
  1271. package/references/personality/voice-dna.md +156 -0
  1272. package/references/personas/hat-perspectives.md +76 -0
  1273. package/references/personas/persona-template.md +63 -0
  1274. package/references/pipeline/act-output-contract.md +88 -0
  1275. package/references/pipeline/chains-index.md +39 -0
  1276. package/references/pws-profile-generation.md +79 -0
  1277. package/references/reasoning/reasoning-schema.md +143 -0
  1278. package/references/reasoning/reasoning-template.md +68 -0
  1279. package/references/reasoning/run-template.md +38 -0
  1280. package/references/research/RESEARCH_14_CLAUDE_CODE_SOURCE_ARCHITECTURE.md +209 -0
  1281. package/references/research/RESEARCH_15_V1.8_OPTIMIZATION_JTBD.md +375 -0
  1282. package/references/research/RESEARCH_16_NATIVE_FIRST_PLUGIN_ARCHITECTURE.md +575 -0
  1283. package/references/research/RESEARCH_17_MCP_UI_FRAMEWORKS.md +272 -0
  1284. package/references/security/agentshield-baseline.json +8 -0
  1285. package/references/security/cve-db.json +220 -0
  1286. package/references/taxonomy/TAXONOMY.md +192 -0
  1287. package/references/templates/MINTO.md +36 -0
  1288. package/references/user-research/2026-04-05-leah-lawrence-session.md +202 -0
  1289. package/references/vault-kit/README.md +35 -0
  1290. package/references/vault-kit/app.json +12 -0
  1291. package/references/vault-kit/appearance.json +12 -0
  1292. package/references/vault-kit/graph.json +35 -0
  1293. package/references/vault-kit/snippets/mindrian-destijl.css +297 -0
  1294. package/references/vault-kit/templates/new-artifact.md +37 -0
  1295. package/references/vault-kit/templates/new-meeting-note.md +35 -0
  1296. package/references/vault-kit/templates/new-team-profile.md +29 -0
  1297. package/references/vault-kit/templates/new-xref.md +35 -0
  1298. package/references/visual/ROOM.md +55 -0
  1299. package/references/visual/palette.json +54 -0
  1300. package/references/visual/symbol-system.md +151 -0
  1301. package/scripts/189-plurai-gate-check.cjs +88 -0
  1302. package/scripts/198-plurai-gate-check.cjs +118 -0
  1303. package/scripts/198-rollback-rehearsal.cjs +233 -0
  1304. package/scripts/83-scope-injection.test.cjs +648 -0
  1305. package/scripts/PYTHON_GATES.md +78 -0
  1306. package/scripts/__pycache__/compute-hsi.cpython-312.pyc +0 -0
  1307. package/scripts/__pycache__/detect-reverse-salients.cpython-312.pyc +0 -0
  1308. package/scripts/__pycache__/rs-engine.cpython-312.pyc +0 -0
  1309. package/scripts/act-command.cjs +518 -0
  1310. package/scripts/admin-brain-write.cjs +442 -0
  1311. package/scripts/admin-command-gate.cjs +178 -0
  1312. package/scripts/agentshield-scan-cli.cjs +226 -0
  1313. package/scripts/agentshield-sessionstart-scan.cjs +282 -0
  1314. package/scripts/analogy-fitness-report.cjs +266 -0
  1315. package/scripts/analyze-room +1079 -0
  1316. package/scripts/apply-teaching-content.cjs +298 -0
  1317. package/scripts/async-artifact-auto-commit.cjs +485 -0
  1318. package/scripts/audit-active-phase-resolver.cjs +160 -0
  1319. package/scripts/audit-body-shape-coverage.cjs +213 -0
  1320. package/scripts/auto-explore-drain.cjs +274 -0
  1321. package/scripts/auto-explore-fingerprint.cjs +354 -0
  1322. package/scripts/auto-explore-fire.cjs +359 -0
  1323. package/scripts/backfill-correlation-id.cjs +425 -0
  1324. package/scripts/backfill-hitl-shape.cjs +276 -0
  1325. package/scripts/backfill-hitl-shape.test.cjs +307 -0
  1326. package/scripts/backup-modifications +68 -0
  1327. package/scripts/banner +182 -0
  1328. package/scripts/brain-derivation-drain.cjs +291 -0
  1329. package/scripts/brain-derive-command.cjs +846 -0
  1330. package/scripts/brain-normalize-books.cypher +505 -0
  1331. package/scripts/brain-normalize-final.cypher +1355 -0
  1332. package/scripts/brain-normalize-problemtype.cypher +475 -0
  1333. package/scripts/brain-normalize-supplement.cypher +449 -0
  1334. package/scripts/brain-normalize-targeted.cypher +344 -0
  1335. package/scripts/brain-normalize-v194.cypher +61 -0
  1336. package/scripts/brain-normalize.cypher +530 -0
  1337. package/scripts/brain-response-sanitize-hook.cjs +165 -0
  1338. package/scripts/build-brain-census.cjs +957 -0
  1339. package/scripts/build-brain-packet-schema.cjs +225 -0
  1340. package/scripts/build-command-registry.cjs +587 -0
  1341. package/scripts/build-connector-registry.cjs +1331 -0
  1342. package/scripts/build-corpus-stats.cjs +408 -0
  1343. package/scripts/build-dist-bundles.cjs +495 -0
  1344. package/scripts/build-ecosystem-graph.cjs +490 -0
  1345. package/scripts/build-graph +1462 -0
  1346. package/scripts/build-graph-from-sqlite.cjs +97 -0
  1347. package/scripts/build-harness-manifest.cjs +1090 -0
  1348. package/scripts/build-jtbd-nudges +205 -0
  1349. package/scripts/build-new-surface.cjs +805 -0
  1350. package/scripts/build-orchestration-projection.cjs +1221 -0
  1351. package/scripts/build-render-coverage.cjs +637 -0
  1352. package/scripts/build-skill-mirrors.cjs +681 -0
  1353. package/scripts/cache-hitrate-report.cjs +216 -0
  1354. package/scripts/causal-to-graph.cjs +163 -0
  1355. package/scripts/check-abstraction-fixture-neutral.cjs +160 -0
  1356. package/scripts/check-admin-identity.cjs +212 -0
  1357. package/scripts/check-brain-tool-liveness.cjs +502 -0
  1358. package/scripts/check-card-fire.cjs +1809 -0
  1359. package/scripts/check-cirs-declaration.cjs +279 -0
  1360. package/scripts/check-deck-design.cjs +104 -0
  1361. package/scripts/check-dual-graph-health.cjs +645 -0
  1362. package/scripts/check-first-touch-drift.cjs +220 -0
  1363. package/scripts/check-flagship-floor.cjs +391 -0
  1364. package/scripts/check-framework-vocabulary-drift.cjs +563 -0
  1365. package/scripts/check-gate-seam.cjs +178 -0
  1366. package/scripts/check-graph-derive-health.cjs +208 -0
  1367. package/scripts/check-graph-export-typemap.cjs +118 -0
  1368. package/scripts/check-help-coverage.cjs +290 -0
  1369. package/scripts/check-hitl-stages.cjs +219 -0
  1370. package/scripts/check-hook-schema-compatibility.cjs +231 -0
  1371. package/scripts/check-hsi-deps +42 -0
  1372. package/scripts/check-kuzu-reintroduction.cjs +251 -0
  1373. package/scripts/check-onboard +43 -0
  1374. package/scripts/check-onboard-statusline.cjs +363 -0
  1375. package/scripts/check-palette-consistency.cjs +106 -0
  1376. package/scripts/check-pending-ambiguous.cjs +244 -0
  1377. package/scripts/check-pending-breakthrough.cjs +189 -0
  1378. package/scripts/check-pending-breakthrough.test.cjs +166 -0
  1379. package/scripts/check-pending-naming-decision.cjs +97 -0
  1380. package/scripts/check-plugin-path-anchoring.cjs +830 -0
  1381. package/scripts/check-publish-needs.cjs +126 -0
  1382. package/scripts/check-registry-drift.cjs +206 -0
  1383. package/scripts/check-release-payload-ceiling.cjs +269 -0
  1384. package/scripts/check-render-coverage.cjs +558 -0
  1385. package/scripts/check-research-isomorphism.cjs +446 -0
  1386. package/scripts/check-reward-before-investment.cjs +261 -0
  1387. package/scripts/check-room-blueprints.cjs +213 -0
  1388. package/scripts/check-schema-aliases.cjs +532 -0
  1389. package/scripts/check-shape-declaration.cjs +958 -0
  1390. package/scripts/check-shape-declaration.test.cjs +185 -0
  1391. package/scripts/check-skill-spec.cjs +312 -0
  1392. package/scripts/check-skill-vs-code-drift.cjs +178 -0
  1393. package/scripts/check-substrate.cjs +513 -0
  1394. package/scripts/check-tool-honesty.cjs +1601 -0
  1395. package/scripts/check-update +60 -0
  1396. package/scripts/check-version-and-sha.cjs +366 -0
  1397. package/scripts/check-voice-style.cjs +184 -0
  1398. package/scripts/check-worktree-hygiene.cjs +530 -0
  1399. package/scripts/classify-insight +59 -0
  1400. package/scripts/coherence-smoke-test.cjs +489 -0
  1401. package/scripts/collect-cold-install-evidence.cjs +411 -0
  1402. package/scripts/compute-bayesian-surprise.py +175 -0
  1403. package/scripts/compute-blindspot-mass.py +209 -0
  1404. package/scripts/compute-disruption-index.py +211 -0
  1405. package/scripts/compute-element-novelty.py +254 -0
  1406. package/scripts/compute-external-whitespace.py +563 -0
  1407. package/scripts/compute-hsi.py +947 -0
  1408. package/scripts/compute-meetings-intelligence +716 -0
  1409. package/scripts/compute-opportunity-state +17 -0
  1410. package/scripts/compute-state +393 -0
  1411. package/scripts/compute-team +1112 -0
  1412. package/scripts/compute-whitespace-embeddings.py +464 -0
  1413. package/scripts/compute-whitespace-gaps.py +562 -0
  1414. package/scripts/compute_topic_forest.py +684 -0
  1415. package/scripts/consolidate-pinecone.py +265 -0
  1416. package/scripts/context-monitor +943 -0
  1417. package/scripts/create-speaker-profile +311 -0
  1418. package/scripts/cross-room-detect.cjs +195 -0
  1419. package/scripts/csv-to-idea-graph.cjs +755 -0
  1420. package/scripts/ctxl-eval.cjs +846 -0
  1421. package/scripts/curation-132-03-dedup-held-rename.cjs +395 -0
  1422. package/scripts/curation-132-05-pseudonymize.cjs +189 -0
  1423. package/scripts/derive-verb-reach-affinity.cjs +507 -0
  1424. package/scripts/detect-reverse-salients.py +242 -0
  1425. package/scripts/diagnostics-command.cjs +345 -0
  1426. package/scripts/dial-memory-refresh-command.cjs +147 -0
  1427. package/scripts/discover-analogy-whitespace.py +433 -0
  1428. package/scripts/discover-hsi-whitespace.py +352 -0
  1429. package/scripts/discover-rs-whitespace.py +327 -0
  1430. package/scripts/discovery-cycle.cjs +625 -0
  1431. package/scripts/disposition-render-v2.cjs +229 -0
  1432. package/scripts/doctor-preflight-format.cjs +101 -0
  1433. package/scripts/doctor.cjs +4327 -0
  1434. package/scripts/dogfood-bridge/append-to-queue.sh +59 -0
  1435. package/scripts/dogfood-bridge/path-filter.cjs +86 -0
  1436. package/scripts/dogfood-derive.cjs +158 -0
  1437. package/scripts/dogfood-emit.cjs +126 -0
  1438. package/scripts/empathy-observation-emit.cjs +108 -0
  1439. package/scripts/enrichment-queue-append.cjs +157 -0
  1440. package/scripts/ensure-brain-baseline.cjs +117 -0
  1441. package/scripts/entity-extract.cjs +1135 -0
  1442. package/scripts/eureka-command.cjs +687 -0
  1443. package/scripts/eureka-critic-run.cjs +662 -0
  1444. package/scripts/eureka-portfolio-report.cjs +2038 -0
  1445. package/scripts/eureka-room-report.cjs +419 -0
  1446. package/scripts/explain-decision-command.cjs +499 -0
  1447. package/scripts/export-pinecone-embeddings.cjs +516 -0
  1448. package/scripts/extract-room-intelligence.cjs +714 -0
  1449. package/scripts/fetch-brain-baseline.cjs +190 -0
  1450. package/scripts/fetch-brain-baseline.py +360 -0
  1451. package/scripts/feynman-minto-guardian.cjs +672 -0
  1452. package/scripts/feynman-timeline-refresh-command.cjs +270 -0
  1453. package/scripts/file-asset +253 -0
  1454. package/scripts/fire-sensor-event.cjs +83 -0
  1455. package/scripts/first-install-router.cjs +968 -0
  1456. package/scripts/frontmatter-schema-validator.cjs +339 -0
  1457. package/scripts/fts-index-drain.cjs +221 -0
  1458. package/scripts/generate-chat-embed.cjs +179 -0
  1459. package/scripts/generate-deck.cjs +322 -0
  1460. package/scripts/generate-export.cjs +474 -0
  1461. package/scripts/generate-hub.cjs +2553 -0
  1462. package/scripts/generate-lobby.cjs +718 -0
  1463. package/scripts/generate-presentation.cjs +988 -0
  1464. package/scripts/generate-presentation.test.cjs +405 -0
  1465. package/scripts/generate-section-intelligence.cjs +330 -0
  1466. package/scripts/generate-snapshot.cjs +1410 -0
  1467. package/scripts/generate-standalone +159 -0
  1468. package/scripts/git-ops +211 -0
  1469. package/scripts/graph-heal-pipeline.cjs +321 -0
  1470. package/scripts/gsd-artifact-graph-hook.cjs +219 -0
  1471. package/scripts/gsd-graph-derive-drain.cjs +571 -0
  1472. package/scripts/gsd-graph-derive-sweep.cjs +195 -0
  1473. package/scripts/heal-command.cjs +1365 -0
  1474. package/scripts/hedge-refit-pipeline.cjs +207 -0
  1475. package/scripts/help-renderer.cjs +303 -0
  1476. package/scripts/hmi-compliance-poll.cjs +451 -0
  1477. package/scripts/hmi-status-command.cjs +510 -0
  1478. package/scripts/hooked-rescore-117.cjs +224 -0
  1479. package/scripts/hooks/pre-commit +533 -0
  1480. package/scripts/hooks/pre-commit-room-minto-guard.cmd +43 -0
  1481. package/scripts/hooks/pre-commit-room-minto-guard.sh +533 -0
  1482. package/scripts/hsi-to-graph.cjs +194 -0
  1483. package/scripts/huji-batch.cjs +821 -0
  1484. package/scripts/huji-eval.cjs +1158 -0
  1485. package/scripts/huji-intake.cjs +563 -0
  1486. package/scripts/huji-pin-smoketest.cjs +454 -0
  1487. package/scripts/huji-run-one-async.cjs +305 -0
  1488. package/scripts/huji-run-one.cjs +671 -0
  1489. package/scripts/install-pre-commit.sh +113 -0
  1490. package/scripts/intent-classifier +106 -0
  1491. package/scripts/intent-classifier.cjs +3620 -0
  1492. package/scripts/interpret-whitespace.cjs +691 -0
  1493. package/scripts/jtbd-command.cjs +810 -0
  1494. package/scripts/jtbd-update.cjs +299 -0
  1495. package/scripts/label-topic-forest.cjs +667 -0
  1496. package/scripts/learn-from-usage +168 -0
  1497. package/scripts/lib/__pycache__/ensure_ml_deps.cpython-312.pyc +0 -0
  1498. package/scripts/lib/ensure_ml_deps.py +97 -0
  1499. package/scripts/load-embeddings-into-neo4j.cjs +569 -0
  1500. package/scripts/memory-artifact-graph-hook.cjs +191 -0
  1501. package/scripts/memory-command.cjs +687 -0
  1502. package/scripts/memory-completion-detector.cjs +217 -0
  1503. package/scripts/memory-lifecycle.cjs +620 -0
  1504. package/scripts/memory-resume-nudge.cjs +233 -0
  1505. package/scripts/methodology-ingest-local.sh +36 -0
  1506. package/scripts/migrate-brain-mcp-from-http-to-stdio.cjs +190 -0
  1507. package/scripts/migrate-brain-mcp-from-http-to-stdio.test.cjs +242 -0
  1508. package/scripts/migrate-hats-to-roomdb.cjs +217 -0
  1509. package/scripts/migrate-lazygraph.cjs +250 -0
  1510. package/scripts/migrate-minto-schema-v88.cjs +296 -0
  1511. package/scripts/migrate-plugin-root-refs.cjs +168 -0
  1512. package/scripts/migrate-room-sections-v275.cjs +556 -0
  1513. package/scripts/migrate-rooms +417 -0
  1514. package/scripts/migrate-skill-frontmatter.cjs +472 -0
  1515. package/scripts/migrate-stale-user-settings.cjs +296 -0
  1516. package/scripts/migrate-telemetry-v1.cjs +384 -0
  1517. package/scripts/migrate-telemetry-v1.test.cjs +356 -0
  1518. package/scripts/minto-debouncer.cjs +428 -0
  1519. package/scripts/mos-status.cjs +622 -0
  1520. package/scripts/mva-detect.cjs +229 -0
  1521. package/scripts/mva-run.cjs +36 -0
  1522. package/scripts/on-agent-complete +163 -0
  1523. package/scripts/on-cwd-changed +149 -0
  1524. package/scripts/on-file-changed +68 -0
  1525. package/scripts/on-stop +617 -0
  1526. package/scripts/on-task-complete +194 -0
  1527. package/scripts/operator-command.cjs +858 -0
  1528. package/scripts/operator-update.cjs +308 -0
  1529. package/scripts/part8-egress-guard-hook.cjs +229 -0
  1530. package/scripts/pipeline-command.cjs +271 -0
  1531. package/scripts/post-compact +383 -0
  1532. package/scripts/post-update-activation.cjs +339 -0
  1533. package/scripts/post-write +283 -0
  1534. package/scripts/power-demo-prompt.md +621 -0
  1535. package/scripts/pre-compact +273 -0
  1536. package/scripts/preflight-auto-explore.cjs +314 -0
  1537. package/scripts/preflight-doctor.cjs +225 -0
  1538. package/scripts/preflight-release-drift.cjs +168 -0
  1539. package/scripts/preflight-tension-surface.cjs +414 -0
  1540. package/scripts/probe-brain-contract.cjs +502 -0
  1541. package/scripts/publish-ops +346 -0
  1542. package/scripts/query-efficiency-telemetry.cjs +364 -0
  1543. package/scripts/query-semantic-scholar.cjs +545 -0
  1544. package/scripts/reapply-modifications +49 -0
  1545. package/scripts/reassign-primary.cjs +125 -0
  1546. package/scripts/recompile-room-references.cjs +601 -0
  1547. package/scripts/release-beta-preflight.sh +69 -0
  1548. package/scripts/render-pdf +588 -0
  1549. package/scripts/render-viz +190 -0
  1550. package/scripts/research-speaker +239 -0
  1551. package/scripts/resolve-room +204 -0
  1552. package/scripts/restore-post-compact-context.cjs +320 -0
  1553. package/scripts/room-auto-create-nudge.cjs +136 -0
  1554. package/scripts/room-naming-selector.cjs +201 -0
  1555. package/scripts/room-registry +767 -0
  1556. package/scripts/rs-discovery-engine.cjs +725 -0
  1557. package/scripts/rs-engine.py +2073 -0
  1558. package/scripts/rs-experts-command.cjs +300 -0
  1559. package/scripts/rs-explain-command.cjs +323 -0
  1560. package/scripts/rs-fetch-command.cjs +170 -0
  1561. package/scripts/rs-thesis-command.cjs +203 -0
  1562. package/scripts/rs-vector-bridge.cjs +410 -0
  1563. package/scripts/run-harness.cjs +890 -0
  1564. package/scripts/scout-cadence-guard.cjs +323 -0
  1565. package/scripts/scout-cadence-runner.cjs +509 -0
  1566. package/scripts/scout-telemetry-aggregator.cjs +268 -0
  1567. package/scripts/sealed-walker.py +96 -0
  1568. package/scripts/seed-brain-commands.cjs +324 -0
  1569. package/scripts/self-update +105 -0
  1570. package/scripts/sentinel-deadline-monitor +241 -0
  1571. package/scripts/sentinel-health-check +198 -0
  1572. package/scripts/sentinel-snapshot +42 -0
  1573. package/scripts/serve-dashboard +103 -0
  1574. package/scripts/serve-dashboard-live +932 -0
  1575. package/scripts/serve-presentation +97 -0
  1576. package/scripts/serve-wiki +90 -0
  1577. package/scripts/session-end-presence.cjs +79 -0
  1578. package/scripts/session-start +1943 -0
  1579. package/scripts/sessionstart-coordinator.cjs +249 -0
  1580. package/scripts/sessionstart-npm-reconcile.cjs +111 -0
  1581. package/scripts/sessionstart-post-update-preflight.cjs +303 -0
  1582. package/scripts/sessionstart-reference-now-seed.cjs +137 -0
  1583. package/scripts/setup-hooks.sh +112 -0
  1584. package/scripts/skillopt-codereview.cjs +830 -0
  1585. package/scripts/skillopt-eval.cjs +519 -0
  1586. package/scripts/skillopt-funnel.cjs +1076 -0
  1587. package/scripts/skillopt-genqueries.cjs +571 -0
  1588. package/scripts/skillopt-inventory.cjs +319 -0
  1589. package/scripts/skillopt-merge.cjs +656 -0
  1590. package/scripts/skillopt-triggerloop.cjs +1126 -0
  1591. package/scripts/soft-alias-runner.cjs +116 -0
  1592. package/scripts/stamp-artifact-write.cjs +276 -0
  1593. package/scripts/stamp-firing-block.cjs +325 -0
  1594. package/scripts/state-write.cjs +97 -0
  1595. package/scripts/statusline-fallback-echo.cjs +297 -0
  1596. package/scripts/statusline-mos +116 -0
  1597. package/scripts/statusline-mos-dispatch +127 -0
  1598. package/scripts/suggest-next-command.cjs +416 -0
  1599. package/scripts/sweep-skill-descriptions.cjs +263 -0
  1600. package/scripts/sync-rooms-graph +264 -0
  1601. package/scripts/telemetry-command-invocation.cjs +120 -0
  1602. package/scripts/test-doctor-recovery.cjs +242 -0
  1603. package/scripts/test-fresh-install.md +140 -0
  1604. package/scripts/track-analytics +156 -0
  1605. package/scripts/transcribe-audio +148 -0
  1606. package/scripts/transcript-ingest.cjs +225 -0
  1607. package/scripts/update-icm-index +153 -0
  1608. package/scripts/v182-brain-optimize.cypher +1191 -0
  1609. package/scripts/validate-model-profiles +93 -0
  1610. package/scripts/vault-content-reformatter.cjs +581 -0
  1611. package/scripts/vault-export-orchestrator.cjs +400 -0
  1612. package/scripts/vault-footer-injector.cjs +315 -0
  1613. package/scripts/vault-import.cjs +839 -0
  1614. package/scripts/vault-integration-test.cjs +318 -0
  1615. package/scripts/vault-regenerate-all.cjs +216 -0
  1616. package/scripts/vault-regenerate-all.test.cjs +138 -0
  1617. package/scripts/vault-rules-generator.cjs +220 -0
  1618. package/scripts/vault-section-minto-generator.cjs +1642 -0
  1619. package/scripts/vault-section-minto-generator.integration.test.cjs +576 -0
  1620. package/scripts/vault-section-minto-generator.test.cjs +291 -0
  1621. package/scripts/vault-section-state-generator.cjs +385 -0
  1622. package/scripts/vault-welcome-generator.cjs +403 -0
  1623. package/scripts/vault-wikilink-injector.cjs +449 -0
  1624. package/scripts/verify-phase-132.cjs +338 -0
  1625. package/scripts/verify-release +744 -0
  1626. package/scripts/whitespace-command.cjs +831 -0
  1627. package/scripts/whitespace-to-brain.cjs +384 -0
  1628. package/scripts/whitespace-to-graph.cjs +220 -0
  1629. package/scripts/wikilink-batch.cjs +102 -0
  1630. package/scripts/wikilink-file.cjs +139 -0
  1631. package/scripts/write-bridge-artifacts.cjs +140 -0
  1632. package/scripts/write-scope-check +12 -0
  1633. package/scripts/write-scope-check.cjs +461 -0
  1634. package/scripts/write-whitespace-sections.cjs +483 -0
  1635. package/settings.json +7 -0
  1636. package/skills/act/SKILL.md +523 -0
  1637. package/skills/admin/SKILL.md +437 -0
  1638. package/skills/agentshield/SKILL.md +50 -0
  1639. package/skills/analyze-needs/SKILL.md +74 -0
  1640. package/skills/analyze-systems/SKILL.md +69 -0
  1641. package/skills/analyze-timing/SKILL.md +94 -0
  1642. package/skills/auto-explore/SKILL.md +84 -0
  1643. package/skills/beautiful-question/SKILL.md +72 -0
  1644. package/skills/bono/SKILL.md +146 -0
  1645. package/skills/brain-connector/SKILL.md +206 -0
  1646. package/skills/brain-derive/SKILL.md +140 -0
  1647. package/skills/build-knowledge/SKILL.md +118 -0
  1648. package/skills/build-thesis/SKILL.md +90 -0
  1649. package/skills/causal/SKILL.md +265 -0
  1650. package/skills/challenge-assumptions/SKILL.md +74 -0
  1651. package/skills/client-discovery-interview/SKILL.md +94 -0
  1652. package/skills/client-discovery-interview/discovery-scaffold.json +41 -0
  1653. package/skills/client-discovery-interview/question-bank.md +149 -0
  1654. package/skills/compare-ventures/SKILL.md +113 -0
  1655. package/skills/context-engine/SKILL.md +169 -0
  1656. package/skills/conversation-mode/SKILL.md +189 -0
  1657. package/skills/correct-reference-now/SKILL.md +86 -0
  1658. package/skills/dashboard/SKILL.md +142 -0
  1659. package/skills/deck/SKILL.md +113 -0
  1660. package/skills/deep-grade/SKILL.md +210 -0
  1661. package/skills/diagnose/SKILL.md +128 -0
  1662. package/skills/diagnostics/SKILL.md +193 -0
  1663. package/skills/dial-memory-refresh/SKILL.md +116 -0
  1664. package/skills/diffusion/SKILL.md +125 -0
  1665. package/skills/discover/SKILL.md +216 -0
  1666. package/skills/doctor/SKILL.md +269 -0
  1667. package/skills/dogfood-flush/SKILL.md +133 -0
  1668. package/skills/dominant-designs/SKILL.md +72 -0
  1669. package/skills/eureka/SKILL.md +313 -0
  1670. package/skills/explain-decision/SKILL.md +138 -0
  1671. package/skills/explore-domains/SKILL.md +74 -0
  1672. package/skills/explore-futures/SKILL.md +64 -0
  1673. package/skills/explore-opportunity/SKILL.md +168 -0
  1674. package/skills/explore-trends/SKILL.md +74 -0
  1675. package/skills/export/SKILL.md +126 -0
  1676. package/skills/feynman-timeline-refresh/SKILL.md +105 -0
  1677. package/skills/file-meeting/SKILL.md +1190 -0
  1678. package/skills/find-analogies/SKILL.md +360 -0
  1679. package/skills/find-bottlenecks/SKILL.md +104 -0
  1680. package/skills/find-connections/SKILL.md +108 -0
  1681. package/skills/funding/SKILL.md +110 -0
  1682. package/skills/futures/SKILL.md +124 -0
  1683. package/skills/grade/SKILL.md +252 -0
  1684. package/skills/grade-grant/SKILL.md +293 -0
  1685. package/skills/graph/SKILL.md +216 -0
  1686. package/skills/hat-briefing/SKILL.md +160 -0
  1687. package/skills/heal/SKILL.md +69 -0
  1688. package/skills/help/SKILL.md +172 -0
  1689. package/skills/hmi-status/SKILL.md +73 -0
  1690. package/skills/ignite/SKILL.md +225 -0
  1691. package/skills/ingest-methodology/SKILL.md +125 -0
  1692. package/skills/intel-pipeline/SKILL.md +128 -0
  1693. package/skills/intelligence-orchestrator/SKILL.md +262 -0
  1694. package/skills/jtbd/SKILL.md +261 -0
  1695. package/skills/larry-personality/SKILL.md +571 -0
  1696. package/skills/larry-personality/framework-chains.md +92 -0
  1697. package/skills/larry-personality/mode-engine.md +185 -0
  1698. package/skills/leadership/SKILL.md +109 -0
  1699. package/skills/lean-canvas/SKILL.md +70 -0
  1700. package/skills/macro-trends/SKILL.md +84 -0
  1701. package/skills/map-unknowns/SKILL.md +72 -0
  1702. package/skills/memory/SKILL.md +212 -0
  1703. package/skills/memory-cortex-reach/SKILL.md +80 -0
  1704. package/skills/models/SKILL.md +197 -0
  1705. package/skills/mos/SKILL.md +160 -0
  1706. package/skills/mos-deck-engine/SKILL.md +329 -0
  1707. package/skills/mos-reason/SKILL.md +484 -0
  1708. package/skills/mullins/SKILL.md +151 -0
  1709. package/skills/mullins-scaffold/SKILL.md +132 -0
  1710. package/skills/mullins-scaffold/scaffold.json +223 -0
  1711. package/skills/mva-brief/SKILL.md +87 -0
  1712. package/skills/mva-option/SKILL.md +120 -0
  1713. package/skills/mva-pipeline/SKILL.md +158 -0
  1714. package/skills/mva-report/SKILL.md +79 -0
  1715. package/skills/new-project/SKILL.md +629 -0
  1716. package/skills/new-surface/SKILL.md +112 -0
  1717. package/skills/onboard/SKILL.md +485 -0
  1718. package/skills/operator/SKILL.md +167 -0
  1719. package/skills/opportunities/SKILL.md +171 -0
  1720. package/skills/organize/SKILL.md +69 -0
  1721. package/skills/persona/SKILL.md +272 -0
  1722. package/skills/pipeline/SKILL.md +164 -0
  1723. package/skills/present/SKILL.md +123 -0
  1724. package/skills/publish/SKILL.md +224 -0
  1725. package/skills/pws-brain/SKILL.md +152 -0
  1726. package/skills/pws-methodology/SKILL.md +92 -0
  1727. package/skills/qualify-opportunity/SKILL.md +111 -0
  1728. package/skills/query/SKILL.md +68 -0
  1729. package/skills/radar/SKILL.md +106 -0
  1730. package/skills/reanalyze/SKILL.md +122 -0
  1731. package/skills/research/SKILL.md +462 -0
  1732. package/skills/room/SKILL.md +381 -0
  1733. package/skills/room-passive/SKILL.md +174 -0
  1734. package/skills/room-proactive/SKILL.md +344 -0
  1735. package/skills/rooms/SKILL.md +678 -0
  1736. package/skills/root-cause/SKILL.md +73 -0
  1737. package/skills/rs-experts/SKILL.md +151 -0
  1738. package/skills/rs-explain/SKILL.md +130 -0
  1739. package/skills/rs-fetch/SKILL.md +115 -0
  1740. package/skills/rs-thesis/SKILL.md +129 -0
  1741. package/skills/scenario-plan/SKILL.md +76 -0
  1742. package/skills/scheduled-tasks/SKILL.md +301 -0
  1743. package/skills/score-innovation/SKILL.md +74 -0
  1744. package/skills/scout/SKILL.md +370 -0
  1745. package/skills/setup/SKILL.md +654 -0
  1746. package/skills/show/SKILL.md +68 -0
  1747. package/skills/skill/SKILL.md +104 -0
  1748. package/skills/snapshot/SKILL.md +178 -0
  1749. package/skills/speakers/SKILL.md +116 -0
  1750. package/skills/splash/SKILL.md +54 -0
  1751. package/skills/stance/SKILL.md +89 -0
  1752. package/skills/status/SKILL.md +107 -0
  1753. package/skills/structure-argument/SKILL.md +78 -0
  1754. package/skills/suggest-next/SKILL.md +137 -0
  1755. package/skills/systems-thinking/SKILL.md +220 -0
  1756. package/skills/think-hats/SKILL.md +82 -0
  1757. package/skills/trending-to-absurd/SKILL.md +100 -0
  1758. package/skills/ui-system/SKILL.md +474 -0
  1759. package/skills/ui-system/design-system/M-OS-DESIGN-SYSTEM.md +273 -0
  1760. package/skills/ui-system/design-system/SPEC.md +72 -0
  1761. package/skills/ui-system/design-system/image-prompt-style.md +29 -0
  1762. package/skills/ui-system/design-system/mos-design-system.css +240 -0
  1763. package/skills/ui-system/rules/design-system.md +60 -0
  1764. package/skills/ui-system/rules/dual-palette.md +156 -0
  1765. package/skills/ui-system/rules/glyph-disambiguation.md +171 -0
  1766. package/skills/ui-system/rules/shape-f-zero-and-six.md +169 -0
  1767. package/skills/update/SKILL.md +254 -0
  1768. package/skills/user-needs/SKILL.md +73 -0
  1769. package/skills/validate/SKILL.md +73 -0
  1770. package/skills/value-proposition/SKILL.md +141 -0
  1771. package/skills/vault/SKILL.md +326 -0
  1772. package/skills/visualize/SKILL.md +69 -0
  1773. package/skills/whitespace/SKILL.md +539 -0
  1774. package/skills/wiki/SKILL.md +94 -0
  1775. package/templates/_base.html +20 -0
  1776. package/templates/chat-panel.html +205 -0
  1777. package/templates/constellation-chat-hooks.js +89 -0
  1778. package/templates/constellation.html +1272 -0
  1779. package/templates/destijl-base.css +268 -0
  1780. package/templates/discovery/discovery-brief-template.md +97 -0
  1781. package/templates/due-diligence.html +49 -0
  1782. package/templates/executive-summary.html +40 -0
  1783. package/templates/icm/CLAUDE.md +31 -0
  1784. package/templates/icm/GROUP-CLAUDE.md +33 -0
  1785. package/templates/icm/INDEX.md +26 -0
  1786. package/templates/import/stage-contracts/01-ingest.md +34 -0
  1787. package/templates/import/stage-contracts/02-classify.md +48 -0
  1788. package/templates/import/stage-contracts/03-route.md +42 -0
  1789. package/templates/import/stage-contracts/04-enrich.md +40 -0
  1790. package/templates/investment-thesis.html +25 -0
  1791. package/templates/meeting-report.html +361 -0
  1792. package/templates/presentation/dashboard.html +1150 -0
  1793. package/templates/presentation/deck.html +540 -0
  1794. package/templates/presentation/diagrams.html +437 -0
  1795. package/templates/presentation/graph.html +391 -0
  1796. package/templates/presentation/insights.html +548 -0
  1797. package/templates/presentation/wiki.html +533 -0
  1798. package/templates/profile.html +136 -0
  1799. package/templates/room-context/last-session.md +56 -0
  1800. package/templates/room-context/methodology-history.md +28 -0
  1801. package/templates/room-context/rejection-log.md +25 -0
  1802. package/templates/room-context/weekly-digest.md +54 -0
  1803. package/templates/room-skeleton/MINTO.md.tmpl +19 -0
  1804. package/templates/room-skeleton/ROOM.md.identity.tmpl +13 -0
  1805. package/templates/room-skeleton/ROOM.md.section.tmpl +33 -0
  1806. package/templates/room-skeleton/STATE.md.tmpl +33 -0
  1807. package/templates/room-skeleton/USER.md.tmpl +23 -0
  1808. package/templates/room-skeleton/references/.gitkeep +0 -0
  1809. package/templates/room-skeleton/references/SECTION-SCHEMA.md +213 -0
  1810. package/templates/room-skeleton/references/SUB-SCHEMAS.md +91 -0
  1811. package/templates/room-skeleton/section-contracts/.gitkeep +0 -0
  1812. package/templates/room-skeleton/section-contracts/business-model.md +32 -0
  1813. package/templates/room-skeleton/section-contracts/competitive-analysis.md +33 -0
  1814. package/templates/room-skeleton/section-contracts/financial-model.md +30 -0
  1815. package/templates/room-skeleton/section-contracts/funding.md +41 -0
  1816. package/templates/room-skeleton/section-contracts/legal-ip.md +30 -0
  1817. package/templates/room-skeleton/section-contracts/market-analysis.md +32 -0
  1818. package/templates/room-skeleton/section-contracts/opportunity-bank.md +39 -0
  1819. package/templates/room-skeleton/section-contracts/problem-definition.md +32 -0
  1820. package/templates/room-skeleton/section-contracts/solution-design.md +36 -0
  1821. package/templates/room-skeleton/section-contracts/strategy.md +35 -0
  1822. package/templates/room-skeleton/section-contracts/team-execution.md +36 -0
  1823. package/templates/shared.css +427 -0
  1824. package/templates/showcase/blueprint.html +432 -0
  1825. package/templates/showcase/library.html +571 -0
  1826. package/templates/showcase/narrative.html +361 -0
  1827. package/templates/showcase/overview.html +468 -0
  1828. package/templates/showcase/synthesis.html +473 -0
  1829. package/templates/signature-footer.html +34 -0
@@ -0,0 +1,2621 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Brain HTTP Client -- calls the Brain HTTP server. Default is
5
+ * `https://theo-mcp.onrender.com`, a BARE origin (no path, no trailing
6
+ * slash): this client appends `/mcp` for tool calls and `/register` for the
7
+ * key ladder, so a `/mcp` suffix here would yield `/mcp/mcp` and a trailing
8
+ * slash a double slash, both 404 and both rendered as "Brain unreachable"
9
+ * rather than a configuration error. Override via the MINDRIAN_BRAIN_URL env
10
+ * var, the per-install rollback lever; that lever (and the fleet-wide
11
+ * patch-release revert of this line) is valid only while the previous
12
+ * origin is still running, because pointing it at a suspended service is a
13
+ * second outage stacked on the first, arriving exactly when the operator is
14
+ * under pressure.
15
+ *
16
+ * Replaces direct MCP tool calls (mcp__neo4j-brain__*, mcp__pinecone-brain__*)
17
+ * with a single HTTP API that handles the graph backend behind one key.
18
+ *
19
+ * Falls back gracefully:
20
+ * 1. If MINDRIAN_BRAIN_KEY is set → calls Brain API
21
+ * 2. If Brain API returns a quota/capacity error → retries once
22
+ * 3. If no key → returns null (Tier 0, no Brain)
23
+ *
24
+ * Usage in commands/skills:
25
+ * const brain = require('./brain-client.cjs');
26
+ * const result = await brain.query('MATCH (f:Framework) RETURN f.name LIMIT 5');
27
+ * const result = await brain.search('innovation framework');
28
+ * const schema = await brain.schema();
29
+ */
30
+
31
+ // Phase 339, 2026-09-03 (FLIP-10, D-13 locked): the flip. BARE origin only --
32
+ // no path, no trailing slash -- because `${BRAIN_URL}/mcp` and
33
+ // `${BRAIN_URL}/register` are both formed by appending below. This does NOT
34
+ // change: input arg keys (byte-for-byte unchanged, tests/test-247-contract-
35
+ // client.cjs stays green untouched), the shim server key `mindrian-brain`,
36
+ // BRAIN_TOOL_MATCHER / hooks/hooks.json for the CLI surface, or the
37
+ // brain_query {rows, diagnostics} shape adaptation (already shipped at
38
+ // :927-945, commit 21fdd7bc). MINDRIAN_BRAIN_URL remains the per-install
39
+ // override and the one-line rollback lever.
40
+ const BRAIN_URL = process.env.MINDRIAN_BRAIN_URL || 'https://theo-mcp.onrender.com';
41
+
42
+ // Per-request hard timeout for every Brain HTTP call (init handshake + tool
43
+ // calls). Node's global fetch() has NO default timeout, so without this a
44
+ // slow/wedged Brain hangs the calling /mos: command indefinitely. The Render
45
+ // service answers in ~1-2s normally; 20s is a generous-but-bounded ceiling.
46
+ // Override via MINDRIAN_BRAIN_TIMEOUT_MS (brain-router wants ~2000 for Tier 3).
47
+ const BRAIN_REQUEST_TIMEOUT_MS = Number(process.env.MINDRIAN_BRAIN_TIMEOUT_MS) || 20000;
48
+
49
+ // Phase 250-01 (AVAIL-02, navigator ruling 2026-08-10): bounded transport
50
+ // retry budget around the single HTTP dispatch seam every Brain tool flows
51
+ // through (callTool()'s tools/call POST below). Retries ONLY transport-class
52
+ // outcomes -- network errors and 5xx responses -- BEFORE a blip ever becomes
53
+ // an unreachable refusal. NEVER retries 401/403 (validation-class, mapped to
54
+ // their own sentinels) or any other status; those are zero-retry by design
55
+ // (retrying an auth failure hammers auth, taxonomy-wrong per the data4sci
56
+ // four-class error taxonomy). Defensive numeric-env convention (repo-wide,
57
+ // e.g. BRAIN_REQUEST_TIMEOUT_MS above): an invalid override falls back to
58
+ // the default rather than throwing or disabling the feature silently.
59
+ const RETRY_MAX_DEFAULT = 2; // 2 retries = 3 attempts total.
60
+ const RETRY_BASE_MS_DEFAULT = 300; // 300ms, then 900ms (base * 3^attempt).
61
+
62
+ function _envNonNegativeInt(name, def) {
63
+ const raw = process.env[name];
64
+ if (raw === undefined || raw === '') return def;
65
+ const n = Number(raw);
66
+ return (Number.isFinite(n) && n >= 0) ? Math.floor(n) : def;
67
+ }
68
+
69
+ function _retryMax() {
70
+ return _envNonNegativeInt('MINDRIAN_BRAIN_RETRY_MAX', RETRY_MAX_DEFAULT);
71
+ }
72
+
73
+ function _retryBaseMs() {
74
+ return _envNonNegativeInt('MINDRIAN_BRAIN_RETRY_BASE_MS', RETRY_BASE_MS_DEFAULT);
75
+ }
76
+
77
+ function _sleep(ms) {
78
+ return new Promise((resolve) => setTimeout(resolve, ms));
79
+ }
80
+
81
+ // Phase 259 (TRUST-01, D-01/D-02/D-03): a dedicated retry budget for HTTP
82
+ // 429 (rate limited), separate from AVAIL-02's RETRY_MAX_DEFAULT /
83
+ // RETRY_BASE_MS_DEFAULT above. Deliberately NOT reusing
84
+ // MINDRIAN_BRAIN_RETRY_MAX / MINDRIAN_BRAIN_RETRY_BASE_MS: those are the
85
+ // operator knob for transport blips (network errors, 5xx), and sharing
86
+ // them would let an operator who sets MINDRIAN_BRAIN_RETRY_MAX=0 for
87
+ // fast-fail silently also disable Retry-After honoring, regressing into
88
+ // the exact bug this phase closes. D-01/D-02's schedule (3 retries,
89
+ // 500/1000/2000ms) is also arithmetically distinct from AVAIL-02's (2
90
+ // retries, base * 3^attempt).
91
+ const RATE_LIMIT_RETRY_MAX_DEFAULT = 3; // 3 retries = 4 attempts total (D-01).
92
+ const RATE_LIMIT_BASE_MS_DEFAULT = 500; // 500ms, then 1000ms, then 2000ms (base * 2^attempt) (D-02).
93
+
94
+ function _rateLimitRetryMax() {
95
+ return _envNonNegativeInt('MINDRIAN_BRAIN_RATELIMIT_RETRY_MAX', RATE_LIMIT_RETRY_MAX_DEFAULT);
96
+ }
97
+
98
+ function _rateLimitBaseMs() {
99
+ return _envNonNegativeInt('MINDRIAN_BRAIN_RATELIMIT_BASE_MS', RATE_LIMIT_BASE_MS_DEFAULT);
100
+ }
101
+
102
+ // OQ-4: an explicit, operator-set, self-declaring ceiling on the wait --
103
+ // UNSET by default, which is what ships D-01 ("honor Retry-After exactly")
104
+ // literally, never a silent Math.min. The Brain's own limiter bounds
105
+ // Retry-After to its window (60s at defaults, verified against
106
+ // ProblemsWorthSolving-Brain src/http/rate-limit.mjs), so an operator
107
+ // only needs this when self-hosting a differently-configured limiter.
108
+ function _rateLimitMaxWaitMs() {
109
+ const raw = process.env.MINDRIAN_BRAIN_RATELIMIT_MAX_WAIT_MS;
110
+ if (raw === undefined || raw === '') return null;
111
+ const n = Number(raw);
112
+ return (Number.isFinite(n) && n >= 0) ? Math.floor(n) : null;
113
+ }
114
+
115
+ // Phase 259 (TRUST-01): parse a Retry-After header value into milliseconds.
116
+ // Returns null when the header is absent or unusable, meaning "fall back
117
+ // to the D-02 exponential schedule". Handles both RFC 9110 forms: the
118
+ // delay-seconds integer form (the Brain's own live wire shape, verified
119
+ // this session against its deployed limiter) and the HTTP-date form
120
+ // (defensive only -- no known live caller sends it). Never lets NaN, a
121
+ // negative, or Infinity reach _sleep: _sleep(NaN) resolves on the very
122
+ // next tick, which is a silent zero-wait that would hammer the limiter.
123
+ // RFC 7231 IMF-fixdate, the exact shape Date.prototype.toUTCString() emits
124
+ // (e.g. "Wed, 21 Oct 2015 07:28:00 GMT") -- the only HTTP-date form worth
125
+ // trusting. Gating on this BEFORE calling Date.parse matters: V8's Date.parse
126
+ // falls back to a lenient legacy-format heuristic for anything that isn't a
127
+ // recognized standard form, and mis-parses garbage like "-5" or "1.5" into
128
+ // real (wrong) timestamps instead of NaN (verified this session).
129
+ const HTTP_DATE_RE = /^[A-Za-z]{3}, \d{2} [A-Za-z]{3} \d{4} \d{2}:\d{2}:\d{2} GMT$/;
130
+
131
+ function _parseRetryAfterMs(raw) {
132
+ if (typeof raw !== 'string') return null;
133
+ const trimmed = raw.trim();
134
+ if (trimmed === '') return null;
135
+ if (/^\d+$/.test(trimmed)) {
136
+ const seconds = Number(trimmed);
137
+ if (!Number.isFinite(seconds) || seconds < 0) return null;
138
+ return seconds * 1000;
139
+ }
140
+ if (!HTTP_DATE_RE.test(trimmed)) return null;
141
+ const parsedDate = Date.parse(trimmed);
142
+ if (!Number.isFinite(parsedDate)) return null;
143
+ const deltaMs = parsedDate - Date.now();
144
+ return deltaMs > 0 ? deltaMs : 0;
145
+ }
146
+
147
+ // Phase 259 (TRUST-01): pure wait-schedule function -- no I/O, no clock
148
+ // beyond the Date.now() inside _parseRetryAfterMs. attemptIndex is 0-based
149
+ // over the RETRIES (0 -> first retry, 1 -> second, 2 -> third), matching
150
+ // Pattern 1 in 259-RESEARCH.md.
151
+ function _rateLimitWaitMs(attemptIndex, retryAfterHeader, baseMs) {
152
+ const parsed = _parseRetryAfterMs(retryAfterHeader);
153
+ const computed = parsed !== null ? parsed : baseMs * Math.pow(2, attemptIndex);
154
+ const ceiling = _rateLimitMaxWaitMs();
155
+ if (ceiling !== null && computed > ceiling) return ceiling;
156
+ return computed;
157
+ }
158
+
159
+ // Phase 87-07 (CASCADE-06): Brain session cache with 5-minute TTL.
160
+ // Every callTool() previously re-ran the `initialize` handshake (~1 network
161
+ // round-trip). With a long-lived MCP server this is wasted work -- sessions
162
+ // live longer than the ~60s transport timeout. Cache the initialized
163
+ // sessionId (keyed by api-key-hash) for 5 minutes.
164
+ //
165
+ // R-87-07-RACE (audit): two concurrent callTool() invocations with the same
166
+ // api_key previously both saw a cache miss, both initialized, and the second
167
+ // overwrote the first -- one of the two initialize handshakes was wasted.
168
+ // Fix: cache the init *Promise*, not the resolved session. The first caller
169
+ // stores { promise: initSession(apiKey), expiresAt }; concurrent callers
170
+ // within the TTL `await entry.promise`. On rejection we remove the entry so
171
+ // the next caller re-initializes fresh.
172
+ //
173
+ // Hash: sha256 truncated to 16 hex chars (64 bits of key space, zero realistic
174
+ // collision). A cheaper non-crypto hash was considered but its narrower int
175
+ // space has non-zero collision probability once the design extends across
176
+ // users; sha256 is effectively free at these volumes and eliminates the
177
+ // concern entirely (R-87-07-RACE).
178
+ const crypto = require('node:crypto');
179
+ const fs = require('node:fs');
180
+ const path = require('node:path');
181
+ // Phase 123 Plan-07: getApiKey() delegates to the single Brain-key resolver.
182
+ // The legacy inline 3-path lookup (env -> CWD .env -> ~/.mindrian.env) is gone;
183
+ // the resolver does env -> ~/.mindrian.env -> CWD .env (D-31 order) + SEC-02
184
+ // POSIX permission check + explicit reason strings. See HARNESS-123-15.
185
+ const { resolveBrainKey } = require('./resolve-brain-key.cjs');
186
+ const SESSION_TTL_MS = 5 * 60 * 1000; // 5 minutes
187
+ /** @type {Map<string, {promise: Promise<string>, expiresAt: number}>} */
188
+ const sessionCache = new Map();
189
+
190
+ function _hashKey(key) {
191
+ return crypto.createHash('sha256').update(String(key)).digest('hex').slice(0, 16);
192
+ }
193
+
194
+ /**
195
+ * SEC-01: Sanitize any user-origin string before interpolation into a
196
+ * Cypher query. Whitelist from 87-CONTEXT.md lines 121-127:
197
+ * [a-zA-Z0-9 ._-]
198
+ * Every other char (including `"`, `'`, backtick, newline, `{`, `}`, `$`,
199
+ * `\`, `;`, `/`, `*`) is stripped. Null/undefined return ''. Non-strings
200
+ * are coerced via String() defensively so the caller never crashes.
201
+ *
202
+ * This replaces the legacy single-quote-escape pattern that only
203
+ * escaped one metacharacter (double-quote) and was trivially bypassable
204
+ * via backticks, newlines, `${...}` expansions, or Cypher comments.
205
+ *
206
+ * @param {*} value
207
+ * @returns {string}
208
+ */
209
+ function sanitizeCypherInput(value) {
210
+ if (value === null || value === undefined) return '';
211
+ if (typeof value !== 'string') {
212
+ try { value = String(value); } catch (_e) { return ''; }
213
+ }
214
+ return value.replace(/[^a-zA-Z0-9 ._-]/g, '');
215
+ }
216
+
217
+ /**
218
+ * SEC-02: Refuse to load a Brain API key from a .env file whose permissions
219
+ * expose it to group or world readers. Unix semantics only -- on Windows
220
+ * POSIX mode bits are not meaningful for NTFS ACLs, so we return true and
221
+ * warn once per process.
222
+ *
223
+ * mode & 0o077 !== 0 => any group/world bit is set => reject
224
+ * mode 0o600 (-rw-------) and 0o400 (-r--------) pass; 0o644, 0o664 fail.
225
+ *
226
+ * On stat failure we return false (no key beats a key we cannot verify).
227
+ *
228
+ * @param {string} envPath
229
+ * @returns {boolean}
230
+ */
231
+ function checkFilePermissions(envPath) {
232
+ try {
233
+ const fs = require('fs');
234
+ if (process.platform === 'win32') {
235
+ if (!checkFilePermissions._warned) {
236
+ process.stderr.write(
237
+ '[mindrian-os] Note: API key file permission check is Linux/macOS only; '
238
+ + 'on Windows rely on NTFS ACLs.\n'
239
+ );
240
+ checkFilePermissions._warned = true;
241
+ }
242
+ return true;
243
+ }
244
+ const stat = fs.statSync(envPath);
245
+ if ((stat.mode & 0o077) !== 0) {
246
+ process.stderr.write(
247
+ `[mindrian-os] Refusing to load API key from ${envPath}: `
248
+ + `permissions too open (must be 0600). chmod 600 ${envPath}\n`
249
+ );
250
+ return false;
251
+ }
252
+ return true;
253
+ } catch (_e) {
254
+ return false;
255
+ }
256
+ }
257
+ checkFilePermissions._warned = false;
258
+
259
+ // Phase 123 Plan-07: getApiKey() delegates to lib/core/resolve-brain-key.cjs.
260
+ //
261
+ // Order: MINDRIAN_BRAIN_KEY env -> ~/.mindrian.env -> CWD .env -> not-found.
262
+ // NOTE: this REVERSES the previous CWD-first-then-~/.mindrian.env order.
263
+ // Rationale: the global backup (~/.mindrian.env, mode 0600 per SEC-02) is more
264
+ // trustworthy than a project's potentially-stale .env file. Cited: Phase 123
265
+ // D-31. The resolver returns { key, source, available, reason }; we surface
266
+ // the key (or null) here and log a non-null reason ONCE per process via
267
+ // console.error -- SEC-02 group/world-bit rejection routes through this
268
+ // channel, never as a silent null.
269
+ //
270
+ // The legacy inline 3-path lookup and the per-file checkFilePermissions gate
271
+ // previously inlined here are gone -- the resolver owns both responsibilities
272
+ // now. checkFilePermissions remains exported on _test for backward-compat
273
+ // with security-trifecta.test.cjs (the helper itself still works locally;
274
+ // it's just not called by getApiKey anymore).
275
+ let _memoizedKey = null;
276
+ let _memoizedAt = 0;
277
+ let _reasonLoggedThisProcess = false;
278
+ const _GETKEY_MEMO_MS = 60 * 1000;
279
+ function getApiKey() {
280
+ if (_memoizedAt && (Date.now() - _memoizedAt) < _GETKEY_MEMO_MS) {
281
+ return _memoizedKey;
282
+ }
283
+ const r = resolveBrainKey();
284
+ if (r && r.available) {
285
+ _memoizedKey = r.key;
286
+ _memoizedAt = Date.now();
287
+ return _memoizedKey;
288
+ }
289
+ if (r && r.reason && !_reasonLoggedThisProcess) {
290
+ // SEC-02 reject + not-found-with-reason both route through stderr ONCE
291
+ // per process -- never a silent null. The session-start status line is
292
+ // the user-visible surface; this is the in-process diagnostic.
293
+ process.stderr.write('[mindrian-os] Brain key not loaded: ' + r.reason + '\n');
294
+ _reasonLoggedThisProcess = true;
295
+ }
296
+ _memoizedKey = null;
297
+ _memoizedAt = Date.now();
298
+ return null;
299
+ }
300
+
301
+ /**
302
+ * Check if Brain is available (key exists).
303
+ */
304
+ function isAvailable() {
305
+ return !!getApiKey();
306
+ }
307
+
308
+ // ---------------------------------------------------------------------------
309
+ // Phase 250-04 (HONEST-03, SEED-011 Option A) -- per-install silent
310
+ // registration. Design doc: docs/BRAIN-IDENTITY-DESIGN.md.
311
+ //
312
+ // The ladder's fourth leg (resolve-brain-key.cjs, read-only) resolves an
313
+ // existing cached install token; THIS module owns the minting side. When the
314
+ // ladder resolves nothing at the first Brain consult, mint a fresh UUID,
315
+ // POST it to the Brain's /register endpoint, cache the returned token at
316
+ // mode 0600, and let the SAME process's next getApiKey() call pick it up.
317
+ //
318
+ // Once-per-process cap: exactly ONE registration attempt per process,
319
+ // success or failure. A failed attempt is the failure edge (registration
320
+ // failed / Brain offline) -- it is NEVER retried within the process (the
321
+ // AVAIL-02 bounded-retry budget explicitly does not apply here; a fresh
322
+ // process retries fresh on its own next launch). NEVER thrown into the
323
+ // caller; NEVER blocks a non-methodology path (this only runs from the
324
+ // Brain-consult chokepoint below, never from session-start or a hot path).
325
+ let _autoRegisterAttemptedThisProcess = false;
326
+ let _autoRegisterFailureReason = null;
327
+
328
+ function _installTokenPath(home) {
329
+ const h = home || process.env.HOME || process.env.USERPROFILE || require('node:os').homedir();
330
+ return path.join(h, '.mindrian-install.json');
331
+ }
332
+
333
+ /**
334
+ * Attempt the one-shot silent registration. Returns the minted key string on
335
+ * success, or null (opt-out, already-attempted, or any failure). On success,
336
+ * updates the module's own memoized key so the CALLER's next getApiKey()
337
+ * returns it immediately (no redundant resolver re-read needed, though a
338
+ * fresh resolveBrainKey() call would ALSO see the cache file now).
339
+ *
340
+ * @returns {Promise<string|null>}
341
+ */
342
+ async function _tryAutoRegister() {
343
+ if (_autoRegisterAttemptedThisProcess) return null;
344
+ _autoRegisterAttemptedThisProcess = true;
345
+
346
+ if (process.env.MINDRIAN_DISABLE_AUTO_REGISTER) {
347
+ _autoRegisterFailureReason = 'auto-registration disabled (MINDRIAN_DISABLE_AUTO_REGISTER set)';
348
+ return null;
349
+ }
350
+
351
+ try {
352
+ const installId = crypto.randomUUID();
353
+ const res = await fetch(`${BRAIN_URL}/register`, {
354
+ method: 'POST',
355
+ signal: AbortSignal.timeout(BRAIN_REQUEST_TIMEOUT_MS),
356
+ headers: { 'Content-Type': 'application/json' },
357
+ // Part 8 posture (T-250-13): install_id ONLY ever crosses in this body.
358
+ body: JSON.stringify({ install_id: installId }),
359
+ });
360
+ if (!res.ok) {
361
+ try { await res.arrayBuffer(); } catch (_e) { /* drain, see _ensureSession's precedent */ }
362
+ _autoRegisterFailureReason = 'registration failed (HTTP ' + res.status + ', offline or unreachable)';
363
+ return null;
364
+ }
365
+ const body = await res.json();
366
+ if (!body || typeof body.token !== 'string' || body.token.length === 0) {
367
+ _autoRegisterFailureReason = 'registration returned a malformed response (offline or unreachable)';
368
+ return null;
369
+ }
370
+
371
+ const cachePath = _installTokenPath();
372
+ const payload = { install_id: installId, token: body.token, minted_at: new Date().toISOString() };
373
+ fs.writeFileSync(cachePath, JSON.stringify(payload, null, 2) + '\n', { mode: 0o600 });
374
+ // writeFileSync's mode option only applies on file CREATION; force it
375
+ // explicitly so a pre-existing looser-permission file is corrected too
376
+ // (SEC-02 posture, mirrors resolve-brain-key.cjs's own gate on this file).
377
+ try { fs.chmodSync(cachePath, 0o600); } catch (_e) { /* best-effort on platforms without chmod semantics */ }
378
+
379
+ _memoizedKey = body.token;
380
+ _memoizedAt = Date.now();
381
+ return body.token;
382
+ } catch (e) {
383
+ _autoRegisterFailureReason = 'registration failed: ' + (e && e.message || String(e)) + ' (offline or unreachable)';
384
+ return null;
385
+ }
386
+ }
387
+
388
+ /**
389
+ * Async-aware availability gate: the "first Brain consult" seam every
390
+ * MCP-surface entry point passes through before dispatching a tool
391
+ * (bin/mindrian-brain-mcp-client.cjs's per-tool gates use this INSTEAD of
392
+ * the synchronous isAvailable(), so silent registration actually fires on
393
+ * that path -- isAvailable() alone cannot attempt an async network mint).
394
+ * Direct brain-client consumers (query/ask/schema/... via callTool) do not
395
+ * need to call this explicitly -- callTool() below performs the identical
396
+ * fallback internally, so BOTH surfaces share the SAME once-per-process cap.
397
+ *
398
+ * @returns {Promise<boolean>}
399
+ */
400
+ async function ensureAvailable() {
401
+ if (isAvailable()) return true;
402
+ await _tryAutoRegister();
403
+ return isAvailable();
404
+ }
405
+
406
+ /**
407
+ * The honest reason the last auto-registration attempt failed (or the
408
+ * opt-out reason), or null if no attempt has failed this process (either
409
+ * none was attempted yet, or the last attempt succeeded). Consumed by the
410
+ * no_key failure-edge refusal copy (refusal-messaging.cjs) so the reason names
411
+ * "registration" / "offline" rather than a generic "no key" framing.
412
+ *
413
+ * @returns {string|null}
414
+ */
415
+ function getAutoRegisterFailureReason() {
416
+ return _autoRegisterFailureReason;
417
+ }
418
+
419
+ /**
420
+ * Phase 87-07: ensure we have a valid initialized Brain session for the given
421
+ * api key, reusing the cached one if non-expired. Uses the pending-promise
422
+ * pattern so concurrent callers share a single in-flight init (R-87-07-RACE).
423
+ *
424
+ * Returns the resolved session marker (an opaque string -- the Brain Streamable
425
+ * HTTP transport does not require us to echo a sessionId on subsequent requests
426
+ * inside the same cache window, but awaiting this promise proves the key is
427
+ * valid against the Brain endpoint exactly once per TTL window).
428
+ *
429
+ * On any init rejection (network error, 401, etc.) the cache entry is removed
430
+ * in the .catch() tail so the next caller retries fresh rather than inheriting
431
+ * a poisoned promise.
432
+ *
433
+ * Sentinel `{ error: 'invalid_key' }` is returned *through* the promise (not
434
+ * thrown) so callers treat 401 identically to the pre-cache flow.
435
+ *
436
+ * @param {string} apiKey
437
+ * @returns {Promise<string|{error:string,message:string}|null>}
438
+ */
439
+ async function _ensureSession(apiKey) {
440
+ const keyHash = _hashKey(apiKey);
441
+ const cached = sessionCache.get(keyHash);
442
+ if (cached && cached.expiresAt > Date.now()) {
443
+ // Cache hit. Works whether the promise is still pending (concurrent init
444
+ // in flight) or already resolved (TTL reuse). Awaiting a resolved promise
445
+ // is a microtask no-op, so the fast path stays fast.
446
+ return cached.promise;
447
+ }
448
+ // Cache miss. Build the promise FIRST, install it in the cache BEFORE the
449
+ // first real await, so concurrent callers within the same event-loop tick
450
+ // see the same in-flight promise (R-87-07-RACE pending-promise pattern).
451
+ const promise = (async () => {
452
+ const initRes = await fetch(`${BRAIN_URL}/mcp`, {
453
+ method: 'POST',
454
+ signal: AbortSignal.timeout(BRAIN_REQUEST_TIMEOUT_MS),
455
+ headers: {
456
+ 'Content-Type': 'application/json',
457
+ 'Accept': 'application/json, text/event-stream',
458
+ 'Authorization': `Bearer ${apiKey}`,
459
+ },
460
+ body: JSON.stringify({
461
+ jsonrpc: '2.0',
462
+ id: 1,
463
+ method: 'initialize',
464
+ params: {
465
+ protocolVersion: '2024-11-05',
466
+ capabilities: {},
467
+ clientInfo: { name: 'mindrian-cli', version: '1.0.0' },
468
+ },
469
+ }),
470
+ });
471
+ if (!initRes.ok) {
472
+ // Debug session doctor-brain-smoke-win-crash (2026-05-30): drain the
473
+ // response body on the non-OK path. Node global fetch (undici) keeps the
474
+ // underlying TLS socket in a keep-alive, not-yet-released state until its
475
+ // body is consumed. An un-drained socket is a live libuv handle at exit;
476
+ // a synchronous process.exit() in a caller tears it down mid-close and
477
+ // asserts on Windows (src/win/async.c UV_HANDLE_CLOSING). Consuming the
478
+ // body lets undici release/recycle the socket cleanly. Cross-platform.
479
+ try { await initRes.arrayBuffer(); } catch (_) { /* body already gone */ }
480
+ if (initRes.status === 401) {
481
+ return { error: 'invalid_key', message: 'Brain API key is invalid.' };
482
+ }
483
+ // Any other non-OK status becomes a throw so the cache entry is purged
484
+ // by the .catch() below and the next caller retries.
485
+ throw new Error(`Brain init HTTP ${initRes.status}`);
486
+ }
487
+ // Opaque session marker. Subsequent tools/call requests don't need to
488
+ // echo this back -- the transport is stateless at the HTTP level. What
489
+ // matters is that we validated the key is live within this TTL window.
490
+ return 'validated-' + Date.now();
491
+ })();
492
+ sessionCache.set(keyHash, { promise, expiresAt: Date.now() + SESSION_TTL_MS });
493
+ // On reject, purge the entry so the next caller initializes fresh. Swallow
494
+ // here (we re-throw in the awaiter below) so Node doesn't see an
495
+ // unhandledRejection on the cache handle itself.
496
+ promise.catch(() => { sessionCache.delete(keyHash); });
497
+ return promise;
498
+ }
499
+
500
+ /**
501
+ * Phase 254 / COMP-02 (D-02 Option A): attach an additive `egress_disclosure`
502
+ * field to a callTool() success return when the Part 8 belt captured an
503
+ * `ambiguous` verdict for this call. Module-private -- used only at
504
+ * callTool()'s three success-return sites below.
505
+ *
506
+ * Returns `result` UNCHANGED when `disclosure` is falsy, when `result` is
507
+ * not a non-null plain object, or when `result` already carries an own
508
+ * `error` property (so `tier_denied` / `invalid_key` / `rate_limited` /
509
+ * `egress_blocked` sentinels are never decorated -- those are refusals, not
510
+ * success returns). Never throws: the assignment is wrapped so a frozen or
511
+ * exotic result object degrades to the undecorated value rather than
512
+ * crashing a Brain call.
513
+ *
514
+ * COPY-ON-ATTACH FOR PLAIN OBJECTS, MUTATE FOR ARRAYS (WR-02, 254-REVIEW.md):
515
+ * for a plain object, this returns a NEW shallow-copied object rather than
516
+ * mutating `result` in place -- every call site hands in a value freshly
517
+ * parsed via `JSON.parse` two lines above, so no other reference exists to
518
+ * preserve, and copy-on-attach closes the "same reference silently gains a
519
+ * field" hazard at zero real cost. For an ARRAY, mutation is structurally
520
+ * required: `callTool`'s contract for `brain_query` must stay a bare array
521
+ * per the 2026-05-11 hotfix note, so wrapping it in a new object here would
522
+ * break the `Array.isArray(result)` shape every `brain_query` consumer keys
523
+ * on. The disclosure is therefore attached as a non-index own property of
524
+ * the SAME array reference -- this is load-bearing for `query()`'s own
525
+ * `Array.isArray(result)` normalization branch below, which reads this
526
+ * property off the array and copies + deletes it onto the new top-level
527
+ * object it constructs (CR-01, 254-REVIEW.md). Any OTHER caller reading a
528
+ * bare array off `callTool('brain_query', ...)` directly (bypassing
529
+ * `query()`) must do the same: check `result.egress_disclosure` before
530
+ * treating `result` as a plain row array, since `JSON.stringify` silently
531
+ * drops non-index array properties.
532
+ *
533
+ * @param {*} result
534
+ * @param {object|null} disclosure
535
+ * @returns {*}
536
+ */
537
+ function _attachEgressDisclosure(result, disclosure) {
538
+ if (!disclosure) return result;
539
+ if (result === null || typeof result !== 'object') return result;
540
+ if (Object.prototype.hasOwnProperty.call(result, 'error')) return result;
541
+ try {
542
+ if (Array.isArray(result)) {
543
+ result.egress_disclosure = disclosure;
544
+ return result;
545
+ }
546
+ return Object.assign({}, result, { egress_disclosure: disclosure });
547
+ } catch (_e) {
548
+ // Frozen or exotic result object -- degrade to the undecorated value. A
549
+ // belt that can crash a Brain call is worse than the gap it closes.
550
+ return result;
551
+ }
552
+ }
553
+
554
+ /**
555
+ * Call a Brain MCP tool via HTTP.
556
+ * @param {string} toolName - e.g., 'brain_query', 'brain_search', 'brain_schema'
557
+ * @param {object} args - tool arguments
558
+ * @returns {object|null} - result or null if unavailable
559
+ */
560
+ async function callTool(toolName, args) {
561
+ let key = getApiKey();
562
+ if (!key) {
563
+ // Phase 250-04: the ladder resolved nothing -- this IS the first Brain
564
+ // consult. Attempt the one-shot silent registration before giving up;
565
+ // shares the same _autoRegisterAttemptedThisProcess cap as
566
+ // ensureAvailable() (the shim's gate), so whichever surface consults
567
+ // first performs the real network attempt and the other reuses it.
568
+ key = await _tryAutoRegister();
569
+ }
570
+ if (!key) return null;
571
+
572
+ // Quick 260819-c8j (WS-D2): the Part 8 in-process classify() belt at the
573
+ // single dispatch seam every callTool() caller flows through. Placement is
574
+ // load-bearing: AFTER the key gate above (so the no-key `null` contract
575
+ // stays byte-unchanged) and BEFORE _ensureSession below (so a blocked
576
+ // payload opens no socket at all -- not even a session init).
577
+ //
578
+ // MECHANISM CHOICE: one guard in one function covers all 16 wrappers,
579
+ // versus ~12 near-identical per-wrapper edits that drift the moment a 17th
580
+ // wrapper lands. Double-classification is NOT double-prompting --
581
+ // classify() returns a verdict, it does not elicit. The 4 existing
582
+ // raw-field guards (query() :601, hatAwareRecommend() :942,
583
+ // suggestValidationSteps() :1056, sendPacket() :1643) are NOT removed and
584
+ // NOT weakened by this belt: their own docblocks prove why the belt cannot
585
+ // replace them (classifying the ASSEMBLED template string instead of the
586
+ // raw field lets the template's own word "Framework" launder an embedded
587
+ // canary from ambiguous to allow, and sanitizeCypherInput strips the '@'
588
+ // the PII pattern keys on) -- this is a belt UNDERNEATH them, never a
589
+ // substitute. An 'ambiguous' verdict never blocks and never prompts, the
590
+ // same posture sendPacket()'s belt already takes at :1643 in spirit.
591
+ //
592
+ // `egress_blocked` is a sentinel OBJECT, not `null`: `null` is the
593
+ // test-pinned transport-failure signal across ~82 degradation tests, and
594
+ // conflating a constitutional refusal with an outage would make the
595
+ // refusal invisible. The sentinel-object idiom is already established by
596
+ // `tier_denied` (:497 area) and `invalid_key` above.
597
+ //
598
+ // Phase 254 / COMP-02 (D-02 Option A, navigator-ruled 2026-09-02):
599
+ // `ambiguous` now DISCLOSES and still PROCEEDS. `ambiguousDisclosure`
600
+ // (declared below, function-scoped so it survives into the retry loop and
601
+ // the SSE parse) captures the typed disclosure ADDITIVELY -- borrowing the
602
+ // idiom `brain-router.cjs:414-422` already uses for `brain_refusal`
603
+ // (an additive field, best-effort, never blocking), not the function
604
+ // itself. Option B (fail-closed on `ambiguous` too) was considered and
605
+ // explicitly rejected for this phase: it would change the behaviour of all
606
+ // 16 `callTool` wrappers at once and needs its own canary suite, out of
607
+ // this phase's scope. A Shape F.1 gate belongs at the MCP handler
608
+ // (`lib/mcp/gate-render.cjs::renderGate`) and never at this transport
609
+ // chokepoint -- inventing an elicitation path inside a stateless server
610
+ // handler is exactly what Option A avoids. The disclosure names the
611
+ // verdict class and the tool; it never carries payload bytes (every
612
+ // `ambiguous`-branch `reason` string in `part8-egress-guard.cjs` is a
613
+ // constant literal, unlike the `block` branch's reason which embeds the
614
+ // matched pattern -- this path never reaches that branch).
615
+ //
616
+ // The host PreToolUse hook (`scripts/part8-egress-guard-hook.cjs`) still
617
+ // gates `ambiguous` with a real Shape F.1 render on the model-issued path.
618
+ // The two enforcement points now DIVERGE BY A STATED DECISION rather than
619
+ // by omission -- COMP-02's literal wording.
620
+ let ambiguousDisclosure = null;
621
+ try {
622
+ const callToolEgressGuard = require('./part8-egress-guard.cjs');
623
+ const callToolEgressVerdict = callToolEgressGuard.classify(args, { toolName: toolName });
624
+ if (callToolEgressVerdict && callToolEgressVerdict.verdict === 'block') {
625
+ return {
626
+ error: 'egress_blocked',
627
+ tool: toolName,
628
+ egress_class: callToolEgressVerdict.class || 'content_set',
629
+ };
630
+ } else if (callToolEgressVerdict && callToolEgressVerdict.verdict === 'ambiguous') {
631
+ ambiguousDisclosure = {
632
+ verdict: 'ambiguous',
633
+ egress_class: callToolEgressVerdict.class || 'unknown',
634
+ reason: callToolEgressVerdict.reason || null,
635
+ tool: toolName,
636
+ disposition: 'proceeded',
637
+ };
638
+ }
639
+ } catch (_e) {
640
+ // Belt-internal error (guard module missing/throws): degrade to existing
641
+ // behavior. This belt is a belt, not the primary control.
642
+ }
643
+
644
+ try {
645
+ // Phase 87-07: reuse cached Brain session (5-min TTL) instead of
646
+ // re-running initialize on every callTool. Concurrent callers share
647
+ // the in-flight promise via the pending-promise pattern.
648
+ const session = await _ensureSession(key);
649
+ if (session && typeof session === 'object' && session.error === 'invalid_key') {
650
+ return session;
651
+ }
652
+ if (!session) return null;
653
+
654
+ // Phase 250-01 (AVAIL-02): bounded retry loop around the tools/call
655
+ // dispatch -- the single HTTP seam every Brain tool flows through after
656
+ // session establishment. NULL CONTRACT PRESERVED: the retry changes WHEN
657
+ // null returns (after the budget instead of after one attempt), never
658
+ // WHAT returns null, and never which statuses map to which sentinels
659
+ // (247-02 do-not-widen note; 82 degradation tests key on this).
660
+ const retryMax = _retryMax();
661
+ const baseMs = _retryBaseMs();
662
+ // Phase 259 (TRUST-01): a SEPARATE counter from the loop's `attempt`,
663
+ // so a 5xx blip earlier in this same callTool invocation cannot eat
664
+ // into the 429 budget, and vice versa. Declared outside the `for` so
665
+ // it survives iterations.
666
+ let rlAttempt = 0;
667
+ let rlTruncated = false; // OQ-4: set when MINDRIAN_BRAIN_RATELIMIT_MAX_WAIT_MS truncated a wait this call.
668
+ for (let attempt = 0; ; attempt += 1) {
669
+ let toolRes;
670
+ try {
671
+ toolRes = await fetch(`${BRAIN_URL}/mcp`, {
672
+ method: 'POST',
673
+ signal: AbortSignal.timeout(BRAIN_REQUEST_TIMEOUT_MS),
674
+ headers: {
675
+ 'Content-Type': 'application/json',
676
+ 'Accept': 'application/json, text/event-stream',
677
+ 'Authorization': `Bearer ${key}`,
678
+ },
679
+ body: JSON.stringify({
680
+ jsonrpc: '2.0',
681
+ id: 2,
682
+ method: 'tools/call',
683
+ params: { name: toolName, arguments: args },
684
+ }),
685
+ });
686
+ } catch (fetchErr) {
687
+ // Network error / timeout at the tools/call seam -- transport-class,
688
+ // retryable. Every other thrown error (outside this inner try) still
689
+ // falls through to the outer catch below, unchanged.
690
+ if (attempt < retryMax) {
691
+ await _sleep(baseMs * Math.pow(3, attempt));
692
+ continue;
693
+ }
694
+ return null;
695
+ }
696
+
697
+ if (!toolRes.ok) {
698
+ // Phase 247-02 (CONTRACT-01 error semantics): HTTP 403 means the Brain's
699
+ // tier gate refused this tool on this key (a MoatViolation), NOT that the
700
+ // Brain is unreachable. Mirrors the invalid_key sentinel precedent in
701
+ // _ensureSession above -- a plain object returned through the promise,
702
+ // never thrown, so callers that already understand sentinel passthrough
703
+ // (query()'s {error:...} passthrough is the existing example) keep
704
+ // working unchanged. 401/403 are validation-class -- ZERO retry, ever
705
+ // (AVAIL-02: retrying an auth failure hammers auth, taxonomy-wrong).
706
+ if (toolRes.status === 403) {
707
+ let rawText = '';
708
+ try { rawText = await toolRes.text(); } catch (_e) { rawText = ''; }
709
+ let message = null;
710
+ try {
711
+ const parsedBody = JSON.parse(rawText);
712
+ if (parsedBody && parsedBody.error && typeof parsedBody.error.message === 'string') {
713
+ message = parsedBody.error.message;
714
+ }
715
+ } catch (_e) {
716
+ // Unparseable body -- fall through to the raw-text fallback below.
717
+ }
718
+ if (!message) {
719
+ message = rawText
720
+ ? rawText.slice(0, 300)
721
+ : 'Brain denied tier access (403, no body).';
722
+ }
723
+ return { error: 'tier_denied', tool: toolName, message: message };
724
+ }
725
+ // Phase 259 (TRUST-01): 429 previously fell through the generic
726
+ // non-OK ladder into the bare `return null` at the bottom of this
727
+ // block, which the MCP shim renders as BRAIN_UNREACHABLE with copy
728
+ // claiming a retry budget was spent -- factually false, since zero
729
+ // retries ran on that leg. Verified wire contract (this session,
730
+ // ProblemsWorthSolving-Brain src/http/rate-limit.mjs::
731
+ // perKeyRateLimit): 120 requests per 60s per key at defaults,
732
+ // Retry-After ALWAYS set as an integer delay-seconds string,
733
+ // floored at 1 and bounded above by the window. D-01: honor it
734
+ // exactly. D-02: fall back to 500/1000/2000ms when absent. D-03:
735
+ // after the budget exhausts, return a distinct rate_limited
736
+ // sentinel, never null.
737
+ if (toolRes.status === 429) {
738
+ const retryAfterRaw = toolRes.headers.get('retry-after');
739
+ // Drain this branch's own body before bailing/continuing, exactly
740
+ // as the 403 block above consumes its own body (an un-drained
741
+ // undici socket is a live libuv handle that asserts on Windows).
742
+ try { await toolRes.arrayBuffer(); } catch (_e) { /* body already gone */ }
743
+ if (rlAttempt < _rateLimitRetryMax()) {
744
+ const ceilingMsForWait = _rateLimitMaxWaitMs();
745
+ const computedWaitMs = _rateLimitWaitMs(rlAttempt, retryAfterRaw, _rateLimitBaseMs());
746
+ if (ceilingMsForWait !== null && computedWaitMs >= ceilingMsForWait) rlTruncated = true;
747
+ await _sleep(computedWaitMs);
748
+ rlAttempt += 1;
749
+ continue;
750
+ }
751
+ const parsedRetryAfterMs = _parseRetryAfterMs(retryAfterRaw);
752
+ const retryAfterS = parsedRetryAfterMs !== null ? Math.ceil(parsedRetryAfterMs / 1000) : null;
753
+ const ceilingMs = _rateLimitMaxWaitMs();
754
+ let message = `Brain rate-limited ${toolName} after ${rlAttempt + 1} attempts (429).`;
755
+ if (rlTruncated && ceilingMs !== null) {
756
+ message += ` At least one Retry-After wait was truncated to the operator-set ceiling of ${ceilingMs}ms.`;
757
+ }
758
+ return {
759
+ error: 'rate_limited',
760
+ tool: toolName,
761
+ retry_after_s: retryAfterS,
762
+ attempts: rlAttempt + 1,
763
+ message: message.slice(0, 300),
764
+ };
765
+ }
766
+ // Drain the body before bailing so undici releases the keep-alive socket
767
+ // (see _ensureSession non-OK note: an un-drained socket is a live libuv
768
+ // handle that asserts on Windows when a caller force-exits).
769
+ try { await toolRes.arrayBuffer(); } catch (_) { /* body already gone */ }
770
+ // 5xx is transport-class (transient) -- retryable within budget.
771
+ // Every OTHER non-OK, non-retried status still returns null -- that
772
+ // remains the sole transport-failure signal (research Pitfall 4: 82
773
+ // degradation tests key on it; do not widen this branch).
774
+ if (toolRes.status >= 500 && toolRes.status < 600 && attempt < retryMax) {
775
+ await _sleep(baseMs * Math.pow(3, attempt));
776
+ continue;
777
+ }
778
+ return null;
779
+ }
780
+
781
+ const text = await toolRes.text();
782
+ // Parse SSE response
783
+ const dataLine = text.split('\n').find(l => l.startsWith('data: '));
784
+ if (!dataLine) return null;
785
+
786
+ const parsed = JSON.parse(dataLine.slice(6));
787
+ if (parsed.result && parsed.result.content) {
788
+ const textContent = parsed.result.content.find(c => c.type === 'text');
789
+ if (textContent) {
790
+ try {
791
+ return _attachEgressDisclosure(JSON.parse(textContent.text), ambiguousDisclosure);
792
+ } catch (e) {
793
+ return _attachEgressDisclosure({ text: textContent.text }, ambiguousDisclosure);
794
+ }
795
+ }
796
+ }
797
+ // The helper's own non-object guard already handles the null case
798
+ // here (parsed.result || null); wrapped anyway so the intent is
799
+ // legible at the call site rather than relying on the guard alone.
800
+ return _attachEgressDisclosure(parsed.result || null, ambiguousDisclosure);
801
+ }
802
+ } catch (err) {
803
+ // Network error, timeout, etc. (outside the retry loop -- e.g. during
804
+ // _ensureSession).
805
+ return null;
806
+ }
807
+ }
808
+
809
+ /**
810
+ * Query Neo4j via Brain (Cypher query).
811
+ * This does NOT use Pinecone, no embedding quota consumed.
812
+ *
813
+ * NOTE (Finding I, v1.10.9 hotfix 2026-04-15): the Brain MCP brain_query
814
+ * tool expects the parameter name `cypher`, not `query`. Previously this
815
+ * function sent { query: cypher } which tripped an MCP input validation
816
+ * error (code -32602, path ["cypher"], "Required"). Downstream scripts
817
+ * like fetch-brain-baseline.cjs and compute-whitespace-gaps.py then
818
+ * silently fell through to empty-baseline mode even though Brain was
819
+ * fully reachable and the key was valid. Witnessed against the live
820
+ * iia-deeptech-centers room on 2026-04-15. brain_search uses `query`
821
+ * which is why Pinecone semantic search kept working and masked this.
822
+ *
823
+ * NOTE (2026-05-11, graph-on-graph P0): `query` now accepts an optional
824
+ * second argument `params` and forwards it to the `brain_query` MCP tool
825
+ * as { cypher, params }. The Brain tool declares `params:
826
+ * z.record(z.any()).optional()`, so a parameterized Cypher gets its
827
+ * bindings through cleanly. `params` MUST be a generic-handles-only object
828
+ * -- framework names, phase identifiers, problem types per Canon Part 8 --
829
+ * NEVER user content (artifact bodies, meeting text, personal identifiers,
830
+ * proprietary numbers). Previously the second arg was silently dropped, so
831
+ * callers (rs-explain-command.cjs, rs-thesis-command.cjs, rs-nl-to-query)
832
+ * that generated parameterized Cypher had their bindings disappear or were
833
+ * pushed toward unsafe string interpolation. A param-less call still sends
834
+ * only { cypher } and behaves exactly as before.
835
+ *
836
+ * NOTE (2026-05-11, graph-on-graph P0 cont.): RESULT-SHAPE NORMALIZATION.
837
+ * The Brain MCP `brain_query` tool serializes its result as
838
+ * `JSON.stringify(records)` where `records` is a BARE ARRAY of row objects.
839
+ * `callTool` returns that array directly (or `{ text: 'Error: ...' }` on a
840
+ * Cypher error, or `null` when the Brain is unreachable / no API key).
841
+ * Consumers across the codebase -- brain-router.cjs, brain-derivation.cjs's
842
+ * `renderRecords`, rs-chain-feeder.cjs, rs-experts-command.cjs,
843
+ * rs-explain-command.cjs, rs-thesis-command.cjs -- all read `result.records`,
844
+ * so the bare-array shape silently dropped every row. `query` therefore now
845
+ * ALWAYS returns `{ records: [...] }` on a successful brain_query; an
846
+ * unreachable Brain / missing key still returns `null`; a Cypher-error
847
+ * response (`{ text: 'Error: ...' }` or `{ error: ... }`) passes through
848
+ * unchanged so callers that inspect the failure can still see it; any other
849
+ * unexpected shape collapses to `{ records: [] }` so callers never crash.
850
+ * `search`, `smartSearch`, `schema`, `stats`, `write`, `callTool` are
851
+ * deliberately untouched -- only `query` is normalized.
852
+ *
853
+ * NOTE (2026-09-01, quick/260901-ipp): a THIRD recognized shape. Theo's
854
+ * `brain_query` contract cutover returns `{ rows: [...], diagnostics: {...} }`
855
+ * instead of the bare array above, and that shape matched none of the
856
+ * branches here -- it fell through to the unexpected-shape safety net, so
857
+ * every post-flip `brain_query` call returned an empty result set that was
858
+ * indistinguishable from a legitimate no-match, with no error and no crash.
859
+ * `query` now recognizes `result.rows` (guarded with `Array.isArray`, never
860
+ * mere key presence, so a malformed `rows` value still lands on the safety
861
+ * net) and normalizes it into `records` while preserving every other field
862
+ * the Brain sent (`diagnostics`, and any future `coverage` block). Preserving
863
+ * `diagnostics` is load-bearing: it is what lets a caller tell a genuine
864
+ * `{ rows: [], diagnostics: {...} }` "nothing matched" apart from an
865
+ * unrecognized-shape response (see the 2026-09-03 note below for what the
866
+ * fallback returns now). Sources: this repo's
867
+ * `docs/2026-09-01-HANDOFF-phases-272-274-275-plus-theo-flip-coordination.md`
868
+ * finding #1, and Theo's own
869
+ * `.planning/phases/09-brain-contract-cutover/09-MOS-LEARNING.md`.
870
+ *
871
+ * NOTE (2026-09-03, quick/260903-eit): the safety net stopped being blind.
872
+ * An unrecognized shape still returns an object carrying `records: []` so
873
+ * callers never crash, but it now ALSO carries
874
+ * `error: 'brain_query_unrecognized_shape'` plus `shape_type` and
875
+ * `shape_keys`, and emits one warn-once `[mindrian-os]` line naming the
876
+ * offending top-level key names. Reason: the bare `{ records: [] }` was
877
+ * byte-identical to a legitimate no-match, which is precisely how the Theo
878
+ * `{ rows, diagnostics }` drift went unnoticed above until a human read this
879
+ * code. This change recognizes ZERO new response shapes -- it only makes the
880
+ * next mismatch audible. Sources: this task directory
881
+ * (`.planning/quick/260903-eit-.../260903-eit-PLAN.md`) alongside the
882
+ * 2026-09-01 sources above.
883
+ */
884
+ async function query(cypher, params) {
885
+ // Part 8 BACKSTOP (Phase 239 / BRAIN-02). This is a BACKSTOP ONLY and is
886
+ // PROVABLY INSUFFICIENT ALONE: MEASURED, the template's own vocabulary word
887
+ // "Framework" satisfies the classifier's positive methodology recognizer
888
+ // and launders an embedded canary from ambiguous to allow -- classifying
889
+ // the payload {cypher:"CANARY7F3A2B"} on its own yields verdict ambiguous,
890
+ // but classifying {cypher:'MATCH (f:Framework) WHERE x="CANARY7F3A2B"'}
891
+ // yields verdict allow. The REAL coverage is the raw-field guard added in
892
+ // hatAwareRecommend and suggestValidationSteps (classify-before-sanitize,
893
+ // classify-before-interpolate). Anyone tempted to delete those raw-field
894
+ // guards because "query() already checks" must read this comment first:
895
+ // it does not.
896
+ // An 'ambiguous' verdict on an assembled template is EXPECTED policy here
897
+ // (every legitimate methodology query looks ambiguous at this tier) and is
898
+ // deliberately NOT blocked; only a proven 'block' (a CONTENT-SET hit) stops
899
+ // the call.
900
+ try {
901
+ const queryEgressGuard = require('./part8-egress-guard.cjs');
902
+ const queryEgressVerdict = queryEgressGuard.classify(
903
+ { cypher: String(cypher || '') },
904
+ { toolName: 'brain_query' }
905
+ );
906
+ if (queryEgressVerdict && queryEgressVerdict.verdict === 'block') {
907
+ _logEventBestEffort(undefined, 'brain_egress_blocked', {
908
+ egress_class: queryEgressVerdict.class || 'content_set',
909
+ verdict: 'block',
910
+ count: 1,
911
+ created_by: 'system',
912
+ source_path: 'system:brain-query',
913
+ });
914
+ return null;
915
+ }
916
+ } catch (_e) {
917
+ // Belt-internal error (guard module missing/throws): degrade to existing
918
+ // behavior. This backstop is a belt, not the primary control.
919
+ }
920
+
921
+ const args = { cypher: cypher };
922
+ if (params && typeof params === 'object' && Object.keys(params).length > 0) {
923
+ args.params = params;
924
+ }
925
+ const result = await callTool('brain_query', args);
926
+ if (result == null) return null; // unreachable / no API key
927
+ if (Array.isArray(result)) {
928
+ // the normal brain_query shape. `_attachEgressDisclosure` (Phase 254 /
929
+ // COMP-02) attaches `egress_disclosure` as a non-index own property of
930
+ // this SAME array when the Part 8 belt captured an `ambiguous` verdict.
931
+ // The `{ records: result }` wrapper below is a BRAND-NEW object, so that
932
+ // property is not carried over automatically -- it must be copied onto
933
+ // the new top-level object explicitly, or it silently relocates to
934
+ // `.records.egress_disclosure` (invisible to every documented/tested
935
+ // caller and to JSON.stringify). See CR-01, 254-REVIEW.md.
936
+ const normalized = { records: result };
937
+ if (Object.prototype.hasOwnProperty.call(result, 'egress_disclosure')) {
938
+ normalized.egress_disclosure = result.egress_disclosure;
939
+ // `normalized.records` is the SAME array reference as `result` (not a
940
+ // copy), so without this the stray non-index property would still sit
941
+ // on it after normalization -- accessible via result.records.egress_disclosure
942
+ // even though the documented, tested contract is the top-level field
943
+ // above. Deleting closes the "silently relocates" failure mode WR-02
944
+ // flagged, not just CR-01's missing top-level copy.
945
+ delete result.egress_disclosure;
946
+ }
947
+ return normalized;
948
+ }
949
+ if (result && Array.isArray(result.records)) return result; // already normalized (defensive)
950
+ // Theo's brain_query contract shape: { rows, diagnostics }. Guard on
951
+ // Array.isArray(result.rows), never mere key presence, so a malformed
952
+ // `rows` value still falls through to the safety net below. Placed
953
+ // adjacent to the other shape checks so all shape recognition stays in
954
+ // one block, ahead of the fallback. Theo's error envelope is
955
+ // { text: 'CODE: detail' } and carries no `rows`, so this branch can
956
+ // never swallow an error.
957
+ if (result && Array.isArray(result.rows)) {
958
+ return Object.assign({}, result, { records: result.rows });
959
+ }
960
+ if (result && (result.error || result.text)) return result; // error / message passthrough
961
+ return _unrecognizedQueryShape(result);
962
+ }
963
+
964
+ // ----------------------------------------------------------------------------
965
+ // _unrecognizedQueryShape -- the query() safety net, made loud (2026-09-03,
966
+ // quick/260903-eit). Mirrors the _legacyPathWarned / _warnLegacyOnce idiom
967
+ // this file already documents as "the existing once-per-process warning
968
+ // pattern in this file".
969
+ //
970
+ // CANON PART 8: key NAMES only, never VALUES. A Brain response envelope's
971
+ // top-level key names are generic schema handles; its values may carry graph
972
+ // payload. Neither the returned `shape_keys` nor the warning line may ever
973
+ // contain a value from the response body.
974
+ // ----------------------------------------------------------------------------
975
+
976
+ let _queryShapeWarned = false;
977
+
978
+ function _unrecognizedQueryShape(result) {
979
+ const shapeType = typeof result;
980
+ // Arrays never reach here in practice (Array.isArray(result) is caught by
981
+ // the branch above), but keep the check anyway so this helper stays
982
+ // correct even if it is ever called from elsewhere.
983
+ const isPlainObject =
984
+ result !== null && shapeType === 'object' && !Array.isArray(result);
985
+ const shapeKeys = isPlainObject
986
+ ? Object.keys(result)
987
+ .slice(0, 12) // cap key count so a hostile/malformed envelope can't flood the log
988
+ .map((k) => String(k).slice(0, 64)) // cap key length for the same reason
989
+ : []; // never Object.keys() a string -- that yields character indices, not key names
990
+
991
+ if (!_queryShapeWarned) {
992
+ _queryShapeWarned = true;
993
+ // eslint-disable-next-line no-console
994
+ console.warn(
995
+ '[mindrian-os] brain_query_unrecognized_shape: the Brain returned a '
996
+ + 'response shape (' + shapeType + ', keys: [' + shapeKeys.join(', ') + ']) '
997
+ + 'that query() does not recognize. This is a CONTRACT MISMATCH, NOT an '
998
+ + 'empty answer -- the caller still receives records: [] so it does not '
999
+ + 'crash, but every downstream reader should treat this as unresolved '
1000
+ + 'until the response contract is reconciled.'
1001
+ );
1002
+ }
1003
+
1004
+ return {
1005
+ records: [],
1006
+ error: 'brain_query_unrecognized_shape',
1007
+ shape_type: shapeType,
1008
+ shape_keys: shapeKeys,
1009
+ };
1010
+ }
1011
+
1012
+ /**
1013
+ * Search Pinecone via Brain (semantic search).
1014
+ * If quota exhausted, returns error with fallback suggestion.
1015
+ */
1016
+ async function search(queryText, options = {}) {
1017
+ const result = await callTool('brain_search', {
1018
+ query: queryText,
1019
+ namespace: options.namespace || undefined,
1020
+ topK: options.topK || 5,
1021
+ });
1022
+
1023
+ // Check for Pinecone quota exhaustion
1024
+ if (result && result.text && result.text.includes('RESOURCE_EXHAUSTED')) {
1025
+ return {
1026
+ error: 'pinecone_quota_exhausted',
1027
+ message: 'Pinecone embedding quota exhausted for this month. Using Neo4j Cypher fallback.',
1028
+ fallback: 'neo4j',
1029
+ };
1030
+ }
1031
+
1032
+ return result;
1033
+ }
1034
+
1035
+ /**
1036
+ * Search with automatic fallback: Pinecone first, Neo4j Cypher if quota exhausted.
1037
+ */
1038
+ async function smartSearch(queryText, options = {}) {
1039
+ // Try Pinecone first
1040
+ const pineconeResult = await search(queryText, options);
1041
+
1042
+ if (pineconeResult && pineconeResult.error === 'pinecone_quota_exhausted') {
1043
+ // Fallback to Neo4j full-text search
1044
+ const cypher = `
1045
+ CALL db.index.fulltext.queryNodes("framework_search", $query)
1046
+ YIELD node, score
1047
+ RETURN node.name AS name, node.description AS description, score
1048
+ LIMIT ${options.topK || 5}
1049
+ `;
1050
+ const neo4jResult = await query(cypher.replace('$query', `"${sanitizeCypherInput(queryText)}"`));
1051
+ if (neo4jResult) {
1052
+ neo4jResult._source = 'neo4j_fallback';
1053
+ neo4jResult._note = 'Pinecone quota exhausted. Results from Neo4j Cypher fulltext search.';
1054
+ }
1055
+ return neo4jResult;
1056
+ }
1057
+
1058
+ return pineconeResult;
1059
+ }
1060
+
1061
+ // brain_schema is near-static (the teaching graph's label/relationship/property
1062
+ // taxonomy changes ~never) and is hit by several modules. Memoize it
1063
+ // process-wide for 30 minutes.
1064
+ //
1065
+ // Phase 339 (2026-09-03, FLIP-05, D-13 as corrected): _schemaCacheOrigin
1066
+ // (declared beside _schemaCacheAt below) keys the memo on the resolved
1067
+ // origin (getBrainUrl()'s own value, compared by identity), so a cached
1068
+ // schema can never be served across an origin change. The correction this
1069
+ // comment states plainly so the next reader does not re-derive it:
1070
+ // BRAIN_URL is a module-scope const resolved once at require time, so no
1071
+ // running process can ever OBSERVE an origin change -- this guard is
1072
+ // therefore provably inert against the incumbent today (the comparison
1073
+ // below is always true within one process). It ships anyway as defense in
1074
+ // depth: it becomes load-bearing the day anything makes the origin mutable
1075
+ // per process, which is exactly the class of future change that would
1076
+ // otherwise reintroduce this bug silently. By the same reasoning, no
1077
+ // exported flush-the-memo function exists here BY DECISION: it would have
1078
+ // zero callers (nothing can observe the origin change it would be flushing
1079
+ // for), which Canon Part 7 and this repo's dead-surface discipline both
1080
+ // argue against; tests/test-339-schema-memo-origin-keyed.cjs Arm 7 asserts
1081
+ // its absence. Flip-day answer for 09-MOS-LEARNING's addendum, verbatim:
1082
+ // "no flush needed: memo is process-local, keyed by origin since beta.17".
1083
+ let _schemaCache = null;
1084
+ let _schemaCacheAt = 0;
1085
+ let _schemaCacheOrigin = null;
1086
+ const SCHEMA_CACHE_TTL_MS = 30 * 60 * 1000;
1087
+
1088
+ /**
1089
+ * Get the Brain Neo4j schema (node labels, relationship types, property keys).
1090
+ * Memoized for 30 minutes (process-wide), keyed on the resolved origin.
1091
+ */
1092
+ async function schema() {
1093
+ if (
1094
+ _schemaCache &&
1095
+ _schemaCacheOrigin === getBrainUrl() &&
1096
+ (Date.now() - _schemaCacheAt) < SCHEMA_CACHE_TTL_MS
1097
+ ) {
1098
+ return _schemaCache;
1099
+ }
1100
+ const result = await callTool('brain_schema', {});
1101
+ // Phase 247-02 audit fix (Rule 1): a sentinel object (tier_denied /
1102
+ // invalid_key -- anything carrying .error) is NOT valid schema data. Caching
1103
+ // it would serve a stale denial for up to 30 minutes even after the Brain
1104
+ // recovers or the key is fixed. Only cache a genuine, error-free result.
1105
+ if (result != null && !(typeof result === 'object' && result.error)) {
1106
+ _schemaCache = result;
1107
+ _schemaCacheAt = Date.now();
1108
+ _schemaCacheOrigin = getBrainUrl();
1109
+ }
1110
+ return result;
1111
+ }
1112
+
1113
+ /**
1114
+ * _inferRungFromQuestion(question) -> one of the four Theo rung ids.
1115
+ *
1116
+ * Theo's `classify_problem_type` tool takes NO parameters (the caller
1117
+ * classifies), and there is no free-text rung classifier anywhere in this
1118
+ * repo (`brain-derivation.cjs::classifyProblemType` scores MINTO triples,
1119
+ * not text). This helper fills that local gap. It is PURE and LOCAL: it
1120
+ * never sends the question anywhere, which is the whole reason it exists
1121
+ * (Canon Part 8 -- the question is user data and stays local).
1122
+ *
1123
+ * Precedence is FIXED and load-bearing (do not reorder): Wicked is the
1124
+ * orthogonal stakeholder-conflict axis and wins outright; UnDefined's
1125
+ * markers are the least ambiguous of the remaining three; WellDefined's
1126
+ * markers are next; IllDefined's markers are the broadest and are also the
1127
+ * default, so they run last and cost nothing when they lose.
1128
+ *
1129
+ * Multi-word markers match as a substring; single-word markers match on a
1130
+ * word boundary so `which` does not fire inside `whichever`. Non-string or
1131
+ * empty input returns `'IllDefined'` and never throws.
1132
+ *
1133
+ * @param {*} question
1134
+ * @returns {'Wicked'|'UnDefined'|'WellDefined'|'IllDefined'}
1135
+ */
1136
+ const _RUNG_MARKERS = [
1137
+ { rung: 'Wicked', markers: ['stakeholder', 'disagree', 'values', 'political'] },
1138
+ { rung: 'UnDefined', markers: ['future of', 'no boundary', 'unbounded'] },
1139
+ { rung: 'WellDefined', markers: ['measure', 'spec', 'test', 'kpi', 'how do we'] },
1140
+ { rung: 'IllDefined', markers: ['next big thing', 'which', 'should we', 'opportunity'] },
1141
+ ];
1142
+
1143
+ function _matchesRungMarker(text, marker) {
1144
+ if (marker.indexOf(' ') !== -1) return text.indexOf(marker) !== -1;
1145
+ return new RegExp('\\b' + marker + '\\b').test(text);
1146
+ }
1147
+
1148
+ function _inferRungFromQuestion(question) {
1149
+ if (typeof question !== 'string' || !question.trim()) return 'IllDefined';
1150
+ const lc = question.trim().toLowerCase();
1151
+ for (let i = 0; i < _RUNG_MARKERS.length; i++) {
1152
+ const entry = _RUNG_MARKERS[i];
1153
+ for (let j = 0; j < entry.markers.length; j++) {
1154
+ if (_matchesRungMarker(lc, entry.markers[j])) return entry.rung;
1155
+ }
1156
+ }
1157
+ return 'IllDefined';
1158
+ }
1159
+
1160
+ /**
1161
+ * _composeTheoAsk(payload, question, deps) -> composed envelope payload.
1162
+ *
1163
+ * Composes `directive`, `next_gate` and `grounding` onto a Theo
1164
+ * `answer_mode: 'structured_rows'` payload so downstream consumers
1165
+ * (`wrapDirective`, `brainRoute`, `rs-chain-feeder.cjs`) get a real
1166
+ * framework and chain instead of an empty scaffold. `deps` is the
1167
+ * injectable wire seam (`{ recommendChain, query }`) the offline test suite
1168
+ * drives; it defaults to this module's own wrappers so production behavior
1169
+ * needs no injection.
1170
+ *
1171
+ * Runs entirely inside one outer try/catch and NEVER throws: on any failure
1172
+ * it still returns the same object shape with `options: []` and
1173
+ * `chain_status: 'unreachable'`, so a rows response with a failed chain
1174
+ * still returns `grounding`.
1175
+ *
1176
+ * Canon Part 8: the only two extra wire calls this makes carry a closed
1177
+ * rung enum (`recommendChain`) and framework names Theo itself just
1178
+ * returned (`query`, label-anchored on `(f:Framework)`, never
1179
+ * relationship-first -- Theo's read allow-list refuses
1180
+ * `DirectedRelationshipTypeScan`). `query_terms` (the question's own words)
1181
+ * is the ONLY key removed from the payload, because it is the question
1182
+ * echoed back; the row projection is by the four NAMED keys
1183
+ * (chapterId/section/score/snippet), never a blind row copy, for the same
1184
+ * reason plus shape stability.
1185
+ *
1186
+ * @param {object} payload - the raw Theo structured_rows payload
1187
+ * @param {string} question - the original question (local only, never sent)
1188
+ * @param {{recommendChain?: Function, query?: Function}} [deps]
1189
+ * @returns {Promise<object>}
1190
+ */
1191
+ function _projectGroundingRows(rows) {
1192
+ return Array.isArray(rows)
1193
+ ? rows
1194
+ .filter((r) => r && typeof r === 'object')
1195
+ .map((r) => ({ chapterId: r.chapterId, section: r.section, score: r.score, snippet: r.snippet }))
1196
+ : [];
1197
+ }
1198
+
1199
+ async function _composeTheoAsk(payload, question, deps) {
1200
+ deps = deps || {};
1201
+ const recommend = deps.recommendChain || recommendChain;
1202
+ const runQuery = deps.query || query;
1203
+
1204
+ try {
1205
+ const rung = _inferRungFromQuestion(question);
1206
+
1207
+ let chainRes = null;
1208
+ let chainStatus;
1209
+ try {
1210
+ chainRes = await recommend(rung, 4);
1211
+ if (chainRes == null || chainRes.error || !Array.isArray(chainRes.chain)) {
1212
+ chainStatus = 'unreachable';
1213
+ } else if (chainRes.chain.length === 0) {
1214
+ chainStatus = 'empty';
1215
+ } else {
1216
+ chainStatus = 'ok';
1217
+ }
1218
+ } catch (_e) {
1219
+ chainStatus = 'unreachable';
1220
+ }
1221
+ const steps = chainStatus === 'ok' ? chainRes.chain : [];
1222
+
1223
+ // COMMANDS: exactly one runQuery call, only when there are steps, in its
1224
+ // own try/catch (on any failure every option gets commands: []).
1225
+ // Label-anchored and never relationship-first: Theo's read allow-list
1226
+ // refuses a template that starts from [:USES_FRAMEWORK].
1227
+ const commandsByFramework = new Map();
1228
+ if (steps.length > 0) {
1229
+ try {
1230
+ const names = steps
1231
+ .map((s) => s && s.framework)
1232
+ .filter((n) => typeof n === 'string');
1233
+ const result = await runQuery(
1234
+ 'MATCH (f:Framework) WHERE f.name IN $names OPTIONAL MATCH (c:MindrianCommand)-[:USES_FRAMEWORK]->(f) RETURN f.name AS framework, collect(DISTINCT c.name) AS commands',
1235
+ { names: names }
1236
+ );
1237
+ const records = (result && Array.isArray(result.records)) ? result.records : [];
1238
+ for (const rec of records) {
1239
+ if (!rec || typeof rec.framework !== 'string') continue;
1240
+ const slugs = Array.isArray(rec.commands)
1241
+ ? rec.commands
1242
+ .filter((c) => typeof c === 'string' && c.length > 0)
1243
+ .map((c) => c.replace(/^\/mos:/, ''))
1244
+ : [];
1245
+ commandsByFramework.set(rec.framework, slugs);
1246
+ }
1247
+ } catch (_e) {
1248
+ // any failure here -> every option gets commands: [] below (Map stays empty)
1249
+ }
1250
+ }
1251
+
1252
+ // CONFIDENCE: top step reads exactly 0.9; a missing/non-finite degree -> 0.5.
1253
+ const top = steps.reduce((max, s) => {
1254
+ const d = s && Number.isFinite(s.degree) ? s.degree : 0;
1255
+ return d > max ? d : max;
1256
+ }, 0);
1257
+ const options = steps.map((s) => {
1258
+ const degree = s && s.degree;
1259
+ let confidence = 0.5;
1260
+ if (top > 0 && Number.isFinite(degree)) {
1261
+ confidence = Math.max(0.5, Math.min(0.9, Math.round((0.5 + 0.4 * (degree / top)) * 100) / 100));
1262
+ }
1263
+ return {
1264
+ framework: s && s.framework,
1265
+ confidence: confidence,
1266
+ commands: commandsByFramework.get(s && s.framework) || [],
1267
+ };
1268
+ });
1269
+
1270
+ const out = Object.assign({}, payload);
1271
+ delete out.query_terms;
1272
+ out.directive = {
1273
+ guided: { questions: [], framework: (steps[0] && steps[0].framework) || null, stage: rung },
1274
+ };
1275
+ out.next_gate = { sub_shape: 'F.1', options: options };
1276
+ out.grounding = {
1277
+ source: 'theo',
1278
+ answer_mode: payload.answer_mode,
1279
+ rows: _projectGroundingRows(payload.rows),
1280
+ problem_type: rung,
1281
+ problem_type_source: 'heuristic',
1282
+ chain_coverage: (chainRes && chainRes.coverage && typeof chainRes.coverage === 'object') ? chainRes.coverage : null,
1283
+ chain_status: chainStatus,
1284
+ confidence_source: 'theo_degree_normalized',
1285
+ };
1286
+ return out;
1287
+ } catch (_e) {
1288
+ const rung = _inferRungFromQuestion(question);
1289
+ const out = Object.assign({}, payload);
1290
+ delete out.query_terms;
1291
+ out.directive = { guided: { questions: [], framework: null, stage: rung } };
1292
+ out.next_gate = { sub_shape: 'F.1', options: [] };
1293
+ out.grounding = {
1294
+ source: 'theo',
1295
+ answer_mode: payload.answer_mode,
1296
+ rows: _projectGroundingRows(payload.rows),
1297
+ problem_type: rung,
1298
+ problem_type_source: 'heuristic',
1299
+ chain_coverage: null,
1300
+ chain_status: 'unreachable',
1301
+ confidence_source: 'theo_degree_normalized',
1302
+ };
1303
+ return out;
1304
+ }
1305
+ }
1306
+
1307
+ /**
1308
+ * Natural-language methodology question against the Brain (wraps brain_ask).
1309
+ *
1310
+ * brain_ask auto-routes Pinecone/Neo4j server-side and handles its own
1311
+ * fallback -- it is the highest-level Brain entry point. Prefer it over a
1312
+ * hand-rolled Cypher query when the caller has a natural-language methodology
1313
+ * question. Canon Part 8: the question string carries only generic methodology
1314
+ * language -- never user artifacts, meeting text, or personal identifiers.
1315
+ *
1316
+ * Two response shapes on success:
1317
+ * - INCUMBENT: carries `directive` already -- returned BYTE-UNCHANGED, the
1318
+ * same object reference, no copy.
1319
+ * - THEO structured_rows: `{ answer_mode: 'structured_rows', rows: [...] }`
1320
+ * with no `directive` -- composed through `_composeTheoAsk` into a full
1321
+ * envelope payload (`directive`, `next_gate`, `grounding` added).
1322
+ * A `{ text: 'Error: ...' } / { error: ... }` sentinel passes through
1323
+ * UNCHANGED on a server-side error; null when the Brain is unreachable or no
1324
+ * API key is configured (graceful degradation -- mirrors query()).
1325
+ *
1326
+ * @param {string} question
1327
+ * @returns {Promise<object|null>}
1328
+ */
1329
+ async function ask(question) {
1330
+ if (typeof question !== 'string' || !question.trim()) return null;
1331
+ const raw = await callTool('brain_ask', { question: question });
1332
+ if (raw == null) return raw; // transport-null contract, byte-locked
1333
+ if (typeof raw !== 'object' || Array.isArray(raw)) return raw;
1334
+ if (raw.error) return raw; // egress_blocked / tier_denied / rate_limited / invalid_key
1335
+ if (raw.directive) return raw; // incumbent proof -- same object reference
1336
+ if (raw.answer_mode === 'structured_rows' && Array.isArray(raw.rows)) {
1337
+ return _composeTheoAsk(raw, question);
1338
+ }
1339
+ return raw;
1340
+ }
1341
+
1342
+ /**
1343
+ * Curated-op call against the Brain (the `op` MODE of brain_ask).
1344
+ *
1345
+ * brain_ask gained an optional curated-op surface (BUG 2 fix). Where ask()
1346
+ * runs the natural-language directive path, askOp() runs one of a closed set
1347
+ * of named, parameterized operations the Brain resolves to a FROZEN
1348
+ * server-side Cypher string. The three ops:
1349
+ *
1350
+ * - 'list_frameworks' params { limit? } -> rows { name, description, category }
1351
+ * - 'framework_edges' params { edge_type, limit? } -> rows { from, to, confidence, transform }
1352
+ * or { framework, problem_type }
1353
+ * - 'framework_chain_slice' params { seeds, max_hops?, limit? } -> rows { from, to, hop_distance }
1354
+ *
1355
+ * Canon Part 8: every param is a generic methodology handle (framework name,
1356
+ * closed enum, integer) -- never user content. No caller Cypher is ever sent;
1357
+ * the Brain owns the query text. This path is ungated -- any valid key may
1358
+ * call it (only query()/write() touch the admin-gated tools).
1359
+ *
1360
+ * Consumers that only need a framework chain for ONE anchor keep using
1361
+ * ask(question) and read next_gate.options[].framework (the directive path).
1362
+ *
1363
+ * Returns the parsed curated-op payload { op, source, count, rows, degraded? }
1364
+ * on success. On any transport / parse failure (Brain unreachable, no API key,
1365
+ * bad payload) returns a graceful { op, count: 0, rows: [], degraded: true } so
1366
+ * the caller never crashes -- mirrors query()'s graceful-degradation contract.
1367
+ *
1368
+ * @param {string} operation - one of the three curated op names
1369
+ * @param {object} [params] - generic-handles-only params object
1370
+ * @returns {Promise<{op: string, source?: string, count: number, rows: Array, degraded?: boolean}>}
1371
+ */
1372
+ /**
1373
+ * _normalizeAskOpResult(result, operation) -- the askOp() shape recognizer,
1374
+ * extracted so tests can drive it directly with zero network. Copies the
1375
+ * dual-shape recognizer STRUCTURE from
1376
+ * lib/core/enrichment-queue.cjs::captureReadinessMiss: one entry point, arms
1377
+ * keyed on payload SHAPE never on key presence alone.
1378
+ *
1379
+ * Theo op answers carry `coverage`, never `count`, so a `count`-keyed
1380
+ * recognizer alone reads a live three-row answer as a degraded zero. Arms,
1381
+ * in order:
1382
+ * 1. INCUMBENT: `count` is a number and `rows` is an array -- the exact
1383
+ * object askOp built before this task. MUST run first so an incumbent
1384
+ * payload can never take arm 2.
1385
+ * 2. THEO: `rows` is an array (no numeric `count` required) -- reports
1386
+ * `coverage.matched` as `count` when it is a finite number, else
1387
+ * `rows.length`.
1388
+ * 3. Everything else (null, an `{error:...}` sentinel, a `{text:...}`
1389
+ * passthrough, a non-array `rows`) -- the existing degraded sentinel.
1390
+ * Degrade ONLY when `rows` is not an array.
1391
+ *
1392
+ * @param {*} result
1393
+ * @param {string} operation
1394
+ * @returns {{op: string, source?: string, count: number, rows: Array, coverage?: object, degraded?: boolean}}
1395
+ */
1396
+ function _normalizeAskOpResult(result, operation) {
1397
+ if (result && typeof result === 'object'
1398
+ && typeof result.count === 'number' && Array.isArray(result.rows)) {
1399
+ return {
1400
+ op: result.op || operation,
1401
+ source: result.source,
1402
+ count: result.count,
1403
+ rows: result.rows,
1404
+ ...(result.degraded ? { degraded: true } : {}),
1405
+ };
1406
+ }
1407
+ if (result && typeof result === 'object' && Array.isArray(result.rows)) {
1408
+ const coverage = result.coverage;
1409
+ const matched = (coverage && typeof coverage === 'object' && Number.isFinite(coverage.matched))
1410
+ ? coverage.matched
1411
+ : result.rows.length;
1412
+ const out = {
1413
+ op: result.op || operation,
1414
+ source: 'theo',
1415
+ count: matched,
1416
+ rows: result.rows,
1417
+ };
1418
+ if (coverage && typeof coverage === 'object') out.coverage = coverage;
1419
+ return out;
1420
+ }
1421
+ return { op: operation, count: 0, rows: [], degraded: true };
1422
+ }
1423
+
1424
+ async function askOp(operation, params = {}) {
1425
+ try {
1426
+ return _normalizeAskOpResult(
1427
+ await callTool('brain_ask', { op: operation, params: params || {} }),
1428
+ operation
1429
+ );
1430
+ } catch (_err) {
1431
+ return { op: operation, count: 0, rows: [], degraded: true };
1432
+ }
1433
+ }
1434
+
1435
+ /**
1436
+ * Get Pinecone stats.
1437
+ */
1438
+ async function stats() {
1439
+ return callTool('brain_stats', {});
1440
+ }
1441
+
1442
+ /**
1443
+ * Return the resolved Brain endpoint (BRAIN_URL above, the module-level
1444
+ * single source of truth). The doctor store-identity sense (class-m-brain-
1445
+ * smoke.cjs layer 6) needs to report which endpoint the wire actually
1446
+ * resolved to, so it must read that value from the one place that decides
1447
+ * it rather than duplicating the canon literal as a second source of truth.
1448
+ *
1449
+ * @returns {string}
1450
+ */
1451
+ function getBrainUrl() {
1452
+ return BRAIN_URL;
1453
+ }
1454
+
1455
+ /**
1456
+ * Enrich local graph with causal edges from Brain's teaching graph.
1457
+ *
1458
+ * Queries the Brain Neo4j for causal framework chains relevant to the
1459
+ * given problem type or section keywords. Returns structured causal data
1460
+ * suitable for writing to local SQLite graph as CAUSES/ROOT_CAUSE_OF edges.
1461
+ *
1462
+ * @param {string} problemType - Room problem type (e.g., 'market-validation')
1463
+ * @param {string[]} sectionKeywords - Keywords from room sections for context
1464
+ * @param {object} [options] - Optional config
1465
+ * @param {number} [options.maxChainDepth=3] - Maximum causal chain depth
1466
+ * @param {number} [options.minConfidence=0.5] - Minimum confidence threshold
1467
+ * @returns {Promise<{ causes: Array, rootCauses: Array } | null>}
1468
+ * causes: [{ from, to, mechanism, confidence, framework }]
1469
+ * rootCauses: [{ from, to, chainLength, intermediateCauses, confidence }]
1470
+ */
1471
+ async function enrichCausalEdges(problemType, sectionKeywords, options = {}) {
1472
+ if (!isAvailable()) return null;
1473
+
1474
+ // SEC-01 defence-in-depth: coerce + bound numeric interpolants so a hostile
1475
+ // non-number (e.g. an object with .toString() side-effects) cannot reach
1476
+ // the Cypher string.
1477
+ const maxDepth = Math.max(1, Math.min(10, Number(options.maxChainDepth) || 3));
1478
+ const minConf = Math.max(0, Math.min(1, Number(options.minConfidence) || 0.5));
1479
+ const keywordFilter = sectionKeywords && sectionKeywords.length > 0
1480
+ ? sectionKeywords.map(k => `"${sanitizeCypherInput(k)}"`).join(', ')
1481
+ : '';
1482
+
1483
+ // Query 1: Direct causal relationships from framework chains
1484
+ const causesCypher = `
1485
+ MATCH (f1:Framework)-[r:ADDRESSES_PROBLEM_TYPE]->(pt:ProblemType)
1486
+ WHERE pt.name CONTAINS "${sanitizeCypherInput(problemType || '')}"
1487
+ WITH f1
1488
+ MATCH (f1)-[co:CO_OCCURS]->(f2:Framework)
1489
+ WHERE co.weight >= ${minConf}
1490
+ RETURN f1.name AS cause_framework,
1491
+ f2.name AS effect_framework,
1492
+ co.weight AS confidence,
1493
+ f1.description AS mechanism
1494
+ LIMIT 20
1495
+ `;
1496
+
1497
+ // Query 2: Root cause chains (multi-hop framework dependencies)
1498
+ const rootCauseCypher = `
1499
+ MATCH path = (root:Framework)-[:CO_OCCURS*1..${maxDepth}]->(leaf:Framework)
1500
+ WHERE root <> leaf
1501
+ ${keywordFilter ? `AND ANY(k IN [${keywordFilter}] WHERE root.name CONTAINS k OR root.description CONTAINS k)` : ''}
1502
+ WITH root, leaf, path, length(path) AS depth
1503
+ WHERE depth >= 2
1504
+ RETURN root.name AS root_cause,
1505
+ leaf.name AS symptom,
1506
+ depth AS chain_length,
1507
+ [n IN nodes(path) | n.name] AS chain_nodes
1508
+ LIMIT 10
1509
+ `;
1510
+
1511
+ try {
1512
+ const [causesResult, rootCausesResult] = await Promise.all([
1513
+ query(causesCypher),
1514
+ query(rootCauseCypher),
1515
+ ]);
1516
+
1517
+ const causes = [];
1518
+ const rootCauses = [];
1519
+
1520
+ // Parse causes
1521
+ if (causesResult && Array.isArray(causesResult.records)) {
1522
+ for (const rec of causesResult.records) {
1523
+ causes.push({
1524
+ from: rec.cause_framework || rec[0],
1525
+ to: rec.effect_framework || rec[1],
1526
+ mechanism: rec.mechanism || rec[3] || '',
1527
+ confidence: parseFloat(rec.confidence || rec[2] || 0),
1528
+ framework: rec.cause_framework || rec[0] || '',
1529
+ });
1530
+ }
1531
+ }
1532
+
1533
+ // Parse root causes
1534
+ if (rootCausesResult && Array.isArray(rootCausesResult.records)) {
1535
+ for (const rec of rootCausesResult.records) {
1536
+ rootCauses.push({
1537
+ from: rec.root_cause || rec[0],
1538
+ to: rec.symptom || rec[1],
1539
+ chainLength: parseInt(rec.chain_length || rec[2] || 1, 10),
1540
+ intermediateCauses: rec.chain_nodes || rec[3] || [],
1541
+ confidence: 1.0 / (parseInt(rec.chain_length || rec[2] || 1, 10) + 1),
1542
+ });
1543
+ }
1544
+ }
1545
+
1546
+ return { causes, rootCauses };
1547
+ } catch (err) {
1548
+ // Brain query failed -- return null for graceful degradation
1549
+ return null;
1550
+ }
1551
+ }
1552
+
1553
+ /**
1554
+ * Hat-aware framework recommendation.
1555
+ *
1556
+ * Reads persistent hat states and adjusts Brain framework queries:
1557
+ * - Black Hat concerns boost risk-related frameworks (Risk Matrix, SWOT threats)
1558
+ * - Yellow Hat opportunities boost HSI scoring and opportunity frameworks
1559
+ * - Blue Hat methodology notes avoid repeating ineffective frameworks
1560
+ *
1561
+ * @param {string} roomDir - Absolute path to room directory
1562
+ * @param {string} problemType - Room problem type
1563
+ * @param {object} [options] - Optional config
1564
+ * @param {number} [options.topK=5] - Number of frameworks to return
1565
+ * @returns {Promise<{ frameworks: Array, hat_influence: object } | null>}
1566
+ */
1567
+ async function hatAwareRecommend(roomDir, problemType, options = {}) {
1568
+ if (!isAvailable()) return null;
1569
+
1570
+ // Lazy-require to avoid circular dependency at module load time
1571
+ const { loadAllHatStates } = require('./hat-persistence.cjs');
1572
+ const hatStates = loadAllHatStates(roomDir);
1573
+ // SEC-01 defence-in-depth: bound topK numeric interpolation.
1574
+ const topK = Math.max(1, Math.min(100, Number(options.topK) || 5));
1575
+
1576
+ const hatInfluence = {
1577
+ risk_boost: false,
1578
+ opportunity_boost: false,
1579
+ avoid_frameworks: [],
1580
+ };
1581
+
1582
+ // Black Hat: if concerns exist, boost risk-related frameworks
1583
+ const blackConcerns = hatStates.black.top_concerns || [];
1584
+ const riskBoost = blackConcerns.length > 0;
1585
+ hatInfluence.risk_boost = riskBoost;
1586
+
1587
+ // Yellow Hat: if opportunities exist, boost HSI/opportunity frameworks
1588
+ const yellowOpps = hatStates.yellow.top_opportunities || [];
1589
+ const oppBoost = yellowOpps.length > 0;
1590
+ hatInfluence.opportunity_boost = oppBoost;
1591
+
1592
+ // Blue Hat: methodology notes may flag ineffective frameworks to avoid
1593
+ const blueNotes = hatStates.blue.methodology_notes || [];
1594
+
1595
+ // Part 8 raw-field egress guard (Phase 239 / BRAIN-02, threats T2, T4, T5).
1596
+ // Classifies the RAW problemType and each RAW blueNotes entry BEFORE
1597
+ // sanitizeCypherInput and BEFORE any Cypher interpolation below -- the raw
1598
+ // field is the only place the content signal is still intact. MEASURED:
1599
+ // classifying the ASSEMBLED cypher string instead would let the template's
1600
+ // own vocabulary word "Framework" launder an embedded canary from ambiguous
1601
+ // to allow, and sanitizeCypherInput strips the char the Part-8 PII pattern
1602
+ // keys on. Fail-closed: any missing or non-allow verdict skips the Brain leg.
1603
+ for (const rawField of [problemType].concat(blueNotes)) {
1604
+ let hatEgressVerdict = null;
1605
+ try {
1606
+ const hatEgressGuard = require('./part8-egress-guard.cjs');
1607
+ hatEgressVerdict = hatEgressGuard.classify(
1608
+ { question: String(rawField || '') },
1609
+ { toolName: 'brain_ask' }
1610
+ );
1611
+ } catch (_e) {
1612
+ hatEgressVerdict = null;
1613
+ }
1614
+ if (!hatEgressVerdict || hatEgressVerdict.verdict !== 'allow') {
1615
+ _logEventBestEffort(options.db, 'brain_egress_blocked', {
1616
+ egress_class: (hatEgressVerdict && hatEgressVerdict.class) || 'unknown',
1617
+ verdict: (hatEgressVerdict && hatEgressVerdict.verdict) || 'unverified',
1618
+ count: 1,
1619
+ created_by: 'system',
1620
+ source_path: 'system:brain-hat-recommend',
1621
+ });
1622
+ return null;
1623
+ }
1624
+ }
1625
+
1626
+ const avoidPatterns = blueNotes
1627
+ .filter(n => /ineffective|didn't work|not useful|skip|avoid/i.test(n))
1628
+ .map(n => {
1629
+ // Extract framework name from notes like "SWOT was ineffective for this stage"
1630
+ const match = n.match(/^(\w[\w\s]+?)\s+(?:was|is|were|proved)\s/i);
1631
+ return match ? match[1].trim() : null;
1632
+ })
1633
+ .filter(Boolean);
1634
+ hatInfluence.avoid_frameworks = avoidPatterns;
1635
+
1636
+ // Build Cypher query with hat-influenced scoring
1637
+ const safeProblemType = sanitizeCypherInput(problemType || '');
1638
+ const avoidClause = avoidPatterns.length > 0
1639
+ ? `AND NOT ANY(avoid IN [${avoidPatterns.map(a => `"${sanitizeCypherInput(a)}"`).join(', ')}] WHERE f.name CONTAINS avoid)`
1640
+ : '';
1641
+
1642
+ // Query: frameworks for problem type, with hat-influenced ordering
1643
+ const cypher = `
1644
+ MATCH (f:Framework)-[:ADDRESSES_PROBLEM_TYPE]->(pt:ProblemType)
1645
+ WHERE pt.name CONTAINS "${safeProblemType}"
1646
+ ${avoidClause}
1647
+ WITH f
1648
+ OPTIONAL MATCH (f)-[co:CO_OCCURS]->(f2:Framework)
1649
+ WITH f, count(co) AS connections
1650
+ RETURN f.name AS name,
1651
+ f.description AS description,
1652
+ connections,
1653
+ CASE
1654
+ WHEN ${riskBoost ? 'true' : 'false'} AND (f.name CONTAINS 'Risk' OR f.name CONTAINS 'SWOT' OR f.name CONTAINS 'Failure') THEN connections + 10
1655
+ WHEN ${oppBoost ? 'true' : 'false'} AND (f.name CONTAINS 'HSI' OR f.name CONTAINS 'Opportunity' OR f.name CONTAINS 'Innovation') THEN connections + 10
1656
+ ELSE connections
1657
+ END AS hat_score
1658
+ ORDER BY hat_score DESC
1659
+ LIMIT ${topK}
1660
+ `;
1661
+
1662
+ try {
1663
+ const result = await query(cypher);
1664
+ const frameworks = [];
1665
+
1666
+ if (result && Array.isArray(result.records)) {
1667
+ for (const rec of result.records) {
1668
+ frameworks.push({
1669
+ name: rec.name || rec[0],
1670
+ description: rec.description || rec[1],
1671
+ connections: parseInt(rec.connections || rec[2] || 0, 10),
1672
+ hat_score: parseInt(rec.hat_score || rec[3] || 0, 10),
1673
+ });
1674
+ }
1675
+ }
1676
+
1677
+ return {
1678
+ frameworks,
1679
+ hat_influence: hatInfluence,
1680
+ black_concerns: blackConcerns.slice(0, 3),
1681
+ yellow_opportunities: yellowOpps.slice(0, 3),
1682
+ blue_avoid: avoidPatterns,
1683
+ };
1684
+ } catch (err) {
1685
+ return null;
1686
+ }
1687
+ }
1688
+
1689
+ /**
1690
+ * Suggest validation steps for a banked opportunity using Brain framework chains.
1691
+ *
1692
+ * Queries Brain Neo4j for frameworks that ADDRESSES_PROBLEM_TYPE matching the
1693
+ * opportunity's domain/problem, then follows FEEDS_INTO chains to build a
1694
+ * suggested validation sequence.
1695
+ *
1696
+ * @param {Object} opportunity - Opportunity object with at minimum: problem, domain, knight_position
1697
+ * @param {Object} [options] - Optional config
1698
+ * @param {number} [options.maxSteps=5] - Maximum validation steps to return
1699
+ * @param {number} [options.chainDepth=3] - Maximum FEEDS_INTO chain depth
1700
+ * @returns {Promise<{ steps: Array<{framework: string, reason: string, order: number}>, chain_source: string } | null>}
1701
+ * Returns null if Brain unavailable (Tier 0 graceful degradation)
1702
+ */
1703
+ async function suggestValidationSteps(opportunity, options = {}) {
1704
+ if (!isAvailable()) return null;
1705
+ if (!opportunity || !opportunity.problem) return null;
1706
+
1707
+ // Part 8 raw-field egress guard (Phase 239 / BRAIN-02, threats T2, T4, T5).
1708
+ // Classifies the RAW opportunity.domain and the RAW opportunity.problem
1709
+ // BEFORE sanitizeCypherInput and BEFORE any Cypher interpolation below --
1710
+ // strictly upstream of sanitizeCypherInput, which is the entire point.
1711
+ // MEASURED: sanitizeCypherInput strips the '@' the Part-8 email pattern
1712
+ // keys on (jane@startup.com -> janestartup.com), flipping a block verdict
1713
+ // into an allow. Classifying the ASSEMBLED cypher string instead would also
1714
+ // let the template's own vocabulary word "Framework" launder an embedded
1715
+ // canary from ambiguous to allow. Fail-closed: any missing or non-allow
1716
+ // verdict skips the Brain leg entirely.
1717
+ for (const rawField of [opportunity.domain, opportunity.problem]) {
1718
+ let validationEgressVerdict = null;
1719
+ try {
1720
+ const validationEgressGuard = require('./part8-egress-guard.cjs');
1721
+ validationEgressVerdict = validationEgressGuard.classify(
1722
+ { question: String(rawField || '') },
1723
+ { toolName: 'brain_ask' }
1724
+ );
1725
+ } catch (_e) {
1726
+ validationEgressVerdict = null;
1727
+ }
1728
+ if (!validationEgressVerdict || validationEgressVerdict.verdict !== 'allow') {
1729
+ _logEventBestEffort(options.db, 'brain_egress_blocked', {
1730
+ egress_class: (validationEgressVerdict && validationEgressVerdict.class) || 'unknown',
1731
+ verdict: (validationEgressVerdict && validationEgressVerdict.verdict) || 'unverified',
1732
+ count: 1,
1733
+ created_by: 'system',
1734
+ source_path: 'system:brain-validation-steps',
1735
+ });
1736
+ return null;
1737
+ }
1738
+ }
1739
+
1740
+ const maxSteps = options.maxSteps || 5;
1741
+ const chainDepth = options.chainDepth || 3;
1742
+ const safeProblem = sanitizeCypherInput(opportunity.problem || '').substring(0, 200);
1743
+ const safeDomain = sanitizeCypherInput(opportunity.domain || '').substring(0, 100);
1744
+ const safeKnight = opportunity.knight_position || 'uncertainty';
1745
+
1746
+ // Query 1: Find frameworks that address this problem type / domain
1747
+ const matchCypher = `
1748
+ MATCH (f:Framework)-[:ADDRESSES_PROBLEM_TYPE]->(pt:ProblemType)
1749
+ WHERE pt.name CONTAINS "${safeDomain}"
1750
+ OR pt.description CONTAINS "${safeDomain}"
1751
+ RETURN f.name AS name, f.description AS description
1752
+ LIMIT 10
1753
+ `;
1754
+
1755
+ // Query 2: Follow FEEDS_INTO chains from matched frameworks
1756
+ const chainCypher = `
1757
+ MATCH (f:Framework)-[:ADDRESSES_PROBLEM_TYPE]->(pt:ProblemType)
1758
+ WHERE pt.name CONTAINS "${safeDomain}"
1759
+ OR pt.description CONTAINS "${safeDomain}"
1760
+ WITH f LIMIT 3
1761
+ MATCH path = (f)-[:FEEDS_INTO*1..${chainDepth}]->(next:Framework)
1762
+ RETURN f.name AS start_framework,
1763
+ next.name AS next_framework,
1764
+ next.description AS next_description,
1765
+ length(path) AS depth
1766
+ ORDER BY depth ASC
1767
+ LIMIT ${maxSteps * 2}
1768
+ `;
1769
+
1770
+ try {
1771
+ const [matchResult, chainResult] = await Promise.all([
1772
+ query(matchCypher),
1773
+ query(chainCypher),
1774
+ ]);
1775
+
1776
+ const steps = [];
1777
+ const seen = new Set();
1778
+
1779
+ // First: add the entry-point frameworks
1780
+ if (matchResult && Array.isArray(matchResult.records)) {
1781
+ for (const rec of matchResult.records) {
1782
+ const name = rec.name || rec[0];
1783
+ if (name && !seen.has(name) && steps.length < maxSteps) {
1784
+ seen.add(name);
1785
+ steps.push({
1786
+ framework: name,
1787
+ reason: safeKnight === 'uncertainty'
1788
+ ? `Explore this ${safeDomain} uncertainty with ${name}`
1789
+ : `Validate this ${safeDomain} risk using ${name}`,
1790
+ order: steps.length + 1,
1791
+ });
1792
+ }
1793
+ }
1794
+ }
1795
+
1796
+ // Then: add FEEDS_INTO chain steps
1797
+ if (chainResult && Array.isArray(chainResult.records)) {
1798
+ for (const rec of chainResult.records) {
1799
+ const name = rec.next_framework || rec[1];
1800
+ const desc = rec.next_description || rec[2] || '';
1801
+ if (name && !seen.has(name) && steps.length < maxSteps) {
1802
+ seen.add(name);
1803
+ steps.push({
1804
+ framework: name,
1805
+ reason: desc ? `Then apply ${name}: ${desc.substring(0, 120)}` : `Then apply ${name} (follows from chain)`,
1806
+ order: steps.length + 1,
1807
+ });
1808
+ }
1809
+ }
1810
+ }
1811
+
1812
+ if (steps.length === 0) return null;
1813
+
1814
+ return {
1815
+ steps,
1816
+ chain_source: 'brain_feeds_into',
1817
+ };
1818
+ } catch (err) {
1819
+ // Brain query failed -- graceful degradation
1820
+ return null;
1821
+ }
1822
+ }
1823
+
1824
+ /**
1825
+ * Write Cypher to Neo4j via Brain (write operations).
1826
+ * Used by sync-rooms-brain for creating Room/RoomGroup nodes and edges.
1827
+ * Returns null if Brain is unavailable -- never throws.
1828
+ *
1829
+ * NOTE (Finding I sibling, v1.10.9 hotfix 2026-04-15): same param-name
1830
+ * mismatch as brain_query had. Brain MCP brain_write expects `cypher`,
1831
+ * not `query`. This function had the mirror bug since inception but
1832
+ * never fired in production because sync-rooms-brain is rarely invoked
1833
+ * against the live Brain. Caught by the plan-checker audit for Phase 85.
1834
+ *
1835
+ * @param {string} cypher - Cypher write query
1836
+ * @returns {Promise<object|null>}
1837
+ */
1838
+ async function write(cypher) {
1839
+ return callTool('brain_write', { cypher: cypher });
1840
+ }
1841
+
1842
+ // ============================================================================
1843
+ // Loop-contract read wrappers (Phase 247-02, CONTRACT-01).
1844
+ // ============================================================================
1845
+ //
1846
+ // Thin pass-through wrappers over callTool for the five loop-contract tools
1847
+ // that had NO client wrapper before this phase (data/brain-surface-contract.json
1848
+ // is the source of truth for the tool name + arg-key shape each wrapper below
1849
+ // must emit; tests/test-247-contract-client.cjs derives its expectations from
1850
+ // that file, not from this comment). brain_stats is the sixth loop-contract
1851
+ // tool and already has a wrapper: stats() above.
1852
+ //
1853
+ // Every wrapper is a straight callTool(name, args) call with NO result
1854
+ // reshaping -- the tier_denied / invalid_key sentinels and the transport-null
1855
+ // pass through unchanged, exactly like write() and stats() already do.
1856
+ // Part 7 (extend, never a fourth brain skill): these live in this file.
1857
+ //
1858
+ // loopSearch() is named to avoid colliding with the existing search() export
1859
+ // above, which wraps the DIFFERENT brain_search tool (Pinecone semantic
1860
+ // search with quota-fallback handling). The loop-contract `search` tool is a
1861
+ // distinct, simpler read tool -- same English word, different Brain tool,
1862
+ // different client function, deliberately renamed so callers cannot conflate
1863
+ // them.
1864
+
1865
+ /**
1866
+ * Loop-contract `normalize_framework_name` tool.
1867
+ * @param {string} raw
1868
+ * @returns {Promise<object|null>}
1869
+ */
1870
+ async function normalizeFrameworkName(raw) {
1871
+ return callTool('normalize_framework_name', { raw: raw });
1872
+ }
1873
+
1874
+ /**
1875
+ * Loop-contract `search` tool (NOT brain_search -- see module-level note above).
1876
+ * @param {string} queryText
1877
+ * @param {number} [topK]
1878
+ * @returns {Promise<object|null>}
1879
+ */
1880
+ async function loopSearch(queryText, topK) {
1881
+ return callTool('search', { query: queryText, topK: topK });
1882
+ }
1883
+
1884
+ // ----------------------------------------------------------------------------
1885
+ // Phase 249-01 (ENRICH-01) -- capture-on-miss for the two readiness-shaped
1886
+ // loop wrappers below. NEITHER wrapper reshapes its return value (247-02's
1887
+ // "zero result reshaping so sentinels propagate unchanged" is preserved
1888
+ // here); capture happens in a try/catch side branch AFTER resolution, and
1889
+ // the raw callTool result is returned unchanged in every case, including
1890
+ // when the capture branch itself fails. opts is OPTIONAL and backward
1891
+ // compatible: no opts / no opts.roomDir means zero capture, zero throw,
1892
+ // identical behavior to the pre-249 wrapper.
1893
+ //
1894
+ // Sentinel discipline: a result carrying .error (tier_denied, invalid_key)
1895
+ // or a null (transport failure) is NEVER a capture trigger -- failure
1896
+ // visibility is Phase 250's refusal territory, not an enrichment miss
1897
+ // (research "Where the readiness probe should run").
1898
+ //
1899
+ // Each successful capture logs a scalar enrichment_queue_captured
1900
+ // memory_event (framework handle + score only), mirroring the existing
1901
+ // brain_packet_rejected precedent's _logEventBestEffort mechanism above --
1902
+ // best-effort, silently skipped when opts.db is absent.
1903
+ // ----------------------------------------------------------------------------
1904
+
1905
+ function _isCapturableResult(result) {
1906
+ return result !== null && typeof result === 'object' && !result.error;
1907
+ }
1908
+
1909
+ function _maybeCaptureEnrichmentMiss(frameworkName, result, opts) {
1910
+ try {
1911
+ if (!opts || typeof opts !== 'object') return;
1912
+ if (typeof opts.roomDir !== 'string' || opts.roomDir.length === 0) return;
1913
+ if (!_isCapturableResult(result)) return;
1914
+ // eslint-disable-next-line global-require
1915
+ const enrichmentQueue = require('./enrichment-queue.cjs');
1916
+ const captureResult = enrichmentQueue.captureReadinessMiss(
1917
+ opts.roomDir, frameworkName, result, opts.contextClass || {}
1918
+ );
1919
+ if (captureResult && captureResult.queued) {
1920
+ _logEventBestEffort(opts.db, 'enrichment_queue_captured', {
1921
+ framework: frameworkName,
1922
+ // Phase 339 (2026-09-03, FLIP-03): recognizes Theo's orchestration_readiness
1923
+ // payload shape (result.score, an integer 0-4 whose practical ceiling is 3,
1924
+ // /home/jsagi/Theo/src/mcp/content/orchestration-readiness.ts:494) alongside
1925
+ // the incumbent's result.readiness_score, in the SAME expression, each
1926
+ // guarded by typeof === 'number' (D-04) and never on key presence. The
1927
+ // incumbent's field is checked first so today's log bytes stay identical
1928
+ // when both are somehow present. Deliberately does NOT rescale, does NOT
1929
+ // default a missing score to 0, and does NOT read coverage -- coverage
1930
+ // semantics live in the capture path in lib/core/enrichment-queue.cjs
1931
+ // (plan 339-05's territory). null is still the final fallback so an
1932
+ // unrecognized shape is honestly null rather than a coerced zero.
1933
+ readiness_score: (typeof result.readiness_score === 'number')
1934
+ ? result.readiness_score
1935
+ : ((typeof result.score === 'number') ? result.score : null),
1936
+ source_path: 'system:enrichment-queue',
1937
+ });
1938
+ }
1939
+ } catch (_e) {
1940
+ // Best-effort capture side branch: NEVER surfaces to the wrapper caller.
1941
+ }
1942
+ }
1943
+
1944
+ /**
1945
+ * Loop-contract `discover_structure` tool.
1946
+ *
1947
+ * @param {string} frameworkName
1948
+ * @param {object} [opts] - { roomDir?, contextClass?, db? }. Phase 249-01:
1949
+ * when opts.roomDir is set and the resolved payload carries
1950
+ * grounded === false, best-effort captures an enrichment-queue entry.
1951
+ * Absent opts / opts.roomDir: zero capture, zero throw, unchanged
1952
+ * backward-compatible behavior.
1953
+ * @returns {Promise<object|null>}
1954
+ */
1955
+ async function discoverStructure(frameworkName, opts) {
1956
+ const result = await callTool('discover_structure', { framework_name: frameworkName });
1957
+ _maybeCaptureEnrichmentMiss(frameworkName, result, opts);
1958
+ return result;
1959
+ }
1960
+
1961
+ /**
1962
+ * Loop-contract `orchestration_readiness` tool.
1963
+ *
1964
+ * @param {string} frameworkName
1965
+ * @param {object} [opts] - { roomDir?, contextClass?, db? }. Phase 249-01:
1966
+ * when opts.roomDir is set and the resolved payload carries
1967
+ * readiness_score <= 2, best-effort captures an enrichment-queue entry.
1968
+ * Absent opts / opts.roomDir: zero capture, zero throw, unchanged
1969
+ * backward-compatible behavior.
1970
+ * @returns {Promise<object|null>}
1971
+ */
1972
+ async function orchestrationReadiness(frameworkName, opts) {
1973
+ const result = await callTool('orchestration_readiness', { framework_name: frameworkName });
1974
+ _maybeCaptureEnrichmentMiss(frameworkName, result, opts);
1975
+ return result;
1976
+ }
1977
+
1978
+ /**
1979
+ * Loop-contract `feeds_into_chains` tool.
1980
+ * @param {string[]} seeds
1981
+ * @param {number} [maxHops]
1982
+ * @returns {Promise<object|null>}
1983
+ */
1984
+ async function feedsIntoChains(seeds, maxHops) {
1985
+ return callTool('feeds_into_chains', { seeds: seeds, max_hops: maxHops });
1986
+ }
1987
+
1988
+ // Quick 260819-c8j (WS-C1): the enum-only Part 8 shape gate for
1989
+ // recommendChain(), below. `problemTypeOf(tuple)` (lib/core/insight-
1990
+ // sensors.cjs) returns `tuple.problem_type` VERBATIM off the /mos:diagnose
1991
+ // tuple, so the `brain_framework_chain:<pt>` companion string is NOT
1992
+ // structurally guaranteed to be a Brain ProblemType enum -- it could carry
1993
+ // anything the classifier wrote. A handle-shaped token (letter-initial,
1994
+ // letters/digits/space/hyphen only, 1..49 chars) cannot carry prose, a
1995
+ // sentence, a URL, an email, or a payload, so this regex is the enum-only
1996
+ // enforcement at the wire: a non-handle input is refused locally with ZERO
1997
+ // network rather than forwarded and hoped-safe.
1998
+ const PROBLEM_TYPE_HANDLE_RE = /^[A-Za-z][A-Za-z0-9 -]{0,48}$/;
1999
+
2000
+ // Projects the LOCAL sensor slugs (`problemTypeOf`'s output shape) onto the
2001
+ // Brain's canonical ProblemType node names. This is a PROJECTION, not an
2002
+ // allow-list: an unmapped token still passes THROUGH _normalizeBrainProblemType
2003
+ // unchanged, because data/brain-census.generated.json records 26 live
2004
+ // ProblemType nodes on the Brain and this map names only the 3 the local
2005
+ // sensors are known to emit today -- a local allow-list would silently block
2006
+ // the other 23 valid types the Brain already knows how to answer.
2007
+ //
2008
+ // Deliberately NOT the same map as this file's sibling,
2009
+ // lib/brain/chain-recommender.cjs's PROBLEM_TYPE_ALIASES (:71 there): that
2010
+ // map projects onto the LOCAL router codes UDP/IDP/WDP (a different target,
2011
+ // consumed by problem-type-router.cjs), not onto Brain ProblemType node
2012
+ // names. The two are not shared on purpose, so a reader cannot conflate a
2013
+ // local router code with a Brain node name. This stays true of BOTH tables
2014
+ // below.
2015
+ //
2016
+ // Phase 339 (2026-09-03): TWO tables, not one, plus an origin-keyed
2017
+ // selector. Theo matches WHERE toLower(m.id) = toLower($problemType) AND
2018
+ // NOTHING MORE (/home/jsagi/Theo/src/mcp/content/recommend-chain.ts:320-321,
2019
+ // the matching rule stated at :65-69): no trimming, no hyphen folding, no
2020
+ // suffix stripping, no fuzzy fallback. The incumbent's target value
2021
+ // 'Undefined Problem' matches nothing on Theo's :DomainConcept nodes, whose
2022
+ // ids are the exact strings 'UnDefined' / 'IllDefined' / 'WellDefined' /
2023
+ // 'Wicked' / 'Trinity' / 'Compass' (recommend-chain.ts:27-47). A single
2024
+ // shared table can never be correct for both origins at once -- the map
2025
+ // ITSELF is what breaks the post-flip match, so the map itself is what this
2026
+ // phase fixes, before the flip ships, not with it.
2027
+ //
2028
+ // This mechanism deliberately does NOT key on a response, does not retry on
2029
+ // PROBLEM_TYPE_NOT_FOUND using available_problem_types, and does not sniff
2030
+ // the server. A runtime retry would cost a round trip on every miss, would
2031
+ // paper over a vocabulary bug at runtime instead of fixing it, and would
2032
+ // break the property this selector exists to preserve: a
2033
+ // MINDRIAN_BRAIN_URL revert moves vocabulary and URL together, with no
2034
+ // second edit anywhere.
2035
+ //
2036
+ // Canon Part 8: both tables carry problem-type handles only, closed
2037
+ // vocabulary, never room content.
2038
+ const BRAIN_PROBLEM_TYPE_ALIASES_INCUMBENT = Object.freeze({
2039
+ 'undefined': 'Undefined Problem',
2040
+ udp: 'Undefined Problem',
2041
+ 'ill-defined': 'Ill-Defined Problem',
2042
+ ill_defined: 'Ill-Defined Problem',
2043
+ idp: 'Ill-Defined Problem',
2044
+ 'well-defined': 'Well-Defined Problem',
2045
+ well_defined: 'Well-Defined Problem',
2046
+ wdp: 'Well-Defined Problem',
2047
+ });
2048
+
2049
+ const BRAIN_PROBLEM_TYPE_ALIASES_THEO = Object.freeze({
2050
+ 'undefined': 'UnDefined',
2051
+ udp: 'UnDefined',
2052
+ 'ill-defined': 'IllDefined',
2053
+ ill_defined: 'IllDefined',
2054
+ idp: 'IllDefined',
2055
+ 'well-defined': 'WellDefined',
2056
+ well_defined: 'WellDefined',
2057
+ wdp: 'WellDefined',
2058
+ });
2059
+
2060
+ // The set of Brain origins whose problem-type vocabulary is Theo's, not the
2061
+ // incumbent's. An ARRAY, not a bare string literal, so a Theo staging
2062
+ // origin is a one-line addition rather than a re-architecture. Exported so
2063
+ // lib/core/doctor/class-m-brain-smoke.cjs can key its per-origin node floor
2064
+ // on this SAME set (plan 339-12), rather than minting a second copy of the
2065
+ // origin list.
2066
+ const THEO_ORIGINS = Object.freeze(['https://theo-mcp.onrender.com']);
2067
+
2068
+ // Selects the alias table by the resolved origin (BRAIN_URL, the same
2069
+ // module-scope const getBrainUrl() reads). Anything not in THEO_ORIGINS
2070
+ // gets the incumbent vocabulary -- the correct default while line 24 names
2071
+ // the incumbent, and the conservative default afterwards for any origin
2072
+ // this set does not yet name.
2073
+ function _brainProblemTypeAliases() {
2074
+ return THEO_ORIGINS.indexOf(BRAIN_URL) !== -1
2075
+ ? BRAIN_PROBLEM_TYPE_ALIASES_THEO
2076
+ : BRAIN_PROBLEM_TYPE_ALIASES_INCUMBENT;
2077
+ }
2078
+
2079
+ /**
2080
+ * _normalizeBrainProblemType(raw) -> canonical string | null
2081
+ * null when raw is not a string, is empty after trim, or fails the
2082
+ * PROBLEM_TYPE_HANDLE_RE shape gate above. A known local slug (matched
2083
+ * case-insensitively on the trimmed token) projects to its Brain canonical
2084
+ * name; anything else well-shaped passes through unchanged for the Brain to
2085
+ * rule on honestly.
2086
+ * @param {*} raw
2087
+ * @returns {string|null}
2088
+ */
2089
+ function _normalizeBrainProblemType(raw) {
2090
+ if (typeof raw !== 'string') return null;
2091
+ const trimmed = raw.trim();
2092
+ if (trimmed.length === 0) return null;
2093
+ if (!PROBLEM_TYPE_HANDLE_RE.test(trimmed)) return null;
2094
+ const lc = trimmed.toLowerCase();
2095
+ const table = _brainProblemTypeAliases();
2096
+ if (Object.prototype.hasOwnProperty.call(table, lc)) {
2097
+ return table[lc];
2098
+ }
2099
+ return trimmed;
2100
+ }
2101
+
2102
+ /**
2103
+ * Loop-contract `recommend_chain` tool. A straight sibling of
2104
+ * feedsIntoChains() above: ZERO result reshaping, so sentinels
2105
+ * (tier_denied/invalid_key) and the transport-null pass through untouched
2106
+ * (247-02 convention) -- match its behavior byte-for-byte except for the
2107
+ * pre-wire shape gate this tool needs and the sibling above does not.
2108
+ *
2109
+ * Live output shape (verified live 2026-08-19 AND now contractually pinned):
2110
+ * { tool, backend, grounded, problem_type, seed_candidates,
2111
+ * chain: [{ step, framework, pagerank, edge_confidence, commands }],
2112
+ * note, execution_hint }
2113
+ * The Brain team froze recommend_chain as a v1 ADDITIVE-ONLY contract in
2114
+ * their own repo (ProblemsWorthSolving-Brain, docs/RECOMMEND-CHAIN-
2115
+ * CONTRACT.md, commit 721d8e1): under v1 these keys may gain siblings but
2116
+ * will not be renamed or removed, and an unknown problem type is GUARANTEED
2117
+ * to come back as { grounded: false, chain: [] } rather than an error or a
2118
+ * throw. This wrapper does not depend on any of that shape -- it is a pure
2119
+ * pass-through -- so a v2 or an out-of-contract change degrades at the
2120
+ * consumer (lib/brain/chain-recommender.cjs), never here.
2121
+ *
2122
+ * @param {string} problemType
2123
+ * @param {number} [maxSteps]
2124
+ * @returns {Promise<object|null>}
2125
+ */
2126
+ async function recommendChain(problemType, maxSteps) {
2127
+ const normalized = _normalizeBrainProblemType(problemType);
2128
+ if (normalized === null) {
2129
+ // Shape gate failed: refuse locally, zero network, zero key consumption,
2130
+ // zero auto-register attempt. T-c8j-01 mitigation.
2131
+ return { error: 'invalid_problem_type', tool: 'recommend_chain' };
2132
+ }
2133
+ const steps = Number.isInteger(maxSteps) && maxSteps >= 1 && maxSteps <= 6 ? maxSteps : 6;
2134
+ return callTool('recommend_chain', { problem_type: normalized, max_steps: steps });
2135
+ }
2136
+
2137
+ // Phase 252-01 (SWEEP-01, guard sweep) -- getTier0Chain() / getFrameworkChain()
2138
+ // DELETED here. They served hardcoded, non-graph-grounded persona chains
2139
+ // with `source: 'tier0'` -- the counterfeit core the 250-01 doctrine kill
2140
+ // list (site #11) marked but deferred flipping. Live grep at 252 execution
2141
+ // time confirmed zero CJS consumers outside this file; their only consumer
2142
+ // was the conversation-mode SKILL instruction (skills/conversation-mode/
2143
+ // SKILL.md), rewritten in this same task to follow the refusal rail instead
2144
+ // of calling either function. The 247-02 do-not-widen note on the null
2145
+ // branch (`callTool()`'s transport-failure contract) is untouched -- this
2146
+ // deletion removes a local fallback CHAIN, not the null contract.
2147
+
2148
+ // ============================================================================
2149
+ // Phase 110-03: Brain Context Packet wire enforcement (Canon Part 8 + Part 9).
2150
+ // ============================================================================
2151
+ //
2152
+ // sendPacket(packet, opts) is the SOLE typed-packet wire path. It runs:
2153
+ // (1) D-08 layer-3 origin allowlist (the belt to the schema's suspenders --
2154
+ // a caller who bypassed ajv still gets caught here).
2155
+ // (2) Unknown-job guard against the closed D-02 vocabulary (12 jobs).
2156
+ // (3) ajv in-validation (reject hard: throw + log brain_packet_rejected).
2157
+ // (4) POST tools/call name:'brain_packet' via the existing callTool transport
2158
+ // (degrade soft when the tool is absent: { advice:null, reason:
2159
+ // 'brain_packet_tool_absent' }; on transport error: { advice:null,
2160
+ // reason: 'brain_unreachable' }). Never throws on Brain-side problems.
2161
+ // (5) ajv out-validation (reject hard, degrade soft: log
2162
+ // brain_response_rejected + return { advice:null, reason:
2163
+ // 'response_schema_invalid' }). Never throws, never partial-ingests.
2164
+ // (6) Returns the validated out payload on success.
2165
+ //
2166
+ // ajv@8.18.0 is transitive via @modelcontextprotocol/sdk -- do NOT add it to
2167
+ // package.json (CLAUDE.md "What NOT to Use"). strict:false at runtime; the
2168
+ // strict:true build gate lives in scripts/build-brain-packet-schema.cjs. The
2169
+ // schema is draft 2020-12, so Ajv2020 is required (ajv/dist/2020).
2170
+ //
2171
+ // SHIPPED_JOBS is the D-02 closed vocabulary (locked in 110-CONTEXT D-02).
2172
+
2173
+ const Ajv2020 = require('ajv/dist/2020').default || require('ajv/dist/2020');
2174
+
2175
+ const _BRAIN_PACKET_SCHEMA_PATH = process.env.MINDRIAN_BRAIN_PACKET_SCHEMA
2176
+ || path.join(__dirname, '..', '..', 'data', 'brain-packet-schema.json');
2177
+
2178
+ const SHIPPED_JOBS = new Set([
2179
+ 'select_methodology',
2180
+ 'suggest_next_move',
2181
+ 'detect_contradiction',
2182
+ 'summarize_neighborhood',
2183
+ 'classify_room_budding',
2184
+ 'rank_assumptions',
2185
+ 'generate_feynman_explanation',
2186
+ 'strengthen_minto',
2187
+ 'prepare_investor_brief',
2188
+ 'opportunity_react',
2189
+ 'opportunity_reflect',
2190
+ 'opportunity_rank',
2191
+ ]);
2192
+
2193
+ let _ajv = null;
2194
+ let _schemaRaw = null;
2195
+ const _jobValidators = new Map(); // job -> { in: fn, out: fn }
2196
+
2197
+ /**
2198
+ * Lazily compile the brain-packet schema. Reads data/brain-packet-schema.json
2199
+ * (or process.env.MINDRIAN_BRAIN_PACKET_SCHEMA, the test seam set up by 110-01)
2200
+ * once at first use; subsequent calls reuse the in-memory Ajv instance.
2201
+ *
2202
+ * NOTE on the schema's pointer shape: the 110-01 schema's per-job $defs are
2203
+ * { in: {...}, out: {...} } -- NOT { properties: { in, out } }. So the JSON
2204
+ * pointer for a job half is #/$defs/<job>/<half>, not the planner's interfaces
2205
+ * sketch shape #/$defs/<job>/properties/<half>. Compiled wrappers carry the
2206
+ * root's $defs inline because ajv 8.x cannot resolve a deep JSON pointer into
2207
+ * a schema indexed only by its absolute $id (a known ajv@8 quirk -- the build
2208
+ * script in scripts/build-brain-packet-schema.cjs validates the root by
2209
+ * compiling it directly with the same Ajv2020 class).
2210
+ */
2211
+ function _ensureSchema() {
2212
+ if (_ajv) return;
2213
+ _schemaRaw = JSON.parse(fs.readFileSync(_BRAIN_PACKET_SCHEMA_PATH, 'utf8'));
2214
+ _ajv = new Ajv2020({ allErrors: true, strict: false });
2215
+ // Pre-add the root so cross-refs (FocusNode, Origin, BankedOpportunities,
2216
+ // BrainResponse, etc.) resolve when sub-schemas reference them. Wrapper
2217
+ // schemas below carry $defs inline as a defensive duplicate.
2218
+ _ajv.addSchema(_schemaRaw);
2219
+ }
2220
+
2221
+ /**
2222
+ * Return the memoized in/out validator for a given (job, half). Half is
2223
+ * 'in' or 'out'. Compiles on first use.
2224
+ *
2225
+ * @param {string} job - D-02 jobname (must be in SHIPPED_JOBS).
2226
+ * @param {'in'|'out'} half
2227
+ * @returns {Function} a compiled ajv validator function.
2228
+ */
2229
+ function _validatorFor(job, half) {
2230
+ _ensureSchema();
2231
+ let pair = _jobValidators.get(job);
2232
+ if (!pair) { pair = {}; _jobValidators.set(job, pair); }
2233
+ if (!pair[half]) {
2234
+ pair[half] = _ajv.compile({
2235
+ $id: 'urn:mindrian:brain-packet:' + job + ':' + half,
2236
+ $ref: '#/$defs/' + job + '/' + half,
2237
+ $defs: _schemaRaw.$defs,
2238
+ });
2239
+ }
2240
+ return pair[half];
2241
+ }
2242
+
2243
+ /**
2244
+ * Reset the lazy schema state. Test hook only -- NOT part of the public API.
2245
+ * Used by 110-05 (the per-job validation suite) and 110-04 (the pre-commit
2246
+ * tripwire) to swap the schema seam (MINDRIAN_BRAIN_PACKET_SCHEMA) and re-run.
2247
+ */
2248
+ function _resetSchema() {
2249
+ _ajv = null;
2250
+ _schemaRaw = null;
2251
+ _jobValidators.clear();
2252
+ }
2253
+
2254
+ /**
2255
+ * Best-effort memory_event log. Skips silently if opts.db is absent OR if the
2256
+ * navigation re-export throws (Brain telemetry should never break a Brain call).
2257
+ *
2258
+ * @param {object} db - SQLite db handle from openRoomDb (or undefined).
2259
+ * @param {string} eventType - one of 'brain_packet_rejected' /
2260
+ * 'brain_response_rejected' / 'brain_legacy_path_used'
2261
+ * (Phase 110-02 added these to EVENT_TYPES).
2262
+ * @param {object} payload - { job, errors, source_path, ... } scalars only.
2263
+ */
2264
+ function _logEventBestEffort(db, eventType, payload) {
2265
+ if (!db) return;
2266
+ try {
2267
+ // Lazy-require to avoid a circular load between brain-client.cjs and
2268
+ // navigation.cjs (navigation.cjs does NOT require brain-client.cjs today,
2269
+ // but this stays robust if a future closure of the loop ever happens).
2270
+ require('./navigation.cjs').logMemoryEvent(db, eventType, payload || {});
2271
+ } catch (_e) { /* best-effort */ }
2272
+ }
2273
+
2274
+ /**
2275
+ * Detect a "Brain doesn't recognize this tool" error in the callTool result.
2276
+ * Per the callTool JSDoc around line ~329, the result on a server-side error
2277
+ * is shaped { text: 'Error: ...' } (Cypher errors pass through the same way).
2278
+ * A missing brain_packet tool will show up as -32602 / 'unknown tool' / 'No
2279
+ * such tool' / a 404-ish marker. Be liberal: D-04 says "degrade gracefully
2280
+ * when the Brain doesn't recognize the contract."
2281
+ *
2282
+ * @param {*} result
2283
+ * @returns {boolean}
2284
+ */
2285
+ function _looksLikeUnknownToolError(result) {
2286
+ if (!result) return false;
2287
+ let t = '';
2288
+ if (typeof result === 'string') t = result;
2289
+ else if (typeof result === 'object') t = result.text || result.error || JSON.stringify(result);
2290
+ const s = String(t).toLowerCase();
2291
+ return s.includes('unknown tool')
2292
+ || s.includes('no such tool')
2293
+ || s.includes('method not found')
2294
+ || s.includes('-32602')
2295
+ || (s.includes('brain_packet') && (s.includes('not') || s.includes('unknown')))
2296
+ || s.includes('tool not found');
2297
+ }
2298
+
2299
+ /**
2300
+ * Unwrap the callTool result to the Brain response object the schema wants to
2301
+ * validate. callTool returns:
2302
+ * - an array (the tools/call content array on a successful brain_query-style
2303
+ * parsed result),
2304
+ * - or a parsed object (when content[0] is text and parses as JSON),
2305
+ * - or { text: '...' } (text content that did not parse as JSON, or an error
2306
+ * string),
2307
+ * - or null (unreachable / no API key -- handled before this is called).
2308
+ *
2309
+ * For brain_packet specifically, the Brain side (when implemented) will return
2310
+ * a BrainResponse-shaped JSON object: { job_id, suggestions: [...] }. Older
2311
+ * test transports inject [{ type:'text', text: JSON.stringify(...) }] (the raw
2312
+ * MCP content array shape). Both shapes are unwrapped to the response object.
2313
+ *
2314
+ * @param {*} result
2315
+ * @returns {object}
2316
+ */
2317
+ function _parseBrainResult(result) {
2318
+ if (Array.isArray(result)) {
2319
+ for (const item of result) {
2320
+ if (item && item.type === 'text' && typeof item.text === 'string') {
2321
+ try { return JSON.parse(item.text); } catch (_) { /* fall through */ }
2322
+ }
2323
+ }
2324
+ return { suggestions: [] };
2325
+ }
2326
+ if (result && typeof result === 'object') {
2327
+ // text-only error/string shape -- try to parse, else return { suggestions: [] }
2328
+ if (typeof result.text === 'string' && !result.job_id && !result.suggestions) {
2329
+ try { return JSON.parse(result.text); } catch (_) { return { suggestions: [] }; }
2330
+ }
2331
+ return result;
2332
+ }
2333
+ if (typeof result === 'string') {
2334
+ try { return JSON.parse(result); } catch (_) { return { suggestions: [] }; }
2335
+ }
2336
+ return { suggestions: [] };
2337
+ }
2338
+
2339
+ // ----------------------------------------------------------------------------
2340
+ // _warnLegacyOnce -- the D-10 dual-path deprecation guard.
2341
+ //
2342
+ // As of v1.13.0-beta.3 there is NO legacy free-form Brain *job* call site --
2343
+ // new job-style work goes through sendPacket(). The guard ships now as a
2344
+ // forward-looking contract: if a free-form job helper is ever added before
2345
+ // v1.14.0, it MUST call _warnLegacyOnce() first; in v1.14.0 both the helper
2346
+ // and this guard are deleted.
2347
+ //
2348
+ // query() / write() / search() / schema() / callTool() / ask() / stats() are
2349
+ // NOT "legacy" -- raw-Cypher methodology lookups carry only generic handles
2350
+ // (framework names, phase identifiers, problem-type enums) and are Part-8
2351
+ // clean by construction; they are PERMANENT.
2352
+ //
2353
+ // Module-level flag idiom mirrors checkFilePermissions._warned (the existing
2354
+ // once-per-process warning pattern in this file).
2355
+ // ----------------------------------------------------------------------------
2356
+
2357
+ let _legacyPathWarned = false;
2358
+
2359
+ function _warnLegacyOnce(db) {
2360
+ if (_legacyPathWarned) return;
2361
+ _legacyPathWarned = true;
2362
+ // eslint-disable-next-line no-console
2363
+ console.warn('[mindrian-os] legacy free-form Brain job call detected. '
2364
+ + 'Migrate to brain-client.sendPacket() -- the legacy job path is removed in v1.14.0.');
2365
+ _logEventBestEffort(db, 'brain_legacy_path_used', {
2366
+ source_path: 'system:brain-legacy',
2367
+ });
2368
+ }
2369
+
2370
+ /**
2371
+ * Validate-then-route a Brain Context Packet. The ONLY door for typed Brain
2372
+ * job calls.
2373
+ *
2374
+ * Per CONTEXT D-07 "reject hard, degrade soft":
2375
+ * - Bad in-packet (a programmer error in OUR code) -> throw.
2376
+ * - Bad origin (D-08 layer 3) -> throw.
2377
+ * - Unknown job -> throw.
2378
+ * - Brain unreachable -> { advice: null, reason: 'brain_unreachable' } (no throw).
2379
+ * - brain_packet tool absent on the Brain -> { advice: null, reason:
2380
+ * 'brain_packet_tool_absent' } (no throw, no log -- D-04 graceful degrade
2381
+ * is NOT a bad-response situation; a half-trusted response IS one).
2382
+ * - Bad out-response -> { advice: null, reason: 'response_schema_invalid' }
2383
+ * + log brain_response_rejected. NEVER throws, NEVER partial-ingests.
2384
+ *
2385
+ * @param {object} packet - MUST come from lib/core/navigation.cjs::buildBrainPacket
2386
+ * (D-01 chokepoint; D-08 layer 2 enforces lexically;
2387
+ * D-08 layer 3 is the origin check below).
2388
+ * @param {object} [opts] - { db?, roomDir?, __transport? }
2389
+ * db -> the SQLite handle for the memory_event log
2390
+ * roomDir -> reserved for future use; not read today
2391
+ * __transport -> optional test seam: a function
2392
+ * (toolName, args) => Promise<result> that
2393
+ * replaces callTool (lets tests inject a
2394
+ * fake without require.cache surgery)
2395
+ * @returns {Promise<object>} the validated Brain out on success; a no-advice
2396
+ * sentinel on a soft-degrade.
2397
+ * @throws on a bad in-packet, a bad origin (D-08 layer 3), or an unknown job.
2398
+ */
2399
+ // PARKED (2026-07-30, Phase 239, BRAIN-03): ZERO production sendPacket(
2400
+ // consumers. Census across lib/, scripts/, bin/, pipelines/: the only
2401
+ // definition is this one; non-definition references are this file's own
2402
+ // export/comments, tests/test-brain-packet-validation-per-job.cjs, and the
2403
+ // D-08 layer-2 guard (scripts/check-schema-aliases.cjs --check-sendpacket).
2404
+ // This reconciles two prior contradictory claims: navigation/packet.cjs:105
2405
+ // ("zero production consumers today") was TRUE; test-150-brain-egress.cjs:12
2406
+ // ("FIRST real sendPacket consumer") was FALSE and is corrected in this
2407
+ // change. RULING: PARKED, not wired -- wiring it to a real job is net-new
2408
+ // feature work, forbidden inside this remediation-only milestone (RESEARCH.md
2409
+ // A3, a navigator-equivalent ruling, cheap to overturn). CONSEQUENCE: the
2410
+ // PB8-10 belt below (step 3.5) is correct code on an unreached path -- do NOT
2411
+ // count it as live Part 8 coverage; the live in-process coverage is sibling
2412
+ // plan 239-05's raw-field guard in hatAwareRecommend()/suggestValidationSteps().
2413
+ // RE-OPEN CONDITION: the first real caller. Caught by the D-08 layer-2
2414
+ // pre-commit guard (requires a preceding buildBrainPacket() call) and by
2415
+ // tests/test-239-sendpacket-parked.cjs LEG 1's census. See also the matching
2416
+ // ADR amendment in docs/architecture/SUBSTRATE-CONTRACT.md (Phase 239-06).
2417
+ async function sendPacket(packet, opts) {
2418
+ const o = opts || {};
2419
+
2420
+ // (1) D-08 layer 3 -- the belt to the schema's suspenders. Runs FIRST so a
2421
+ // caller who bypassed ajv still gets caught.
2422
+ if (!packet || typeof packet.origin !== 'string') {
2423
+ throw new Error('brain packet missing origin (D-08): packets must come from buildBrainPacket');
2424
+ }
2425
+ if (packet.origin === 'test_fixture' && process.env.MINDRIAN_TEST_MODE !== '1') {
2426
+ throw new Error('brain packet origin "test_fixture" only valid when MINDRIAN_TEST_MODE=1');
2427
+ }
2428
+ if (packet.origin !== 'navigation_api' && packet.origin !== 'test_fixture') {
2429
+ throw new Error('brain packet origin "' + packet.origin
2430
+ + '" not in the closed allowlist (D-08): navigation_api | test_fixture');
2431
+ }
2432
+
2433
+ // (2) Unknown-job guard.
2434
+ if (!SHIPPED_JOBS.has(packet.job)) {
2435
+ throw new Error('brain packet: unknown job "' + packet.job
2436
+ + '" (not in the D-02 closed vocabulary)');
2437
+ }
2438
+
2439
+ // (3) in-validation -- reject hard.
2440
+ const inFn = _validatorFor(packet.job, 'in');
2441
+ if (!inFn(packet)) {
2442
+ // Pitfall 1: snapshot errors immediately -- validate.errors is overwritten
2443
+ // on every subsequent validate() call.
2444
+ const errsSnapshot = (inFn.errors || []).slice();
2445
+ const errStr = errsSnapshot
2446
+ .map(function (e) { return (e.instancePath || '(root)') + ' ' + (e.message || ''); })
2447
+ .join('; ');
2448
+ _logEventBestEffort(o.db, 'brain_packet_rejected', {
2449
+ job: packet.job,
2450
+ errors: errsSnapshot.length,
2451
+ source_path: 'system:brain-packet',
2452
+ });
2453
+ throw new Error('brain packet rejected for job "' + packet.job + '": ' + errStr);
2454
+ }
2455
+
2456
+ // (3.5) PB8-10 -- the in-sendPacket defense-in-depth belt. sendPacket is the
2457
+ // SOLE typed-packet wire path; this is the last LOCAL check before the
2458
+ // packet goes on the wire. It COMPLEMENTS the required PreToolUse hook
2459
+ // (D-02, the primary), never replaces it. Runs the pure LOCAL classifier
2460
+ // (zero network, zero judge wire, D-01):
2461
+ // - 'block' verdict -> refuse the send, returning the same soft-degrade
2462
+ // sentinel the unreachable path uses; best-effort log brain_egress_blocked.
2463
+ // - 'ambiguous' verdict -> belt posture: the hook is the gate, so do NOT
2464
+ // double-prompt; best-effort log brain_egress_ambiguous and allow through.
2465
+ // The whole belt is wrapped so a belt-INTERNAL error degrades to the
2466
+ // existing behavior (the hook remains the primary). It NEVER throws and
2467
+ // NEVER opens a Brain wire to judge.
2468
+ try {
2469
+ const _guard = require('./part8-egress-guard.cjs');
2470
+ const _verdict = _guard.classify(packet, { toolName: 'brain_packet' });
2471
+ if (_verdict && _verdict.verdict === 'block') {
2472
+ _logEventBestEffort(o.db, 'brain_egress_blocked', {
2473
+ egress_class: _verdict.class || 'content_set',
2474
+ verdict: 'block',
2475
+ count: 1,
2476
+ created_by: 'system',
2477
+ source_path: 'system:brain-packet',
2478
+ });
2479
+ return { advice: null, reason: 'egress_blocked' };
2480
+ }
2481
+ if (_verdict && _verdict.verdict === 'ambiguous') {
2482
+ _logEventBestEffort(o.db, 'brain_egress_ambiguous', {
2483
+ egress_class: _verdict.class || 'unknown',
2484
+ verdict: 'ambiguous',
2485
+ count: 1,
2486
+ created_by: 'system',
2487
+ source_path: 'system:brain-packet',
2488
+ });
2489
+ // belt posture: allow the hook to have been the gate; do not double-prompt.
2490
+ }
2491
+ } catch (_e) {
2492
+ // belt-internal error degrades to existing behavior (the hook is the primary, D-02).
2493
+ }
2494
+
2495
+ // (4) Build the wire envelope + POST. Reuse callTool's transport (it does
2496
+ // tools/call over Streamable HTTP + SSE-parse). Tests can inject a fake
2497
+ // via opts.__transport.
2498
+ const transport = (typeof o.__transport === 'function') ? o.__transport : callTool;
2499
+ let result;
2500
+ try {
2501
+ result = await transport('brain_packet', { packet: packet });
2502
+ } catch (_e) {
2503
+ // Network / transport error: treat like the Brain being unreachable.
2504
+ // No log -- it is not a leak, and not a contract violation either.
2505
+ return { advice: null, reason: 'brain_unreachable' };
2506
+ }
2507
+ if (result == null) {
2508
+ // callTool returns null when there is no API key or when the HTTP layer
2509
+ // returned a non-ok status -- functionally the same as "Brain unreachable".
2510
+ return { advice: null, reason: 'brain_unreachable' };
2511
+ }
2512
+ if (_looksLikeUnknownToolError(result)) {
2513
+ // The live Brain has no brain_packet tool yet (D-04 generalized).
2514
+ // Degrade soft, NO brain_response_rejected log -- this is NOT a bad
2515
+ // response; it is the absence of a contract handler.
2516
+ return { advice: null, reason: 'brain_packet_tool_absent' };
2517
+ }
2518
+
2519
+ // (5) out-validation -- reject hard but degrade soft.
2520
+ const parsed = _parseBrainResult(result);
2521
+ const outFn = _validatorFor(packet.job, 'out');
2522
+ if (!outFn(parsed)) {
2523
+ const errsSnapshot = (outFn.errors || []).slice();
2524
+ _logEventBestEffort(o.db, 'brain_response_rejected', {
2525
+ job: packet.job,
2526
+ errors: errsSnapshot.length,
2527
+ source_path: 'system:brain-packet',
2528
+ });
2529
+ // NEVER throw on a bad response; NEVER partial-ingest.
2530
+ return { advice: null, reason: 'response_schema_invalid' };
2531
+ }
2532
+
2533
+ // (6) Success -- the caller gets a known-good shape (typically then calls
2534
+ // navigation.storeBrainSuggestions).
2535
+ return parsed;
2536
+ }
2537
+
2538
+ module.exports = {
2539
+ isAvailable,
2540
+ ensureAvailable,
2541
+ getAutoRegisterFailureReason,
2542
+ getApiKey,
2543
+ callTool,
2544
+ query,
2545
+ write,
2546
+ search,
2547
+ smartSearch,
2548
+ ask,
2549
+ askOp,
2550
+ // Quick 260910-hni: the pure local rung heuristic ask()/_composeTheoAsk
2551
+ // uses to classify a question before the one recommend_chain wire call.
2552
+ // A NAMED export per the locked design, not a _test-only member.
2553
+ _inferRungFromQuestion,
2554
+ schema,
2555
+ stats,
2556
+ getBrainUrl,
2557
+ // Phase 339 (FLIP-02): the set of Brain origins whose problem-type
2558
+ // vocabulary is Theo's. Exported so class-m-brain-smoke.cjs's per-origin
2559
+ // node floor (plan 339-12) keys on this SAME set rather than minting a
2560
+ // second copy.
2561
+ THEO_ORIGINS,
2562
+ enrichCausalEdges,
2563
+ hatAwareRecommend,
2564
+ suggestValidationSteps,
2565
+ // getFrameworkChain: DELETED (Phase 252-01, SWEEP-01) -- the counterfeit
2566
+ // Tier-0 hardcoded-chain fallback. Zero CJS consumers at deletion time.
2567
+ // Phase 247-02 (CONTRACT-01): loop-contract read wrappers. brain_stats is
2568
+ // the sixth loop tool and is already exported above as stats().
2569
+ normalizeFrameworkName,
2570
+ loopSearch,
2571
+ discoverStructure,
2572
+ orchestrationReadiness,
2573
+ feedsIntoChains,
2574
+ // Quick 260819-c8j (WS-C1): recommend_chain is the seventh loop-contract
2575
+ // read wrapper, a sibling of feedsIntoChains above.
2576
+ recommendChain,
2577
+ // Phase 110-03 (Brain Context Packet Contract wire-level enforcement):
2578
+ // sendPacket is the SOLE typed-packet wire path; _warnLegacyOnce is the
2579
+ // forward-looking D-10 deprecation guard with no current call site.
2580
+ sendPacket,
2581
+ _warnLegacyOnce,
2582
+ // SEC-01/SEC-02 + CASCADE-06 test surface: not part of the public API.
2583
+ // See lib/memory/security-trifecta.test.cjs + brain-cache-lru.test.cjs.
2584
+ // Helpers are small and pure. sessionCache + _ensureSession + _hashKey +
2585
+ // SESSION_TTL_MS are exposed for Phase 87-07 cache-behavior tests.
2586
+ // Phase 110-03: ajv middleware test seam (_resetSchema clears the lazy
2587
+ // ajv state so a test can swap MINDRIAN_BRAIN_PACKET_SCHEMA and re-run;
2588
+ // _setLegacyWarned resets the once-per-session deprecation flag;
2589
+ // _validatorFor / _parseBrainResult / _looksLikeUnknownToolError are
2590
+ // the helpers Plan 110-05 covers in its per-job round-trip suite).
2591
+ _test: {
2592
+ sanitizeCypherInput,
2593
+ checkFilePermissions,
2594
+ sessionCache,
2595
+ SESSION_TTL_MS,
2596
+ _hashKey,
2597
+ _ensureSession,
2598
+ _resetSchema,
2599
+ _validatorFor,
2600
+ _parseBrainResult,
2601
+ _looksLikeUnknownToolError,
2602
+ _ensureSchema,
2603
+ SHIPPED_JOBS,
2604
+ _setLegacyWarned: function (v) { _legacyPathWarned = !!v; },
2605
+ // Quick 260903-eit: reset seam for query()'s unrecognized-shape
2606
+ // warn-once flag, mirroring _setLegacyWarned above.
2607
+ _setQueryShapeWarned: function (v) { _queryShapeWarned = !!v; },
2608
+ // Phase 250-04 test surface: silent registration internals.
2609
+ _tryAutoRegister,
2610
+ _installTokenPath,
2611
+ // Phase 259 (TRUST-01) test surface: the pure 429 wait-schedule helpers,
2612
+ // so the exact D-01/D-02 schedule is unit-tested with zero sleeps.
2613
+ _parseRetryAfterMs,
2614
+ _rateLimitWaitMs,
2615
+ // Quick 260910-hni test surface: the composer's injectable deps seam
2616
+ // and the widened askOp recognizer, both offline-drivable with zero
2617
+ // network (see tests/test-339-theo-ask-compose.cjs).
2618
+ _composeTheoAsk,
2619
+ _normalizeAskOpResult,
2620
+ },
2621
+ };