@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,524 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /*
5
+ * Copyright (c) 2026 Mindrian. BSL 1.1.
6
+ *
7
+ * Phase 91-01 -- USER.md persistence + persona update-detect ops
8
+ * ==============================================================
9
+ * USER.md is a per-user, per-room artifact that holds the user's
10
+ * canonical role, current Larry persona, journey stage, role-blend
11
+ * weights, and supporting metadata. This module owns the read / write /
12
+ * update-decide primitives.
13
+ *
14
+ * Three responsibilities, one entry point each:
15
+ *
16
+ * readUserMd(path)
17
+ * Graceful-degradation read. null when absent, emptyUser() shell
18
+ * with parse_failed:true on malformed content, full struct when
19
+ * the file is well-formed. NEVER throws.
20
+ *
21
+ * writeUserMdAtomic(path, data)
22
+ * Phase 87-02 atomic write pattern: openSync('wx') + writeSync +
23
+ * fsyncSync (best-effort) + closeSync + renameSync. Cleans up tmp
24
+ * on any error path. Preserves the user-authored body below the
25
+ * frontmatter delimiter.
26
+ *
27
+ * detectPersonaUpdate({ current, signal })
28
+ * Pure function returning { shouldUpdate, reason } where reason is
29
+ * one of:
30
+ * 'first_detection' -- current === null
31
+ * 'user_override' -- signal.source === 'user_override'
32
+ * 'no_change' -- signal.persona === current.persona
33
+ * 'confidence_below_threshold' -- signal.confidence < threshold
34
+ * 'awaiting_consecutive_signal' -- threshold met but < 3 consecutive
35
+ * 'threshold_met' -- threshold met + >= 3 consecutive
36
+ *
37
+ * Canon Part 8 posture:
38
+ * USER.md is a LOCAL artifact. This module never touches the network.
39
+ * Test 21 of lib/memory/user-md-persona.test.cjs grep-guards this
40
+ * module against Brain MCP query helpers, network primitives, http
41
+ * URL literals, and shell-out HTTP clients. Brain queries that carry
42
+ * persona MUST go through brain-derivation-prompts.cjs and carry only
43
+ * the Larry-or-Brain persona scalar (a generic framework handle),
44
+ * never the role_blend weights or user_id.
45
+ *
46
+ * Phase 87-02 atomic-write pattern source-of-truth:
47
+ * lib/core/decision-capture.cjs lines 320-380 (atomicRewriteMintoWith*)
48
+ * lib/core/write-lock.cjs (the openSync('wx') primitive proven under
49
+ * the 20-fork concurrency fence).
50
+ *
51
+ * Three-surface compatible (CLI hook, Desktop MCP handler, Cowork
52
+ * shared runner). Pure CJS, node built-ins only, zero npm dependencies.
53
+ *
54
+ * License: BSL 1.1.
55
+ */
56
+
57
+ const fs = require('node:fs');
58
+ const path = require('node:path');
59
+
60
+ const taxonomy = require('./persona-taxonomy.cjs');
61
+
62
+ const SCHEMA_VERSION = 1;
63
+ const DEFAULT_UPDATE_THRESHOLD = 0.75;
64
+ const CONSECUTIVE_SIGNAL_REQUIRED = 3;
65
+
66
+ // ---------- emptyUser shell ----------
67
+
68
+ /**
69
+ * Build an empty USER.md struct. Used as the fallback shape when
70
+ * USER.md is absent (caller wants a non-null default), as the parse
71
+ * failure shape, and as the shell that writeUserMdAtomic merges into
72
+ * before serializing.
73
+ *
74
+ * @returns {object} Empty user struct.
75
+ */
76
+ function emptyUser() {
77
+ return {
78
+ schema_version: SCHEMA_VERSION,
79
+ user_id: null,
80
+ canonical_role: null,
81
+ larry_persona: null,
82
+ brain_persona: null,
83
+ journey_stage: null,
84
+ role_blend: {
85
+ founder: 0,
86
+ researcher: 0,
87
+ operator: 0,
88
+ investor: 0,
89
+ mentor: 0,
90
+ domain_expert: 0,
91
+ student: 0,
92
+ },
93
+ problem_type: 'unknown',
94
+ venture_stage: 'unknown',
95
+ last_detected_at: null,
96
+ last_updated_at: null,
97
+ detection_confidence: 0.0,
98
+ update_threshold: DEFAULT_UPDATE_THRESHOLD,
99
+ consecutive_signal_count: 0,
100
+ parse_failed: false,
101
+ };
102
+ }
103
+
104
+ // ---------- Narrow-dialect YAML scalar parser ----------
105
+ //
106
+ // Mirrors the dialect used in lib/core/folder-memory-shared.cjs
107
+ // parseBrainFrontmatter. Flat scalars only: strings, ints, floats,
108
+ // booleans, null. Single one-level-deep object support for role_blend.
109
+ // Duplicated here intentionally so user-md-ops keeps zero cross-import
110
+ // into folder-memory layers (flat dep graph).
111
+
112
+ function unquoteScalar(raw) {
113
+ const s = raw.trim();
114
+ if (s.length === 0) return '';
115
+ if (s[0] === '"') {
116
+ if (s.length < 2 || s[s.length - 1] !== '"') return undefined;
117
+ return s.slice(1, -1);
118
+ }
119
+ if (s[0] === "'") {
120
+ if (s.length < 2 || s[s.length - 1] !== "'") return undefined;
121
+ return s.slice(1, -1);
122
+ }
123
+ return s;
124
+ }
125
+
126
+ function castScalar(s) {
127
+ if (s === 'null' || s === '~') return null;
128
+ if (s === 'true') return true;
129
+ if (s === 'false') return false;
130
+ if (/^-?\d+$/.test(s)) {
131
+ const n = parseInt(s, 10);
132
+ if (Number.isSafeInteger(n)) return n;
133
+ }
134
+ if (/^-?\d+\.\d+$/.test(s)) {
135
+ const f = parseFloat(s);
136
+ if (Number.isFinite(f)) return f;
137
+ }
138
+ return s;
139
+ }
140
+
141
+ function parseFrontmatter(src) {
142
+ if (typeof src !== 'string' || src.length === 0) {
143
+ return { ok: false };
144
+ }
145
+ const lines = src.split(/\r?\n/);
146
+ const root = {};
147
+ // role_blend is the only nested scalar map we know about. Track its
148
+ // open block so we can absorb indented children.
149
+ let inRoleBlend = false;
150
+ for (let i = 0; i < lines.length; i++) {
151
+ const line = lines[i];
152
+ if (line.length === 0 || /^\s*$/.test(line) || /^\s*#/.test(line)) {
153
+ continue;
154
+ }
155
+ // Indented child of role_blend?
156
+ if (inRoleBlend && /^\s+/.test(line)) {
157
+ const childMatch = line.match(/^\s+([A-Za-z_][A-Za-z0-9_-]*):\s*(.*)$/);
158
+ if (!childMatch) {
159
+ return { ok: false };
160
+ }
161
+ const ck = childMatch[1];
162
+ const cv = childMatch[2];
163
+ if (cv.trim().length === 0) {
164
+ root.role_blend[ck] = '';
165
+ continue;
166
+ }
167
+ const rawC = unquoteScalar(cv);
168
+ if (rawC === undefined) return { ok: false };
169
+ root.role_blend[ck] = castScalar(rawC);
170
+ continue;
171
+ }
172
+ // Flat top-level key: value. Closes any open block.
173
+ inRoleBlend = false;
174
+ const m = line.match(/^([A-Za-z_][A-Za-z0-9_-]*):(.*)$/);
175
+ if (!m) return { ok: false };
176
+ const key = m[1];
177
+ const rest = m[2];
178
+ if (rest.trim().length === 0) {
179
+ // Empty-RHS top-level key. role_blend is the known container; for
180
+ // any other key treat as empty string scalar.
181
+ if (key === 'role_blend') {
182
+ root.role_blend = {};
183
+ inRoleBlend = true;
184
+ } else {
185
+ root[key] = '';
186
+ }
187
+ continue;
188
+ }
189
+ const raw = unquoteScalar(rest);
190
+ if (raw === undefined) return { ok: false };
191
+ root[key] = castScalar(raw);
192
+ }
193
+ return { ok: true, data: root };
194
+ }
195
+
196
+ // ---------- readUserMd ----------
197
+
198
+ /**
199
+ * Read and parse a USER.md file with graceful degradation.
200
+ *
201
+ * Precedence:
202
+ * 1. USER.md absent / non-string / empty path -> null
203
+ * 2. Frontmatter delimiter missing -> emptyUser + parse_failed:true
204
+ * 3. Narrow-dialect YAML unparseable -> emptyUser + parse_failed:true
205
+ * 4. Read error (EACCES, etc.) -> emptyUser + parse_failed:true
206
+ * 5. Valid -> full struct, parse_failed:false
207
+ *
208
+ * Schema-tolerant validation: an unknown enum value (e.g. larry_persona
209
+ * not in LARRY_PERSONAS) coerces that single field to null and leaves
210
+ * parse_failed:false. The caller can distinguish "no persona detected
211
+ * yet" from "the file is corrupted" without try/catch.
212
+ *
213
+ * NEVER throws.
214
+ *
215
+ * @param {string} userMdPath Absolute path to USER.md.
216
+ * @returns {object|null} User struct or null when path is absent.
217
+ */
218
+ function readUserMd(userMdPath) {
219
+ if (typeof userMdPath !== 'string' || userMdPath.length === 0) {
220
+ return null;
221
+ }
222
+ let stat;
223
+ try {
224
+ stat = fs.statSync(userMdPath);
225
+ } catch (_e) {
226
+ return null; // absent
227
+ }
228
+ if (!stat.isFile()) return null;
229
+
230
+ if (stat.size === 0) {
231
+ const shell = emptyUser();
232
+ shell.parse_failed = true;
233
+ return shell;
234
+ }
235
+
236
+ let raw;
237
+ try {
238
+ raw = fs.readFileSync(userMdPath, 'utf8');
239
+ } catch (_e) {
240
+ const shell = emptyUser();
241
+ shell.parse_failed = true;
242
+ return shell;
243
+ }
244
+
245
+ const fmRe = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
246
+ const m = raw.match(fmRe);
247
+ if (!m) {
248
+ const shell = emptyUser();
249
+ shell.parse_failed = true;
250
+ return shell;
251
+ }
252
+
253
+ const parsed = parseFrontmatter(m[1]);
254
+ if (!parsed.ok) {
255
+ const shell = emptyUser();
256
+ shell.parse_failed = true;
257
+ return shell;
258
+ }
259
+ const fm = parsed.data;
260
+
261
+ const out = emptyUser();
262
+ out.parse_failed = false;
263
+ out.schema_version = typeof fm.schema_version === 'number'
264
+ ? fm.schema_version : SCHEMA_VERSION;
265
+ out.user_id = typeof fm.user_id === 'string' && fm.user_id.length > 0
266
+ ? fm.user_id : null;
267
+ out.canonical_role = typeof fm.canonical_role === 'string' && fm.canonical_role.length > 0
268
+ ? fm.canonical_role : null;
269
+ // Schema-tolerant enum coercion: invalid -> null, NOT parse_failed.
270
+ if (typeof fm.larry_persona === 'string'
271
+ && taxonomy.LARRY_PERSONAS.indexOf(fm.larry_persona) >= 0) {
272
+ out.larry_persona = fm.larry_persona;
273
+ } else {
274
+ out.larry_persona = null;
275
+ }
276
+ if (typeof fm.brain_persona === 'string'
277
+ && taxonomy.BRAIN_PERSONAS.indexOf(fm.brain_persona) >= 0) {
278
+ out.brain_persona = fm.brain_persona;
279
+ } else {
280
+ out.brain_persona = null;
281
+ }
282
+ if (typeof fm.journey_stage === 'string'
283
+ && taxonomy.JOURNEY_STAGES.indexOf(fm.journey_stage) >= 0) {
284
+ out.journey_stage = fm.journey_stage;
285
+ } else {
286
+ out.journey_stage = null;
287
+ }
288
+ if (typeof fm.problem_type === 'string'
289
+ && taxonomy.PROBLEM_TYPES.indexOf(fm.problem_type) >= 0) {
290
+ out.problem_type = fm.problem_type;
291
+ } else {
292
+ out.problem_type = 'unknown';
293
+ }
294
+ if (typeof fm.venture_stage === 'string'
295
+ && taxonomy.VENTURE_STAGES.indexOf(fm.venture_stage) >= 0) {
296
+ out.venture_stage = fm.venture_stage;
297
+ } else {
298
+ out.venture_stage = 'unknown';
299
+ }
300
+ out.last_detected_at = typeof fm.last_detected_at === 'string'
301
+ && fm.last_detected_at.length > 0
302
+ ? fm.last_detected_at : null;
303
+ out.last_updated_at = typeof fm.last_updated_at === 'string'
304
+ && fm.last_updated_at.length > 0
305
+ ? fm.last_updated_at : null;
306
+ out.detection_confidence = typeof fm.detection_confidence === 'number'
307
+ ? fm.detection_confidence : 0.0;
308
+ out.update_threshold = typeof fm.update_threshold === 'number'
309
+ ? fm.update_threshold : DEFAULT_UPDATE_THRESHOLD;
310
+ out.consecutive_signal_count = typeof fm.consecutive_signal_count === 'number'
311
+ ? fm.consecutive_signal_count : 0;
312
+ // role_blend: inherit defaults, then overwrite per-axis from fm.
313
+ if (fm.role_blend && typeof fm.role_blend === 'object') {
314
+ const allowed = Object.keys(out.role_blend);
315
+ for (const k of allowed) {
316
+ if (typeof fm.role_blend[k] === 'number') {
317
+ out.role_blend[k] = fm.role_blend[k];
318
+ }
319
+ }
320
+ }
321
+ return out;
322
+ }
323
+
324
+ // ---------- Frontmatter emitter ----------
325
+
326
+ function emitYamlScalar(v) {
327
+ if (v === null || v === undefined) return 'null';
328
+ if (typeof v === 'boolean') return v ? 'true' : 'false';
329
+ if (typeof v === 'number') return String(v);
330
+ // String. Quote if it could be misparsed as another type.
331
+ const s = String(v);
332
+ // Plain-scalar safe set: ASCII alnum, dash, underscore, slash, dot,
333
+ // colon (for ISO timestamps), plus the +Z/T separators. Otherwise
334
+ // round-trip through quotes.
335
+ if (/^[A-Za-z0-9._/:+\- ]+$/.test(s) && s.length > 0
336
+ && s !== 'true' && s !== 'false' && s !== 'null' && s !== '~') {
337
+ return s;
338
+ }
339
+ return '"' + s.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
340
+ }
341
+
342
+ function buildFrontmatter(data) {
343
+ const merged = Object.assign(emptyUser(), data || {});
344
+ // Re-merge role_blend separately so partial caller input does not
345
+ // erase the empty-shell defaults.
346
+ if (data && typeof data.role_blend === 'object' && data.role_blend !== null) {
347
+ merged.role_blend = Object.assign(emptyUser().role_blend, data.role_blend);
348
+ }
349
+ const lines = ['---'];
350
+ lines.push('schema_version: ' + emitYamlScalar(merged.schema_version));
351
+ lines.push('user_id: ' + emitYamlScalar(merged.user_id));
352
+ lines.push('canonical_role: ' + emitYamlScalar(merged.canonical_role));
353
+ lines.push('larry_persona: ' + emitYamlScalar(merged.larry_persona));
354
+ lines.push('brain_persona: ' + emitYamlScalar(merged.brain_persona));
355
+ lines.push('journey_stage: ' + emitYamlScalar(merged.journey_stage));
356
+ lines.push('role_blend:');
357
+ const axes = ['founder', 'researcher', 'operator', 'investor',
358
+ 'mentor', 'domain_expert', 'student'];
359
+ for (const k of axes) {
360
+ const v = merged.role_blend[k];
361
+ lines.push(' ' + k + ': ' + emitYamlScalar(typeof v === 'number' ? v : 0));
362
+ }
363
+ lines.push('problem_type: ' + emitYamlScalar(merged.problem_type));
364
+ lines.push('venture_stage: ' + emitYamlScalar(merged.venture_stage));
365
+ lines.push('last_detected_at: ' + emitYamlScalar(merged.last_detected_at));
366
+ lines.push('last_updated_at: ' + emitYamlScalar(merged.last_updated_at));
367
+ lines.push('detection_confidence: ' + emitYamlScalar(merged.detection_confidence));
368
+ lines.push('update_threshold: ' + emitYamlScalar(merged.update_threshold));
369
+ lines.push('consecutive_signal_count: ' + emitYamlScalar(merged.consecutive_signal_count));
370
+ lines.push('---');
371
+ return lines.join('\n');
372
+ }
373
+
374
+ // ---------- writeUserMdAtomic ----------
375
+
376
+ /**
377
+ * Write USER.md atomically using the Phase 87-02 pattern.
378
+ *
379
+ * Sequence:
380
+ * 1. Read existing USER.md body (if any) to preserve user-authored
381
+ * content below the frontmatter delimiter.
382
+ * 2. Build new frontmatter from data merged into emptyUser() shell.
383
+ * 3. Compose newContent = newFrontmatter + tail + preservedBody.
384
+ * 4. Open tmp = path + '.tmp.<pid>.<rnd>.user' with openSync('wx')
385
+ * to fail-fast on stale tmp.
386
+ * 5. writeSync newContent. fsyncSync best-effort (try/catch for
387
+ * ENOTSUP on tmpfs).
388
+ * 6. closeSync.
389
+ * 7. renameSync(tmp, path) -- atomic on POSIX + Windows-NTFS.
390
+ * 8. On any error: unlink tmp + rethrow with descriptive message.
391
+ *
392
+ * The 'wx' flag plus per-pid + random tmp name makes mid-write crashes
393
+ * recoverable (next call re-creates a fresh tmp).
394
+ *
395
+ * @param {string} userMdPath Absolute path to USER.md.
396
+ * @param {object} data Partial USER.md struct; merged into shell.
397
+ * @returns {void}
398
+ */
399
+ function writeUserMdAtomic(userMdPath, data) {
400
+ if (typeof userMdPath !== 'string' || userMdPath.length === 0) {
401
+ throw new Error('writeUserMdAtomic: userMdPath must be a non-empty string');
402
+ }
403
+ // Preserve any user-authored body below the existing frontmatter.
404
+ let preservedBody = '\n# USER.md\n';
405
+ try {
406
+ const existing = fs.readFileSync(userMdPath, 'utf8');
407
+ const fmRe = /^---\r?\n[\s\S]*?\r?\n---\r?\n?([\s\S]*)$/;
408
+ const m = existing.match(fmRe);
409
+ if (m && typeof m[1] === 'string') {
410
+ preservedBody = m[1];
411
+ // Ensure exactly one leading newline so the body starts on its
412
+ // own line below the frontmatter close delimiter.
413
+ if (preservedBody.length === 0 || preservedBody[0] !== '\n') {
414
+ preservedBody = '\n' + preservedBody;
415
+ }
416
+ }
417
+ } catch (_e) {
418
+ // No existing file or unreadable. Use the default body shell.
419
+ }
420
+
421
+ const newFrontmatter = buildFrontmatter(data);
422
+ const newContent = newFrontmatter + '\n' + preservedBody.replace(/^\n/, '\n');
423
+ // ^ ensure exactly one '\n' between '---' and the body.
424
+
425
+ // Per-pid + random tmp name. The 'wx' flag on openSync would otherwise
426
+ // race with sibling writers in the same process if they shared a name.
427
+ const rnd = Math.random().toString(36).slice(2, 10);
428
+ const tmpPath = userMdPath + '.tmp.' + process.pid + '.' + rnd + '.user';
429
+
430
+ let fd;
431
+ try {
432
+ fd = fs.openSync(tmpPath, 'wx');
433
+ } catch (e) {
434
+ if (e && e.code === 'EEXIST') {
435
+ // Stale tmp from a prior crash on this exact pid+random slot.
436
+ // Astronomically unlikely with random suffix, but be defensive.
437
+ try { fs.unlinkSync(tmpPath); } catch (_) {}
438
+ fd = fs.openSync(tmpPath, 'wx');
439
+ } else {
440
+ throw e;
441
+ }
442
+ }
443
+ try {
444
+ fs.writeSync(fd, newContent);
445
+ try { fs.fsyncSync(fd); } catch (_e) {
446
+ // ENOTSUP on tmpfs / overlayfs / Windows ImDisk. Best-effort
447
+ // durability is acceptable for USER.md (not load-bearing across
448
+ // power loss; persona will re-detect on next session).
449
+ }
450
+ } finally {
451
+ try { fs.closeSync(fd); } catch (_) {}
452
+ }
453
+
454
+ try {
455
+ fs.renameSync(tmpPath, userMdPath);
456
+ } catch (e) {
457
+ try { fs.unlinkSync(tmpPath); } catch (_) {}
458
+ throw e;
459
+ }
460
+ }
461
+
462
+ // ---------- detectPersonaUpdate ----------
463
+
464
+ /**
465
+ * Decide whether a new persona signal should overwrite the current
466
+ * USER.md persona.
467
+ *
468
+ * The decision tree is deliberately ordered for early-exit + low-noise
469
+ * behavior:
470
+ *
471
+ * 1. current === null -> 'first_detection' (always update)
472
+ * 2. signal.source === 'user_*' -> 'user_override' (bypass threshold)
473
+ * 3. signal.persona === current -> 'no_change' (no rewrite)
474
+ * 4. confidence < update_threshold -> 'confidence_below_threshold'
475
+ * 5. consecutive < 3 -> 'awaiting_consecutive_signal'
476
+ * 6. consecutive >= 3 -> 'threshold_met' (update)
477
+ *
478
+ * Caller is responsible for incrementing consecutive_signal_count; this
479
+ * function reads the count off `signal` so the caller controls the
480
+ * window definition (per-turn vs per-session vs per-window).
481
+ *
482
+ * @param {object} args
483
+ * @param {object|null} args.current Current USER.md persona struct or null.
484
+ * @param {object} args.signal New persona detection signal.
485
+ * @returns {{shouldUpdate: boolean, reason: string}}
486
+ */
487
+ function detectPersonaUpdate(args) {
488
+ const current = (args && args.current) || null;
489
+ const signal = (args && args.signal) || {};
490
+
491
+ if (current === null) {
492
+ return { shouldUpdate: true, reason: 'first_detection' };
493
+ }
494
+ if (signal.source === 'user_override') {
495
+ return { shouldUpdate: true, reason: 'user_override' };
496
+ }
497
+ if (signal.persona === current.persona) {
498
+ return { shouldUpdate: false, reason: 'no_change' };
499
+ }
500
+ const threshold = typeof current.update_threshold === 'number'
501
+ ? current.update_threshold
502
+ : DEFAULT_UPDATE_THRESHOLD;
503
+ const conf = typeof signal.confidence === 'number' ? signal.confidence : 0.0;
504
+ if (conf < threshold) {
505
+ return { shouldUpdate: false, reason: 'confidence_below_threshold' };
506
+ }
507
+ const consec = typeof signal.consecutive_signal_count === 'number'
508
+ ? signal.consecutive_signal_count : 0;
509
+ if (consec < CONSECUTIVE_SIGNAL_REQUIRED) {
510
+ return { shouldUpdate: false, reason: 'awaiting_consecutive_signal' };
511
+ }
512
+ return { shouldUpdate: true, reason: 'threshold_met' };
513
+ }
514
+
515
+ module.exports = {
516
+ emptyUser,
517
+ readUserMd,
518
+ writeUserMdAtomic,
519
+ detectPersonaUpdate,
520
+ // Constants exposed for callers that want to honor the same defaults.
521
+ DEFAULT_UPDATE_THRESHOLD,
522
+ CONSECUTIVE_SIGNAL_REQUIRED,
523
+ SCHEMA_VERSION,
524
+ };