@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,287 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ rs-math.py -- Shared math helpers for the Reverse Salient Engine (Phase 89)
4
+ ==============================================================================
5
+
6
+ Ports the authoritative Hughes 1983 / Kwan 2023 reverse-salient algorithm into
7
+ reusable pure functions. Consumed by:
8
+ - scripts/rs-engine.py (Plan 89-01, Mode A single-room)
9
+ - scripts/rs-engine.py Mode B/C (Plans 89-04, 89-05)
10
+
11
+ Authoritative source: .planning/phases/89-reverse-salient-engine/source/lsa.py
12
+ (Newton Kwan et al., 2023). The distinguishing property of this algorithm is
13
+ TOPIC-KEYWORD-MEMBERSHIP COUNTING + signed abs-diff detection -- NOT
14
+ cosine-on-SVD, NOT MiniLM cosine. Do not replace these with more modern
15
+ defaults; ALGORITHM-SOURCE.md documents why the keyword-membership signal is
16
+ load-bearing.
17
+
18
+ License: BSL-1.1 (see LICENSE at repo root).
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ from typing import Iterable, List, Sequence, Tuple
24
+
25
+ import numpy as np
26
+
27
+
28
+ # ---------------------------------------------------------------------------
29
+ # Step 1: TF-IDF + Truncated SVD
30
+ # ---------------------------------------------------------------------------
31
+
32
+ def build_tfidf_svd(
33
+ texts: Sequence[str],
34
+ n_components: int = 80,
35
+ max_features: int = 2000,
36
+ max_df: float = 0.5,
37
+ random_state: int = 256,
38
+ ) -> Tuple[object, object, object]:
39
+ """Fit TF-IDF + TruncatedSVD on a corpus.
40
+
41
+ Ports source/lsa.py lines 39-55 faithfully. Parameter choices
42
+ (2000 features, max_df=0.5, 80 components, n_iter=10, seed=256) are taken
43
+ verbatim from the authoritative source. For corpora smaller than 80 docs,
44
+ n_components is clamped to max(1, N-1) to avoid SVD underflow.
45
+
46
+ Returns (vectorizer, svd_model, X) where X is the sparse TF-IDF matrix.
47
+ """
48
+ # Imported lazily so rs-math.py can be imported without sklearn for tests
49
+ # that exercise only abs_diff_topk / classify_direction.
50
+ from sklearn.decomposition import TruncatedSVD
51
+ from sklearn.feature_extraction.text import TfidfVectorizer
52
+
53
+ vectorizer = TfidfVectorizer(
54
+ stop_words="english",
55
+ max_features=max_features,
56
+ max_df=max_df,
57
+ smooth_idf=True,
58
+ )
59
+ X = vectorizer.fit_transform(texts)
60
+
61
+ n_rows, n_terms = X.shape
62
+ effective_components = max(1, min(n_components, n_rows - 1, n_terms - 1))
63
+ svd_model = TruncatedSVD(
64
+ n_components=effective_components,
65
+ algorithm="randomized",
66
+ n_iter=10,
67
+ random_state=random_state,
68
+ )
69
+ svd_model.fit(X)
70
+
71
+ return vectorizer, svd_model, X
72
+
73
+
74
+ # ---------------------------------------------------------------------------
75
+ # Step 2: Extract top-k keywords per SVD component
76
+ # ---------------------------------------------------------------------------
77
+
78
+ def extract_topic_keywords(svd_model, terms: Sequence[str], top_k: int = 7) -> List[List[str]]:
79
+ """For each SVD component, return the top_k terms by weight descending.
80
+
81
+ Ports source/lsa.py lines 59-72 faithfully. `terms` is typically the
82
+ output of `vectorizer.get_feature_names_out()`.
83
+ """
84
+ topics: List[List[str]] = []
85
+ for comp in svd_model.components_:
86
+ sorted_terms = sorted(
87
+ zip(terms, comp), key=lambda pair: pair[1], reverse=True
88
+ )[:top_k]
89
+ topics.append([t for t, _ in sorted_terms])
90
+ return topics
91
+
92
+
93
+ # ---------------------------------------------------------------------------
94
+ # Step 3: Topic-keyword-membership counting (the authoritative signature)
95
+ # ---------------------------------------------------------------------------
96
+
97
+ def count_topic_membership(
98
+ tokenized_papers: Sequence[Sequence[str]],
99
+ topics: Sequence[Sequence[str]],
100
+ ) -> np.ndarray:
101
+ """Count how many of each paper's tokens appear in each topic's keyword set.
102
+
103
+ Ports source/lsa.py lines 80-98 faithfully.
104
+
105
+ CRITICAL: this is NOT cosine-on-SVD. It measures whether papers share
106
+ topic-level keywords (top 7 terms per SVD component). ALGORITHM-SOURCE.md
107
+ line 72 warns that swapping this for cosine-on-SVD changes the signal
108
+ entirely. Do not optimize this.
109
+
110
+ Returns a (n_papers, n_topics) float32 matrix.
111
+ """
112
+ n_papers = len(tokenized_papers)
113
+ n_topics = len(topics)
114
+
115
+ # Precompute topic keyword sets for O(1) membership lookup per token.
116
+ topic_sets = [set(t) for t in topics]
117
+
118
+ counts = np.zeros((n_papers, n_topics), dtype=np.float32)
119
+ for i, tokens in enumerate(tokenized_papers):
120
+ for word in tokens:
121
+ for j, kw_set in enumerate(topic_sets):
122
+ if word in kw_set:
123
+ counts[i, j] += 1.0
124
+ return counts
125
+
126
+
127
+ # ---------------------------------------------------------------------------
128
+ # Step 4: Row-normalize + pairwise L1 distance + invert to similarity
129
+ # ---------------------------------------------------------------------------
130
+
131
+ def normalize_and_l1_similarity(topic_count_matrix: np.ndarray) -> np.ndarray:
132
+ """Row-normalize, compute pairwise L1 distance, invert, and rescale.
133
+
134
+ Ports source/lsa.py lines 102-126 faithfully. Steps:
135
+ 1. Row-normalize (each paper becomes a topic distribution summing to 1)
136
+ 2. Pairwise L1 distance via broadcast
137
+ 3. Invert: sim = max(dist) - dist (closer pairs score higher)
138
+ 4. Rescale to [0, 1] centered at 0.5
139
+
140
+ Diagonal entries should be ~1.0 (self-similarity maximum) after rescaling.
141
+
142
+ Returns a (N, N) float32 similarity matrix.
143
+ """
144
+ matrix = np.asarray(topic_count_matrix, dtype=np.float32)
145
+ n = matrix.shape[0]
146
+
147
+ row_sums = matrix.sum(axis=1)
148
+ # Papers with zero keyword hits would divide by zero; substitute 1.0 so the
149
+ # row stays all-zeros after normalization (most-dissimilar bucket). This
150
+ # matches the np.nan_to_num behavior in source/lsa.py line 106.
151
+ safe_row_sums = np.where(row_sums == 0.0, 1.0, row_sums)
152
+ normalized = (matrix.T / safe_row_sums).T
153
+
154
+ # Pairwise L1 distance: |normalized[i] - normalized[j]| summed across
155
+ # topics, same broadcast trick as the authoritative source.
156
+ diff = np.abs(normalized[:, None, :] - normalized[None, :, :])
157
+ sum_matrix = diff.sum(axis=2).astype(np.float32)
158
+
159
+ # Invert so that smaller distances become larger similarities.
160
+ sum_matrix = sum_matrix.max() - sum_matrix
161
+
162
+ # Rescale to [0, 1] centered at 0.5 (authoritative step).
163
+ rng = sum_matrix.max() - sum_matrix.min()
164
+ if rng == 0:
165
+ # Degenerate corpus (all identical) -- return identity-like matrix so
166
+ # abs_diff_topk still produces deterministic output.
167
+ return np.eye(n, dtype=np.float32)
168
+ midpoint = (sum_matrix.max() + sum_matrix.min()) / 2.0
169
+ return ((sum_matrix - midpoint) / rng) + 0.5
170
+
171
+
172
+ # ---------------------------------------------------------------------------
173
+ # Step 5: abs-diff top-k iterative argmax with symmetric cleanup
174
+ # ---------------------------------------------------------------------------
175
+
176
+ def abs_diff_topk(
177
+ lsa_matrix: np.ndarray,
178
+ semantic_matrix: np.ndarray,
179
+ k: int = 1000,
180
+ skip_diagonal: bool = True,
181
+ ) -> List[Tuple[int, int, float, float]]:
182
+ """Return the top-k pairs by |semantic - lsa| differential.
183
+
184
+ Ports source/comparison.py lines 130-147 faithfully. The whole innovation
185
+ detection is this single signal: where do semantic and structural
186
+ similarity disagree most?
187
+
188
+ Each returned tuple is (i, j, signed_diff, abs_diff) where:
189
+ - signed_diff = semantic_matrix[i, j] - lsa_matrix[i, j]
190
+ - abs_diff = |signed_diff|
191
+ - signed_diff > 0 -> structural_transfer (different words, similar meaning)
192
+ - signed_diff < 0 -> semantic_implementation (same words, different meaning)
193
+
194
+ `skip_diagonal=True` zeroes out self-pairs before iteration so
195
+ (i, i) never wins. Symmetric cleanup ensures (j, i) is suppressed when
196
+ (i, j) is selected, preventing duplicate reporting.
197
+ """
198
+ lsa = np.asarray(lsa_matrix, dtype=np.float64)
199
+ sem = np.asarray(semantic_matrix, dtype=np.float64)
200
+ if lsa.shape != sem.shape:
201
+ raise ValueError(
202
+ f"shape mismatch: lsa {lsa.shape} vs semantic {sem.shape}"
203
+ )
204
+ n = lsa.shape[0]
205
+ if n < 2:
206
+ return []
207
+
208
+ signed_diff = sem - lsa
209
+ abs_diff = np.abs(signed_diff).copy()
210
+
211
+ if skip_diagonal:
212
+ np.fill_diagonal(abs_diff, -np.inf)
213
+
214
+ # Upper triangle only: symmetric matrix means (i,j) == (j,i); the
215
+ # authoritative source handles this by suppressing the mirror after each
216
+ # pick. We precompute mask so argmax never returns lower-triangle.
217
+ iu = np.tril_indices(n, k=-1)
218
+ abs_diff[iu] = -np.inf
219
+
220
+ results: List[Tuple[int, int, float, float]] = []
221
+ k = max(0, min(k, (n * (n - 1)) // 2))
222
+ for _ in range(k):
223
+ flat_index = int(np.argmax(abs_diff))
224
+ i, j = np.unravel_index(flat_index, abs_diff.shape)
225
+ max_val = float(abs_diff[i, j])
226
+ if max_val == -np.inf or max_val <= 0.0:
227
+ break
228
+ results.append((int(i), int(j), float(signed_diff[i, j]), float(max_val)))
229
+ # Symmetric cleanup: source/comparison.py lines 145-146 subtract the
230
+ # max-val so the mirror never wins next iteration. We mask to -inf
231
+ # directly since we already restrict to upper triangle.
232
+ abs_diff[i, j] = -np.inf
233
+ abs_diff[j, i] = -np.inf
234
+
235
+ return results
236
+
237
+
238
+ # ---------------------------------------------------------------------------
239
+ # Step 6: Direction classification
240
+ # ---------------------------------------------------------------------------
241
+
242
+ def classify_direction(signed_diff: float) -> str:
243
+ """Classify an RS pair by the sign of (semantic - lsa).
244
+
245
+ Based on ALGORITHM-SOURCE.md lines 150-153:
246
+ - signed > 0 -> structural_transfer (different keywords, similar meaning)
247
+ - signed <= 0 -> semantic_implementation (same keywords, different meaning)
248
+
249
+ The zero case is rare in practice (post-rescaling); we bucket it with
250
+ semantic_implementation for deterministic output.
251
+ """
252
+ return "structural_transfer" if float(signed_diff) > 0.0 else "semantic_implementation"
253
+
254
+
255
+ # ---------------------------------------------------------------------------
256
+ # Convenience: full LSA pipeline on raw text corpus
257
+ # ---------------------------------------------------------------------------
258
+
259
+ def build_lsa_matrix(texts: Sequence[str], n_components: int = 80, top_k: int = 7) -> np.ndarray:
260
+ """Run the full authoritative LSA pipeline end-to-end.
261
+
262
+ Equivalent to running build_tfidf_svd + extract_topic_keywords +
263
+ count_topic_membership + normalize_and_l1_similarity in sequence. Provided
264
+ as a single entry point so scripts/rs-engine.py and plans 89-04/89-05 can
265
+ call one function and not re-wire the pipeline.
266
+
267
+ Tokenization mirrors source/lsa.py lines 14-17: simple whitespace split on
268
+ the raw text, so topic-keyword matching compares against the same tokens
269
+ that TF-IDF saw. Do not pre-strip punctuation (see ALGORITHM-SOURCE.md
270
+ Pitfall 1 in RESEARCH.md line 569-573).
271
+ """
272
+ tokenized = [t.split() for t in texts]
273
+ vec, svd, _X = build_tfidf_svd(texts, n_components=n_components)
274
+ topics = extract_topic_keywords(svd, vec.get_feature_names_out(), top_k=top_k)
275
+ counts = count_topic_membership(tokenized, topics)
276
+ return normalize_and_l1_similarity(counts)
277
+
278
+
279
+ __all__ = [
280
+ "build_tfidf_svd",
281
+ "extract_topic_keywords",
282
+ "count_topic_membership",
283
+ "normalize_and_l1_similarity",
284
+ "abs_diff_topk",
285
+ "classify_direction",
286
+ "build_lsa_matrix",
287
+ ]
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ rs_rooms.py -- Multi-room artifact loader for the Reverse Salient Engine
4
+ ==============================================================================
5
+
6
+ Plan 89-04 Mode A extension: load artifacts from N user rooms simultaneously
7
+ and tag every artifact with its source room_id so the engine can filter
8
+ pairs to cross-room bridges only. Rediscovers the Yissum x CU x Hopkins
9
+ connections the user found manually.
10
+
11
+ Design notes:
12
+ - Room artifacts live on the filesystem (room/section/artifact.md), NOT in
13
+ a SQLite `artifacts` table. The Phase 89 baseline (Plan 89-01, scripts/
14
+ rs-engine.py:discover_artifacts) walks the filesystem because
15
+ lib/core/lazygraph-ops.cjs has no such table. This module follows that
16
+ same pattern per-room and aggregates into one flat corpus.
17
+ - global_id = "{room_id}::{artifact_id}" guarantees uniqueness across
18
+ rooms even when two rooms name their artifacts identically. This is the
19
+ plan's explicit risk-mitigation.
20
+ - Missing/unreadable rooms are skipped with a stderr warning; the engine
21
+ continues with whatever rooms successfully loaded. Mode B/C and Mode A
22
+ single-room require artifacts; cross-room requires >= 2 rooms actually
23
+ contributing artifacts for any cross-room pair to exist.
24
+
25
+ Authoritative source invariants preserved:
26
+ - Empty body filter (>= 50 chars of real content, matching discover_artifacts).
27
+ - Skip dirs (.git, .lazygraph, .mindrian, node_modules, .obsidian).
28
+ - Skip files (STATE.md, ROOM.md, MINTO.md -- metadata, not venture content).
29
+ - Section = first-segment of relative path (same as discover_artifacts).
30
+
31
+ License: BSL-1.1 (see LICENSE at repo root).
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ import os
37
+ import re
38
+ import sys
39
+ from pathlib import Path
40
+ from typing import Dict, List, Sequence
41
+
42
+
43
+ # --- Constants (kept in sync with scripts/rs-engine.py discover_artifacts) ---
44
+
45
+ SKIP_FILES = {"STATE.md", "ROOM.md", "MINTO.md"}
46
+ SKIP_DIRS = {".lazygraph", ".git", ".mindrian", "node_modules", ".obsidian"}
47
+ MIN_BODY_CHARS = 50
48
+
49
+
50
+ # --- Helpers (minimal local copies so rs_rooms does not import from scripts/) -
51
+
52
+ def _extract_title(content: str, filepath: Path) -> str:
53
+ match = re.search(r"^# (.+)$", content, re.MULTILINE)
54
+ if match:
55
+ return match.group(1).strip()
56
+ return filepath.stem.replace("-", " ").title()
57
+
58
+
59
+ def _extract_body(content: str) -> str:
60
+ fm_match = re.match(r"^---\n[\s\S]*?\n---\n?", content)
61
+ if fm_match:
62
+ return content[fm_match.end():]
63
+ return content
64
+
65
+
66
+ # --- Public API -------------------------------------------------------------
67
+
68
+ def load_multi_room_corpus(room_paths: Sequence[str]) -> List[Dict]:
69
+ """Load artifacts from N rooms, tagging each with its source room_id.
70
+
71
+ Args:
72
+ room_paths: list of filesystem paths, each pointing to one room root.
73
+ Room root = the directory that contains section subdirectories
74
+ (e.g. `~/rooms/yissum-deeptech`, not `~/rooms/yissum-deeptech/room`).
75
+
76
+ Returns:
77
+ Flat list of artifact dicts. Each artifact carries:
78
+ - global_id : f"{room_id}::{artifact_id}" (unique across corpus)
79
+ - room_id : basename of the room path (e.g. "yissum-deeptech")
80
+ - artifact_id : per-room id (section/stem), same shape as 89-01
81
+ - section : first path segment under the room root
82
+ - title : H1 header or humanized filename
83
+ - text : body text (post-frontmatter strip)
84
+ - path : absolute filesystem path to the source .md
85
+ - source_room_path: absolute path to the room root (debugging)
86
+
87
+ Behavior:
88
+ - Missing rooms (no directory) print a stderr note and are skipped.
89
+ Not fatal: the engine may still have enough artifacts from other rooms.
90
+ - Rooms with zero usable artifacts (empty sections, files < 50 chars)
91
+ are silently skipped.
92
+ - Artifact order is deterministic: rooms in input order, files sorted
93
+ by filesystem walk. This matters because index-based pair lookups
94
+ in rs-engine.py assume the artifact list is stable across runs.
95
+ """
96
+ corpus: List[Dict] = []
97
+ seen_room_ids: Dict[str, int] = {}
98
+
99
+ for rp in room_paths:
100
+ room_path = Path(rp).resolve()
101
+ if not room_path.exists():
102
+ print(
103
+ f"[rs-rooms] Skip {rp}: path does not exist",
104
+ file=sys.stderr,
105
+ )
106
+ continue
107
+ if not room_path.is_dir():
108
+ print(
109
+ f"[rs-rooms] Skip {rp}: not a directory",
110
+ file=sys.stderr,
111
+ )
112
+ continue
113
+
114
+ room_id = room_path.name
115
+ # Disambiguate if two different paths share a basename. The second
116
+ # occurrence gets a suffix so global_id uniqueness survives.
117
+ seen_count = seen_room_ids.get(room_id, 0)
118
+ if seen_count > 0:
119
+ room_id = f"{room_id}-{seen_count + 1}"
120
+ seen_room_ids[room_path.name] = seen_count + 1
121
+
122
+ room_contribution = _load_single_room(room_path, room_id)
123
+ if not room_contribution:
124
+ print(
125
+ f"[rs-rooms] Skip {rp}: no usable artifacts (>= {MIN_BODY_CHARS} chars)",
126
+ file=sys.stderr,
127
+ )
128
+ continue
129
+ corpus.extend(room_contribution)
130
+
131
+ return corpus
132
+
133
+
134
+ def _load_single_room(room_path: Path, room_id: str) -> List[Dict]:
135
+ """Walk one room and return artifact dicts tagged with room_id."""
136
+ artifacts: List[Dict] = []
137
+ for root, dirs, files in os.walk(room_path):
138
+ dirs[:] = [d for d in dirs if d not in SKIP_DIRS]
139
+ rel_root = Path(root).relative_to(room_path)
140
+ if str(rel_root) == ".":
141
+ # Skip room-root files -- not part of any section.
142
+ continue
143
+ section = str(rel_root).split(os.sep)[0]
144
+ for fname in sorted(files):
145
+ if not fname.endswith(".md"):
146
+ continue
147
+ if fname in SKIP_FILES:
148
+ continue
149
+ fpath = Path(root) / fname
150
+ try:
151
+ content = fpath.read_text(encoding="utf-8")
152
+ except (OSError, UnicodeDecodeError):
153
+ continue
154
+ body = _extract_body(content).strip()
155
+ if len(body) < MIN_BODY_CHARS:
156
+ continue
157
+ artifact_id = str(rel_root / Path(fname).stem).replace(os.sep, "/")
158
+ global_id = f"{room_id}::{artifact_id}"
159
+ artifacts.append({
160
+ "global_id": global_id,
161
+ "room_id": room_id,
162
+ "artifact_id": artifact_id,
163
+ "section": section,
164
+ "title": _extract_title(content, fpath),
165
+ "text": body,
166
+ "path": str(fpath),
167
+ "source_room_path": str(room_path),
168
+ })
169
+ return artifacts
170
+
171
+
172
+ def summarize_corpus(corpus: Sequence[Dict]) -> Dict[str, int]:
173
+ """Return {room_id: artifact_count} for a loaded corpus.
174
+
175
+ Used by the engine to warn when a single room dominates the corpus
176
+ (>= 95% share would skew LSA toward that room's vocabulary and make
177
+ cross-room pair discovery unreliable). The 5% warning threshold is
178
+ documented in the plan's Risks section.
179
+ """
180
+ counts: Dict[str, int] = {}
181
+ for art in corpus:
182
+ rid = art.get("room_id") or "unknown"
183
+ counts[rid] = counts.get(rid, 0) + 1
184
+ return counts
185
+
186
+
187
+ __all__ = [
188
+ "load_multi_room_corpus",
189
+ "summarize_corpus",
190
+ "MIN_BODY_CHARS",
191
+ "SKIP_FILES",
192
+ "SKIP_DIRS",
193
+ ]