@mindrian_os/install 1.13.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (597) hide show
  1. package/.claude-plugin/plugin.json +21 -0
  2. package/.mcp.json +9 -0
  3. package/CHANGELOG.md +3333 -0
  4. package/LICENSE +123 -0
  5. package/README.md +673 -0
  6. package/agents/brain-query.md +80 -0
  7. package/agents/framework-runner.md +237 -0
  8. package/agents/grading.md +188 -0
  9. package/agents/investor.md +128 -0
  10. package/agents/larry-extended.md +135 -0
  11. package/agents/opportunity-scanner.md +91 -0
  12. package/agents/persona-analyst.md +132 -0
  13. package/agents/research.md +89 -0
  14. package/agents/reverse-salient-agent.md +27 -0
  15. package/bin/cli.js +142 -0
  16. package/bin/mindrian-mcp-server.cjs +182 -0
  17. package/bin/mindrian-tools.cjs +765 -0
  18. package/commands/act.md +439 -0
  19. package/commands/admin.md +404 -0
  20. package/commands/analyze-needs.md +42 -0
  21. package/commands/analyze-systems.md +39 -0
  22. package/commands/analyze-timing.md +42 -0
  23. package/commands/auto-explore.md +64 -0
  24. package/commands/beautiful-question.md +40 -0
  25. package/commands/brain-derive.md +78 -0
  26. package/commands/build-knowledge.md +42 -0
  27. package/commands/build-thesis.md +46 -0
  28. package/commands/causal.md +234 -0
  29. package/commands/challenge-assumptions.md +33 -0
  30. package/commands/compare-ventures.md +83 -0
  31. package/commands/dashboard.md +110 -0
  32. package/commands/deep-grade.md +82 -0
  33. package/commands/diagnose.md +58 -0
  34. package/commands/diagnostics.md +151 -0
  35. package/commands/doctor.md +151 -0
  36. package/commands/dominant-designs.md +40 -0
  37. package/commands/explain-decision.md +87 -0
  38. package/commands/explore-domains.md +42 -0
  39. package/commands/explore-futures.md +40 -0
  40. package/commands/explore-trends.md +42 -0
  41. package/commands/export.md +103 -0
  42. package/commands/file-meeting.md +724 -0
  43. package/commands/find-analogies.md +188 -0
  44. package/commands/find-bottlenecks.md +62 -0
  45. package/commands/find-connections.md +76 -0
  46. package/commands/funding.md +81 -0
  47. package/commands/grade.md +203 -0
  48. package/commands/graph.md +128 -0
  49. package/commands/hat-briefing.md +125 -0
  50. package/commands/heal.md +196 -0
  51. package/commands/help.md +399 -0
  52. package/commands/hmi-status.md +172 -0
  53. package/commands/jtbd.md +241 -0
  54. package/commands/leadership.md +73 -0
  55. package/commands/lean-canvas.md +40 -0
  56. package/commands/macro-trends.md +40 -0
  57. package/commands/map-unknowns.md +40 -0
  58. package/commands/memory.md +173 -0
  59. package/commands/models.md +175 -0
  60. package/commands/mos-reason.md +285 -0
  61. package/commands/mullins.md +120 -0
  62. package/commands/new-project.md +481 -0
  63. package/commands/onboard.md +434 -0
  64. package/commands/operator.md +149 -0
  65. package/commands/opportunities.md +144 -0
  66. package/commands/organize.md +497 -0
  67. package/commands/persona.md +198 -0
  68. package/commands/pipeline.md +112 -0
  69. package/commands/present.md +91 -0
  70. package/commands/publish.md +201 -0
  71. package/commands/query.md +124 -0
  72. package/commands/radar.md +72 -0
  73. package/commands/reanalyze.md +91 -0
  74. package/commands/research.md +196 -0
  75. package/commands/room.md +352 -0
  76. package/commands/rooms.md +598 -0
  77. package/commands/root-cause.md +40 -0
  78. package/commands/rs-experts.md +85 -0
  79. package/commands/rs-explain.md +100 -0
  80. package/commands/rs-fetch.md +94 -0
  81. package/commands/rs-thesis.md +85 -0
  82. package/commands/scenario-plan.md +40 -0
  83. package/commands/scheduled-tasks.md +285 -0
  84. package/commands/score-innovation.md +43 -0
  85. package/commands/scout.md +239 -0
  86. package/commands/setup.md +618 -0
  87. package/commands/snapshot.md +147 -0
  88. package/commands/speakers.md +84 -0
  89. package/commands/splash.md +28 -0
  90. package/commands/status.md +75 -0
  91. package/commands/structure-argument.md +42 -0
  92. package/commands/suggest-next.md +80 -0
  93. package/commands/systems-thinking.md +40 -0
  94. package/commands/think-hats.md +42 -0
  95. package/commands/update.md +181 -0
  96. package/commands/user-needs.md +40 -0
  97. package/commands/validate.md +40 -0
  98. package/commands/value-proposition.md +61 -0
  99. package/commands/vault.md +180 -0
  100. package/commands/visualize.md +52 -0
  101. package/commands/whitespace.md +507 -0
  102. package/commands/wiki.md +69 -0
  103. package/hooks/hooks.json +381 -0
  104. package/hooks/run-hook.cmd +64 -0
  105. package/lib/__init__.py +0 -0
  106. package/lib/__pycache__/__init__.cpython-312.pyc +0 -0
  107. package/lib/agents/auto-explore-agent.cjs +1043 -0
  108. package/lib/agents/reverse-salient-agent.cjs +679 -0
  109. package/lib/agents/tension-hook-agent.cjs +544 -0
  110. package/lib/brain/ROOM.md +44 -0
  111. package/lib/brain/chain-recommender.cjs +301 -0
  112. package/lib/chat/chat-context.js +185 -0
  113. package/lib/chat/chat-panel.js +721 -0
  114. package/lib/chat/fabric-chat.cjs +288 -0
  115. package/lib/chat/generative-tools.js +219 -0
  116. package/lib/conversation/ROOM.md +39 -0
  117. package/lib/conversation/classifier-rules.json +38 -0
  118. package/lib/conversation/classifier.cjs +264 -0
  119. package/lib/conversation/operator.cjs +287 -0
  120. package/lib/copy/115-spec-strings.cjs +55 -0
  121. package/lib/core/__init__.py +0 -0
  122. package/lib/core/__nav-stub.cjs +14 -0
  123. package/lib/core/__pycache__/__init__.cpython-312.pyc +0 -0
  124. package/lib/core/__pycache__/rs-math.cpython-312.pyc +0 -0
  125. package/lib/core/__pycache__/rs_cache.cpython-312.pyc +0 -0
  126. package/lib/core/__pycache__/rs_corpus.cpython-312.pyc +0 -0
  127. package/lib/core/__pycache__/rs_hybrid.cpython-312.pyc +0 -0
  128. package/lib/core/__pycache__/rs_math.cpython-312.pyc +0 -0
  129. package/lib/core/__pycache__/rs_rooms.cpython-312.pyc +0 -0
  130. package/lib/core/artifact-id.cjs +148 -0
  131. package/lib/core/asset-ops.cjs +151 -0
  132. package/lib/core/auto-commit-throttle.cjs +129 -0
  133. package/lib/core/bearer-token.cjs +199 -0
  134. package/lib/core/brain-client.cjs +865 -0
  135. package/lib/core/brain-derivation-prompts.cjs +326 -0
  136. package/lib/core/brain-derivation-queue.cjs +431 -0
  137. package/lib/core/brain-derivation.cjs +580 -0
  138. package/lib/core/brain-md-schema.cjs +528 -0
  139. package/lib/core/brain-md-staleness.cjs +357 -0
  140. package/lib/core/brain-response-sanitize.cjs +188 -0
  141. package/lib/core/bridge-writer.cjs +477 -0
  142. package/lib/core/chat-context-builder.cjs +253 -0
  143. package/lib/core/cross-room-aggregator.cjs +762 -0
  144. package/lib/core/daily-briefing.cjs +438 -0
  145. package/lib/core/decision-capture.cjs +618 -0
  146. package/lib/core/deep-links.cjs +82 -0
  147. package/lib/core/dispatch-optimizer.cjs +354 -0
  148. package/lib/core/dual-path-detector.cjs +84 -0
  149. package/lib/core/dual-path-detector.test.cjs +334 -0
  150. package/lib/core/exports-log.cjs +79 -0
  151. package/lib/core/feynman-minto-invariants.cjs +605 -0
  152. package/lib/core/folder-memory-async.cjs +338 -0
  153. package/lib/core/folder-memory-shared.cjs +890 -0
  154. package/lib/core/folder-memory.cjs +416 -0
  155. package/lib/core/framework-chain-composer.cjs +411 -0
  156. package/lib/core/frontmatter-schemas.cjs +330 -0
  157. package/lib/core/git-ops.cjs +141 -0
  158. package/lib/core/graph-ops.cjs +258 -0
  159. package/lib/core/hat-persistence.cjs +362 -0
  160. package/lib/core/index.cjs +60 -0
  161. package/lib/core/integration-registry.cjs +232 -0
  162. package/lib/core/intelligence-cascade.cjs +661 -0
  163. package/lib/core/lazygraph-ops.cjs +1057 -0
  164. package/lib/core/lru-cache.cjs +139 -0
  165. package/lib/core/mcp-profiles.cjs +182 -0
  166. package/lib/core/meeting-ops.cjs +54 -0
  167. package/lib/core/memory-ops.cjs +600 -0
  168. package/lib/core/migrations/ROOM.md +33 -0
  169. package/lib/core/migrations/phase-109-nodes-provenance.cjs +339 -0
  170. package/lib/core/migrations/phase-109-session-focus.cjs +99 -0
  171. package/lib/core/model-profiles.cjs +246 -0
  172. package/lib/core/mullins-scaffold.cjs +160 -0
  173. package/lib/core/nav-dial.cjs +316 -0
  174. package/lib/core/navigation/ROOM.md +15 -0
  175. package/lib/core/navigation/explanation.cjs +43 -0
  176. package/lib/core/navigation/focus.cjs +135 -0
  177. package/lib/core/navigation/ingestion.cjs +82 -0
  178. package/lib/core/navigation/insights.cjs +350 -0
  179. package/lib/core/navigation/memory-events.cjs +118 -0
  180. package/lib/core/navigation/neighborhood.cjs +78 -0
  181. package/lib/core/navigation/packet.cjs +182 -0
  182. package/lib/core/navigation/room-home.cjs +127 -0
  183. package/lib/core/navigation/transitions.cjs +82 -0
  184. package/lib/core/navigation-engine-shared.cjs +242 -0
  185. package/lib/core/navigation-engine.cjs +664 -0
  186. package/lib/core/navigation.cjs +60 -0
  187. package/lib/core/nl-graph-queries.cjs +164 -0
  188. package/lib/core/offer-presenter.cjs +406 -0
  189. package/lib/core/opportunity-extractor.cjs +183 -0
  190. package/lib/core/opportunity-ops.cjs +1371 -0
  191. package/lib/core/persona-ops.cjs +537 -0
  192. package/lib/core/persona-taxonomy.cjs +190 -0
  193. package/lib/core/platform-gates.cjs +120 -0
  194. package/lib/core/platform.cjs +257 -0
  195. package/lib/core/proactive-intelligence.cjs +528 -0
  196. package/lib/core/problem-type-router.cjs +315 -0
  197. package/lib/core/reasoning-ops.cjs +639 -0
  198. package/lib/core/reverse-salient-persona-suffix.cjs +115 -0
  199. package/lib/core/room-classifier-strict-mode.cjs +229 -0
  200. package/lib/core/room-db.cjs +127 -0
  201. package/lib/core/room-ops-async.cjs +92 -0
  202. package/lib/core/room-ops-shared.cjs +64 -0
  203. package/lib/core/room-ops-sync.cjs +70 -0
  204. package/lib/core/room-ops.cjs +32 -0
  205. package/lib/core/room-type-detector.cjs +386 -0
  206. package/lib/core/rs-brain-substrate-prompts.cjs +129 -0
  207. package/lib/core/rs-brain-substrate.cjs +570 -0
  208. package/lib/core/rs-breakthrough-scorer.cjs +255 -0
  209. package/lib/core/rs-canon-violations.cjs +82 -0
  210. package/lib/core/rs-chain-feeder.cjs +343 -0
  211. package/lib/core/rs-commercial-assessor.cjs +280 -0
  212. package/lib/core/rs-differential-scorer.cjs +376 -0
  213. package/lib/core/rs-domain-analyzer.cjs +385 -0
  214. package/lib/core/rs-egress-prompts.cjs +113 -0
  215. package/lib/core/rs-egress-telemetry.cjs +225 -0
  216. package/lib/core/rs-egress-violations.cjs +53 -0
  217. package/lib/core/rs-expert-mapper.cjs +467 -0
  218. package/lib/core/rs-fetcher-academic.cjs +697 -0
  219. package/lib/core/rs-fetcher-experts.cjs +314 -0
  220. package/lib/core/rs-fetcher-industry.cjs +731 -0
  221. package/lib/core/rs-fetcher-patents.cjs +564 -0
  222. package/lib/core/rs-innovation-classifier.cjs +194 -0
  223. package/lib/core/rs-mind-map.cjs +656 -0
  224. package/lib/core/rs-neo4j-writer.cjs +388 -0
  225. package/lib/core/rs-nl-to-query.cjs +425 -0
  226. package/lib/core/rs-pinecone-bridge.cjs +303 -0
  227. package/lib/core/rs-preprocessor.cjs +350 -0
  228. package/lib/core/rs-query-matrix.cjs +316 -0
  229. package/lib/core/rs-query-to-text.cjs +438 -0
  230. package/lib/core/rs-sqlite-mirror.cjs +443 -0
  231. package/lib/core/rs-thesis-generator.cjs +188 -0
  232. package/lib/core/rs_cache.py +479 -0
  233. package/lib/core/rs_corpus.py +468 -0
  234. package/lib/core/rs_hybrid.py +586 -0
  235. package/lib/core/rs_math.py +287 -0
  236. package/lib/core/rs_rooms.py +193 -0
  237. package/lib/core/scheduled-scanner.cjs +463 -0
  238. package/lib/core/scratchpad-ops.cjs +201 -0
  239. package/lib/core/section-8-trace-schema.cjs +138 -0
  240. package/lib/core/section-registry.cjs +111 -0
  241. package/lib/core/session-state.cjs +144 -0
  242. package/lib/core/shallow-doc-parser.cjs +174 -0
  243. package/lib/core/shallow-doc-parser.test.cjs +226 -0
  244. package/lib/core/skill-activation-router.cjs +284 -0
  245. package/lib/core/state-ops.cjs +46 -0
  246. package/lib/core/statusline-cache.cjs +266 -0
  247. package/lib/core/token-estimator.cjs +348 -0
  248. package/lib/core/user-archetype.cjs +239 -0
  249. package/lib/core/user-md-ops.cjs +524 -0
  250. package/lib/core/visual-ops.cjs +624 -0
  251. package/lib/core/write-lock.cjs +149 -0
  252. package/lib/graph/canvas-graph.js +467 -0
  253. package/lib/graph/constellation-config.cjs +299 -0
  254. package/lib/graph/graph-detail-panel.js +165 -0
  255. package/lib/hmi/ROOM.md +47 -0
  256. package/lib/hmi/across-session-memory.cjs +604 -0
  257. package/lib/hmi/cross-room-memory.cjs +575 -0
  258. package/lib/hmi/decoy-tier.cjs +395 -0
  259. package/lib/hmi/jtbd-classifier.cjs +219 -0
  260. package/lib/hmi/jtbd-state.cjs +199 -0
  261. package/lib/hmi/jtbd-taxonomy.json +392 -0
  262. package/lib/hmi/selector-dispatcher.cjs +546 -0
  263. package/lib/hmi/selector-telemetry.cjs +263 -0
  264. package/lib/hmi/shape-f0-renderer.cjs +139 -0
  265. package/lib/hmi/shape-f1-fallback.cjs +80 -0
  266. package/lib/hmi/shape-f1-renderer.cjs +138 -0
  267. package/lib/hmi/shape-f2-renderer.cjs +132 -0
  268. package/lib/hmi/shape-f3-renderer.cjs +66 -0
  269. package/lib/hmi/shape-f4-renderer.cjs +72 -0
  270. package/lib/hmi/shape-f5-renderer.cjs +155 -0
  271. package/lib/hmi/shape-f6-plan-review-renderer.cjs +312 -0
  272. package/lib/hmi/shape-f6-renderer.cjs +144 -0
  273. package/lib/hmi/shape-g-renderer.cjs +219 -0
  274. package/lib/hmi/shape-h-renderer.cjs +222 -0
  275. package/lib/hmi/tier-check.cjs +63 -0
  276. package/lib/import/PRECONDITIONS.md +41 -0
  277. package/lib/import/branding.cjs +210 -0
  278. package/lib/import/branding.test.cjs +235 -0
  279. package/lib/import/classifications-sync.cjs +104 -0
  280. package/lib/import/classifications-sync.test.cjs +129 -0
  281. package/lib/import/enricher.cjs +296 -0
  282. package/lib/import/enricher.test.cjs +273 -0
  283. package/lib/import/integration.test.cjs +376 -0
  284. package/lib/import/manifest.cjs +129 -0
  285. package/lib/import/manifest.schema.json +185 -0
  286. package/lib/import/manifest.test.cjs +123 -0
  287. package/lib/import/meeting-detector.cjs +92 -0
  288. package/lib/import/meeting-detector.test.cjs +100 -0
  289. package/lib/import/person-detector.cjs +229 -0
  290. package/lib/import/person-detector.test.cjs +149 -0
  291. package/lib/import/report.cjs +186 -0
  292. package/lib/import/report.test.cjs +186 -0
  293. package/lib/import/room-md-scaffolder.cjs +49 -0
  294. package/lib/import/router.cjs +224 -0
  295. package/lib/import/router.test.cjs +356 -0
  296. package/lib/import/run-all-tests.cjs +36 -0
  297. package/lib/import/smoke-test.cjs +213 -0
  298. package/lib/import/smoke-test.test.cjs +148 -0
  299. package/lib/import/test-fixtures/collision-vault/preexisting-room/STATE.md +8 -0
  300. package/lib/import/test-fixtures/collision-vault/preexisting-room/problem-definition/onboarding/onboarding.md +7 -0
  301. package/lib/import/test-fixtures/collision-vault/source/onboarding.md +5 -0
  302. package/lib/import/test-fixtures/obsidian-vault/.obsidian/workspace.json +1 -0
  303. package/lib/import/test-fixtures/obsidian-vault/notes/with-wikilinks.md +4 -0
  304. package/lib/import/test-fixtures/tiny-vault/notes/2026-01-15-team-sync.md +9 -0
  305. package/lib/import/test-fixtures/tiny-vault/notes/empty.md +3 -0
  306. package/lib/import/test-fixtures/tiny-vault/notes/onboarding.md +5 -0
  307. package/lib/import/test-fixtures/tiny-vault/notes/pricing.md +5 -0
  308. package/lib/import/test-fixtures/tiny-vault/notes/random.md +4 -0
  309. package/lib/import/undo.test.cjs +199 -0
  310. package/lib/import/vault-scanner.cjs +105 -0
  311. package/lib/import/vault-scanner.test.cjs +67 -0
  312. package/lib/mcp/app-html/dashboard.html +316 -0
  313. package/lib/mcp/app-html/graph.html +428 -0
  314. package/lib/mcp/app-html/mindrian-platform.html +1841 -0
  315. package/lib/mcp/app-html/wiki.html +383 -0
  316. package/lib/mcp/app-views.cjs +322 -0
  317. package/lib/mcp/brain-router.cjs +418 -0
  318. package/lib/mcp/capability-registry.cjs +62 -0
  319. package/lib/mcp/larry-context.cjs +46 -0
  320. package/lib/mcp/larry-server-instructions.md +114 -0
  321. package/lib/mcp/pipeline-state.cjs +275 -0
  322. package/lib/mcp/prompts.cjs +302 -0
  323. package/lib/mcp/resources.cjs +227 -0
  324. package/lib/mcp/session-catchup.cjs +327 -0
  325. package/lib/mcp/surface-detect.cjs +75 -0
  326. package/lib/mcp/tool-router.cjs +1034 -0
  327. package/lib/memory/aaak-compress.cjs +403 -0
  328. package/lib/memory/aaak-compress.test.cjs +288 -0
  329. package/lib/memory/async-artifact-auto-commit.test.cjs +223 -0
  330. package/lib/memory/bearer-token.test.cjs +315 -0
  331. package/lib/memory/brain-cache-lru.test.cjs +259 -0
  332. package/lib/memory/brain-client-query-shape.test.cjs +160 -0
  333. package/lib/memory/brain-derivation-graceful-degradation.test.cjs +1019 -0
  334. package/lib/memory/brain-derivation-queue.test.cjs +539 -0
  335. package/lib/memory/brain-derivation.test.cjs +634 -0
  336. package/lib/memory/brain-derive-command.test.cjs +534 -0
  337. package/lib/memory/brain-md-invariants-validator.test.cjs +704 -0
  338. package/lib/memory/brain-md-schema.test.cjs +467 -0
  339. package/lib/memory/brain-md-staleness.test.cjs +525 -0
  340. package/lib/memory/brain-server-resolution.test.cjs +314 -0
  341. package/lib/memory/chain-recommender.test.cjs +233 -0
  342. package/lib/memory/chat-context.test.cjs +128 -0
  343. package/lib/memory/command-registry.test.cjs +220 -0
  344. package/lib/memory/cross-room-aggregator.test.cjs +909 -0
  345. package/lib/memory/dashboard-server.test.cjs +256 -0
  346. package/lib/memory/debouncer-drain-at-prompt.test.cjs +389 -0
  347. package/lib/memory/decision-capture.test.cjs +632 -0
  348. package/lib/memory/decision-capture.worker.cjs +70 -0
  349. package/lib/memory/explain-decision-command.test.cjs +521 -0
  350. package/lib/memory/explain-decision-footer.test.cjs +316 -0
  351. package/lib/memory/explored-materials-store.cjs +392 -0
  352. package/lib/memory/feynman-minto-guardian.test.cjs +736 -0
  353. package/lib/memory/feynman-minto-invariants.test.cjs +511 -0
  354. package/lib/memory/feynman-prompts-drift.test.cjs +144 -0
  355. package/lib/memory/feynman-prompts.cjs +151 -0
  356. package/lib/memory/feynman-prompts.test.cjs +96 -0
  357. package/lib/memory/folder-memory-quadruple.test.cjs +548 -0
  358. package/lib/memory/folder-memory.test.cjs +503 -0
  359. package/lib/memory/framework-chain-composer.test.cjs +515 -0
  360. package/lib/memory/frontmatter-schema-validator.test.cjs +290 -0
  361. package/lib/memory/heal-command.test.cjs +604 -0
  362. package/lib/memory/index-artifact-transaction.test.cjs +333 -0
  363. package/lib/memory/lazygraph-rs-discoveries-view.test.cjs +122 -0
  364. package/lib/memory/mcp-input-validation.test.cjs +240 -0
  365. package/lib/memory/mcp-server-brain-deps.test.cjs +270 -0
  366. package/lib/memory/mcp-stack-fallback.test.cjs +433 -0
  367. package/lib/memory/minto-debouncer.test.cjs +407 -0
  368. package/lib/memory/minto-debouncer.worker.cjs +46 -0
  369. package/lib/memory/minto-migration-v88.test.cjs +265 -0
  370. package/lib/memory/minto-schema-v88.test.cjs +390 -0
  371. package/lib/memory/mos-status-renderer.test.cjs +631 -0
  372. package/lib/memory/narrative-schema.cjs +376 -0
  373. package/lib/memory/narrative-schema.test.cjs +209 -0
  374. package/lib/memory/nav-dial.test.cjs +414 -0
  375. package/lib/memory/navigation-engine-core.test.cjs +722 -0
  376. package/lib/memory/navigation-invariants.test.cjs +483 -0
  377. package/lib/memory/offer-presenter.test.cjs +554 -0
  378. package/lib/memory/on-stop-snapshot.test.cjs +404 -0
  379. package/lib/memory/pending-tension-store.cjs +373 -0
  380. package/lib/memory/post-compact-reinjection.test.cjs +854 -0
  381. package/lib/memory/post-write-triple.test.cjs +317 -0
  382. package/lib/memory/pre-compact-snapshot.test.cjs +495 -0
  383. package/lib/memory/problem-type-router.test.cjs +656 -0
  384. package/lib/memory/query-efficiency-telemetry.test.cjs +370 -0
  385. package/lib/memory/recompile-room-references.test.cjs +392 -0
  386. package/lib/memory/recompile-room-references.worker.cjs +42 -0
  387. package/lib/memory/record-decision-dual-write.test.cjs +454 -0
  388. package/lib/memory/room-classifier-strict-mode.test.cjs +417 -0
  389. package/lib/memory/room-minto-hook.test.cjs +398 -0
  390. package/lib/memory/rs-discovery-engine.test.cjs +323 -0
  391. package/lib/memory/run-feynman-tests.cjs +1247 -0
  392. package/lib/memory/security-trifecta.test.cjs +312 -0
  393. package/lib/memory/session-start-brain-staleness.test.cjs +363 -0
  394. package/lib/memory/session-start-triple-injection.test.cjs +514 -0
  395. package/lib/memory/sessionstart-banner-formatter.cjs +318 -0
  396. package/lib/memory/sessionstart-minto-banner.test.cjs +373 -0
  397. package/lib/memory/skill-activation-router.test.cjs +419 -0
  398. package/lib/memory/stamp-artifact-write.test.cjs +304 -0
  399. package/lib/memory/statusline-active-room.test.cjs +315 -0
  400. package/lib/memory/statusline-minto-segment.test.cjs +292 -0
  401. package/lib/memory/sync-async-entry-points.test.cjs +204 -0
  402. package/lib/memory/test-bridge-writer-enhanced.cjs +452 -0
  403. package/lib/memory/test-rs-brain-substrate-shape.cjs +529 -0
  404. package/lib/memory/test-rs-brain-substrate.cjs +636 -0
  405. package/lib/memory/test-rs-breakthrough-scorer.cjs +375 -0
  406. package/lib/memory/test-rs-canon-violations.cjs +218 -0
  407. package/lib/memory/test-rs-chain-feeder-core.cjs +344 -0
  408. package/lib/memory/test-rs-chain-feeder-skill-spawn.cjs +297 -0
  409. package/lib/memory/test-rs-commercial-assessor.cjs +385 -0
  410. package/lib/memory/test-rs-differential-scorer.cjs +480 -0
  411. package/lib/memory/test-rs-discovery-engine.cjs +603 -0
  412. package/lib/memory/test-rs-domain-analyzer.cjs +492 -0
  413. package/lib/memory/test-rs-egress-primitives.cjs +420 -0
  414. package/lib/memory/test-rs-expert-mapper.cjs +547 -0
  415. package/lib/memory/test-rs-explain-command.cjs +443 -0
  416. package/lib/memory/test-rs-fetcher-academic.cjs +848 -0
  417. package/lib/memory/test-rs-fetcher-experts.cjs +496 -0
  418. package/lib/memory/test-rs-fetcher-industry.cjs +702 -0
  419. package/lib/memory/test-rs-fetcher-patents.cjs +674 -0
  420. package/lib/memory/test-rs-innovation-classifier.cjs +301 -0
  421. package/lib/memory/test-rs-mind-map.cjs +646 -0
  422. package/lib/memory/test-rs-neo4j-writer.cjs +518 -0
  423. package/lib/memory/test-rs-nl-to-query.cjs +449 -0
  424. package/lib/memory/test-rs-pinecone-bridge.cjs +277 -0
  425. package/lib/memory/test-rs-preprocessor.cjs +433 -0
  426. package/lib/memory/test-rs-query-matrix.cjs +391 -0
  427. package/lib/memory/test-rs-query-to-text.cjs +551 -0
  428. package/lib/memory/test-rs-sqlite-mirror.cjs +649 -0
  429. package/lib/memory/test-rs-thesis-generator.cjs +360 -0
  430. package/lib/memory/triple-context-formatter.cjs +473 -0
  431. package/lib/memory/triple-context-formatter.test.cjs +442 -0
  432. package/lib/memory/user-md-persona.test.cjs +565 -0
  433. package/lib/memory/userpromptsubmit-integration.test.cjs +690 -0
  434. package/lib/memory/validators/README.md +157 -0
  435. package/lib/memory/validators/brain-md-invariants.cjs +475 -0
  436. package/lib/memory/validators/brain-substrate-invariants.cjs +285 -0
  437. package/lib/memory/validators/external-academic-invariants.cjs +249 -0
  438. package/lib/memory/validators/external-industry-invariants.cjs +271 -0
  439. package/lib/memory/validators/external-patents-invariants.cjs +266 -0
  440. package/lib/memory/validators/minto-invariants.cjs +62 -0
  441. package/lib/memory/validators/navigation-invariants.cjs +340 -0
  442. package/lib/memory/validators/queue-health.cjs +95 -0
  443. package/lib/memory/validators/snapshot-integrity.cjs +129 -0
  444. package/lib/memory/validators/stale-lifecycle.cjs +116 -0
  445. package/lib/memory/vault-section-minto-generator-atomic.test.cjs +556 -0
  446. package/lib/memory/vault-section-minto-generator-atomic.worker.cjs +73 -0
  447. package/lib/memory/write-lock-atomic.test.cjs +137 -0
  448. package/lib/memory/write-lock-atomic.worker.cjs +55 -0
  449. package/lib/parity/check-parity.cjs +83 -0
  450. package/lib/presentation/presentation-server.cjs +101 -0
  451. package/lib/presentation/presentation-watcher.cjs +123 -0
  452. package/lib/quickview/hub-server.cjs +719 -0
  453. package/lib/quickview/server.cjs +533 -0
  454. package/lib/render/JTBD-PALETTES.md +145 -0
  455. package/lib/render/ROOM.md +59 -0
  456. package/lib/render/render-v2.cjs +486 -0
  457. package/lib/render/render-v2.test.cjs +267 -0
  458. package/lib/render/render.cjs +65 -0
  459. package/lib/state/ROOM.md +46 -0
  460. package/lib/state/state-md-parser.cjs +215 -0
  461. package/lib/statusline/ROOM.md +38 -0
  462. package/lib/statusline/banner-suppression.cjs +50 -0
  463. package/lib/statusline/surface-detect.cjs +85 -0
  464. package/lib/update-bootstrap.sh.template +145 -0
  465. package/lib/vault/frontmatter-schema.cjs +297 -0
  466. package/lib/vault/room-scanner.cjs +352 -0
  467. package/lib/vault/wikilink-builder.cjs +231 -0
  468. package/lib/vault/wikilink-builder.test.cjs +182 -0
  469. package/lib/wiki/graph-links.cjs +281 -0
  470. package/lib/wiki/page-renderer.cjs +229 -0
  471. package/lib/wiki/wiki-chat.cjs +81 -0
  472. package/lib/wiki/wiki-layout.cjs +1459 -0
  473. package/lib/wiki/wiki-search.cjs +142 -0
  474. package/lib/wiki/wiki-server.cjs +678 -0
  475. package/lib/wiki/wiki-watcher.cjs +105 -0
  476. package/lib/workflow/ROOM.md +47 -0
  477. package/lib/workflow/command-resolver.cjs +155 -0
  478. package/lib/workflow/command-resolver.test.cjs +235 -0
  479. package/package.json +44 -0
  480. package/pipelines/analogy/01-decompose.md +80 -0
  481. package/pipelines/analogy/02-abstract.md +87 -0
  482. package/pipelines/analogy/03-search.md +135 -0
  483. package/pipelines/analogy/04-transfer.md +101 -0
  484. package/pipelines/analogy/05-validate.md +106 -0
  485. package/pipelines/analogy/CHAIN.md +56 -0
  486. package/pipelines/discovery/01-explore-domains.md +44 -0
  487. package/pipelines/discovery/02-think-hats.md +50 -0
  488. package/pipelines/discovery/03-analyze-needs.md +54 -0
  489. package/pipelines/discovery/CHAIN.md +37 -0
  490. package/pipelines/thesis/01-structure-argument.md +45 -0
  491. package/pipelines/thesis/02-challenge-assumptions.md +48 -0
  492. package/pipelines/thesis/03-build-thesis.md +54 -0
  493. package/pipelines/thesis/CHAIN.md +37 -0
  494. package/references/brain/causal-directives.md +91 -0
  495. package/references/brain/causal-enrichment.cypher +165 -0
  496. package/references/brain/command-triggers-schema.md +226 -0
  497. package/references/brain/graph-architecture.md +317 -0
  498. package/references/brain/query-patterns.md +460 -0
  499. package/references/brain/room-hierarchy-schema.md +218 -0
  500. package/references/brain/schema.md +76 -0
  501. package/references/capability-radar/capabilities-index.md +241 -0
  502. package/references/capability-radar/changelog-cache.md +81 -0
  503. package/references/causal/causal-schema.md +103 -0
  504. package/references/design/email-template-standard.md +155 -0
  505. package/references/design/graph-visualization-standard.md +178 -0
  506. package/references/document-generation.md +179 -0
  507. package/references/hsi/HSI-TOOLS-REFERENCE.md +222 -0
  508. package/references/import-config.md +141 -0
  509. package/references/integrations/detection-patterns.md +101 -0
  510. package/references/meeting/artifact-template.md +377 -0
  511. package/references/meeting/cross-meeting-intelligence.md +216 -0
  512. package/references/meeting/cross-relationship-patterns.md +202 -0
  513. package/references/meeting/live-join-interface.md +244 -0
  514. package/references/meeting/section-mapping.md +192 -0
  515. package/references/meeting/segment-classification.md +258 -0
  516. package/references/meeting/speaker-profile-template.md +219 -0
  517. package/references/meeting/summary-template.md +348 -0
  518. package/references/meeting/transcript-patterns.md +226 -0
  519. package/references/methodology/analyze-needs.md +135 -0
  520. package/references/methodology/analyze-systems.md +121 -0
  521. package/references/methodology/analyze-timing.md +149 -0
  522. package/references/methodology/beautiful-question.md +109 -0
  523. package/references/methodology/build-knowledge.md +161 -0
  524. package/references/methodology/build-thesis.md +237 -0
  525. package/references/methodology/challenge-assumptions.md +127 -0
  526. package/references/methodology/diagnose.md +169 -0
  527. package/references/methodology/dominant-designs.md +212 -0
  528. package/references/methodology/explore-domains.md +147 -0
  529. package/references/methodology/explore-futures.md +163 -0
  530. package/references/methodology/explore-trends.md +129 -0
  531. package/references/methodology/find-bottlenecks.md +131 -0
  532. package/references/methodology/grade.md +211 -0
  533. package/references/methodology/index.md +97 -0
  534. package/references/methodology/leadership.md +200 -0
  535. package/references/methodology/lean-canvas.md +116 -0
  536. package/references/methodology/macro-trends.md +192 -0
  537. package/references/methodology/map-unknowns.md +137 -0
  538. package/references/methodology/mullins-7-domains.md +104 -0
  539. package/references/methodology/problem-types.md +65 -0
  540. package/references/methodology/root-cause.md +178 -0
  541. package/references/methodology/sapphire-encoding.md +355 -0
  542. package/references/methodology/scenario-plan.md +178 -0
  543. package/references/methodology/score-innovation.md +154 -0
  544. package/references/methodology/structure-argument.md +158 -0
  545. package/references/methodology/systems-thinking.md +159 -0
  546. package/references/methodology/think-hats.md +147 -0
  547. package/references/methodology/triz-matrix.json +751 -0
  548. package/references/methodology/triz-principles.md +501 -0
  549. package/references/methodology/user-needs.md +199 -0
  550. package/references/methodology/validate.md +163 -0
  551. package/references/methodology/value-proposition.md +244 -0
  552. package/references/opportunities/funding-lifecycle.md +103 -0
  553. package/references/opportunities/grant-api-patterns.md +99 -0
  554. package/references/opportunities/opportunity-template.md +84 -0
  555. package/references/personality/assessment-philosophy.md +72 -0
  556. package/references/personality/lexicon.md +100 -0
  557. package/references/personality/persona-chains.md +56 -0
  558. package/references/personality/pws-lexicon-full.md +499 -0
  559. package/references/personality/voice-dna.md +156 -0
  560. package/references/personas/hat-perspectives.md +76 -0
  561. package/references/personas/persona-template.md +63 -0
  562. package/references/pipeline/act-output-contract.md +88 -0
  563. package/references/pipeline/chains-index.md +39 -0
  564. package/references/pws-profile-generation.md +79 -0
  565. package/references/reasoning/reasoning-schema.md +143 -0
  566. package/references/reasoning/reasoning-template.md +68 -0
  567. package/references/reasoning/run-template.md +38 -0
  568. package/references/research/RESEARCH_14_CLAUDE_CODE_SOURCE_ARCHITECTURE.md +209 -0
  569. package/references/research/RESEARCH_15_V1.8_OPTIMIZATION_JTBD.md +375 -0
  570. package/references/research/RESEARCH_16_NATIVE_FIRST_PLUGIN_ARCHITECTURE.md +575 -0
  571. package/references/research/RESEARCH_17_MCP_UI_FRAMEWORKS.md +272 -0
  572. package/references/taxonomy/TAXONOMY.md +192 -0
  573. package/references/templates/MINTO.md +36 -0
  574. package/references/user-research/2026-04-05-leah-lawrence-session.md +202 -0
  575. package/references/vault-kit/README.md +35 -0
  576. package/references/vault-kit/app.json +12 -0
  577. package/references/vault-kit/appearance.json +12 -0
  578. package/references/vault-kit/graph.json +35 -0
  579. package/references/vault-kit/snippets/mindrian-destijl.css +297 -0
  580. package/references/vault-kit/templates/new-artifact.md +37 -0
  581. package/references/vault-kit/templates/new-meeting-note.md +35 -0
  582. package/references/vault-kit/templates/new-team-profile.md +29 -0
  583. package/references/vault-kit/templates/new-xref.md +35 -0
  584. package/references/visual/symbol-system.md +151 -0
  585. package/skills/MOSDeckEngine/SKILL.md +325 -0
  586. package/skills/brain-connector/SKILL.md +114 -0
  587. package/skills/context-engine/SKILL.md +147 -0
  588. package/skills/conversation-mode/SKILL.md +102 -0
  589. package/skills/larry-personality/SKILL.md +219 -0
  590. package/skills/larry-personality/framework-chains.md +92 -0
  591. package/skills/larry-personality/mode-engine.md +185 -0
  592. package/skills/mullins-scaffold/SKILL.md +61 -0
  593. package/skills/mullins-scaffold/scaffold.json +146 -0
  594. package/skills/pws-methodology/SKILL.md +49 -0
  595. package/skills/room-passive/SKILL.md +165 -0
  596. package/skills/room-proactive/SKILL.md +250 -0
  597. package/skills/ui-system/SKILL.md +277 -0
