@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,674 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /*
5
+ * Copyright (c) 2026 Mindrian. BSL 1.1.
6
+ * Phase 89.2 Plan 03 -- patents fetcher fixture suite.
7
+ *
8
+ * 17 scenarios total: 11 fetcher tests + 6 validator tests.
9
+ *
10
+ * Test 1 happy path 2 sources (Google Patents + USPTO mock fetch)
11
+ * Test 2 dedup determinism (same input twice -> identical output)
12
+ * Test 3 Google Patents 429 rate-limit graceful (uspto continues)
13
+ * Test 4 USPTO timeout graceful (google_patents continues)
14
+ * Test 5 Google Patents malformed JSON-LD graceful (uspto continues)
15
+ * Test 6 USPTO malformed JSON graceful (api_error logged)
16
+ * Test 7 per-source budget exhausted (google_patents skipped)
17
+ * Test 8 CANON PART 8 adversarial: leaked-artifact-body in query throws
18
+ * Test 9 CANON PART 8 adversarial: leaked-venture-financials throws
19
+ * Test 10 CANON PART 8 adversarial: leaked-meeting-fragment throws
20
+ * Test 11 CANON PART 8 adversarial: leaked-SSN-style figure throws
21
+ *
22
+ * V1 validator Check A: telemetry file absent -> {severity: null}
23
+ * V2 validator Check B: per-source budget exceeded -> warning
24
+ * V3 validator Check C: status enum violation -> warning
25
+ * V4 validator Check D: query_text literal present -> CRITICAL canon_boundary
26
+ * V5 validator Check E: query_text_hash format violation -> warning
27
+ * V6 validator Check F: fetched_at malformed ISO-8601 -> warning
28
+ *
29
+ * Suite-end audits:
30
+ * A1 every captured outbound URL + every captured telemetry record
31
+ * JSON.stringify scanned against FORBIDDEN_PATTERNS; ZERO hits
32
+ * A2 parity gate: rs-egress-prompts FORBIDDEN_PATTERNS byte-for-byte
33
+ * === cross-room-aggregator FORBIDDEN_PATTERNS at every regex.source
34
+ *
35
+ * Pure CJS, zero npm deps, node built-ins only (assert, fs, os, path, crypto).
36
+ */
37
+
38
+ const assert = require('node:assert/strict');
39
+ const fs = require('node:fs');
40
+ const os = require('node:os');
41
+ const path = require('node:path');
42
+ const crypto = require('node:crypto');
43
+
44
+ // ---------- Suite bookkeeping ----------
45
+
46
+ let passed = 0;
47
+ let failed = 0;
48
+ const failures = [];
49
+
50
+ const SCENARIO_RESULTS = [];
51
+
52
+ const CAPTURED_URLS = [];
53
+ const CAPTURED_URLS_ALL = [];
54
+
55
+ let testHomeDir = null;
56
+ let originalHome = null;
57
+
58
+ const ALL_TMP_ROOTS = [];
59
+
60
+ process.on('exit', function () {
61
+ if (originalHome !== null) {
62
+ process.env.HOME = originalHome;
63
+ }
64
+ for (const d of ALL_TMP_ROOTS) {
65
+ try { fs.rmSync(d, { recursive: true, force: true }); } catch (_e) { /* best effort */ }
66
+ }
67
+ });
68
+
69
+ function setupScopedHome() {
70
+ if (originalHome === null) {
71
+ originalHome = process.env.HOME;
72
+ }
73
+ testHomeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rsfetpat-home-'));
74
+ ALL_TMP_ROOTS.push(testHomeDir);
75
+ process.env.HOME = testHomeDir;
76
+ resetRequireCache();
77
+ }
78
+
79
+ function resetRequireCache() {
80
+ const targets = [
81
+ '../core/rs-egress-violations.cjs',
82
+ '../core/rs-egress-prompts.cjs',
83
+ '../core/rs-egress-telemetry.cjs',
84
+ '../core/rs-fetcher-patents.cjs',
85
+ '../core/cross-room-aggregator.cjs',
86
+ './validators/external-patents-invariants.cjs',
87
+ ];
88
+ for (const t of targets) {
89
+ try {
90
+ const p = require.resolve(t);
91
+ delete require.cache[p];
92
+ } catch (_e) { /* best effort */ }
93
+ }
94
+ }
95
+
96
+ // ---------- Mock fetch ----------
97
+
98
+ let originalFetch = null;
99
+ const mockResponses = new Map();
100
+
101
+ function installMockFetch(responses) {
102
+ if (originalFetch === null) {
103
+ originalFetch = global.fetch;
104
+ }
105
+ mockResponses.clear();
106
+ CAPTURED_URLS.length = 0;
107
+ for (const [k, v] of Object.entries(responses)) {
108
+ mockResponses.set(k, v);
109
+ }
110
+ global.fetch = async function (url, opts) {
111
+ CAPTURED_URLS.push(String(url));
112
+ CAPTURED_URLS_ALL.push(String(url));
113
+ for (const [prefix, handler] of mockResponses.entries()) {
114
+ if (String(url).indexOf(prefix) >= 0) {
115
+ return handler(url, opts);
116
+ }
117
+ }
118
+ return { ok: false, status: 404, headers: new Map(), async json() { return {}; }, async text() { return ''; } };
119
+ };
120
+ }
121
+
122
+ function restoreFetch() {
123
+ if (originalFetch !== null) {
124
+ global.fetch = originalFetch;
125
+ }
126
+ mockResponses.clear();
127
+ }
128
+
129
+ // ---------- Scenario runner ----------
130
+
131
+ async function runScenario(name, fn) {
132
+ const start = Date.now();
133
+ try {
134
+ await fn();
135
+ passed += 1;
136
+ process.stdout.write(' ok ' + name + ' (' + (Date.now() - start) + 'ms)\n');
137
+ } catch (err) {
138
+ failed += 1;
139
+ failures.push({ name: name, err: err });
140
+ process.stderr.write(' FAIL ' + name + '\n');
141
+ process.stderr.write(' ' + (err && err.stack ? err.stack : err) + '\n');
142
+ } finally {
143
+ restoreFetch();
144
+ resetRequireCache();
145
+ }
146
+ }
147
+
148
+ // ---------- Helpers ----------
149
+
150
+ function makeGooglePatentsHtml(query, n) {
151
+ // Simulated patents.google.com HTML payload with embedded JSON-LD <script>
152
+ // tags carrying patent metadata. The real surface emits one JSON-LD per
153
+ // result; mock mirrors that shape so the parser exercise is end-to-end.
154
+ let scripts = '';
155
+ for (let i = 0; i < n; i++) {
156
+ const tag = crypto.createHash('sha256').update(query).digest('hex').slice(0, 6);
157
+ const obj = {
158
+ '@type': 'Patent',
159
+ patentNumber: 'US' + (10000000 + i) + tag.slice(0, 0).toUpperCase() + 'B2',
160
+ name: 'Google Patents result ' + i + ' for ' + query,
161
+ description: 'Abstract for Google Patents result ' + i + '.',
162
+ inventor: [{ '@type': 'Person', name: 'Inventor A' + i }, { '@type': 'Person', name: 'Inventor B' + i }],
163
+ assignee: { '@type': 'Organization', name: 'Org-' + i + '-Inc' },
164
+ filingDate: '2023-04-' + ((i % 28) + 1).toString().padStart(2, '0'),
165
+ };
166
+ // Re-shape patentNumber so it matches /^[A-Z]{2}\d+[A-Z]\d?$/ assertion.
167
+ obj.patentNumber = 'US' + (10000000 + i) + 'B2';
168
+ scripts += '<script type="application/ld+json">' + JSON.stringify(obj) + '</script>';
169
+ }
170
+ return '<!doctype html><html><head>' + scripts + '</head><body>results</body></html>';
171
+ }
172
+
173
+ function makeUsptoResponse(query, n) {
174
+ const records = [];
175
+ for (let i = 0; i < n; i++) {
176
+ const tag = crypto.createHash('sha256').update(query).digest('hex').slice(0, 6);
177
+ records.push({
178
+ patentNumber: 'US' + (20000000 + i) + 'B1',
179
+ patentTitle: 'USPTO patent ' + i + ' for ' + query,
180
+ patentAbstract: 'USPTO abstract ' + i,
181
+ inventorName: ['Last' + i + ', First' + i, 'Last' + i + 'B, First' + i + 'B'],
182
+ assigneeEntityName: 'USPTO Assignee ' + i + ' Inc',
183
+ filingDate: '2023-05-' + ((i % 28) + 1).toString().padStart(2, '0'),
184
+ _tag: tag,
185
+ });
186
+ }
187
+ return { results: records, total: n };
188
+ }
189
+
190
+ function buildAllSourcesMockOk(query, perSource) {
191
+ return {
192
+ 'patents.google.com': async function (url) {
193
+ return {
194
+ ok: true,
195
+ status: 200,
196
+ headers: new Map([['x-ratelimit-remaining', '49']]),
197
+ async text() { return makeGooglePatentsHtml(query, perSource); },
198
+ async json() { throw new Error('google patents returns HTML'); },
199
+ };
200
+ },
201
+ 'api.uspto.gov': async function (url) {
202
+ return {
203
+ ok: true,
204
+ status: 200,
205
+ headers: new Map(),
206
+ async json() { return makeUsptoResponse(query, perSource); },
207
+ async text() { return JSON.stringify(makeUsptoResponse(query, perSource)); },
208
+ };
209
+ },
210
+ };
211
+ }
212
+
213
+ // ---------- A1/A2 sweep helpers ----------
214
+
215
+ function getForbiddenPatternsFromAggregator() {
216
+ const aggregator = require('../core/cross-room-aggregator.cjs');
217
+ return aggregator.FORBIDDEN_PATTERNS;
218
+ }
219
+
220
+ function scanAgainstForbidden(stringified) {
221
+ const patterns = getForbiddenPatternsFromAggregator();
222
+ for (const re of patterns) {
223
+ if (re.test(stringified)) {
224
+ return { hit: true, pattern: re.source };
225
+ }
226
+ }
227
+ return { hit: false };
228
+ }
229
+
230
+ // ---------- Begin scenarios ----------
231
+
232
+ console.log('=== 89.2-03 fetcher-patents suite: starting ===');
233
+
234
+ (async function main() {
235
+
236
+ // ---------- Test 1: happy path 2 sources ----------
237
+ await runScenario('Test 1: happy path 2 sources (Google Patents + USPTO)', async function () {
238
+ setupScopedHome();
239
+ const queries = ['quantum entanglement detection'];
240
+ installMockFetch(buildAllSourcesMockOk(queries[0], 5));
241
+ const fetcher = require('../core/rs-fetcher-patents.cjs');
242
+ const out = await fetcher.fetchPatents(queries, {});
243
+ assert.ok(out && Array.isArray(out.patents), 'patents is array');
244
+ assert.ok(out.patents.length >= 8,
245
+ 'expected >= 8 patents across 2 sources after dedup; got ' + out.patents.length);
246
+ const sample = out.patents[0];
247
+ for (const f of ['patent_id', 'title', 'abstract', 'inventors', 'assignee', 'filing_date', 'source', 'fetched_at']) {
248
+ assert.ok(Object.prototype.hasOwnProperty.call(sample, f),
249
+ 'patent missing field: ' + f);
250
+ }
251
+ assert.ok(Array.isArray(sample.inventors), 'inventors is array');
252
+ assert.ok(/^[A-Z]{2}\d+[A-Z]\d?$/.test(sample.patent_id),
253
+ 'patent_id matches /^[A-Z]{2}\\d+[A-Z]\\d?$/; got ' + sample.patent_id);
254
+ const sources = new Set(out.patents.map(p => p.source));
255
+ for (const s of ['google_patents', 'uspto']) {
256
+ assert.ok(sources.has(s), 'source coverage missing: ' + s);
257
+ }
258
+ SCENARIO_RESULTS.push({ test: 'T1', surface: 'patents', payload: out });
259
+ });
260
+
261
+ // ---------- Test 2: dedup determinism ----------
262
+ await runScenario('Test 2: dedup determinism (same input twice == identical output)', async function () {
263
+ setupScopedHome();
264
+ const queries = ['CRISPR gene editing'];
265
+ installMockFetch(buildAllSourcesMockOk(queries[0], 5));
266
+ const fetcher = require('../core/rs-fetcher-patents.cjs');
267
+ const r1 = await fetcher.fetchPatents(queries, {});
268
+ CAPTURED_URLS.length = 0;
269
+ setupScopedHome();
270
+ installMockFetch(buildAllSourcesMockOk(queries[0], 5));
271
+ const fetcher2 = require('../core/rs-fetcher-patents.cjs');
272
+ const r2 = await fetcher2.fetchPatents(queries, {});
273
+ const stripT = function (patents) {
274
+ return patents.map(p => {
275
+ const c = Object.assign({}, p);
276
+ delete c.fetched_at;
277
+ return c;
278
+ });
279
+ };
280
+ assert.equal(JSON.stringify(stripT(r1.patents)), JSON.stringify(stripT(r2.patents)),
281
+ 'dedup output must be deterministic across runs');
282
+ SCENARIO_RESULTS.push({ test: 'T2', surface: 'patents', payload: stripT(r1.patents) });
283
+ });
284
+
285
+ // ---------- Test 3: Google Patents 429 rate-limit graceful ----------
286
+ await runScenario('Test 3: Google Patents 429 rate-limit graceful (uspto continues)', async function () {
287
+ setupScopedHome();
288
+ const queries = ['graphene heat conductivity'];
289
+ const mocks = buildAllSourcesMockOk(queries[0], 4);
290
+ mocks['patents.google.com'] = async function () {
291
+ return {
292
+ ok: false,
293
+ status: 429,
294
+ headers: new Map([['retry-after', '60']]),
295
+ async text() { return 'rate limited'; },
296
+ async json() { return { error: 'rate limited' }; },
297
+ };
298
+ };
299
+ installMockFetch(mocks);
300
+ const fetcher = require('../core/rs-fetcher-patents.cjs');
301
+ const out = await fetcher.fetchPatents(queries, {});
302
+ const sources = new Set(out.patents.map(p => p.source));
303
+ assert.ok(!sources.has('google_patents'), 'google_patents must be empty after 429');
304
+ assert.ok(sources.has('uspto'), 'uspto populates');
305
+
306
+ const telemetry = require('../core/rs-egress-telemetry.cjs');
307
+ const payload = JSON.parse(fs.readFileSync(telemetry.TELEMETRY_FILE, 'utf8'));
308
+ const rateLimited = payload.entries.filter(e => e.status === 'rate_limited');
309
+ assert.ok(rateLimited.length >= 1, 'rate_limited telemetry recorded');
310
+ assert.equal(rateLimited[0].http_status, 429, 'http_status 429 recorded');
311
+ SCENARIO_RESULTS.push({ test: 'T3', surface: 'patents', payload: out });
312
+ });
313
+
314
+ // ---------- Test 4: USPTO timeout graceful ----------
315
+ await runScenario('Test 4: USPTO timeout graceful (google_patents continues)', async function () {
316
+ setupScopedHome();
317
+ const queries = ['CAR-T cell therapy'];
318
+ const mocks = buildAllSourcesMockOk(queries[0], 3);
319
+ mocks['api.uspto.gov'] = async function () {
320
+ const err = new Error('aborted');
321
+ err.name = 'AbortError';
322
+ throw err;
323
+ };
324
+ installMockFetch(mocks);
325
+ const fetcher = require('../core/rs-fetcher-patents.cjs');
326
+ const out = await fetcher.fetchPatents(queries, {});
327
+ const sources = new Set(out.patents.map(p => p.source));
328
+ assert.ok(!sources.has('uspto'), 'uspto empty after timeout');
329
+ assert.ok(sources.has('google_patents'), 'google_patents populates');
330
+
331
+ const telemetry = require('../core/rs-egress-telemetry.cjs');
332
+ const payload = JSON.parse(fs.readFileSync(telemetry.TELEMETRY_FILE, 'utf8'));
333
+ const timed = payload.entries.filter(e => e.status === 'timeout');
334
+ assert.ok(timed.length >= 1, 'timeout telemetry recorded');
335
+ SCENARIO_RESULTS.push({ test: 'T4', surface: 'patents', payload: out });
336
+ });
337
+
338
+ // ---------- Test 5: Google Patents malformed JSON-LD graceful ----------
339
+ await runScenario('Test 5: Google Patents malformed (no JSON-LD) graceful', async function () {
340
+ setupScopedHome();
341
+ const queries = ['mRNA vaccine technology'];
342
+ const mocks = buildAllSourcesMockOk(queries[0], 3);
343
+ mocks['patents.google.com'] = async function () {
344
+ return {
345
+ ok: true,
346
+ status: 200,
347
+ headers: new Map(),
348
+ async text() { return '<!doctype html><html><body>no json-ld here</body></html>'; },
349
+ async json() { throw new Error('not json'); },
350
+ };
351
+ };
352
+ installMockFetch(mocks);
353
+ const fetcher = require('../core/rs-fetcher-patents.cjs');
354
+ const out = await fetcher.fetchPatents(queries, {});
355
+ const sources = new Set(out.patents.map(p => p.source));
356
+ assert.ok(!sources.has('google_patents'), 'google_patents empty after no-JSON-LD');
357
+ assert.ok(sources.has('uspto'), 'uspto still populates');
358
+ // Note: no JSON-LD found returns [] (not an api_error) since the parser
359
+ // gracefully handles absent script tags. Either ok-with-zero-results or
360
+ // api_error is acceptable; assert behavior (skip), not telemetry name.
361
+ SCENARIO_RESULTS.push({ test: 'T5', surface: 'patents', payload: out });
362
+ });
363
+
364
+ // ---------- Test 6: USPTO malformed JSON graceful ----------
365
+ await runScenario('Test 6: USPTO malformed JSON graceful (api_error logged)', async function () {
366
+ setupScopedHome();
367
+ const queries = ['fusion plasma confinement'];
368
+ const mocks = buildAllSourcesMockOk(queries[0], 3);
369
+ mocks['api.uspto.gov'] = async function () {
370
+ return {
371
+ ok: true,
372
+ status: 200,
373
+ headers: new Map(),
374
+ async json() { throw new SyntaxError('invalid JSON'); },
375
+ async text() { return '<<<not-json>>>'; },
376
+ };
377
+ };
378
+ installMockFetch(mocks);
379
+ const fetcher = require('../core/rs-fetcher-patents.cjs');
380
+ const out = await fetcher.fetchPatents(queries, {});
381
+ const sources = new Set(out.patents.map(p => p.source));
382
+ assert.ok(!sources.has('uspto'), 'uspto empty after parse failure');
383
+ assert.ok(sources.has('google_patents'), 'google_patents still populates');
384
+
385
+ const telemetry = require('../core/rs-egress-telemetry.cjs');
386
+ const payload = JSON.parse(fs.readFileSync(telemetry.TELEMETRY_FILE, 'utf8'));
387
+ const errd = payload.entries.filter(e => e.status === 'api_error');
388
+ assert.ok(errd.length >= 1, 'api_error telemetry recorded for uspto');
389
+ SCENARIO_RESULTS.push({ test: 'T6', surface: 'patents', payload: out });
390
+ });
391
+
392
+ // ---------- Test 7: per-source budget exhausted ----------
393
+ await runScenario('Test 7: per-source budget exhausted (google_patents skipped)', async function () {
394
+ setupScopedHome();
395
+ const telemetry = require('../core/rs-egress-telemetry.cjs');
396
+ // Seed 50 google_patents entries within last 24h (default budget = 50).
397
+ const now = Date.now();
398
+ const entries = [];
399
+ for (let i = 0; i < 50; i++) {
400
+ entries.push({
401
+ source: 'google_patents',
402
+ query_text_hash: 'aaaaaaaaaaaaaaaa',
403
+ status: 'ok',
404
+ fetched_at: new Date(now - (i * 1000)).toISOString(),
405
+ });
406
+ }
407
+ fs.mkdirSync(path.dirname(telemetry.TELEMETRY_FILE), { recursive: true });
408
+ fs.writeFileSync(telemetry.TELEMETRY_FILE,
409
+ JSON.stringify({ schema_version: '1.0', entries: entries }, null, 2));
410
+
411
+ const queries = ['plasma confinement tokamak'];
412
+ installMockFetch(buildAllSourcesMockOk(queries[0], 3));
413
+ const fetcher = require('../core/rs-fetcher-patents.cjs');
414
+ const out = await fetcher.fetchPatents(queries, {});
415
+ const sources = new Set(out.patents.map(p => p.source));
416
+ assert.ok(!sources.has('google_patents'), 'google_patents skipped after budget exhausted');
417
+ assert.ok(sources.has('uspto'), 'uspto runs normally');
418
+
419
+ const ourUrls = CAPTURED_URLS.filter(u => u.indexOf('patents.google.com') >= 0);
420
+ assert.equal(ourUrls.length, 0, 'no google_patents URL fetched after budget exhaust');
421
+ SCENARIO_RESULTS.push({ test: 'T7', surface: 'patents', payload: out });
422
+ });
423
+
424
+ // ---------- Test 8: CANON PART 8 adversarial: leaked-artifact-body ----------
425
+ await runScenario('Test 8: CANON PART 8 adversarial leaked-artifact-body throws', async function () {
426
+ setupScopedHome();
427
+ // Pattern 3 (meeting with) leak.
428
+ const queries = ['CRISPR <<artifact: meeting with Genentech Q4 financials>>'];
429
+ installMockFetch(buildAllSourcesMockOk('clean', 1));
430
+ const fetcher = require('../core/rs-fetcher-patents.cjs');
431
+ let threw = null;
432
+ try {
433
+ await fetcher.fetchPatents(queries, {});
434
+ } catch (e) {
435
+ threw = e;
436
+ }
437
+ assert.ok(threw, 'expected throw on adversarial query');
438
+ assert.equal(threw.name, 'ExternalEgressViolation', 'class name');
439
+ assert.equal(threw.meta.surface, 'patents', 'meta.surface');
440
+ assert.ok(typeof threw.meta.matched_pattern === 'string'
441
+ && threw.meta.matched_pattern.length > 0, 'meta.matched_pattern present');
442
+
443
+ assert.equal(CAPTURED_URLS.length, 0,
444
+ 'NO fetch() calls allowed before throw; got ' + CAPTURED_URLS.length);
445
+
446
+ const telemetry = require('../core/rs-egress-telemetry.cjs');
447
+ if (fs.existsSync(telemetry.TELEMETRY_FILE)) {
448
+ const payload = JSON.parse(fs.readFileSync(telemetry.TELEMETRY_FILE, 'utf8'));
449
+ assert.equal(payload.entries.length, 0,
450
+ 'NO telemetry entries allowed; got ' + payload.entries.length);
451
+ }
452
+ SCENARIO_RESULTS.push({ test: 'T8', surface: 'patents', payload: { threw: threw.meta.matched_pattern } });
453
+ });
454
+
455
+ // ---------- Test 9: CANON PART 8 adversarial: leaked-venture-financials ----------
456
+ await runScenario('Test 9: CANON PART 8 adversarial leaked-venture-financials throws', async function () {
457
+ setupScopedHome();
458
+ // Pattern 1 (currency $5M) leak.
459
+ const queries = ['Lawrence said our patent strategy needs $5M biotech'];
460
+ installMockFetch(buildAllSourcesMockOk('clean', 1));
461
+ const fetcher = require('../core/rs-fetcher-patents.cjs');
462
+ let threw = null;
463
+ try {
464
+ await fetcher.fetchPatents(queries, {});
465
+ } catch (e) {
466
+ threw = e;
467
+ }
468
+ assert.ok(threw, 'expected throw on adversarial query');
469
+ assert.equal(threw.name, 'ExternalEgressViolation', 'class name');
470
+ assert.equal(CAPTURED_URLS.length, 0, 'NO fetch() calls allowed');
471
+ SCENARIO_RESULTS.push({ test: 'T9', surface: 'patents', payload: { threw: threw.meta.matched_pattern } });
472
+ });
473
+
474
+ // ---------- Test 10: CANON PART 8 adversarial: leaked-meeting-fragment ----------
475
+ await runScenario('Test 10: CANON PART 8 adversarial leaked-meeting-fragment throws', async function () {
476
+ setupScopedHome();
477
+ const queries = ['biotech meeting with Pfizer about IP'];
478
+ installMockFetch(buildAllSourcesMockOk('clean', 1));
479
+ const fetcher = require('../core/rs-fetcher-patents.cjs');
480
+ let threw = null;
481
+ try {
482
+ await fetcher.fetchPatents(queries, {});
483
+ } catch (e) {
484
+ threw = e;
485
+ }
486
+ assert.ok(threw, 'expected throw on adversarial query');
487
+ assert.equal(threw.name, 'ExternalEgressViolation', 'class name');
488
+ assert.equal(CAPTURED_URLS.length, 0, 'NO fetch() calls allowed');
489
+ SCENARIO_RESULTS.push({ test: 'T10', surface: 'patents', payload: { threw: threw.meta.matched_pattern } });
490
+ });
491
+
492
+ // ---------- Test 11: CANON PART 8 adversarial: leaked-SSN ----------
493
+ await runScenario('Test 11: CANON PART 8 adversarial leaked-SSN-style throws', async function () {
494
+ setupScopedHome();
495
+ // Pattern 5 (SSN-style 123-45-6789) leak.
496
+ const queries = ['inventor 123-45-6789 patents quantum'];
497
+ installMockFetch(buildAllSourcesMockOk('clean', 1));
498
+ const fetcher = require('../core/rs-fetcher-patents.cjs');
499
+ let threw = null;
500
+ try {
501
+ await fetcher.fetchPatents(queries, {});
502
+ } catch (e) {
503
+ threw = e;
504
+ }
505
+ assert.ok(threw, 'expected throw on adversarial query');
506
+ assert.equal(threw.name, 'ExternalEgressViolation', 'class name');
507
+ assert.equal(CAPTURED_URLS.length, 0, 'NO fetch() calls allowed');
508
+ SCENARIO_RESULTS.push({ test: 'T11', surface: 'patents', payload: { threw: threw.meta.matched_pattern } });
509
+ });
510
+
511
+ // ---------- V1: validator Check A telemetry-file-absent ----------
512
+ await runScenario('V1: validator Check A telemetry-file-absent -> {severity: null}', async function () {
513
+ setupScopedHome();
514
+ const validator = require('./validators/external-patents-invariants.cjs');
515
+ const result = validator.validate('/dev/null', {});
516
+ assert.equal(result.severity, null, 'severity null when telemetry absent');
517
+ assert.equal(result.violations.length, 0, 'no violations when telemetry absent');
518
+ });
519
+
520
+ // ---------- V2: validator Check B per-source budget exceeded ----------
521
+ await runScenario('V2: validator Check B per-source budget exceeded -> warning', async function () {
522
+ setupScopedHome();
523
+ const telemetry = require('../core/rs-egress-telemetry.cjs');
524
+ // Seed 75 google_patents entries within last 24h (over default 50 budget).
525
+ const now = Date.now();
526
+ const entries = [];
527
+ for (let i = 0; i < 75; i++) {
528
+ entries.push({
529
+ source: 'google_patents',
530
+ query_text_hash: 'aaaaaaaaaaaaaaaa',
531
+ status: 'ok',
532
+ fetched_at: new Date(now - (i * 1000)).toISOString(),
533
+ });
534
+ }
535
+ fs.mkdirSync(path.dirname(telemetry.TELEMETRY_FILE), { recursive: true });
536
+ fs.writeFileSync(telemetry.TELEMETRY_FILE,
537
+ JSON.stringify({ schema_version: '1.0', entries: entries }, null, 2));
538
+
539
+ const validator = require('./validators/external-patents-invariants.cjs');
540
+ const result = validator.validate('/dev/null', {});
541
+ assert.ok(result.violations.length >= 1, 'expected at least one violation');
542
+ const budgetViolations = result.violations.filter(v => v.category === 'budget_exceeded');
543
+ assert.ok(budgetViolations.length >= 1, 'expected budget_exceeded violation');
544
+ assert.equal(budgetViolations[0].severity, 'warning', 'budget_exceeded severity is warning');
545
+ });
546
+
547
+ // ---------- V3: validator Check C status enum violation ----------
548
+ await runScenario('V3: validator Check C status enum violation -> warning', async function () {
549
+ setupScopedHome();
550
+ const telemetry = require('../core/rs-egress-telemetry.cjs');
551
+ const entries = [{
552
+ source: 'google_patents',
553
+ query_text_hash: 'aaaaaaaaaaaaaaaa',
554
+ status: 'unknown_status',
555
+ fetched_at: new Date().toISOString(),
556
+ }];
557
+ fs.mkdirSync(path.dirname(telemetry.TELEMETRY_FILE), { recursive: true });
558
+ fs.writeFileSync(telemetry.TELEMETRY_FILE,
559
+ JSON.stringify({ schema_version: '1.0', entries: entries }, null, 2));
560
+
561
+ const validator = require('./validators/external-patents-invariants.cjs');
562
+ const result = validator.validate('/dev/null', {});
563
+ const statusViolations = result.violations.filter(v => v.category === 'status_enum_violation');
564
+ assert.ok(statusViolations.length >= 1, 'expected status_enum_violation');
565
+ assert.equal(statusViolations[0].severity, 'warning', 'status_enum severity is warning');
566
+ });
567
+
568
+ // ---------- V4: validator Check D query_text literal -> CRITICAL ----------
569
+ await runScenario('V4: validator Check D query_text literal present -> CRITICAL canon_boundary', async function () {
570
+ setupScopedHome();
571
+ const telemetry = require('../core/rs-egress-telemetry.cjs');
572
+ const entries = [{
573
+ source: 'google_patents',
574
+ query_text: 'literal CRISPR patent query',
575
+ query_text_hash: 'aaaaaaaaaaaaaaaa',
576
+ status: 'ok',
577
+ fetched_at: new Date().toISOString(),
578
+ }];
579
+ fs.mkdirSync(path.dirname(telemetry.TELEMETRY_FILE), { recursive: true });
580
+ fs.writeFileSync(telemetry.TELEMETRY_FILE,
581
+ JSON.stringify({ schema_version: '1.0', entries: entries }, null, 2));
582
+
583
+ const validator = require('./validators/external-patents-invariants.cjs');
584
+ const result = validator.validate('/dev/null', {});
585
+ const canonViolations = result.violations.filter(v => v.category === 'canon_boundary');
586
+ assert.ok(canonViolations.length >= 1, 'expected canon_boundary violation');
587
+ assert.equal(canonViolations[0].severity, 'critical',
588
+ 'canon_boundary severity is critical');
589
+ });
590
+
591
+ // ---------- V5: validator Check E query_text_hash format ----------
592
+ await runScenario('V5: validator Check E query_text_hash format violation -> warning', async function () {
593
+ setupScopedHome();
594
+ const telemetry = require('../core/rs-egress-telemetry.cjs');
595
+ const entries = [{
596
+ source: 'google_patents',
597
+ query_text_hash: 'NOT-A-HEX-HASH-XYZ',
598
+ status: 'ok',
599
+ fetched_at: new Date().toISOString(),
600
+ }];
601
+ fs.mkdirSync(path.dirname(telemetry.TELEMETRY_FILE), { recursive: true });
602
+ fs.writeFileSync(telemetry.TELEMETRY_FILE,
603
+ JSON.stringify({ schema_version: '1.0', entries: entries }, null, 2));
604
+
605
+ const validator = require('./validators/external-patents-invariants.cjs');
606
+ const result = validator.validate('/dev/null', {});
607
+ const hashViolations = result.violations.filter(v => v.category === 'hash_format_invalid');
608
+ assert.ok(hashViolations.length >= 1, 'expected hash_format_invalid violation');
609
+ assert.equal(hashViolations[0].severity, 'warning', 'severity is warning');
610
+ });
611
+
612
+ // ---------- V6: validator Check F fetched_at malformed ISO-8601 ----------
613
+ await runScenario('V6: validator Check F fetched_at malformed -> warning', async function () {
614
+ setupScopedHome();
615
+ const telemetry = require('../core/rs-egress-telemetry.cjs');
616
+ const entries = [{
617
+ source: 'google_patents',
618
+ query_text_hash: 'aaaaaaaaaaaaaaaa',
619
+ status: 'ok',
620
+ fetched_at: 'NOT-A-VALID-DATE',
621
+ }];
622
+ fs.mkdirSync(path.dirname(telemetry.TELEMETRY_FILE), { recursive: true });
623
+ fs.writeFileSync(telemetry.TELEMETRY_FILE,
624
+ JSON.stringify({ schema_version: '1.0', entries: entries }, null, 2));
625
+
626
+ const validator = require('./validators/external-patents-invariants.cjs');
627
+ const result = validator.validate('/dev/null', {});
628
+ const dateViolations = result.violations.filter(v => v.category === 'fetched_at_malformed');
629
+ assert.ok(dateViolations.length >= 1, 'expected fetched_at_malformed violation');
630
+ assert.equal(dateViolations[0].severity, 'warning', 'severity is warning');
631
+ });
632
+
633
+ // ---------- A1 sweep: zero forbidden matches in any captured output ----------
634
+ await runScenario('A1: zero forbidden matches across captured payloads + URLs', async function () {
635
+ for (const rec of SCENARIO_RESULTS) {
636
+ const stringified = JSON.stringify(rec.payload);
637
+ const hit = scanAgainstForbidden(stringified);
638
+ assert.equal(hit.hit, false,
639
+ 'A1 violation in ' + rec.test + '/' + rec.surface +
640
+ ' against pattern ' + (hit.pattern || 'n/a'));
641
+ }
642
+ for (const u of CAPTURED_URLS_ALL) {
643
+ const hit = scanAgainstForbidden(u);
644
+ assert.equal(hit.hit, false,
645
+ 'A1 outbound URL leaked forbidden pattern ' + (hit.pattern || 'n/a') +
646
+ ' in ' + u.slice(0, 100));
647
+ }
648
+ });
649
+
650
+ // ---------- A2 sweep: parity gate ----------
651
+ await runScenario('A2: FORBIDDEN_PATTERNS parity (rs-egress-prompts === cross-room-aggregator)', async function () {
652
+ const promptsModule = require('../core/rs-egress-prompts.cjs');
653
+ const aggregator = require('../core/cross-room-aggregator.cjs');
654
+ const ours = promptsModule.FORBIDDEN_PATTERNS;
655
+ const truth = aggregator.FORBIDDEN_PATTERNS;
656
+ assert.equal(ours.length, truth.length, 'lengths match');
657
+ for (let i = 0; i < truth.length; i++) {
658
+ assert.equal(ours[i].source, truth[i].source, 'pattern source mismatch at index ' + i);
659
+ assert.equal(ours[i].flags, truth[i].flags, 'pattern flags mismatch at index ' + i);
660
+ }
661
+ });
662
+
663
+ // ---------- Final report ----------
664
+ if (failed > 0) {
665
+ console.error('\n=== ' + failed + ' FAILURES ===');
666
+ for (const f of failures) {
667
+ console.error(' ' + f.name);
668
+ }
669
+ process.exit(1);
670
+ }
671
+
672
+ console.log('=== 89.2-03 fetcher-patents suite: 17/17 passed ===');
673
+ process.exit(0);
674
+ })();