@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,479 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ rs_cache.py -- Pinecone rs-external Index + 30-day Lazy TTL Cache (Phase 89)
4
+ ============================================================================
5
+
6
+ Velma-pattern cache for external research corpora: fetch once, embed once,
7
+ query forever (within 30 days). Wraps the Pinecone integrated-embedding API
8
+ so scripts/rs-engine.py --mode external can skip re-fetching and
9
+ re-embedding on repeat runs of the same topic.
10
+
11
+ Architecture (RESEARCH Q3):
12
+ - Dedicated index 'rs-external' (separate from pws-brain Brain IP index).
13
+ - Per-topic namespace 'external:{topic-slug}' holds the embedded corpus.
14
+ - Records carry metadata: source, doi, title, year, fetched_at, abstract.
15
+ - Text field is 'abstract' per Pinecone fieldMap convention (Pinecone MCP
16
+ rule: consistent schema per index; changing field_map requires a new
17
+ index name).
18
+ - Integrated embedding via multilingual-e5-large (1024-dim), matching the
19
+ pws-brain Brain index so embeddings are semantically compatible.
20
+
21
+ Freshness policy:
22
+ - TTL 30 days. On query, if max(fetched_at) across the namespace is within
23
+ TTL, skip fetch+upsert and use the cached namespace directly (warm path).
24
+ - Outside TTL or empty namespace, cold path: fetcher runs, upserts, then
25
+ proceeds. Lazy invalidation on query -- no cron job (CLAUDE.md decision:
26
+ plugin runs entirely in Claude's environment, no background daemons).
27
+
28
+ Public API:
29
+ namespace_slug(topic) -> "external:{topic-slug}"
30
+ ensure_index() -> pinecone Index handle
31
+ get_namespace_freshness(namespace) -> age in days, or None if empty
32
+ upsert_corpus(topic, documents) -> namespace name
33
+ query_namespace(namespace, query, top_k) -> search results
34
+ fetch_all_from_namespace(namespace, limit) -> list of {id, values, metadata}
35
+
36
+ Three-surface coverage (CLAUDE.md tri-polar rule):
37
+ CLI Consumed by scripts/rs-engine.py --mode external.
38
+ Desktop ReverseSalientAgent invokes rs-engine.py as a child process
39
+ (Plan 89-07).
40
+ Cowork Cache is per-namespace, not per-room, so team members running
41
+ the same topic share the warm path transparently.
42
+
43
+ Canon alignment:
44
+ Part 8 Graph Boundary: rs-external is SIGNAL egress only. User-specific
45
+ room content is NEVER written here. What ships to Pinecone is the
46
+ abstract text of public papers (already public via OpenAlex/arXiv) plus
47
+ their public metadata (DOI, title, year). No user artifacts, no user
48
+ decisions, no user meetings.
49
+
50
+ License: BSL-1.1 (see LICENSE at repo root).
51
+ """
52
+
53
+ from __future__ import annotations
54
+
55
+ import os
56
+ import re
57
+ import sys
58
+ import time
59
+ from datetime import datetime, timezone, timedelta
60
+ from typing import Any, Dict, List, Optional, Sequence
61
+
62
+ # --- Guarded imports ---------------------------------------------------------
63
+
64
+ try:
65
+ from pinecone import Pinecone # type: ignore
66
+ except ImportError:
67
+ print(
68
+ "rs_cache: pinecone SDK not installed. "
69
+ "Run: pip install -r requirements-hsi.txt",
70
+ file=sys.stderr,
71
+ )
72
+ raise
73
+
74
+
75
+ # --- Constants ---------------------------------------------------------------
76
+
77
+ INDEX_NAME = "rs-external"
78
+ DEFAULT_EMBED_MODEL = "multilingual-e5-large"
79
+ EMBED_MODEL = os.environ.get("RS_EMBEDDING_MODEL", DEFAULT_EMBED_MODEL)
80
+ # multilingual-e5-large renders to MiniLM-style local fallback elsewhere;
81
+ # Pinecone only supports the integrated model name for create_index_for_model.
82
+ # When RS_EMBEDDING_MODEL is 'minilm' the rs-external path is a no-op and
83
+ # rs-engine.py should route through its local compute_embeddings path.
84
+ PINECONE_EMBED_MODEL = DEFAULT_EMBED_MODEL
85
+
86
+ TTL_DAYS = 30
87
+ MAX_NAMESPACE_VECTORS = 10_000
88
+ # Pinecone inference batch size for integrated embedding.
89
+ UPSERT_BATCH_SIZE = 96
90
+ # Pinecone list() page size cap.
91
+ LIST_BATCH_SIZE = 96
92
+ # Ready poll timeout for index creation.
93
+ INDEX_READY_TIMEOUT_SEC = 300
94
+
95
+
96
+ # --- Slug ------------------------------------------------------------------
97
+
98
+
99
+ def namespace_slug(topic: str) -> str:
100
+ """Return the canonical namespace name for a topic.
101
+
102
+ The slug lowercases, replaces non-alphanumeric runs with a single hyphen,
103
+ strips leading/trailing hyphens, and prefixes with "external:" so
104
+ namespaces written by this module cannot collide with any Mode A
105
+ room-local namespace pattern.
106
+
107
+ Plan verify block 2 asserts:
108
+ namespace_slug('Quantum Biology & Sensing!') == 'external:quantum-biology-sensing'
109
+ """
110
+ s = (topic or "").strip().lower()
111
+ s = re.sub(r"[^a-z0-9]+", "-", s).strip("-")
112
+ return f"external:{s}"
113
+
114
+
115
+ # --- Pinecone client + index lifecycle ---------------------------------------
116
+
117
+
118
+ def _pc() -> Pinecone:
119
+ api_key = os.environ.get("PINECONE_API_KEY")
120
+ if not api_key:
121
+ raise RuntimeError(
122
+ "PINECONE_API_KEY not set. rs-external cache requires a Pinecone "
123
+ "API key. Set PINECONE_API_KEY in your environment or run "
124
+ "rs-engine.py with RS_EMBEDDING_MODEL=minilm to skip the Pinecone "
125
+ "path (Mode A / local fallback only)."
126
+ )
127
+ return Pinecone(api_key=api_key)
128
+
129
+
130
+ def ensure_index() -> Any:
131
+ """Create the rs-external index if missing, then return an Index handle.
132
+
133
+ fieldMap convention (Pinecone MCP rule): the text field is "abstract".
134
+ Once the index exists with a given field_map, changing it would require
135
+ a new index name -- this function NEVER recreates. Plan 89-03 Risk 2.
136
+ """
137
+ pc = _pc()
138
+ try:
139
+ exists = pc.has_index(INDEX_NAME)
140
+ except Exception:
141
+ # Fallback for SDK versions that do not expose has_index directly.
142
+ names = [getattr(i, "name", None) for i in pc.list_indexes()]
143
+ exists = INDEX_NAME in names
144
+
145
+ if not exists:
146
+ pc.create_index_for_model(
147
+ name=INDEX_NAME,
148
+ cloud="aws",
149
+ region="us-east-1",
150
+ embed={
151
+ "model": PINECONE_EMBED_MODEL,
152
+ "field_map": {"text": "abstract"},
153
+ },
154
+ )
155
+ # Poll until ready. The describe_index response exposes `.status` as
156
+ # an object with `.ready` attribute (see scripts/consolidate-pinecone.py
157
+ # line 147 for the pattern in use). Plan 89-03 wrote this as a dict
158
+ # lookup which would raise AttributeError on the real SDK -- Rule 1.
159
+ deadline = time.time() + INDEX_READY_TIMEOUT_SEC
160
+ while True:
161
+ desc = pc.describe_index(INDEX_NAME)
162
+ ready = False
163
+ try:
164
+ ready = bool(desc.status.ready) # type: ignore[attr-defined]
165
+ except AttributeError:
166
+ status = getattr(desc, "status", {}) or {}
167
+ if isinstance(status, dict):
168
+ ready = bool(status.get("ready"))
169
+ if ready:
170
+ break
171
+ if time.time() > deadline:
172
+ raise RuntimeError(
173
+ f"rs_cache: index {INDEX_NAME!r} did not become ready "
174
+ f"within {INDEX_READY_TIMEOUT_SEC}s"
175
+ )
176
+ time.sleep(2)
177
+
178
+ return pc.Index(INDEX_NAME)
179
+
180
+
181
+ # --- Freshness -------------------------------------------------------------
182
+
183
+
184
+ def _namespace_vector_count(idx: Any, namespace: str) -> int:
185
+ """Return the vector_count for the namespace, or 0 if absent.
186
+
187
+ describe_index_stats returns a DescribeIndexStatsResponse; both attribute
188
+ access (.namespaces) and .to_dict() shapes are supported across SDK
189
+ versions. This function tolerates both.
190
+ """
191
+ try:
192
+ stats = idx.describe_index_stats()
193
+ except Exception:
194
+ return 0
195
+
196
+ ns_map: Dict[str, Any] = {}
197
+ # Attribute form.
198
+ namespaces_attr = getattr(stats, "namespaces", None)
199
+ if isinstance(namespaces_attr, dict):
200
+ ns_map = namespaces_attr
201
+ else:
202
+ try:
203
+ d = stats.to_dict()
204
+ ns_map = d.get("namespaces", {}) or {}
205
+ except Exception:
206
+ ns_map = {}
207
+
208
+ entry = ns_map.get(namespace)
209
+ if not entry:
210
+ return 0
211
+ if isinstance(entry, dict):
212
+ return int(entry.get("vector_count", 0) or 0)
213
+ return int(getattr(entry, "vector_count", 0) or 0)
214
+
215
+
216
+ def _parse_fetched_at(raw: Any) -> Optional[datetime]:
217
+ """Parse an ISO-8601 fetched_at string. Tolerates trailing 'Z'."""
218
+ if not raw:
219
+ return None
220
+ if isinstance(raw, datetime):
221
+ return raw
222
+ if not isinstance(raw, str):
223
+ return None
224
+ s = raw.strip()
225
+ if not s:
226
+ return None
227
+ # Python's fromisoformat accepts offsets but not trailing 'Z' until 3.11.
228
+ if s.endswith("Z"):
229
+ s = s[:-1] + "+00:00"
230
+ try:
231
+ dt = datetime.fromisoformat(s)
232
+ except ValueError:
233
+ return None
234
+ if dt.tzinfo is None:
235
+ dt = dt.replace(tzinfo=timezone.utc)
236
+ return dt
237
+
238
+
239
+ def get_namespace_freshness(namespace: str) -> Optional[float]:
240
+ """Return age-in-days of the namespace's max fetched_at.
241
+
242
+ Returns None if:
243
+ - The namespace does not exist or is empty.
244
+ - No fetched_at metadata is present on any sampled record.
245
+ - Any error occurs reaching Pinecone (treated as "unknown -> refetch").
246
+
247
+ Sampling strategy: the integrated-embedding index does not accept raw
248
+ vectors to .query() (that API is for inference-disabled indexes). We
249
+ sample one record via list() + fetch() and read its fetched_at
250
+ metadata. All records in a namespace share a common upsert timestamp
251
+ (upsert_corpus stamps them with a single `now`), so one sample is
252
+ sufficient for warm-vs-cold decisions under the 30-day TTL.
253
+
254
+ Plan 89-03 proposed a raw-vector query for this purpose -- that would
255
+ raise on an integrated-embedding index. Rule 1 bug fix: sample via
256
+ list+fetch instead. The behavioral contract (age or None) is preserved.
257
+ """
258
+ try:
259
+ idx = ensure_index()
260
+ except Exception as e:
261
+ print(f"rs_cache: freshness check skipped ({e})", file=sys.stderr)
262
+ return None
263
+
264
+ if _namespace_vector_count(idx, namespace) == 0:
265
+ return None
266
+
267
+ sample_id: Optional[str] = None
268
+ try:
269
+ for ids_batch in idx.list(namespace=namespace, limit=1):
270
+ if ids_batch:
271
+ sample_id = ids_batch[0]
272
+ break
273
+ except Exception:
274
+ return None
275
+
276
+ if not sample_id:
277
+ return None
278
+
279
+ try:
280
+ fetched = idx.fetch(ids=[sample_id], namespace=namespace)
281
+ except Exception:
282
+ return None
283
+
284
+ vectors_map: Dict[str, Any] = {}
285
+ vectors_attr = getattr(fetched, "vectors", None)
286
+ if isinstance(vectors_attr, dict):
287
+ vectors_map = vectors_attr
288
+ else:
289
+ try:
290
+ vectors_map = fetched.to_dict().get("vectors", {}) or {}
291
+ except Exception:
292
+ vectors_map = {}
293
+
294
+ rec = vectors_map.get(sample_id)
295
+ if rec is None:
296
+ return None
297
+
298
+ metadata: Dict[str, Any] = {}
299
+ if isinstance(rec, dict):
300
+ metadata = rec.get("metadata", {}) or {}
301
+ else:
302
+ metadata = getattr(rec, "metadata", {}) or {}
303
+
304
+ fetched_at = _parse_fetched_at(metadata.get("fetched_at"))
305
+ if fetched_at is None:
306
+ return None
307
+
308
+ now = datetime.now(timezone.utc)
309
+ delta = now - fetched_at
310
+ return max(0.0, delta.total_seconds() / 86400.0)
311
+
312
+
313
+ # --- Upsert ------------------------------------------------------------------
314
+
315
+
316
+ def _build_records(documents: Sequence[Dict[str, Any]], stamp: str) -> List[Dict[str, Any]]:
317
+ records: List[Dict[str, Any]] = []
318
+ for d in documents:
319
+ external_id = (d.get("external_id") or "").strip()
320
+ if not external_id:
321
+ # Skip records without a stable id -- they cannot be retrieved
322
+ # deterministically and would pollute the namespace.
323
+ continue
324
+ abstract = (d.get("abstract") or "").strip()
325
+ if not abstract:
326
+ continue
327
+ rec: Dict[str, Any] = {
328
+ "_id": external_id,
329
+ "abstract": abstract,
330
+ "source": (d.get("source") or "unknown")[:64],
331
+ "doi": (d.get("doi") or "")[:256],
332
+ "title": (d.get("title") or "")[:500],
333
+ "year": int(d.get("year") or 0),
334
+ "fetched_at": stamp,
335
+ }
336
+ records.append(rec)
337
+ return records
338
+
339
+
340
+ def upsert_corpus(topic: str, documents: Sequence[Dict[str, Any]]) -> str:
341
+ """Upsert fetched docs into the topic's namespace. Returns the namespace.
342
+
343
+ Guardrail: raises ValueError when the corpus exceeds MAX_NAMESPACE_VECTORS
344
+ so a single topic cannot silently balloon into a 100k-vector namespace.
345
+ Plan 89-03 Risk 3 mitigation -- the caller should shard by year or
346
+ scope the topic tighter before re-running.
347
+
348
+ fetched_at is stamped once per call. All records in the batch share the
349
+ same timestamp, which lets get_namespace_freshness sample any one record
350
+ and infer the whole namespace's TTL position.
351
+ """
352
+ if len(documents) > MAX_NAMESPACE_VECTORS:
353
+ raise ValueError(
354
+ f"rs_cache: topic {topic!r} has {len(documents)} docs > "
355
+ f"{MAX_NAMESPACE_VECTORS}. Shard by year or tighten the topic."
356
+ )
357
+
358
+ idx = ensure_index()
359
+ ns = namespace_slug(topic)
360
+ stamp = datetime.now(timezone.utc).isoformat()
361
+ records = _build_records(documents, stamp)
362
+
363
+ if not records:
364
+ # Nothing usable; keep behavior predictable and do not touch the
365
+ # namespace. Upstream will treat this as a miss next run.
366
+ return ns
367
+
368
+ for i in range(0, len(records), UPSERT_BATCH_SIZE):
369
+ batch = records[i:i + UPSERT_BATCH_SIZE]
370
+ idx.upsert_records(namespace=ns, records=batch)
371
+
372
+ return ns
373
+
374
+
375
+ # --- Query ------------------------------------------------------------------
376
+
377
+
378
+ def query_namespace(
379
+ namespace: str,
380
+ query_text: str,
381
+ top_k: int = 100,
382
+ ) -> Any:
383
+ """Run a text query against the namespace. Integrated embedding at search
384
+ time -- Pinecone embeds the query with the same multilingual-e5-large
385
+ model used at upsert.
386
+
387
+ Returns the raw SearchRecordsResponse. Callers walk `.result.hits` (or
388
+ equivalent) to extract ids and scores.
389
+ """
390
+ idx = ensure_index()
391
+ return idx.search(
392
+ namespace=namespace,
393
+ query={"inputs": {"text": query_text}, "top_k": max(1, int(top_k))},
394
+ )
395
+
396
+
397
+ def fetch_all_from_namespace(
398
+ namespace: str,
399
+ limit: int = MAX_NAMESPACE_VECTORS,
400
+ ) -> List[Dict[str, Any]]:
401
+ """Retrieve records (id + values + metadata) from a namespace.
402
+
403
+ Used by scripts/rs-engine.py --mode external warm path to build the
404
+ semantic similarity matrix from cached vectors instead of re-embedding
405
+ on every run.
406
+
407
+ Pinecone `fetch` returns records with `.values` (1024-dim embedding) and
408
+ `.metadata` (abstract, title, doi, year, source, fetched_at). On an
409
+ integrated-embedding index, values are populated at upsert time.
410
+
411
+ `limit` caps results; the function returns up to `limit` records in
412
+ list() order (Pinecone does not guarantee ordering). Use
413
+ MAX_NAMESPACE_VECTORS when the caller wants everything.
414
+ """
415
+ if limit <= 0:
416
+ return []
417
+
418
+ idx = ensure_index()
419
+ out: List[Dict[str, Any]] = []
420
+
421
+ for ids_batch in idx.list(namespace=namespace, limit=LIST_BATCH_SIZE):
422
+ if not ids_batch:
423
+ continue
424
+ try:
425
+ fetched = idx.fetch(ids=ids_batch, namespace=namespace)
426
+ except Exception as e:
427
+ print(f"rs_cache: fetch batch failed ({e})", file=sys.stderr)
428
+ continue
429
+
430
+ vectors_map: Dict[str, Any] = {}
431
+ vectors_attr = getattr(fetched, "vectors", None)
432
+ if isinstance(vectors_attr, dict):
433
+ vectors_map = vectors_attr
434
+ else:
435
+ try:
436
+ vectors_map = fetched.to_dict().get("vectors", {}) or {}
437
+ except Exception:
438
+ vectors_map = {}
439
+
440
+ for rid, rec in vectors_map.items():
441
+ if isinstance(rec, dict):
442
+ values = rec.get("values") or []
443
+ metadata = rec.get("metadata", {}) or {}
444
+ else:
445
+ values = list(getattr(rec, "values", []) or [])
446
+ metadata = getattr(rec, "metadata", {}) or {}
447
+ out.append({
448
+ "id": rid,
449
+ "values": list(values) if values is not None else [],
450
+ "metadata": dict(metadata) if isinstance(metadata, dict) else {},
451
+ })
452
+ if len(out) >= limit:
453
+ return out
454
+
455
+ return out
456
+
457
+
458
+ # --- TTL helpers (public for callers that want policy control) ------------
459
+
460
+
461
+ def is_fresh(age_days: Optional[float], ttl_days: int = TTL_DAYS) -> bool:
462
+ """True when the namespace age is known and under the TTL."""
463
+ if age_days is None:
464
+ return False
465
+ return age_days <= ttl_days
466
+
467
+
468
+ __all__ = [
469
+ "INDEX_NAME",
470
+ "TTL_DAYS",
471
+ "MAX_NAMESPACE_VECTORS",
472
+ "namespace_slug",
473
+ "ensure_index",
474
+ "get_namespace_freshness",
475
+ "upsert_corpus",
476
+ "query_namespace",
477
+ "fetch_all_from_namespace",
478
+ "is_fresh",
479
+ ]