@@ -0,0 +1,285 @@
1
+ /*
2
+ * Copyright (c) 2026 Mindrian. BSL 1.1.
3
+ * Phase 89.1a Plan 02 -- Brain methodology substrate invariants validator.
4
+ *
5
+ * Registry-compatible drop-in for the Phase 88-13 guardian. Auto-
6
+ * discovered by scripts/feynman-minto-guardian.cjs's validator loader
7
+ * (walks lib/memory/validators/*.cjs). Zero guardian.cjs edits needed.
8
+ *
9
+ * Contract (see lib/memory/validators/README.md):
10
+ * module.exports = {
11
+ * id: 'brain-substrate-invariants',
12
+ * severity_map: { critical, error, warning, info },
13
+ * scope: 'room',
14
+ * validate(roomPath, ctx) -> { severity, violations[] },
15
+ * }
16
+ *
17
+ * Five checks:
18
+ * A) Cache missing -> zero violations (valid default)
19
+ * B) Cache unreadable / malformed / schema_version drift -> warning
20
+ * C) Cache substrate.length != embedding_count -> critical (partial pull, Pitfall 5)
21
+ * D) Cache substrate[].metadata body contains forbidden regex hit -> critical
22
+ * (Canon Part 8 attack surface; Pitfall 4)
23
+ * E) Cache substrate[] entry shape -> critical (cache_entry_shape_invalid)
24
+ * Phase 89.1 Plan 01 relaxation: each entry MUST carry EITHER a 1024-dim
25
+ * finite-Float embedding (legacy/mocked path) OR a Pinecone score in
26
+ * [0,1] (live brain_search wire path). Neither present is critical;
27
+ * both present is allowed. id must match SLUG_REGEX OR the permissive
28
+ * Pinecone ID regex [a-zA-Z0-9_:-]{1,512}.
29
+ *
30
+ * Fail-open: unexpected throws are caught by the guardian's validator
31
+ * runner. This validator does not swallow its own exceptions; the
32
+ * guardian owns that contract. Individual I/O paths (readFileSync) are
33
+ * wrapped defensively so a missing cache never produces a violation.
34
+ *
35
+ * Pure CJS, node built-ins only + re-export of prompts module for
36
+ * FORBIDDEN_PATTERNS + SLUG_REGEX parity. Importing the canonical
37
+ * vocabulary (rather than redefining) ensures any Canon Part 8
38
+ * amendment at lib/core/cross-room-aggregator.cjs propagates through
39
+ * lib/core/rs-brain-substrate-prompts.cjs to this validator with zero
40
+ * code change.
41
+ */
42
+ 'use strict';
43
+
44
+ const fs = require('node:fs');
45
+ const path = require('node:path');
46
+
47
+ const prompts = require('../../core/rs-brain-substrate-prompts.cjs');
48
+ const { FORBIDDEN_PATTERNS, SLUG_REGEX } = prompts;
49
+
50
+ // ---------- Constants ----------
51
+
52
+ const SEVERITY_MAP = Object.freeze({ critical: 3, error: 2, warning: 1, info: 0 });
53
+ const EXPECTED_EMBEDDING_DIM = 1024;
54
+ const CACHE_SCHEMA_VERSION = '1.0';
55
+ // Cap canon_boundary hits per validate() invocation so one poisoned
56
+ // cache cannot spam the invariant report. Five matches is the same
57
+ // threshold Plan 90-05 brain-md-invariants uses.
58
+ const MAX_CANON_HITS_REPORTED = 5;
59
+
60
+ // ---------- Severity aggregator ----------
61
+
62
+ function aggregateSeverity(violations) {
63
+ if (!violations || violations.length === 0) return null;
64
+ let max = 0;
65
+ const inv = { 0: 'info', 1: 'warning', 2: 'error', 3: 'critical' };
66
+ for (const v of violations) {
67
+ const s = SEVERITY_MAP[v.severity];
68
+ if (typeof s === 'number' && s > max) max = s;
69
+ }
70
+ return inv[max];
71
+ }
72
+
73
+ // ---------- validate (Checks A-E) ----------
74
+
75
+ function validate(roomPath, _ctx) {
76
+ const violations = [];
77
+
78
+ // Defensive guard: non-string roomPath -> zero violations (guardian
79
+ // always passes a string, but the contract demands a structured
80
+ // result on every path).
81
+ if (typeof roomPath !== 'string' || roomPath.length === 0) {
82
+ return { severity: null, violations: [] };
83
+ }
84
+
85
+ const cachePath = path.join(roomPath, '.mindrian', 'brain-substrate-cache.json');
86
+
87
+ // --- Check A: cache absent -> zero violations (healthy default) ---
88
+ if (!fs.existsSync(cachePath)) {
89
+ return { severity: null, violations: [] };
90
+ }
91
+
92
+ // --- Check B: unreadable / malformed / schema drift ---
93
+ let payload = null;
94
+ try {
95
+ const raw = fs.readFileSync(cachePath, 'utf8');
96
+ payload = JSON.parse(raw);
97
+ } catch (err) {
98
+ violations.push({
99
+ category: 'cache_unreadable',
100
+ severity: 'warning',
101
+ message: 'brain-substrate-cache.json unreadable: ' + String(err && err.message),
102
+ action_hint: 'invalidate_cache',
103
+ });
104
+ return { severity: aggregateSeverity(violations), violations };
105
+ }
106
+
107
+ if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
108
+ violations.push({
109
+ category: 'cache_malformed_root',
110
+ severity: 'warning',
111
+ message: 'brain-substrate-cache.json root is not a plain object',
112
+ action_hint: 'invalidate_cache',
113
+ });
114
+ return { severity: aggregateSeverity(violations), violations };
115
+ }
116
+
117
+ if (payload.schema_version !== CACHE_SCHEMA_VERSION) {
118
+ violations.push({
119
+ category: 'cache_schema_drift',
120
+ severity: 'warning',
121
+ message: 'schema_version expected ' + CACHE_SCHEMA_VERSION +
122
+ ' got ' + String(payload.schema_version),
123
+ action_hint: 'invalidate_cache',
124
+ });
125
+ }
126
+
127
+ if (!Array.isArray(payload.substrate)) {
128
+ violations.push({
129
+ category: 'cache_malformed_substrate',
130
+ severity: 'critical',
131
+ message: 'substrate is not an array',
132
+ action_hint: 'invalidate_cache',
133
+ });
134
+ return { severity: aggregateSeverity(violations), violations };
135
+ }
136
+
137
+ // --- Check C: completeness (embedding_count vs substrate.length) ---
138
+ // Pitfall 5 partial-pull completeness guard. Defense-in-depth against
139
+ // the reader-side check in lib/core/rs-brain-substrate.cjs readCache;
140
+ // either layer alone blocks a partial cache.
141
+ if (typeof payload.embedding_count === 'number' &&
142
+ payload.substrate.length !== payload.embedding_count) {
143
+ violations.push({
144
+ category: 'cache_partial_pull',
145
+ severity: 'critical',
146
+ message: 'embedding_count ' + payload.embedding_count +
147
+ ' != substrate.length ' + payload.substrate.length,
148
+ action_hint: 'invalidate_cache',
149
+ });
150
+ }
151
+
152
+ // --- Checks D + E: per-entry body scan + shape assertion ---
153
+ //
154
+ // Phase 89.1 Plan 01 relaxation: Check E now accepts entries with EITHER a
155
+ // 1024-dim Float embedding (legacy/mocked path; 89.1a-03 fixtures) OR a
156
+ // Pinecone score in [0,1] (live brain_search wire path; searchRecords does
157
+ // not return values today). Entry MUST carry at least one of the two; if
158
+ // neither is present that is a critical cache_entry_shape_invalid.
159
+ //
160
+ // Pinecone IDs (e.g. 'mindrian-knowledge_t2-trending-absurd_2cddc6ac2cf58ea0')
161
+ // exceed the 64-char SLUG_REGEX limit on the prompts module. The slug check
162
+ // here is a permissive fallback: accept [a-zA-Z0-9_:-]{1,512} per Pinecone
163
+ // ID rules (https://docs.pinecone.io/), which is wider than SLUG_REGEX but
164
+ // still bounded and forbids whitespace + arbitrary unicode.
165
+ const PINECONE_ID_REGEX = /^[a-zA-Z0-9_:-]{1,512}$/;
166
+ let canonHitCount = 0;
167
+ for (let i = 0; i < payload.substrate.length; i += 1) {
168
+ const entry = payload.substrate[i];
169
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
170
+ violations.push({
171
+ category: 'cache_entry_shape_invalid',
172
+ severity: 'critical',
173
+ message: 'substrate[' + i + '] is not an object',
174
+ action_hint: 'invalidate_cache',
175
+ });
176
+ continue;
177
+ }
178
+
179
+ // Check E.1: id slug. Accept SLUG_REGEX OR permissive Pinecone ID regex.
180
+ if (typeof entry.id !== 'string' ||
181
+ (!SLUG_REGEX.test(entry.id) && !PINECONE_ID_REGEX.test(entry.id))) {
182
+ violations.push({
183
+ category: 'cache_entry_shape_invalid',
184
+ severity: 'critical',
185
+ message: 'substrate[' + i + '].id fails id regex: ' +
186
+ String(entry.id).slice(0, 80),
187
+ field: 'substrate[' + i + '].id',
188
+ action_hint: 'invalidate_cache',
189
+ });
190
+ }
191
+
192
+ // Check E.2 + E.3 + E.4: embedding-or-score required.
193
+ // hasEmbedding: legacy/mocked 1024-dim path
194
+ // hasScore: live brain_search Pinecone score path
195
+ // Neither -> critical (cache_entry_shape_invalid).
196
+ const hasEmbedding = Array.isArray(entry.embedding);
197
+ const hasScore = typeof entry.score === 'number';
198
+ if (!hasEmbedding && !hasScore) {
199
+ violations.push({
200
+ category: 'cache_entry_shape_invalid',
201
+ severity: 'critical',
202
+ message: 'substrate[' + i + '] missing both embedding[] and score',
203
+ field: 'substrate[' + i + ']',
204
+ action_hint: 'invalidate_cache',
205
+ });
206
+ }
207
+ if (hasEmbedding) {
208
+ if (entry.embedding.length !== EXPECTED_EMBEDDING_DIM) {
209
+ violations.push({
210
+ category: 'cache_entry_shape_invalid',
211
+ severity: 'critical',
212
+ message: 'substrate[' + i + '].embedding.length expected ' +
213
+ EXPECTED_EMBEDDING_DIM + ' got ' + entry.embedding.length,
214
+ field: 'substrate[' + i + '].embedding',
215
+ action_hint: 'invalidate_cache',
216
+ });
217
+ } else {
218
+ // Embedding must be all finite numbers.
219
+ for (let j = 0; j < entry.embedding.length; j += 1) {
220
+ const val = entry.embedding[j];
221
+ if (typeof val !== 'number' || !Number.isFinite(val)) {
222
+ violations.push({
223
+ category: 'cache_entry_shape_invalid',
224
+ severity: 'critical',
225
+ message: 'substrate[' + i + '].embedding[' + j + '] non-finite',
226
+ field: 'substrate[' + i + '].embedding[' + j + ']',
227
+ action_hint: 'invalidate_cache',
228
+ });
229
+ break; // one report per entry is enough
230
+ }
231
+ }
232
+ }
233
+ }
234
+ if (hasScore) {
235
+ if (!Number.isFinite(entry.score) || entry.score < 0 || entry.score > 1) {
236
+ violations.push({
237
+ category: 'cache_entry_shape_invalid',
238
+ severity: 'critical',
239
+ message: 'substrate[' + i + '].score out of [0,1]: ' + String(entry.score),
240
+ field: 'substrate[' + i + '].score',
241
+ action_hint: 'invalidate_cache',
242
+ });
243
+ }
244
+ }
245
+
246
+ // Check D: Canon Part 8 metadata body scan (Pitfall 4 attack surface).
247
+ // JSON.stringify the metadata so nested strings are visible to
248
+ // FORBIDDEN_PATTERNS. Cap at MAX_CANON_HITS_REPORTED overall to
249
+ // avoid report spam from a fully-poisoned cache.
250
+ if (canonHitCount < MAX_CANON_HITS_REPORTED &&
251
+ entry.metadata && typeof entry.metadata === 'object') {
252
+ const s = JSON.stringify(entry.metadata);
253
+ for (const re of FORBIDDEN_PATTERNS) {
254
+ if (re.test(s)) {
255
+ violations.push({
256
+ category: 'canon_boundary',
257
+ severity: 'critical',
258
+ message: 'substrate[' + i + '].metadata matched forbidden regex ' +
259
+ re.source,
260
+ field: 'substrate[' + i + '].metadata',
261
+ action_hint: 'invalidate_cache',
262
+ });
263
+ canonHitCount += 1;
264
+ break; // one match per entry is enough to fail the entry
265
+ }
266
+ }
267
+ }
268
+ }
269
+
270
+ return { severity: aggregateSeverity(violations), violations };
271
+ }
272
+
273
+ // ---------- Exports ----------
274
+
275
+ module.exports = {
276
+ id: 'brain-substrate-invariants',
277
+ severity_map: SEVERITY_MAP,
278
+ scope: 'room',
279
+ validate,
280
+ // Exported for downstream introspection + Phase 89.1a Plan 03
281
+ // adversarial fixture suite. Not required by the registry loader.
282
+ EXPECTED_EMBEDDING_DIM,
283
+ CACHE_SCHEMA_VERSION,
284
+ MAX_CANON_HITS_REPORTED,
285
+ };
@@ -0,0 +1,249 @@
1
+ /*
2
+ * Copyright (c) 2026 Mindrian. BSL 1.1.
3
+ * Phase 89.2 Plan 02 -- External academic fetcher invariants validator.
4
+ *
5
+ * Registry-compatible drop-in for the Phase 88-13 guardian. Auto-discovered
6
+ * by scripts/feynman-minto-guardian.cjs's validator loader (walks
7
+ * lib/memory/validators/*.cjs). Zero guardian.cjs edits needed.
8
+ *
9
+ * Mirrors brain-substrate-invariants.cjs structure byte-for-byte. The one
10
+ * difference is scope: external-papers.json telemetry lives at the GLOBAL
11
+ * per-user path ~/.mindrian/telemetry/external-papers.json (NOT per-room),
12
+ * so this validator declares scope='global'. The guardian still invokes it
13
+ * once per pass; roomPath is ignored.
14
+ *
15
+ * Six checks (Checks A-F):
16
+ * A) Telemetry file absent -> {severity: null} (healthy default)
17
+ * B) Per-source budget exceeded in rolling 24h -> warning per source
18
+ * C) status enum membership -> warning per entry
19
+ * Allowed statuses: ok | rate_limited | api_error | network_error |
20
+ * timeout | api_key_missing
21
+ * D) Canon Part 8 audit: query_text literal -> CRITICAL canon_boundary
22
+ * The telemetry primitive guarantees query_text NEVER persists
23
+ * (only query_text_hash). If a literal query_text field ever
24
+ * appears, that is a Canon Part 8 breach by definition.
25
+ * E) query_text_hash format -> warning
26
+ * Must match /^[a-f0-9]{16}$/ (sha256 16-char hex prefix).
27
+ * F) fetched_at parseable as ISO-8601 -> warning
28
+ *
29
+ * Fail-open: unexpected throws are caught by the guardian's validator
30
+ * runner. This validator does not swallow its own exceptions; the
31
+ * guardian owns that contract. Individual I/O paths (readFileSync) are
32
+ * wrapped defensively so a missing telemetry file never produces a
33
+ * violation.
34
+ *
35
+ * Pure CJS, node built-ins only + the rs-egress-telemetry primitive
36
+ * shipped in Plan 89.2-01 for TELEMETRY_FILE + DEFAULT_BUDGETS + WINDOW_MS.
37
+ */
38
+ 'use strict';
39
+
40
+ const fs = require('node:fs');
41
+
42
+ const {
43
+ TELEMETRY_FILE,
44
+ DEFAULT_BUDGETS,
45
+ WINDOW_MS,
46
+ } = require('../../core/rs-egress-telemetry.cjs');
47
+
48
+ // ---------- Constants ----------
49
+
50
+ const SEVERITY_MAP = Object.freeze({ critical: 3, error: 2, warning: 1, info: 0 });
51
+
52
+ // Mirrors lib/core/rs-egress-telemetry.cjs ALLOWED_STATUSES set. Kept in
53
+ // sync by parity gate (any drift fails an invariant scenario downstream).
54
+ const ALLOWED_STATUSES = Object.freeze(new Set([
55
+ 'ok',
56
+ 'rate_limited',
57
+ 'api_error',
58
+ 'network_error',
59
+ 'timeout',
60
+ 'api_key_missing',
61
+ ]));
62
+
63
+ // 16-char lowercase hex. sha256 prefix. Must match
64
+ // rs-egress-telemetry.cjs::hashQueryText output shape.
65
+ const CANON_PART_8_HASH_REGEX = /^[a-f0-9]{16}$/;
66
+
67
+ // Cap canon_boundary hits per validate() invocation so one poisoned
68
+ // ledger cannot spam the invariant report. Mirrors brain-substrate
69
+ // invariants and Plan 90-05 brain-md-invariants threshold.
70
+ const MAX_CANON_HITS_REPORTED = 5;
71
+
72
+ // Cap warning-class violations to bound report size on a fully-corrupt
73
+ // ledger. Critical hits are not capped (Check D never exceeds
74
+ // MAX_CANON_HITS_REPORTED on its own).
75
+ const MAX_WARNINGS_REPORTED = 25;
76
+
77
+ // ---------- Severity aggregator ----------
78
+
79
+ function aggregateSeverity(violations) {
80
+ if (!violations || violations.length === 0) return null;
81
+ let max = 0;
82
+ const inv = { 0: 'info', 1: 'warning', 2: 'error', 3: 'critical' };
83
+ for (const v of violations) {
84
+ const s = SEVERITY_MAP[v.severity];
85
+ if (typeof s === 'number' && s > max) max = s;
86
+ }
87
+ return inv[max];
88
+ }
89
+
90
+ // ---------- validate (Checks A-F) ----------
91
+ //
92
+ // scope='global'; roomPath is ignored (telemetry is per-user, not per-room).
93
+ // ctx is unused but accepted to match the validator contract.
94
+
95
+ function validate(_roomPath, _ctx) {
96
+ const violations = [];
97
+
98
+ // ---- Check A: telemetry file absent -> healthy default ----
99
+ if (!fs.existsSync(TELEMETRY_FILE)) {
100
+ return { severity: null, violations: [] };
101
+ }
102
+
103
+ // Read + parse defensively. A malformed ledger is a warning (not
104
+ // critical) because the telemetry primitive treats malformed payloads
105
+ // as cold-start; budgets just reset rather than crashing the fetcher.
106
+ let payload = null;
107
+ try {
108
+ const raw = fs.readFileSync(TELEMETRY_FILE, 'utf8');
109
+ payload = JSON.parse(raw);
110
+ } catch (err) {
111
+ violations.push({
112
+ validator: 'external-academic-invariants',
113
+ category: 'telemetry_unreadable',
114
+ severity: 'warning',
115
+ message: 'external-papers.json unreadable: ' + String(err && err.message),
116
+ action_hint: 'invalidate_telemetry',
117
+ });
118
+ return { severity: aggregateSeverity(violations), violations };
119
+ }
120
+
121
+ if (!payload || typeof payload !== 'object' || !Array.isArray(payload.entries)) {
122
+ violations.push({
123
+ validator: 'external-academic-invariants',
124
+ category: 'telemetry_malformed_root',
125
+ severity: 'warning',
126
+ message: 'external-papers.json root is not a plain object with entries[]',
127
+ action_hint: 'invalidate_telemetry',
128
+ });
129
+ return { severity: aggregateSeverity(violations), violations };
130
+ }
131
+
132
+ // ---- Check B: per-source budget exceeded in rolling 24h ----
133
+ // Aggregate counts per source within the rolling window, then compare
134
+ // against DEFAULT_BUDGETS. One warning per over-budget source so the
135
+ // operator sees which surface is the contention point.
136
+ const now = Date.now();
137
+ const sourceCounts = {};
138
+ for (const entry of payload.entries) {
139
+ if (!entry || typeof entry !== 'object') continue;
140
+ if (typeof entry.source !== 'string') continue;
141
+ if (typeof entry.fetched_at !== 'string') continue;
142
+ const t = Date.parse(entry.fetched_at);
143
+ if (Number.isNaN(t)) continue;
144
+ if ((now - t) > WINDOW_MS) continue;
145
+ sourceCounts[entry.source] = (sourceCounts[entry.source] || 0) + 1;
146
+ }
147
+ for (const source of Object.keys(sourceCounts)) {
148
+ const cap = DEFAULT_BUDGETS[source];
149
+ if (typeof cap === 'number' && sourceCounts[source] > cap) {
150
+ violations.push({
151
+ validator: 'external-academic-invariants',
152
+ category: 'budget_exceeded',
153
+ severity: 'warning',
154
+ message: 'source ' + source + ' has ' + sourceCounts[source] +
155
+ ' calls in last 24h; budget cap ' + cap,
156
+ field: 'entries[].source=' + source,
157
+ action_hint: 'rotate_budget',
158
+ });
159
+ }
160
+ }
161
+
162
+ // ---- Checks C / D / E / F: per-entry scans ----
163
+ let canonHitCount = 0;
164
+ let warningCount = 0;
165
+ for (let i = 0; i < payload.entries.length; i += 1) {
166
+ const entry = payload.entries[i];
167
+ if (!entry || typeof entry !== 'object') continue;
168
+
169
+ // ---- Check D: canon_boundary literal query_text presence ----
170
+ // The telemetry primitive guarantees query_text NEVER persists. If
171
+ // any entry carries the field at all, that is a Canon Part 8 breach.
172
+ if (Object.prototype.hasOwnProperty.call(entry, 'query_text')
173
+ && canonHitCount < MAX_CANON_HITS_REPORTED) {
174
+ violations.push({
175
+ validator: 'external-academic-invariants',
176
+ category: 'canon_boundary',
177
+ severity: 'critical',
178
+ message: 'entries[' + i + '] carries literal query_text (Canon Part 8 breach)',
179
+ field: 'entries[' + i + '].query_text',
180
+ action_hint: 'purge_telemetry',
181
+ });
182
+ canonHitCount += 1;
183
+ }
184
+
185
+ // ---- Check C: status enum membership ----
186
+ if (typeof entry.status === 'string' && !ALLOWED_STATUSES.has(entry.status)
187
+ && warningCount < MAX_WARNINGS_REPORTED) {
188
+ violations.push({
189
+ validator: 'external-academic-invariants',
190
+ category: 'status_enum_violation',
191
+ severity: 'warning',
192
+ message: 'entries[' + i + '].status is not in allowed set: ' +
193
+ String(entry.status).slice(0, 40),
194
+ field: 'entries[' + i + '].status',
195
+ action_hint: 'invalidate_telemetry',
196
+ });
197
+ warningCount += 1;
198
+ }
199
+
200
+ // ---- Check E: query_text_hash format ----
201
+ if (typeof entry.query_text_hash === 'string'
202
+ && !CANON_PART_8_HASH_REGEX.test(entry.query_text_hash)
203
+ && warningCount < MAX_WARNINGS_REPORTED) {
204
+ violations.push({
205
+ validator: 'external-academic-invariants',
206
+ category: 'hash_format_invalid',
207
+ severity: 'warning',
208
+ message: 'entries[' + i + '].query_text_hash fails /^[a-f0-9]{16}$/: ' +
209
+ String(entry.query_text_hash).slice(0, 40),
210
+ field: 'entries[' + i + '].query_text_hash',
211
+ action_hint: 'invalidate_telemetry',
212
+ });
213
+ warningCount += 1;
214
+ }
215
+
216
+ // ---- Check F: fetched_at parseable as ISO-8601 ----
217
+ if (typeof entry.fetched_at === 'string'
218
+ && Number.isNaN(Date.parse(entry.fetched_at))
219
+ && warningCount < MAX_WARNINGS_REPORTED) {
220
+ violations.push({
221
+ validator: 'external-academic-invariants',
222
+ category: 'fetched_at_malformed',
223
+ severity: 'warning',
224
+ message: 'entries[' + i + '].fetched_at not parseable as ISO-8601: ' +
225
+ String(entry.fetched_at).slice(0, 40),
226
+ field: 'entries[' + i + '].fetched_at',
227
+ action_hint: 'invalidate_telemetry',
228
+ });
229
+ warningCount += 1;
230
+ }
231
+ }
232
+
233
+ return { severity: aggregateSeverity(violations), violations };
234
+ }
235
+
236
+ // ---------- Exports ----------
237
+
238
+ module.exports = {
239
+ id: 'external-academic-invariants',
240
+ severity_map: SEVERITY_MAP,
241
+ scope: 'global',
242
+ validate: validate,
243
+ // Exported for downstream introspection + adversarial fixture suite.
244
+ // Not required by the registry loader.
245
+ ALLOWED_STATUSES: ALLOWED_STATUSES,
246
+ CANON_PART_8_HASH_REGEX: CANON_PART_8_HASH_REGEX,
247
+ MAX_CANON_HITS_REPORTED: MAX_CANON_HITS_REPORTED,
248
+ MAX_WARNINGS_REPORTED: MAX_WARNINGS_REPORTED,
249
+ };