@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,547 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /*
5
+ * Copyright (c) 2026 Mindrian. BSL 1.1.
6
+ * Phase 89.3 Plan 04 -- expert mapper fixture suite.
7
+ *
8
+ * mapAuthorsToAura(experts, opts) is the Cypher MATCH + MERGE-and-resolve
9
+ * module that takes Plan 89.2-05 mapExperts output and resolves authors,
10
+ * institutions, and citation edges in the user's own Aura. Tier 1 (Aura
11
+ * connected) runs full Cypher MATCH-then-MERGE; Tier 0 (no Aura) returns
12
+ * a degraded subset from rs-sqlite-mirror's Author + Institution rows in
13
+ * room.db with a friendly note (Claude's Discretion path 3 from CONTEXT.md).
14
+ *
15
+ * Output shape:
16
+ * {resolved_authors, resolved_institutions, citation_edges, missed_count,
17
+ * resolution_quality: 'full'|'degraded', tier: 'aura'|'sqlite', note?}
18
+ *
19
+ * 10 scenarios + A1 sweep:
20
+ * 1 Happy path Tier 1 -- mock Aura returns 5 matched authors
21
+ * 2 Tier 1 with unmatched authors -- MERGEs new nodes (3 MATCH + 2 MERGE = 5 resolved)
22
+ * 3 Tier 0 graceful degradation -- room.db has 3 of 5 authors; 2 missed; degraded note returned
23
+ * 4 Tier dispatch error -- no opts; throws TypeError
24
+ * 5 missed_count accuracy in Tier 1 -- 0 MATCH + 2 MERGE failures = 3 resolved + 2 missed
25
+ * 6 citation_edges populated -- mock returns 2 CO_AUTHORED records
26
+ * 7 Empty experts array returns empty result (early return; no driver/openGraph call)
27
+ * 8 Aura unreachable throws AuraUnreachableError (re-thrown)
28
+ * 9 CANON PART 8 adversarial -- forbidden in expert.name -- pre-Cypher Layer 1 catches
29
+ * 10 CANON PART 8 adversarial -- forbidden in expert.institution -- Layer 1 OR Layer 2 catches
30
+ *
31
+ * End-of-suite sweep:
32
+ * A1: re-scan every captured result object (JSON.stringify) against
33
+ * FORBIDDEN_PATTERNS as cross-scenario Canon Part 8 guarantee. For
34
+ * throw-scenarios, no result captured (audit fired pre-write).
35
+ *
36
+ * Pure CJS, zero npm deps, node built-ins only (assert, fs, path, os, crypto).
37
+ */
38
+
39
+ const assert = require('node:assert/strict');
40
+ const fs = require('node:fs');
41
+ const path = require('node:path');
42
+ const os = require('node:os');
43
+ const crypto = require('node:crypto');
44
+
45
+ const REPO_ROOT = path.resolve(__dirname, '..', '..');
46
+
47
+ // Source of truth for parity gate.
48
+ const crossRoomAggregator = require('../core/cross-room-aggregator.cjs');
49
+ const FORBIDDEN_PATTERNS = crossRoomAggregator.FORBIDDEN_PATTERNS;
50
+
51
+ // Lazy-required for tmp room seeding (Test 3 + 7).
52
+ const lazygraph = require('../core/lazygraph-ops.cjs');
53
+
54
+ // Track every tmp room dir for cleanup.
55
+ const tmpRooms = [];
56
+
57
+ // Every result object captured for end-of-suite A1 sweep.
58
+ const capturedResults = [];
59
+
60
+ let passed = 0;
61
+ let failed = 0;
62
+ const failures = [];
63
+
64
+ function resetRequireCache() {
65
+ const targets = [
66
+ '../core/rs-egress-violations.cjs',
67
+ '../core/rs-egress-prompts.cjs',
68
+ '../core/cross-room-aggregator.cjs',
69
+ '../core/rs-neo4j-writer.cjs',
70
+ '../core/rs-expert-mapper.cjs',
71
+ ];
72
+ for (const t of targets) {
73
+ try {
74
+ const p = require.resolve(t);
75
+ delete require.cache[p];
76
+ } catch (_e) { /* best effort */ }
77
+ }
78
+ }
79
+
80
+ async function runScenario(name, fn) {
81
+ const start = Date.now();
82
+ try {
83
+ await fn();
84
+ passed += 1;
85
+ process.stdout.write(' ok ' + name + ' (' + (Date.now() - start) + 'ms)\n');
86
+ } catch (err) {
87
+ failed += 1;
88
+ failures.push({ name: name, err: err });
89
+ process.stderr.write(' FAIL ' + name + '\n');
90
+ process.stderr.write(' ' + (err && err.stack ? err.stack : err) + '\n');
91
+ } finally {
92
+ resetRequireCache();
93
+ }
94
+ }
95
+
96
+ function captureResult(name, result) {
97
+ capturedResults.push({ scenario: name, result: result });
98
+ }
99
+
100
+ // ---------- Tmp room helper ----------
101
+
102
+ function makeTmpRoom() {
103
+ const slug = crypto.randomUUID();
104
+ const roomDir = path.join(os.tmpdir(), 'mos-89-3-04-' + slug);
105
+ fs.mkdirSync(roomDir, { recursive: true });
106
+ tmpRooms.push(roomDir);
107
+ return {
108
+ roomDir: roomDir,
109
+ cleanup: function () {
110
+ try {
111
+ fs.rmSync(roomDir, { recursive: true, force: true });
112
+ } catch (_e) { /* best effort */ }
113
+ },
114
+ };
115
+ }
116
+
117
+ // Seed the tmp room.db with N Author + Institution rows + AFFILIATED_WITH
118
+ // edges. Mirrors the schema rs-sqlite-mirror writes; the mapper Tier 0
119
+ // path SELECTs from this.
120
+ async function seedRoomDb(roomDir, authors) {
121
+ const handle = await lazygraph.openGraph(roomDir);
122
+ try {
123
+ const insertNode = handle.conn.prepare('INSERT OR REPLACE INTO nodes (id, type, properties) VALUES (?, ?, ?)');
124
+ const insertEdge = handle.conn.prepare('INSERT OR REPLACE INTO edges (source, target, type, properties) VALUES (?, ?, ?, ?)');
125
+ for (const a of authors) {
126
+ const authorKey = a.name + '|' + (a.orcid || '__no_orcid__');
127
+ insertNode.run(authorKey, 'Author', JSON.stringify({
128
+ name: a.name,
129
+ orcid: a.orcid || '',
130
+ h_index_estimate: typeof a.h_index_estimate === 'number' ? a.h_index_estimate : 0,
131
+ }));
132
+ if (a.institution) {
133
+ insertNode.run(a.institution, 'Institution', JSON.stringify({ name: a.institution }));
134
+ insertEdge.run(authorKey, a.institution, 'AFFILIATED_WITH', JSON.stringify({}));
135
+ }
136
+ // Seed a Paper + AUTHORED_BY edge so citation_edges has something to scan.
137
+ if (a.paper_id) {
138
+ insertNode.run(a.paper_id, 'Paper', JSON.stringify({ title: 'Seed Paper', source: 'openalex' }));
139
+ insertEdge.run(a.paper_id, authorKey, 'AUTHORED_BY', JSON.stringify({}));
140
+ }
141
+ }
142
+ } finally {
143
+ await lazygraph.closeGraph(handle.db);
144
+ }
145
+ }
146
+
147
+ // ---------- Mock driver factory ----------
148
+ //
149
+ // Mirrors the neo4j-driver session/run/close API. Each session.run pushes
150
+ // {cypher, params} onto driver.opLog. Returns a Promise that resolves
151
+ // based on opts.matchResults / opts.mergeResults / opts.coAuthorResults
152
+ // arrays (popped per call). opts.simulateConnectionError throws on first
153
+ // session.run. opts.simulateMergeFailureFor: array of author keys whose
154
+ // MERGE call should throw a Neo4j ConstraintError-like error (Test 5).
155
+
156
+ function makeMockDriver(opts) {
157
+ opts = opts || {};
158
+ const matchResults = (opts.matchResults || []).slice();
159
+ const mergeResults = (opts.mergeResults || []).slice();
160
+ const coAuthorResults = opts.coAuthorResults || [];
161
+ const driver = {
162
+ opLog: [],
163
+ runCount: 0,
164
+ sessionsClosed: 0,
165
+ session: function () {
166
+ const drv = this;
167
+ return {
168
+ run: async function (cypher, params) {
169
+ drv.opLog.push({ cypher: cypher, params: params });
170
+ drv.runCount += 1;
171
+ if (opts.simulateConnectionError) {
172
+ throw new Error('connect ECONNREFUSED 127.0.0.1:7687');
173
+ }
174
+ // Route based on cypher content.
175
+ if (/CO_AUTHORED|MATCH \(a1:Author\)/.test(cypher)) {
176
+ return { records: coAuthorResults.map(makeRecord), summary: { counters: {} } };
177
+ }
178
+ if (/^MERGE|^\s*MERGE/.test(cypher)) {
179
+ // MERGE branch.
180
+ if (opts.simulateMergeFailureFor && params && params.key &&
181
+ opts.simulateMergeFailureFor.indexOf(params.key) !== -1) {
182
+ throw new Error('Neo4j.ClientError.Schema.ConstraintValidationFailed: MERGE failed');
183
+ }
184
+ const next = mergeResults.shift();
185
+ return { records: (next || []).map(makeRecord), summary: { counters: { nodesCreated: 1 } } };
186
+ }
187
+ // MATCH branch (default).
188
+ const next = matchResults.shift();
189
+ return { records: (next || []).map(makeRecord), summary: { counters: {} } };
190
+ },
191
+ close: async function () {
192
+ drv.sessionsClosed += 1;
193
+ },
194
+ };
195
+ },
196
+ };
197
+ return driver;
198
+ }
199
+
200
+ // neo4j-driver Record shim: get(key) returns the field value.
201
+ function makeRecord(obj) {
202
+ return {
203
+ get: function (key) {
204
+ return obj[key];
205
+ },
206
+ keys: Object.keys(obj),
207
+ _fields: Object.values(obj),
208
+ };
209
+ }
210
+
211
+ // ---------- Fixture builders ----------
212
+
213
+ function makeExpert(overrides) {
214
+ const base = {
215
+ name: 'Smith, Jane',
216
+ orcid: '0000-0001-2345-6789',
217
+ institution: 'Stanford University',
218
+ paper_count: 1,
219
+ h_index_estimate: 1,
220
+ public_email_or_null: null,
221
+ };
222
+ if (overrides) {
223
+ for (const k of Object.keys(overrides)) {
224
+ base[k] = overrides[k];
225
+ }
226
+ }
227
+ return base;
228
+ }
229
+
230
+ function makeFiveExperts() {
231
+ return [
232
+ makeExpert({ name: 'Smith, Jane', orcid: '0000-0001-1111-1111', institution: 'Stanford', paper_count: 4, h_index_estimate: 3 }),
233
+ makeExpert({ name: 'Alpha, Adam', orcid: '0000-0002-2222-2222', institution: 'MIT', paper_count: 3, h_index_estimate: 2 }),
234
+ makeExpert({ name: 'Beta, Bob', orcid: '0000-0003-3333-3333', institution: 'Harvard', paper_count: 2, h_index_estimate: 2 }),
235
+ makeExpert({ name: 'Gamma, Greg', orcid: '0000-0004-4444-4444', institution: 'Berkeley', paper_count: 1, h_index_estimate: 1 }),
236
+ makeExpert({ name: 'Delta, Dora', orcid: '0000-0005-5555-5555', institution: 'CalTech', paper_count: 1, h_index_estimate: 1 }),
237
+ ];
238
+ }
239
+
240
+ // ---------- Scenarios ----------
241
+
242
+ async function scenario1HappyPathTier1() {
243
+ const m = require('../core/rs-expert-mapper.cjs');
244
+ const experts = makeFiveExperts();
245
+ // Mock returns 1-record MATCH for each expert (5 calls -> 5 single-record results)
246
+ const driver = makeMockDriver({
247
+ matchResults: experts.map(function (e, i) {
248
+ return [{ name: e.name, orcid: e.orcid, aura_node_id: 100 + i }];
249
+ }),
250
+ coAuthorResults: [],
251
+ });
252
+
253
+ const result = await m.mapAuthorsToAura(experts, { tier: 'aura', driver: driver });
254
+ captureResult('1-happy-tier1', result);
255
+
256
+ assert.equal(result.resolved_authors.length, 5, 'expected 5 resolved, got ' + result.resolved_authors.length);
257
+ assert.equal(result.missed_count, 0, 'missed_count must be 0, got ' + result.missed_count);
258
+ assert.equal(result.resolution_quality, 'full', 'resolution_quality must be full, got ' + result.resolution_quality);
259
+ assert.equal(result.tier, 'aura', 'tier must be aura, got ' + result.tier);
260
+ assert.strictEqual(result.note, undefined, 'note must be undefined when full, got ' + JSON.stringify(result.note));
261
+ assert.equal(result.schema_version, '1.0');
262
+ }
263
+
264
+ async function scenario2Tier1WithUnmatchedMerges() {
265
+ const m = require('../core/rs-expert-mapper.cjs');
266
+ const experts = makeFiveExperts();
267
+ // First 3 experts match; last 2 return zero rows (unmatched -> trigger MERGE)
268
+ const driver = makeMockDriver({
269
+ matchResults: [
270
+ [{ name: experts[0].name, orcid: experts[0].orcid, aura_node_id: 200 }],
271
+ [{ name: experts[1].name, orcid: experts[1].orcid, aura_node_id: 201 }],
272
+ [{ name: experts[2].name, orcid: experts[2].orcid, aura_node_id: 202 }],
273
+ [], // unmatched
274
+ [], // unmatched
275
+ ],
276
+ mergeResults: [
277
+ [{ name: experts[3].name, aura_node_id: 203 }],
278
+ [{ name: experts[4].name, aura_node_id: 204 }],
279
+ ],
280
+ });
281
+
282
+ const result = await m.mapAuthorsToAura(experts, { tier: 'aura', driver: driver });
283
+ captureResult('2-tier1-unmatched-merge', result);
284
+
285
+ assert.equal(result.resolved_authors.length, 5, 'expected 5 resolved (3 MATCH + 2 MERGE), got ' + result.resolved_authors.length);
286
+ assert.equal(result.missed_count, 0, 'none should be missed; all resolved by MATCH or MERGE');
287
+ // Verify both MATCH and MERGE Cyphers ran.
288
+ const cypherLog = driver.opLog.map(function (o) { return o.cypher; }).join('\n');
289
+ assert.ok(/MATCH/.test(cypherLog), 'MATCH Cypher must have run');
290
+ assert.ok(/MERGE/.test(cypherLog), 'MERGE Cypher must have run for unmatched');
291
+ }
292
+
293
+ async function scenario3Tier0GracefulDegradation() {
294
+ const m = require('../core/rs-expert-mapper.cjs');
295
+ const tmp = makeTmpRoom();
296
+ try {
297
+ // Seed 3 of the 5 authors into room.db
298
+ await seedRoomDb(tmp.roomDir, [
299
+ { name: 'Smith, Jane', orcid: '0000-0001-1111-1111', institution: 'Stanford', h_index_estimate: 3, paper_id: 'p1' },
300
+ { name: 'Alpha, Adam', orcid: '0000-0002-2222-2222', institution: 'MIT', h_index_estimate: 2, paper_id: 'p2' },
301
+ { name: 'Beta, Bob', orcid: '0000-0003-3333-3333', institution: 'Harvard', h_index_estimate: 2, paper_id: 'p1' },
302
+ ]);
303
+
304
+ const experts = makeFiveExperts();
305
+ const result = await m.mapAuthorsToAura(experts, { tier: 'sqlite', roomDir: tmp.roomDir });
306
+ captureResult('3-tier0-degraded', result);
307
+
308
+ assert.equal(result.resolved_authors.length, 3, 'expected 3 resolved from sqlite, got ' + result.resolved_authors.length);
309
+ assert.equal(result.missed_count, 2, 'expected 2 missed (Gamma + Delta absent from db), got ' + result.missed_count);
310
+ assert.equal(result.resolution_quality, 'degraded', 'resolution_quality must be degraded for Tier 0');
311
+ assert.equal(result.tier, 'sqlite');
312
+ assert.ok(typeof result.note === 'string' && result.note.length > 0, 'note must be non-empty for degraded result');
313
+ assert.ok(/Aura/i.test(result.note), 'note should mention Aura, got: ' + result.note);
314
+ assert.ok(/multi-hop/i.test(result.note), 'note should mention multi-hop, got: ' + result.note);
315
+ assert.ok(Array.isArray(result.citation_edges), 'citation_edges must be an array');
316
+ // citation_edges should be best-effort populated from AUTHORED_BY edges (Smith + Beta share paper p1)
317
+ assert.ok(result.citation_edges.length >= 1, 'citation_edges must be populated from shared-paper AUTHORED_BY edges, got ' + result.citation_edges.length);
318
+ } finally {
319
+ tmp.cleanup();
320
+ }
321
+ }
322
+
323
+ async function scenario4TierDispatchError() {
324
+ const m = require('../core/rs-expert-mapper.cjs');
325
+ let threw = null;
326
+ try {
327
+ await m.mapAuthorsToAura([{ name: 'Smith' }], {});
328
+ } catch (err) {
329
+ threw = err;
330
+ }
331
+ assert.ok(threw, 'must throw on missing opts.tier/driver/roomDir');
332
+ assert.equal(threw.name, 'TypeError', 'must throw TypeError, got ' + threw.name);
333
+ assert.ok(/opts\.tier OR opts\.driver OR opts\.roomDir/.test(threw.message), 'error message must mention all three options, got: ' + threw.message);
334
+ }
335
+
336
+ async function scenario5MissedCountAccuracy() {
337
+ const m = require('../core/rs-expert-mapper.cjs');
338
+ const experts = makeFiveExperts();
339
+ // ALL MATCH return zero rows -> all 5 trigger MERGE; simulate 2 MERGE failures
340
+ const driver = makeMockDriver({
341
+ matchResults: [[], [], [], [], []],
342
+ mergeResults: [
343
+ [{ name: experts[0].name, aura_node_id: 300 }],
344
+ [{ name: experts[1].name, aura_node_id: 301 }],
345
+ [{ name: experts[2].name, aura_node_id: 302 }],
346
+ // experts[3] and experts[4] MERGE will fail (simulateMergeFailureFor)
347
+ ],
348
+ simulateMergeFailureFor: [
349
+ experts[3].name + '|' + experts[3].orcid,
350
+ experts[4].name + '|' + experts[4].orcid,
351
+ ],
352
+ });
353
+
354
+ const result = await m.mapAuthorsToAura(experts, { tier: 'aura', driver: driver });
355
+ captureResult('5-missed-count', result);
356
+
357
+ assert.equal(result.resolved_authors.length, 3, 'expected 3 resolved, got ' + result.resolved_authors.length);
358
+ assert.equal(result.missed_count, 2, 'expected 2 missed (MERGE failures), got ' + result.missed_count);
359
+ assert.equal(result.resolution_quality, 'full', 'still full because tier is aura');
360
+ }
361
+
362
+ async function scenario6CitationEdgesPopulated() {
363
+ const m = require('../core/rs-expert-mapper.cjs');
364
+ const experts = makeFiveExperts().slice(0, 3);
365
+ const driver = makeMockDriver({
366
+ matchResults: experts.map(function (e, i) {
367
+ return [{ name: e.name, orcid: e.orcid, aura_node_id: 400 + i }];
368
+ }),
369
+ coAuthorResults: [
370
+ { source_author: experts[0].name, target_author: experts[1].name, paper_id: 'openalex:W001' },
371
+ { source_author: experts[0].name, target_author: experts[2].name, paper_id: 'openalex:W002' },
372
+ ],
373
+ });
374
+
375
+ const result = await m.mapAuthorsToAura(experts, { tier: 'aura', driver: driver });
376
+ captureResult('6-citation-edges', result);
377
+
378
+ assert.ok(result.citation_edges.length >= 2, 'citation_edges must have >= 2 records, got ' + result.citation_edges.length);
379
+ for (const edge of result.citation_edges) {
380
+ assert.ok('source_author' in edge, 'edge missing source_author');
381
+ assert.ok('target_author' in edge, 'edge missing target_author');
382
+ assert.ok('paper_id' in edge, 'edge missing paper_id');
383
+ assert.equal(edge.type, 'CO_AUTHORED', 'edge type must be CO_AUTHORED, got ' + edge.type);
384
+ }
385
+ }
386
+
387
+ async function scenario7EmptyExpertsArray() {
388
+ const m = require('../core/rs-expert-mapper.cjs');
389
+ const tmp = makeTmpRoom();
390
+ try {
391
+ const result = await m.mapAuthorsToAura([], { tier: 'sqlite', roomDir: tmp.roomDir });
392
+ captureResult('7-empty-experts', result);
393
+
394
+ assert.deepEqual(result.resolved_authors, [], 'resolved_authors must be empty');
395
+ assert.deepEqual(result.resolved_institutions, [], 'resolved_institutions must be empty');
396
+ assert.deepEqual(result.citation_edges, [], 'citation_edges must be empty');
397
+ assert.equal(result.missed_count, 0);
398
+ assert.equal(result.resolution_quality, 'degraded', 'still degraded because tier is sqlite');
399
+ assert.equal(result.tier, 'sqlite');
400
+ } finally {
401
+ tmp.cleanup();
402
+ }
403
+ }
404
+
405
+ async function scenario8AuraUnreachable() {
406
+ const m = require('../core/rs-expert-mapper.cjs');
407
+ const experts = makeFiveExperts().slice(0, 1);
408
+ const driver = makeMockDriver({ simulateConnectionError: true });
409
+
410
+ let threw = null;
411
+ try {
412
+ await m.mapAuthorsToAura(experts, { tier: 'aura', driver: driver });
413
+ } catch (err) {
414
+ threw = err;
415
+ }
416
+ assert.ok(threw, 'must throw on Aura connection failure');
417
+ assert.equal(threw.name, 'AuraUnreachableError', 'must throw AuraUnreachableError, got ' + threw.name);
418
+ assert.ok(threw.meta && typeof threw.meta.original === 'string', 'meta.original must carry original message');
419
+ assert.ok(/ECONNREFUSED|connect/i.test(threw.meta.original), 'meta.original must reflect connection failure, got: ' + threw.meta.original);
420
+ }
421
+
422
+ async function scenario9AdvForbiddenInName() {
423
+ const m = require('../core/rs-expert-mapper.cjs');
424
+ // FORBIDDEN_PATTERNS[2] matches Lawrence said -- pattern in expert.name
425
+ const poisonedExpert = makeExpert({ name: 'Lawrence said our PI', orcid: null, institution: 'X', paper_count: 1, h_index_estimate: 1 });
426
+ const driver = makeMockDriver({ matchResults: [[]] });
427
+
428
+ let threw = null;
429
+ let result = null;
430
+ try {
431
+ result = await m.mapAuthorsToAura([poisonedExpert], { tier: 'aura', driver: driver });
432
+ } catch (err) {
433
+ threw = err;
434
+ }
435
+
436
+ assert.ok(threw, 'must throw on adversarial name (Layer 1 OR Layer 2 catches)');
437
+ assert.equal(threw.name, 'ExternalEgressViolation', 'must throw ExternalEgressViolation, got ' + threw.name);
438
+ assert.ok(threw.meta && typeof threw.meta.surface === 'string', 'meta.surface must be set');
439
+ assert.ok(threw.meta.surface === 'rs-expert-mapper' || threw.meta.surface === 'rs-expert-mapper-output',
440
+ 'surface must be one of the 2 expert-mapper tags, got: ' + threw.meta.surface);
441
+ // session.run NEVER called (Layer 1 caught pre-Cypher).
442
+ assert.equal(driver.opLog.length, 0, 'session.run must NOT have run; Layer 1 audit caught pre-Cypher; got opLog.length=' + driver.opLog.length);
443
+ captureResult('9-adv-name-throw', { thrown: threw.name, surface: threw.meta.surface });
444
+ }
445
+
446
+ async function scenario10AdvForbiddenInInstitution() {
447
+ // FORBIDDEN_PATTERNS[3] matches /\bmeeting with\b/i in expert.institution
448
+ // This test verifies BOTH layers are wired (grep for 2 distinct surface
449
+ // strings: 'rs-expert-mapper' AND 'rs-expert-mapper-output' in source).
450
+ const fs2 = require('node:fs');
451
+ const sourcePath = path.join(REPO_ROOT, 'lib', 'core', 'rs-expert-mapper.cjs');
452
+ const source = fs2.readFileSync(sourcePath, 'utf-8');
453
+ assert.ok(/'rs-expert-mapper'/.test(source) || /SURFACE_PRE\s*=\s*'rs-expert-mapper'/.test(source),
454
+ 'source must wire SURFACE_PRE = rs-expert-mapper');
455
+ assert.ok(/'rs-expert-mapper-output'/.test(source) || /SURFACE_POST\s*=\s*'rs-expert-mapper-output'/.test(source),
456
+ 'source must wire SURFACE_POST = rs-expert-mapper-output');
457
+
458
+ // Now exercise the actual throw path.
459
+ const m = require('../core/rs-expert-mapper.cjs');
460
+ const poisonedExpert = makeExpert({ name: 'Smith, Jane', orcid: '0000-0001-2345-6789', institution: 'meeting with Stanford', paper_count: 1, h_index_estimate: 1 });
461
+ const driver = makeMockDriver({ matchResults: [[]] });
462
+
463
+ let threw = null;
464
+ try {
465
+ await m.mapAuthorsToAura([poisonedExpert], { tier: 'aura', driver: driver });
466
+ } catch (err) {
467
+ threw = err;
468
+ }
469
+ assert.ok(threw, 'must throw on adversarial institution (Layer 1 catches pre-Cypher)');
470
+ assert.equal(threw.name, 'ExternalEgressViolation', 'must throw ExternalEgressViolation, got ' + threw.name);
471
+ assert.ok(threw.meta && /meeting with/i.test(threw.meta.matched_pattern || ''),
472
+ 'matched_pattern must reflect meeting with regex, got: ' + (threw.meta && threw.meta.matched_pattern));
473
+ captureResult('10-adv-institution-throw', { thrown: threw.name, surface: threw.meta.surface });
474
+ }
475
+
476
+ // ---------- A1 end-of-suite sweep ----------
477
+
478
+ function a1Sweep() {
479
+ let hits = 0;
480
+ for (const rec of capturedResults) {
481
+ let json;
482
+ try {
483
+ json = JSON.stringify(rec.result);
484
+ } catch (_e) {
485
+ continue;
486
+ }
487
+ if (typeof json !== 'string') continue;
488
+ for (const re of FORBIDDEN_PATTERNS) {
489
+ if (re.test(json)) {
490
+ hits += 1;
491
+ process.stderr.write(
492
+ ' A1 HIT scenario=' + rec.scenario +
493
+ ' pattern=' + re.source + '\n'
494
+ );
495
+ }
496
+ }
497
+ }
498
+ return hits;
499
+ }
500
+
501
+ // ---------- Main ----------
502
+
503
+ (async function main() {
504
+ process.stdout.write('=== 89.3-04 rs-expert-mapper suite (REPO_ROOT=' + REPO_ROOT + ') ===\n');
505
+
506
+ await runScenario('1-happy-path-tier1', scenario1HappyPathTier1);
507
+ await runScenario('2-tier1-unmatched-MERGE', scenario2Tier1WithUnmatchedMerges);
508
+ await runScenario('3-tier0-graceful-degradation', scenario3Tier0GracefulDegradation);
509
+ await runScenario('4-tier-dispatch-error', scenario4TierDispatchError);
510
+ await runScenario('5-missed-count-accuracy', scenario5MissedCountAccuracy);
511
+ await runScenario('6-citation-edges-populated', scenario6CitationEdgesPopulated);
512
+ await runScenario('7-empty-experts-early-return', scenario7EmptyExpertsArray);
513
+ await runScenario('8-aura-unreachable', scenario8AuraUnreachable);
514
+ await runScenario('9-adv-CANON-PART-8-name', scenario9AdvForbiddenInName);
515
+ await runScenario('10-adv-CANON-PART-8-institution', scenario10AdvForbiddenInInstitution);
516
+
517
+ const sweepHits = a1Sweep();
518
+ if (sweepHits > 0) {
519
+ failed += 1;
520
+ failures.push({
521
+ name: 'A1-sweep',
522
+ err: new Error('A1 sweep found ' + sweepHits + ' forbidden-pattern hits'),
523
+ });
524
+ process.stderr.write(' FAIL A1-sweep (' + sweepHits + ' hits)\n');
525
+ } else {
526
+ process.stdout.write(' ok A1-sweep (zero forbidden-pattern hits)\n');
527
+ }
528
+
529
+ // Final cleanup of all tmp rooms.
530
+ for (const dir of tmpRooms) {
531
+ try { fs.rmSync(dir, { recursive: true, force: true }); } catch (_e) { /* best effort */ }
532
+ }
533
+
534
+ if (failed > 0) {
535
+ process.stdout.write('=== 89.3-04 rs-expert-mapper suite: ' + passed + '/10 passed' + ' (' + failed + ' failed) ===\n');
536
+ for (const f of failures) {
537
+ process.stderr.write(' FAILURE: ' + f.name + ': ' + (f.err && f.err.message ? f.err.message : String(f.err)) + '\n');
538
+ }
539
+ process.exit(1);
540
+ }
541
+
542
+ process.stdout.write('=== 89.3-04 rs-expert-mapper suite: 10/10 passed ===\n');
543
+ process.exit(0);
544
+ })().catch(function (err) {
545
+ process.stderr.write('FATAL: ' + (err && err.stack ? err.stack : err) + '\n');
546
+ process.exit(2);
547
+ });