mirror-mind 0.0.1 → 0.32.1

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 (467) hide show
  1. package/.claude/hooks/log-session-end.sh +85 -0
  2. package/.claude/hooks/log-user-prompt.sh +85 -0
  3. package/.claude/hooks/mirror-inject.sh +85 -0
  4. package/.claude/hooks/session-start.sh +85 -0
  5. package/.gemini/hooks/log-assistant.sh +85 -0
  6. package/.gemini/hooks/log-user.sh +85 -0
  7. package/.gemini/hooks/session-end.sh +85 -0
  8. package/.gemini/hooks/session-start.sh +85 -0
  9. package/.pi/extensions/mirror-logger.ts +561 -0
  10. package/.pi/skills/mm-backup/SKILL.md +21 -0
  11. package/.pi/skills/mm-build/SKILL.md +1206 -0
  12. package/.pi/skills/mm-consolidate/SKILL.md +112 -0
  13. package/.pi/skills/mm-consult/SKILL.md +70 -0
  14. package/.pi/skills/mm-conversations/SKILL.md +16 -0
  15. package/.pi/skills/mm-discard/SKILL.md +39 -0
  16. package/.pi/skills/mm-explore/SKILL.md +255 -0
  17. package/.pi/skills/mm-help/SKILL.md +74 -0
  18. package/.pi/skills/mm-identity/SKILL.md +96 -0
  19. package/.pi/skills/mm-journal/SKILL.md +16 -0
  20. package/.pi/skills/mm-journey/SKILL.md +23 -0
  21. package/.pi/skills/mm-journeys/SKILL.md +15 -0
  22. package/.pi/skills/mm-memories/SKILL.md +17 -0
  23. package/.pi/skills/mm-mirror/SKILL.md +98 -0
  24. package/.pi/skills/mm-mute/SKILL.md +18 -0
  25. package/.pi/skills/mm-new/SKILL.md +18 -0
  26. package/.pi/skills/mm-recall/SKILL.md +17 -0
  27. package/.pi/skills/mm-release-notes/SKILL.md +52 -0
  28. package/.pi/skills/mm-seed/SKILL.md +19 -0
  29. package/.pi/skills/mm-shadow/SKILL.md +117 -0
  30. package/.pi/skills/mm-soul/SKILL.md +502 -0
  31. package/.pi/skills/mm-tasks/SKILL.md +24 -0
  32. package/.pi/skills/mm-update/SKILL.md +32 -0
  33. package/.pi/skills/mm-week/SKILL.md +28 -0
  34. package/.pi/skills/mm-welcome/SKILL.md +24 -0
  35. package/AGENTS.md +188 -0
  36. package/README.md +126 -9
  37. package/bin/loader.js +67 -0
  38. package/bin/mirror-hook.js +7 -0
  39. package/bin/mirror.js +7 -0
  40. package/docs/releases/index.md +99 -0
  41. package/docs/releases/pending-cutoffs.md +510 -0
  42. package/docs/releases/v0.10.0.md +123 -0
  43. package/docs/releases/v0.10.1.md +66 -0
  44. package/docs/releases/v0.10.10.md +51 -0
  45. package/docs/releases/v0.10.11.md +39 -0
  46. package/docs/releases/v0.10.12.md +57 -0
  47. package/docs/releases/v0.10.13.md +35 -0
  48. package/docs/releases/v0.10.14.md +42 -0
  49. package/docs/releases/v0.10.15.md +53 -0
  50. package/docs/releases/v0.10.16.md +35 -0
  51. package/docs/releases/v0.10.17.md +43 -0
  52. package/docs/releases/v0.10.2.md +64 -0
  53. package/docs/releases/v0.10.3.md +78 -0
  54. package/docs/releases/v0.10.4.md +42 -0
  55. package/docs/releases/v0.10.5.md +57 -0
  56. package/docs/releases/v0.10.6.md +47 -0
  57. package/docs/releases/v0.10.7.md +53 -0
  58. package/docs/releases/v0.10.8.md +49 -0
  59. package/docs/releases/v0.10.9.md +37 -0
  60. package/docs/releases/v0.11.0.md +86 -0
  61. package/docs/releases/v0.12.0.md +67 -0
  62. package/docs/releases/v0.13.0.md +64 -0
  63. package/docs/releases/v0.14.0.md +68 -0
  64. package/docs/releases/v0.15.0.md +48 -0
  65. package/docs/releases/v0.16.0.md +50 -0
  66. package/docs/releases/v0.16.1.md +55 -0
  67. package/docs/releases/v0.16.2.md +36 -0
  68. package/docs/releases/v0.17.0.md +50 -0
  69. package/docs/releases/v0.17.1.md +58 -0
  70. package/docs/releases/v0.17.2.md +42 -0
  71. package/docs/releases/v0.17.3.md +58 -0
  72. package/docs/releases/v0.18.0.md +84 -0
  73. package/docs/releases/v0.18.1.md +51 -0
  74. package/docs/releases/v0.18.2.md +69 -0
  75. package/docs/releases/v0.19.0.md +85 -0
  76. package/docs/releases/v0.19.1.md +46 -0
  77. package/docs/releases/v0.20.0.md +85 -0
  78. package/docs/releases/v0.21.0.md +61 -0
  79. package/docs/releases/v0.21.1.md +47 -0
  80. package/docs/releases/v0.22.0.md +77 -0
  81. package/docs/releases/v0.22.1.md +42 -0
  82. package/docs/releases/v0.23.0.md +105 -0
  83. package/docs/releases/v0.24.0.md +125 -0
  84. package/docs/releases/v0.25.0.md +107 -0
  85. package/docs/releases/v0.26.0.md +139 -0
  86. package/docs/releases/v0.26.1.md +65 -0
  87. package/docs/releases/v0.26.2.md +43 -0
  88. package/docs/releases/v0.27.0.md +63 -0
  89. package/docs/releases/v0.28.0.md +59 -0
  90. package/docs/releases/v0.29.0.md +61 -0
  91. package/docs/releases/v0.29.1.md +59 -0
  92. package/docs/releases/v0.29.2.md +59 -0
  93. package/docs/releases/v0.29.3.md +93 -0
  94. package/docs/releases/v0.30.0.md +75 -0
  95. package/docs/releases/v0.30.1.md +93 -0
  96. package/docs/releases/v0.30.2.md +66 -0
  97. package/docs/releases/v0.31.0.md +122 -0
  98. package/docs/releases/v0.31.1.md +142 -0
  99. package/docs/releases/v0.31.10.md +118 -0
  100. package/docs/releases/v0.31.11.md +88 -0
  101. package/docs/releases/v0.31.12.md +105 -0
  102. package/docs/releases/v0.31.13.md +99 -0
  103. package/docs/releases/v0.31.14.md +97 -0
  104. package/docs/releases/v0.31.2.md +115 -0
  105. package/docs/releases/v0.31.3.md +142 -0
  106. package/docs/releases/v0.31.4.md +117 -0
  107. package/docs/releases/v0.31.5.md +155 -0
  108. package/docs/releases/v0.31.6.md +132 -0
  109. package/docs/releases/v0.31.7.md +132 -0
  110. package/docs/releases/v0.31.8.md +120 -0
  111. package/docs/releases/v0.31.9.md +120 -0
  112. package/docs/releases/v0.32.0.md +261 -0
  113. package/docs/releases/v0.32.1.md +59 -0
  114. package/docs/releases/v0.8.0.md +118 -0
  115. package/docs/releases/v0.9.0.md +98 -0
  116. package/docs/releases/v0.9.1.md +83 -0
  117. package/package.json +102 -6
  118. package/plugins/mirror-mind/.claude-plugin/plugin.json +13 -0
  119. package/plugins/mirror-mind/hooks/hooks.json +50 -0
  120. package/plugins/mirror-mind/hooks/log-session-end.sh +98 -0
  121. package/plugins/mirror-mind/hooks/log-user-prompt.sh +98 -0
  122. package/plugins/mirror-mind/hooks/mirror-inject.sh +98 -0
  123. package/plugins/mirror-mind/hooks/session-start.sh +98 -0
  124. package/plugins/mirror-mind/mcp/launch.sh +52 -0
  125. package/plugins/mirror-mind/skills/mm-backup/SKILL.md +27 -0
  126. package/plugins/mirror-mind/skills/mm-build/SKILL.md +1206 -0
  127. package/plugins/mirror-mind/skills/mm-consolidate/SKILL.md +112 -0
  128. package/plugins/mirror-mind/skills/mm-consult/SKILL.md +83 -0
  129. package/plugins/mirror-mind/skills/mm-conversations/SKILL.md +17 -0
  130. package/plugins/mirror-mind/skills/mm-discard/SKILL.md +40 -0
  131. package/plugins/mirror-mind/skills/mm-explore/SKILL.md +241 -0
  132. package/plugins/mirror-mind/skills/mm-help/SKILL.md +104 -0
  133. package/plugins/mirror-mind/skills/mm-identity/SKILL.md +96 -0
  134. package/plugins/mirror-mind/skills/mm-journal/SKILL.md +21 -0
  135. package/plugins/mirror-mind/skills/mm-journey/SKILL.md +37 -0
  136. package/plugins/mirror-mind/skills/mm-journeys/SKILL.md +15 -0
  137. package/plugins/mirror-mind/skills/mm-memories/SKILL.md +20 -0
  138. package/plugins/mirror-mind/skills/mm-mirror/SKILL.md +65 -0
  139. package/plugins/mirror-mind/skills/mm-mute/SKILL.md +18 -0
  140. package/plugins/mirror-mind/skills/mm-new/SKILL.md +18 -0
  141. package/plugins/mirror-mind/skills/mm-recall/SKILL.md +17 -0
  142. package/plugins/mirror-mind/skills/mm-release-notes/SKILL.md +31 -0
  143. package/plugins/mirror-mind/skills/mm-seed/SKILL.md +15 -0
  144. package/plugins/mirror-mind/skills/mm-shadow/SKILL.md +117 -0
  145. package/plugins/mirror-mind/skills/mm-soul/SKILL.md +488 -0
  146. package/plugins/mirror-mind/skills/mm-tasks/SKILL.md +36 -0
  147. package/plugins/mirror-mind/skills/mm-update/SKILL.md +30 -0
  148. package/plugins/mirror-mind/skills/mm-week/SKILL.md +46 -0
  149. package/plugins/mirror-mind/skills/mm-welcome/SKILL.md +22 -0
  150. package/scripts/codex-hooks/session-end.sh +85 -0
  151. package/scripts/codex-hooks/session-start.sh +85 -0
  152. package/scripts/codex-mirror.sh +48 -0
  153. package/templates/identity/README.md +119 -0
  154. package/templates/identity/ego/behavior.yaml +52 -0
  155. package/templates/identity/ego/constraints.yaml +15 -0
  156. package/templates/identity/ego/identity.yaml +43 -0
  157. package/templates/identity/journeys/personal-growth.yaml +39 -0
  158. package/templates/identity/organization/identity.yaml +24 -0
  159. package/templates/identity/organization/principles.yaml +17 -0
  160. package/templates/identity/personas/coach.yaml +94 -0
  161. package/templates/identity/personas/designer.yaml +93 -0
  162. package/templates/identity/personas/doctor.yaml +96 -0
  163. package/templates/identity/personas/engineer.yaml +77 -0
  164. package/templates/identity/personas/financial.yaml +97 -0
  165. package/templates/identity/personas/prompt-engineer.yaml +122 -0
  166. package/templates/identity/personas/researcher.yaml +88 -0
  167. package/templates/identity/personas/strategist.yaml +91 -0
  168. package/templates/identity/personas/teacher.yaml +95 -0
  169. package/templates/identity/personas/therapist.yaml +91 -0
  170. package/templates/identity/personas/thinker.yaml +74 -0
  171. package/templates/identity/personas/writer.yaml +76 -0
  172. package/templates/identity/self/config.yaml +16 -0
  173. package/templates/identity/self/soul.yaml +105 -0
  174. package/templates/identity/user/identity.yaml +30 -0
  175. package/ts/src/backup/staging.ts +62 -0
  176. package/ts/src/backup/zipBackup.ts +267 -0
  177. package/ts/src/backup/zipReader.ts +109 -0
  178. package/ts/src/backup/zipWriter.ts +148 -0
  179. package/ts/src/builder/activeCheckpoint.ts +134 -0
  180. package/ts/src/builder/aggregateStatus.ts +34 -0
  181. package/ts/src/builder/approve.ts +149 -0
  182. package/ts/src/builder/argv.ts +292 -0
  183. package/ts/src/builder/ariadMethod.ts +536 -0
  184. package/ts/src/builder/artifacts/artifactSurfaces.ts +106 -0
  185. package/ts/src/builder/artifacts/artifactWriter.ts +113 -0
  186. package/ts/src/builder/artifacts/closureArtifacts.ts +199 -0
  187. package/ts/src/builder/artifacts/deliveryStoryArtifacts.ts +316 -0
  188. package/ts/src/builder/artifacts/planArtifacts.ts +242 -0
  189. package/ts/src/builder/artifacts/scaffoldSections.ts +273 -0
  190. package/ts/src/builder/artifacts/scaffoldState.ts +227 -0
  191. package/ts/src/builder/artifacts/storyFiles.ts +61 -0
  192. package/ts/src/builder/artifacts/storyIndex.ts +166 -0
  193. package/ts/src/builder/cadence.ts +59 -0
  194. package/ts/src/builder/card.ts +192 -0
  195. package/ts/src/builder/checkpointRefused.ts +66 -0
  196. package/ts/src/builder/closure.ts +964 -0
  197. package/ts/src/builder/commands.ts +2053 -0
  198. package/ts/src/builder/cursorPosition.ts +176 -0
  199. package/ts/src/builder/cursorTransitions.ts +77 -0
  200. package/ts/src/builder/deliveryCursor.ts +577 -0
  201. package/ts/src/builder/deliveryStoryClosure.ts +521 -0
  202. package/ts/src/builder/deliveryStoryPlan.ts +536 -0
  203. package/ts/src/builder/deliveryStoryReady.ts +160 -0
  204. package/ts/src/builder/deliveryStoryRoadmapClosure.ts +151 -0
  205. package/ts/src/builder/expand.ts +336 -0
  206. package/ts/src/builder/flowUnit.ts +339 -0
  207. package/ts/src/builder/homeSurface.ts +122 -0
  208. package/ts/src/builder/implementationGuard.ts +125 -0
  209. package/ts/src/builder/index.ts +11 -0
  210. package/ts/src/builder/lifecycleRefusal.ts +92 -0
  211. package/ts/src/builder/lifecycleRibbon.ts +159 -0
  212. package/ts/src/builder/load.ts +313 -0
  213. package/ts/src/builder/methodAdoption.ts +113 -0
  214. package/ts/src/builder/methodDefinition.ts +429 -0
  215. package/ts/src/builder/methodInspection.ts +371 -0
  216. package/ts/src/builder/plan.ts +408 -0
  217. package/ts/src/builder/planPreauthorization.ts +265 -0
  218. package/ts/src/builder/prepare.ts +225 -0
  219. package/ts/src/builder/projectPaths.ts +64 -0
  220. package/ts/src/builder/pull.ts +356 -0
  221. package/ts/src/builder/pullCandidates.ts +463 -0
  222. package/ts/src/builder/pullCandidatesRender.ts +238 -0
  223. package/ts/src/builder/refinementField.ts +93 -0
  224. package/ts/src/builder/releaseIntent.ts +173 -0
  225. package/ts/src/builder/resumeState.ts +84 -0
  226. package/ts/src/builder/resumeSurface.ts +149 -0
  227. package/ts/src/builder/roadmapGrammar.ts +300 -0
  228. package/ts/src/builder/roadmapScan.ts +97 -0
  229. package/ts/src/builder/roadmapScope.ts +210 -0
  230. package/ts/src/builder/scopePhrases.ts +146 -0
  231. package/ts/src/builder/storyPaths.ts +196 -0
  232. package/ts/src/builder/storyPlanPreauthorization.ts +287 -0
  233. package/ts/src/builder/surfaceProtocol.ts +42 -0
  234. package/ts/src/builder/templateGeneration.ts +148 -0
  235. package/ts/src/builder/transition.ts +178 -0
  236. package/ts/src/consult/args.ts +99 -0
  237. package/ts/src/consult/core.ts +122 -0
  238. package/ts/src/consult/modelCatalog.ts +55 -0
  239. package/ts/src/consult/render.ts +47 -0
  240. package/ts/src/conversation/append.ts +414 -0
  241. package/ts/src/conversation/backfill.ts +387 -0
  242. package/ts/src/conversation/closeTail.ts +467 -0
  243. package/ts/src/conversation/extraction.ts +333 -0
  244. package/ts/src/conversation/extractionDriver.ts +130 -0
  245. package/ts/src/conversation/extractionRun.ts +70 -0
  246. package/ts/src/conversation/journeyInference.ts +165 -0
  247. package/ts/src/conversation/journeyRepair.ts +114 -0
  248. package/ts/src/conversation/lifecycleFaces.ts +197 -0
  249. package/ts/src/conversation/lifecycleWrites.ts +313 -0
  250. package/ts/src/conversation/listing.ts +77 -0
  251. package/ts/src/conversation/logger.ts +576 -0
  252. package/ts/src/conversation/loggerCli.ts +282 -0
  253. package/ts/src/conversation/loggerRuntime.ts +259 -0
  254. package/ts/src/conversation/metadataLifecycle.ts +443 -0
  255. package/ts/src/conversation/recall.ts +66 -0
  256. package/ts/src/conversation/sessionComposites.ts +258 -0
  257. package/ts/src/conversation/sessionImport.ts +90 -0
  258. package/ts/src/conversation/transcriptBackfill.ts +141 -0
  259. package/ts/src/cultivation/applyActions.ts +241 -0
  260. package/ts/src/cultivation/cluster.ts +86 -0
  261. package/ts/src/cultivation/consolidationStore.ts +281 -0
  262. package/ts/src/cultivation/promptContext.ts +62 -0
  263. package/ts/src/cultivation/propose.ts +305 -0
  264. package/ts/src/cultivation/scan.ts +155 -0
  265. package/ts/src/db/backupGate.ts +47 -0
  266. package/ts/src/db/bootstrap.ts +108 -0
  267. package/ts/src/db/bootstrapLock.ts +223 -0
  268. package/ts/src/db/copyGuard.ts +60 -0
  269. package/ts/src/db/database.ts +397 -0
  270. package/ts/src/db/decode.ts +66 -0
  271. package/ts/src/db/ftsIntegrity.ts +41 -0
  272. package/ts/src/db/migrateOnOpen.ts +214 -0
  273. package/ts/src/db/migrations.ts +576 -0
  274. package/ts/src/db/readyOnOpen.ts +35 -0
  275. package/ts/src/db/rowDecode.ts +42 -0
  276. package/ts/src/db/schema.ts +432 -0
  277. package/ts/src/db/schemaInventory.ts +225 -0
  278. package/ts/src/db/schemaInventorySnapshot.ts +2107 -0
  279. package/ts/src/db/schemaState.ts +91 -0
  280. package/ts/src/db/schemaTsDivergence.ts +114 -0
  281. package/ts/src/descriptor/descriptorRead.ts +49 -0
  282. package/ts/src/docs/docsLint.ts +265 -0
  283. package/ts/src/explorer/handoff.ts +522 -0
  284. package/ts/src/explorer/render.ts +335 -0
  285. package/ts/src/explorer/story.ts +769 -0
  286. package/ts/src/explorer/transition.ts +72 -0
  287. package/ts/src/extensions/bindings.ts +170 -0
  288. package/ts/src/extensions/catalog.ts +249 -0
  289. package/ts/src/extensions/catalogCommands.ts +513 -0
  290. package/ts/src/extensions/catalogWrites.ts +652 -0
  291. package/ts/src/extensions/contextRuntime.ts +325 -0
  292. package/ts/src/extensions/dispatch.ts +374 -0
  293. package/ts/src/extensions/errors.ts +24 -0
  294. package/ts/src/extensions/manifest.ts +356 -0
  295. package/ts/src/extensions/migrations.ts +350 -0
  296. package/ts/src/extraction/conversation.ts +304 -0
  297. package/ts/src/extraction/conversationMetadata.ts +206 -0
  298. package/ts/src/extraction/fencing.ts +106 -0
  299. package/ts/src/extraction/json.ts +17 -0
  300. package/ts/src/extraction/prompts.ts +470 -0
  301. package/ts/src/frontDoor/args.ts +30 -0
  302. package/ts/src/frontDoor/argvShape.ts +106 -0
  303. package/ts/src/frontDoor/buildLoadRuntime.ts +139 -0
  304. package/ts/src/frontDoor/buildRoute.ts +398 -0
  305. package/ts/src/frontDoor/cli.ts +1865 -0
  306. package/ts/src/frontDoor/cloneRoleGuard.ts +98 -0
  307. package/ts/src/frontDoor/consultRoute.ts +50 -0
  308. package/ts/src/frontDoor/contentTailRoute.ts +239 -0
  309. package/ts/src/frontDoor/conversationLoggerRoute.ts +73 -0
  310. package/ts/src/frontDoor/cultivationRoute.ts +210 -0
  311. package/ts/src/frontDoor/dbPath.ts +131 -0
  312. package/ts/src/frontDoor/dbSafetyToolsRoute.ts +240 -0
  313. package/ts/src/frontDoor/exploreRoute.ts +572 -0
  314. package/ts/src/frontDoor/extensionCatalogRoute.ts +252 -0
  315. package/ts/src/frontDoor/frontDoorLog.ts +57 -0
  316. package/ts/src/frontDoor/identityWrite.ts +36 -0
  317. package/ts/src/frontDoor/journeyWriteRoute.ts +26 -0
  318. package/ts/src/frontDoor/lifecycleWriteRoute.ts +110 -0
  319. package/ts/src/frontDoor/liveBackup.ts +100 -0
  320. package/ts/src/frontDoor/mirrorModeRoute.ts +163 -0
  321. package/ts/src/frontDoor/namedRefusal.ts +35 -0
  322. package/ts/src/frontDoor/nodeSupport.ts +24 -0
  323. package/ts/src/frontDoor/render/consolidate.ts +199 -0
  324. package/ts/src/frontDoor/render/conversations.ts +33 -0
  325. package/ts/src/frontDoor/render/cultivationShared.ts +21 -0
  326. package/ts/src/frontDoor/render/descriptor.ts +20 -0
  327. package/ts/src/frontDoor/render/detectPersona.ts +50 -0
  328. package/ts/src/frontDoor/render/icons.ts +13 -0
  329. package/ts/src/frontDoor/render/identity.ts +59 -0
  330. package/ts/src/frontDoor/render/identityRows.ts +17 -0
  331. package/ts/src/frontDoor/render/inspectPersona.ts +53 -0
  332. package/ts/src/frontDoor/render/journeyStatus.ts +78 -0
  333. package/ts/src/frontDoor/render/journeys.ts +79 -0
  334. package/ts/src/frontDoor/render/list.ts +41 -0
  335. package/ts/src/frontDoor/render/memories.ts +67 -0
  336. package/ts/src/frontDoor/render/recall.ts +49 -0
  337. package/ts/src/frontDoor/render/shadow.ts +155 -0
  338. package/ts/src/frontDoor/render/tasks.ts +99 -0
  339. package/ts/src/frontDoor/render/tasksImportSync.ts +63 -0
  340. package/ts/src/frontDoor/render/week.ts +113 -0
  341. package/ts/src/frontDoor/routing.ts +999 -0
  342. package/ts/src/frontDoor/runtimeRoute.ts +623 -0
  343. package/ts/src/frontDoor/searchRoute.ts +166 -0
  344. package/ts/src/frontDoor/seedPaths.ts +98 -0
  345. package/ts/src/frontDoor/soulRoute.ts +433 -0
  346. package/ts/src/frontDoor/tasksImportSyncRoute.ts +110 -0
  347. package/ts/src/frontDoor/tasksWriteRoute.ts +101 -0
  348. package/ts/src/frontDoor/usage.ts +176 -0
  349. package/ts/src/guards/claudePlugin.ts +252 -0
  350. package/ts/src/guards/packContents.ts +306 -0
  351. package/ts/src/guards/piSourcedSkills.ts +70 -0
  352. package/ts/src/guards/retiredRouteShadows.ts +57 -0
  353. package/ts/src/guards/retiredSurfaces.ts +564 -0
  354. package/ts/src/hooks/claude.ts +176 -0
  355. package/ts/src/hooks/codex.ts +45 -0
  356. package/ts/src/hooks/gemini.ts +126 -0
  357. package/ts/src/hooks/main.ts +109 -0
  358. package/ts/src/hooks/mirrorState.ts +71 -0
  359. package/ts/src/hooks/payload.ts +68 -0
  360. package/ts/src/hooks/runtime.ts +159 -0
  361. package/ts/src/identity/applyConsolidationIdentityUpdate.ts +67 -0
  362. package/ts/src/identity/descriptorGenerate.ts +162 -0
  363. package/ts/src/identity/identityEdit.ts +143 -0
  364. package/ts/src/identity/identityKey.ts +77 -0
  365. package/ts/src/identity/identityRead.ts +69 -0
  366. package/ts/src/identity/identityStore.ts +80 -0
  367. package/ts/src/identity/journeyListing.ts +53 -0
  368. package/ts/src/identity/personaInspect.ts +44 -0
  369. package/ts/src/identity/personaListing.ts +37 -0
  370. package/ts/src/identity/setIdentity.ts +64 -0
  371. package/ts/src/index.ts +231 -0
  372. package/ts/src/init/init.ts +111 -0
  373. package/ts/src/journey/journeyOptions.ts +131 -0
  374. package/ts/src/journey/journeyRemoval.ts +158 -0
  375. package/ts/src/journey/journeyStatus.ts +149 -0
  376. package/ts/src/journey/journeySyncFile.ts +87 -0
  377. package/ts/src/journey/journeyWrite.ts +203 -0
  378. package/ts/src/journey/parentJourney.ts +41 -0
  379. package/ts/src/journey/validateParentJourney.ts +55 -0
  380. package/ts/src/mcp/boundary.ts +151 -0
  381. package/ts/src/mcp/guards.ts +244 -0
  382. package/ts/src/mcp/main.ts +121 -0
  383. package/ts/src/mcp/payload.ts +141 -0
  384. package/ts/src/mcp/protocol.ts +141 -0
  385. package/ts/src/mcp/readModels.ts +241 -0
  386. package/ts/src/mcp/registry.ts +201 -0
  387. package/ts/src/mcp/serve.ts +120 -0
  388. package/ts/src/mcp/tools/deterministic.ts +137 -0
  389. package/ts/src/mcp/tools/providerCrossing.ts +187 -0
  390. package/ts/src/mcp/wire.ts +8 -0
  391. package/ts/src/memory/journal.ts +180 -0
  392. package/ts/src/memory/listing.ts +107 -0
  393. package/ts/src/memory/memoryWrite.ts +88 -0
  394. package/ts/src/memory/reinforcement.ts +35 -0
  395. package/ts/src/mirror/context.ts +161 -0
  396. package/ts/src/mirror/defaultResolution.ts +199 -0
  397. package/ts/src/mirror/orchestration.ts +364 -0
  398. package/ts/src/mirror/reception.ts +194 -0
  399. package/ts/src/mirror/render.ts +75 -0
  400. package/ts/src/mirror/runtimeSession.ts +205 -0
  401. package/ts/src/mode/operatingMode.ts +148 -0
  402. package/ts/src/observability/callOutcome.ts +115 -0
  403. package/ts/src/observability/inspectLedger.ts +329 -0
  404. package/ts/src/observability/ledgerHooks.ts +126 -0
  405. package/ts/src/observability/llmCalls.ts +154 -0
  406. package/ts/src/persona/detectPersona.ts +81 -0
  407. package/ts/src/planning/promptAssembly.ts +111 -0
  408. package/ts/src/planning/weekPending.ts +81 -0
  409. package/ts/src/planning/weekPlan.ts +183 -0
  410. package/ts/src/planning/weekSave.ts +117 -0
  411. package/ts/src/providers/config.ts +189 -0
  412. package/ts/src/providers/cost.ts +64 -0
  413. package/ts/src/providers/credits.ts +204 -0
  414. package/ts/src/providers/embedding.ts +321 -0
  415. package/ts/src/providers/familyProviders.ts +147 -0
  416. package/ts/src/providers/llm.ts +334 -0
  417. package/ts/src/providers/openrouter.ts +244 -0
  418. package/ts/src/providers/redaction.ts +110 -0
  419. package/ts/src/providers/replay.ts +15 -0
  420. package/ts/src/providers/transport.ts +410 -0
  421. package/ts/src/repair/encodingRepair.ts +253 -0
  422. package/ts/src/runtime/backup.ts +151 -0
  423. package/ts/src/runtime/channel.ts +146 -0
  424. package/ts/src/runtime/config.ts +168 -0
  425. package/ts/src/runtime/diagnose.ts +686 -0
  426. package/ts/src/runtime/git.ts +510 -0
  427. package/ts/src/runtime/installKind.ts +220 -0
  428. package/ts/src/runtime/migrate.ts +154 -0
  429. package/ts/src/runtime/packageIdentity.ts +102 -0
  430. package/ts/src/runtime/releaseNotes.ts +244 -0
  431. package/ts/src/runtime/status.ts +557 -0
  432. package/ts/src/runtime/strategies/clone.ts +200 -0
  433. package/ts/src/runtime/strategies/package.ts +328 -0
  434. package/ts/src/runtime/treeRoot.ts +30 -0
  435. package/ts/src/runtime/update.ts +445 -0
  436. package/ts/src/runtime/updateGate.ts +40 -0
  437. package/ts/src/runtime/updatePipeline.ts +159 -0
  438. package/ts/src/runtime/version.ts +36 -0
  439. package/ts/src/runtime/wiring.ts +130 -0
  440. package/ts/src/search/memorySearch.ts +338 -0
  441. package/ts/src/search/ranker.ts +154 -0
  442. package/ts/src/seed/seed.ts +256 -0
  443. package/ts/src/seed/seedYaml.ts +133 -0
  444. package/ts/src/soul/apply.ts +218 -0
  445. package/ts/src/soul/harvest.ts +267 -0
  446. package/ts/src/soul/prompts/soul_beauty_voice.md +103 -0
  447. package/ts/src/soul/prompts/soul_self_voice.md +219 -0
  448. package/ts/src/soul/prompts/soul_wisdom_voice.md +121 -0
  449. package/ts/src/soul/prompts.ts +85 -0
  450. package/ts/src/soul/render.ts +358 -0
  451. package/ts/src/soul/state.ts +212 -0
  452. package/ts/src/soul/transition.ts +37 -0
  453. package/ts/src/tasks/journeyPathParse.ts +113 -0
  454. package/ts/src/tasks/taskImportSync.ts +142 -0
  455. package/ts/src/tasks/taskStore.ts +312 -0
  456. package/ts/src/tasks/weekView.ts +120 -0
  457. package/ts/src/util/clipCodePoints.ts +26 -0
  458. package/ts/src/util/paths.ts +63 -0
  459. package/ts/src/util/program.ts +9 -0
  460. package/ts/src/util/pyGenerators.ts +144 -0
  461. package/ts/src/util/pythonText.ts +317 -0
  462. package/ts/src/util/semver.ts +45 -0
  463. package/ts/src/util/shellWord.ts +25 -0
  464. package/ts/src/util/slug.ts +64 -0
  465. package/ts/src/welcome/card.ts +339 -0
  466. package/ts/src/welcome/statusLine.ts +101 -0
  467. package/ts/src/welcome/updateCache.ts +129 -0
@@ -0,0 +1,577 @@
1
+ // CV22.DS7.US8 plateau 2 — the Builder delivery cursor.
2
+ //
3
+ // Port of `src/memory/builder/delivery_cursor.py`. This is the row the whole D2
4
+ // decision is about, so the contract is narrower than "behaves the same":
5
+ //
6
+ // **TypeScript must write the bytes Python would write for the same state.**
7
+ //
8
+ // Not as a tidiness preference. `setDeliveryCursor` with an `expectedCursor`
9
+ // writes through a compare-and-swap whose WHERE clause matches on STRING
10
+ // EQUALITY of the serialized metadata. A cursor stored with different bytes for
11
+ // the same state cannot be swapped, so a cursor the Python engine wrote --
12
+ // every database from before CV22.DS10.TS5 carries them -- would fail its first
13
+ // TypeScript lifecycle write. (Until TS5 the same property also kept the
14
+ // `MIRROR_TS_BUILD=0` revert able to swap a cursor TypeScript wrote.)
15
+ // Hence `pythonJsonDumps` (Python's `", "`/`": "` separators) rather than
16
+ // `JSON.stringify`, `ensure_ascii=False` so `ação` stays raw UTF-8, and the key
17
+ // order of Python's `_serialize_cursor` dict reproduced literally.
18
+ //
19
+ // Python's `object()` sentinels for "argument absent" become a discriminated
20
+ // type here. `_KEEP_PREAUTHORIZATION` and `_KEEP_RELEASE_INTENT` distinguish
21
+ // three cases that a nullable field cannot: keep what is stored, set a value, or
22
+ // explicitly clear it. Collapsing them would silently drop a pending
23
+ // authorization receipt on every unrelated cursor write.
24
+ //
25
+ // Every read is lenient by design. A malformed payload — bad JSON, an array, a
26
+ // missing method, a negative generation, a boolean where an int belongs, a
27
+ // `delivery_story` receipt with no children — reads as ABSENT rather than
28
+ // raising. Validating more strictly turns an old row into a crash; validating
29
+ // less turns a corrupt row into authority.
30
+
31
+ import type { Database, WritableDatabase } from "#db/database.ts";
32
+ import {
33
+ compareAndSwapRuntimeSessionMetadata,
34
+ getRuntimeSession,
35
+ upsertRuntimeSession,
36
+ } from "#mirror/runtimeSession.ts";
37
+ import { pythonJsonDumps } from "#util/pyGenerators.ts";
38
+ import { effectiveCadenceProfile } from "./cadence.ts";
39
+
40
+ const CURSOR_SESSION_PREFIX = "__builder_delivery_cursor__:";
41
+
42
+ /** Python `DeliveryCursorConflict`. */
43
+ export class DeliveryCursorConflict extends Error {
44
+ constructor(message: string) {
45
+ super(message);
46
+ this.name = "DeliveryCursorConflict";
47
+ }
48
+ }
49
+
50
+ /** Python `PlanPreauthorizationReceipt`. */
51
+ export interface PlanPreauthorizationReceipt {
52
+ readonly journey: string;
53
+ readonly method: string;
54
+ readonly cursorGeneration: number;
55
+ readonly activeItem: string;
56
+ readonly activeItemLevel: string;
57
+ readonly flowUnit: string;
58
+ readonly childWorkItems: readonly string[];
59
+ readonly planContractVersion: string;
60
+ readonly policy: string;
61
+ readonly stopBoundary: string;
62
+ readonly scopeFingerprint: string;
63
+ readonly status: string;
64
+ readonly reason: string | null;
65
+ }
66
+
67
+ /** Python `BuilderDeliveryCursor`. */
68
+ export interface BuilderDeliveryCursor {
69
+ readonly journey: string;
70
+ readonly method: string;
71
+ readonly activeItem: string | null;
72
+ readonly activeItemTitle: string | null;
73
+ readonly activeItemLevel: string | null;
74
+ readonly activeCheckpoint: string | null;
75
+ readonly pendingConfirmation: string | null;
76
+ readonly lastDeliveryEvent: string | null;
77
+ readonly cadenceProfile: string | null;
78
+ readonly cadenceLimits: readonly string[];
79
+ readonly granularityDecision: string | null;
80
+ readonly navigatorFlowUnit: string | null;
81
+ readonly childWorkItems: readonly string[];
82
+ readonly aggregateCheckpointStatus: readonly string[];
83
+ readonly cursorGeneration: number;
84
+ readonly planPreauthorization: PlanPreauthorizationReceipt | null;
85
+ readonly releaseIntentDeliveryStory: string | null;
86
+ readonly releaseIntent: string | null;
87
+ }
88
+
89
+ /**
90
+ * Python's `_KEEP_*` sentinels, as a type.
91
+ *
92
+ * `{ kind: "keep" }` is the absent argument, `{ kind: "set" }` an explicit value
93
+ * including `null`. The distinction is load-bearing: an unrelated write must
94
+ * PRESERVE a pending receipt, while `plan_preauthorization=None` must clear it.
95
+ */
96
+ export type Keepable<T> = { readonly kind: "keep" } | { readonly kind: "set"; readonly value: T };
97
+
98
+ export const KEEP = { kind: "keep" } as const;
99
+
100
+ export function setTo<T>(value: T): Keepable<T> {
101
+ return { kind: "set", value };
102
+ }
103
+
104
+ const ALLOWED_RELEASE_INTENTS = new Set(["planned", "none", "undecided"]);
105
+ const ALLOWED_RECEIPT_STATUSES = new Set(["pending", "consumed", "invalidated"]);
106
+
107
+ /** Python `_session_id`. */
108
+ export function cursorSessionId(journey: string): string {
109
+ return `${CURSOR_SESSION_PREFIX}${journey}`;
110
+ }
111
+
112
+ function normalizeRequired(value: string, fieldName: string): string {
113
+ const normalized = typeof value === "string" ? value.trim() : "";
114
+ if (!normalized) throw new Error(`${fieldName} must not be empty`);
115
+ return normalized;
116
+ }
117
+
118
+ /** Python `_normalize_optional`: strip, and an empty result is `None`. */
119
+ function normalizeOptional(value: string | null | undefined): string | null {
120
+ if (value === null || value === undefined) return null;
121
+ const normalized = typeof value === "string" ? value.trim() : "";
122
+ return normalized || null;
123
+ }
124
+
125
+ /** Python `_normalize_optional_tuple`: drop entries that normalize away. */
126
+ function normalizeOptionalTuple(values: readonly string[] | undefined): string[] {
127
+ const normalized: string[] = [];
128
+ for (const value of values ?? []) {
129
+ const item = normalizeOptional(value);
130
+ if (item !== null) normalized.push(item);
131
+ }
132
+ return normalized;
133
+ }
134
+
135
+ /** Python `_optional_string`: non-strings read as absent. */
136
+ function optionalString(value: unknown): string | null {
137
+ if (typeof value !== "string") return null;
138
+ const normalized = value.trim();
139
+ return normalized || null;
140
+ }
141
+
142
+ /** Python `_optional_string_tuple`: non-lists read as empty; non-strings drop. */
143
+ function optionalStringTuple(value: unknown): string[] {
144
+ if (!Array.isArray(value)) return [];
145
+ const normalized: string[] = [];
146
+ for (const item of value) {
147
+ if (typeof item === "string" && item.trim()) normalized.push(item.trim());
148
+ }
149
+ return normalized;
150
+ }
151
+
152
+ /**
153
+ * Python `_normalize_nonnegative_int`.
154
+ *
155
+ * The `boolean` guard is REDUNDANT in JavaScript and kept deliberately:
156
+ * `Number.isInteger(true)` is already `false`, whereas Python's `True` IS an
157
+ * `int` and needs the explicit `isinstance(value, bool)` check to be rejected.
158
+ * Mutation testing confirmed the redundancy — removing the guard stayed green —
159
+ * so it documents why Python has a check that looks superfluous here, rather
160
+ * than claiming coverage it does not have.
161
+ */
162
+ function normalizeNonNegativeInt(value: number | null | undefined): number {
163
+ if (value === null || value === undefined) return 0;
164
+ if (typeof value === "boolean" || !Number.isInteger(value) || value < 0) {
165
+ throw new Error("cursor_generation must be a non-negative integer");
166
+ }
167
+ return value;
168
+ }
169
+
170
+ /** Python `_optional_nonnegative_int`: anything invalid reads as 0, no throw. */
171
+ function optionalNonNegativeInt(value: unknown): number {
172
+ if (typeof value === "boolean" || typeof value !== "number") return 0;
173
+ if (!Number.isInteger(value) || value < 0) return 0;
174
+ return value;
175
+ }
176
+
177
+ /** Python `_normalize_release_intent`: refuses an unknown value. */
178
+ function normalizeReleaseIntent(value: string | null): string | null {
179
+ const normalized = normalizeOptional(value);
180
+ if (normalized === null) return null;
181
+ if (!ALLOWED_RELEASE_INTENTS.has(normalized)) {
182
+ throw new Error("release_intent must be planned, none, or undecided");
183
+ }
184
+ return normalized;
185
+ }
186
+
187
+ /** Python `_optional_release_intent`: an unknown value READS as absent. */
188
+ function optionalReleaseIntent(value: unknown): string | null {
189
+ const normalized = optionalString(value);
190
+ return normalized !== null && ALLOWED_RELEASE_INTENTS.has(normalized) ? normalized : null;
191
+ }
192
+
193
+ /**
194
+ * Python `_deserialize_preauthorization`: one composite guard, so any single
195
+ * defect drops the whole receipt.
196
+ */
197
+ function deserializePreauthorization(value: unknown): PlanPreauthorizationReceipt | null {
198
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
199
+ const record = value as Record<string, unknown>;
200
+ const strings = {
201
+ journey: optionalString(record.journey),
202
+ method: optionalString(record.method),
203
+ activeItem: optionalString(record.active_item),
204
+ activeItemLevel: optionalString(record.active_item_level),
205
+ flowUnit: optionalString(record.flow_unit),
206
+ planContractVersion: optionalString(record.plan_contract_version),
207
+ policy: optionalString(record.policy),
208
+ stopBoundary: optionalString(record.stop_boundary),
209
+ scopeFingerprint: optionalString(record.scope_fingerprint),
210
+ };
211
+ const generation = record.cursor_generation;
212
+ const status = optionalString(record.status) ?? "pending";
213
+ const reason = optionalString(record.reason);
214
+ const children = optionalStringTuple(record.child_work_items);
215
+ const generationInvalid =
216
+ typeof generation === "boolean" ||
217
+ typeof generation !== "number" ||
218
+ !Number.isInteger(generation) ||
219
+ generation < 0;
220
+ if (
221
+ Object.values(strings).some((item) => item === null) ||
222
+ generationInvalid ||
223
+ (strings.flowUnit === "delivery_story" && children.length === 0) ||
224
+ !ALLOWED_RECEIPT_STATUSES.has(status)
225
+ ) {
226
+ return null;
227
+ }
228
+ return {
229
+ journey: strings.journey ?? "",
230
+ method: strings.method ?? "",
231
+ cursorGeneration: generation as number,
232
+ activeItem: strings.activeItem ?? "",
233
+ activeItemLevel: strings.activeItemLevel ?? "",
234
+ flowUnit: strings.flowUnit ?? "",
235
+ childWorkItems: children,
236
+ planContractVersion: strings.planContractVersion ?? "",
237
+ policy: strings.policy ?? "",
238
+ stopBoundary: strings.stopBoundary ?? "",
239
+ scopeFingerprint: strings.scopeFingerprint ?? "",
240
+ status,
241
+ reason,
242
+ };
243
+ }
244
+
245
+ /**
246
+ * Python `_serialize_cursor`. The key ORDER is the byte contract, so it is
247
+ * written out literally rather than derived from the interface.
248
+ */
249
+ export function serializeCursor(cursor: BuilderDeliveryCursor): string {
250
+ return pythonJsonDumps({
251
+ method: cursor.method,
252
+ active_item: cursor.activeItem,
253
+ active_item_title: cursor.activeItemTitle,
254
+ active_item_level: cursor.activeItemLevel,
255
+ active_checkpoint: cursor.activeCheckpoint,
256
+ pending_confirmation: cursor.pendingConfirmation,
257
+ last_delivery_event: cursor.lastDeliveryEvent,
258
+ cadence_profile: cursor.cadenceProfile,
259
+ cadence_limits: cursor.cadenceLimits,
260
+ granularity_decision: cursor.granularityDecision,
261
+ navigator_flow_unit: cursor.navigatorFlowUnit,
262
+ child_work_items: cursor.childWorkItems,
263
+ aggregate_checkpoint_status: cursor.aggregateCheckpointStatus,
264
+ cursor_generation: cursor.cursorGeneration,
265
+ plan_preauthorization:
266
+ cursor.planPreauthorization === null ? null : serializeReceipt(cursor.planPreauthorization),
267
+ release_intent_delivery_story: cursor.releaseIntentDeliveryStory,
268
+ release_intent: cursor.releaseIntent,
269
+ });
270
+ }
271
+
272
+ /** Python's `asdict(receipt)` — dataclass field order. */
273
+ function serializeReceipt(receipt: PlanPreauthorizationReceipt): Record<string, unknown> {
274
+ return {
275
+ journey: receipt.journey,
276
+ method: receipt.method,
277
+ cursor_generation: receipt.cursorGeneration,
278
+ active_item: receipt.activeItem,
279
+ active_item_level: receipt.activeItemLevel,
280
+ flow_unit: receipt.flowUnit,
281
+ child_work_items: receipt.childWorkItems,
282
+ plan_contract_version: receipt.planContractVersion,
283
+ policy: receipt.policy,
284
+ stop_boundary: receipt.stopBoundary,
285
+ scope_fingerprint: receipt.scopeFingerprint,
286
+ status: receipt.status,
287
+ reason: receipt.reason,
288
+ };
289
+ }
290
+
291
+ /** Python `get_delivery_cursor`. */
292
+ export function getDeliveryCursor(db: Database, journey: string): BuilderDeliveryCursor | null {
293
+ const normalizedJourney = normalizeRequired(journey, "journey");
294
+ const session = getRuntimeSession(db, cursorSessionId(normalizedJourney));
295
+ if (!session?.active || !session.metadata) return null;
296
+ let data: unknown;
297
+ try {
298
+ data = JSON.parse(session.metadata);
299
+ } catch {
300
+ return null;
301
+ }
302
+ if (typeof data !== "object" || data === null || Array.isArray(data)) return null;
303
+ const record = data as Record<string, unknown>;
304
+ const method = optionalString(record.method);
305
+ if (method === null) return null;
306
+ return {
307
+ journey: normalizedJourney,
308
+ method,
309
+ activeItem: optionalString(record.active_item),
310
+ activeItemTitle: optionalString(record.active_item_title),
311
+ activeItemLevel: optionalString(record.active_item_level),
312
+ activeCheckpoint: optionalString(record.active_checkpoint),
313
+ pendingConfirmation: optionalString(record.pending_confirmation),
314
+ lastDeliveryEvent: optionalString(record.last_delivery_event),
315
+ cadenceProfile: optionalString(record.cadence_profile),
316
+ cadenceLimits: optionalStringTuple(record.cadence_limits),
317
+ granularityDecision: optionalString(record.granularity_decision),
318
+ navigatorFlowUnit: optionalString(record.navigator_flow_unit),
319
+ childWorkItems: optionalStringTuple(record.child_work_items),
320
+ aggregateCheckpointStatus: optionalStringTuple(record.aggregate_checkpoint_status),
321
+ cursorGeneration: optionalNonNegativeInt(record.cursor_generation),
322
+ planPreauthorization: deserializePreauthorization(record.plan_preauthorization),
323
+ releaseIntentDeliveryStory: optionalString(record.release_intent_delivery_story),
324
+ releaseIntent: optionalReleaseIntent(record.release_intent),
325
+ };
326
+ }
327
+
328
+ export interface SetDeliveryCursorOptions {
329
+ readonly journey: string;
330
+ readonly method: string;
331
+ readonly activeItem?: string | null;
332
+ readonly activeItemTitle?: string | null;
333
+ readonly activeItemLevel?: string | null;
334
+ readonly activeCheckpoint?: string | null;
335
+ readonly pendingConfirmation?: string | null;
336
+ readonly lastDeliveryEvent?: string | null;
337
+ readonly cadenceProfile?: string | null;
338
+ readonly cadenceLimits?: readonly string[];
339
+ readonly granularityDecision?: string | null;
340
+ readonly navigatorFlowUnit?: string | null;
341
+ readonly childWorkItems?: readonly string[];
342
+ readonly aggregateCheckpointStatus?: readonly string[];
343
+ readonly cursorGeneration?: number | null;
344
+ readonly planPreauthorization?: Keepable<PlanPreauthorizationReceipt | null>;
345
+ readonly expectedCursor?: BuilderDeliveryCursor | null;
346
+ readonly releaseIntentDeliveryStory?: Keepable<string | null>;
347
+ readonly releaseIntent?: Keepable<string | null>;
348
+ }
349
+
350
+ export interface CursorWriteDeps {
351
+ readonly nowIso: () => string;
352
+ }
353
+
354
+ /**
355
+ * Python `set_delivery_cursor`.
356
+ *
357
+ * Two subtleties a port loses:
358
+ *
359
+ * * `cursor_generation=None` with an EXISTING row carries the stored
360
+ * generation forward; with no row it normalizes to 0. So an ordinary write
361
+ * never resets the generation, and only an explicit value bumps it.
362
+ * * receipt invalidation runs ONLY when the receipt argument was absent. A
363
+ * write that sets the receipt explicitly is trusted as-is, which is how a
364
+ * freshly recorded receipt survives the very write that records it.
365
+ */
366
+ export function setDeliveryCursor(
367
+ db: WritableDatabase,
368
+ options: SetDeliveryCursorOptions,
369
+ deps: CursorWriteDeps,
370
+ ): BuilderDeliveryCursor {
371
+ const normalizedJourney = normalizeRequired(options.journey, "journey");
372
+ const normalizedMethod = normalizeRequired(options.method, "method");
373
+ const previous = getDeliveryCursor(db, normalizedJourney);
374
+
375
+ const generationGiven =
376
+ options.cursorGeneration !== undefined && options.cursorGeneration !== null;
377
+ const resolvedGeneration =
378
+ !generationGiven && previous !== null
379
+ ? previous.cursorGeneration
380
+ : normalizeNonNegativeInt(options.cursorGeneration ?? null);
381
+
382
+ const receiptArgument = options.planPreauthorization ?? KEEP;
383
+ const receiptWasExplicit = receiptArgument.kind === "set";
384
+ const resolvedReceipt = receiptWasExplicit
385
+ ? receiptArgument.value
386
+ : (previous?.planPreauthorization ?? null);
387
+
388
+ const releaseStoryArgument = options.releaseIntentDeliveryStory ?? KEEP;
389
+ const resolvedReleaseStory =
390
+ releaseStoryArgument.kind === "set"
391
+ ? releaseStoryArgument.value
392
+ : (previous?.releaseIntentDeliveryStory ?? null);
393
+
394
+ const releaseIntentArgument = options.releaseIntent ?? KEEP;
395
+ const resolvedReleaseIntent =
396
+ releaseIntentArgument.kind === "set"
397
+ ? releaseIntentArgument.value
398
+ : (previous?.releaseIntent ?? null);
399
+
400
+ let cursor: BuilderDeliveryCursor = {
401
+ journey: normalizedJourney,
402
+ method: normalizedMethod,
403
+ activeItem: normalizeOptional(options.activeItem),
404
+ activeItemTitle: normalizeOptional(options.activeItemTitle),
405
+ activeItemLevel: normalizeOptional(options.activeItemLevel),
406
+ activeCheckpoint: normalizeOptional(options.activeCheckpoint),
407
+ pendingConfirmation: normalizeOptional(options.pendingConfirmation),
408
+ lastDeliveryEvent: normalizeOptional(options.lastDeliveryEvent),
409
+ cadenceProfile: normalizeOptional(options.cadenceProfile),
410
+ cadenceLimits: normalizeOptionalTuple(options.cadenceLimits),
411
+ granularityDecision: normalizeOptional(options.granularityDecision),
412
+ navigatorFlowUnit: normalizeOptional(options.navigatorFlowUnit),
413
+ childWorkItems: normalizeOptionalTuple(options.childWorkItems),
414
+ aggregateCheckpointStatus: normalizeOptionalTuple(options.aggregateCheckpointStatus),
415
+ cursorGeneration: resolvedGeneration,
416
+ planPreauthorization: resolvedReceipt,
417
+ releaseIntentDeliveryStory: normalizeOptional(resolvedReleaseStory),
418
+ releaseIntent: normalizeReleaseIntent(resolvedReleaseIntent),
419
+ };
420
+
421
+ if (!receiptWasExplicit) {
422
+ cursor = {
423
+ ...cursor,
424
+ planPreauthorization: invalidateForCoordinateChange(previous, cursor),
425
+ };
426
+ }
427
+
428
+ const metadata = serializeCursor(cursor);
429
+ const now = deps.nowIso();
430
+
431
+ if (options.expectedCursor !== undefined && options.expectedCursor !== null) {
432
+ if (options.expectedCursor.journey !== normalizedJourney) {
433
+ throw new Error("expected cursor journey does not match");
434
+ }
435
+ const swapped = compareAndSwapRuntimeSessionMetadata(
436
+ db,
437
+ cursorSessionId(normalizedJourney),
438
+ { expectedMetadata: serializeCursor(options.expectedCursor), metadata },
439
+ now,
440
+ );
441
+ if (!swapped) {
442
+ throw new DeliveryCursorConflict("delivery cursor changed before atomic update");
443
+ }
444
+ } else {
445
+ upsertRuntimeSession(
446
+ db,
447
+ cursorSessionId(normalizedJourney),
448
+ {
449
+ interface: "builder_delivery_cursor",
450
+ journey: normalizedJourney,
451
+ active: true,
452
+ metadata,
453
+ },
454
+ now,
455
+ );
456
+ }
457
+
458
+ return cursor;
459
+ }
460
+
461
+ /** Python `clear_delivery_cursor`. */
462
+ export function clearDeliveryCursor(
463
+ db: WritableDatabase,
464
+ journey: string,
465
+ deps: CursorWriteDeps,
466
+ ): void {
467
+ const normalizedJourney = normalizeRequired(journey, "journey");
468
+ upsertRuntimeSession(
469
+ db,
470
+ cursorSessionId(normalizedJourney),
471
+ {
472
+ interface: "builder_delivery_cursor",
473
+ journey: normalizedJourney,
474
+ active: false,
475
+ metadata: null,
476
+ },
477
+ deps.nowIso(),
478
+ );
479
+ }
480
+
481
+ /**
482
+ * Python `render_delivery_cursor_sync_report`: plain text, no card frame.
483
+ *
484
+ * Note the DEFAULTS shown for two fields that are `None` on the cursor:
485
+ * `cadence profile` prints `stepwise` and `navigator flow unit` prints
486
+ * `story_by_story` when unset, so the report shows the effective value rather
487
+ * than the stored one. Every other absent field prints `none`. The cadence's
488
+ * default is `cadence.ts`'s, which every orienting surface prints too (CR115).
489
+ */
490
+ export function renderDeliveryCursorSyncReport(cursor: BuilderDeliveryCursor): string {
491
+ return `${[
492
+ "■ Builder Delivery Cursor Synced",
493
+ "",
494
+ "journey",
495
+ cursor.journey,
496
+ "",
497
+ "method",
498
+ cursor.method,
499
+ "",
500
+ "active item",
501
+ cursor.activeItem || "none",
502
+ "",
503
+ "active item title",
504
+ cursor.activeItemTitle || "none",
505
+ "",
506
+ "active item level",
507
+ cursor.activeItemLevel || "none",
508
+ "",
509
+ "cadence profile",
510
+ effectiveCadenceProfile(cursor),
511
+ "",
512
+ "cadence limits",
513
+ cursor.cadenceLimits.length > 0 ? cursor.cadenceLimits.join(", ") : "none",
514
+ "",
515
+ "navigator flow unit",
516
+ cursor.navigatorFlowUnit || "story_by_story",
517
+ "",
518
+ "child work items",
519
+ cursor.childWorkItems.length > 0 ? cursor.childWorkItems.join(", ") : "none",
520
+ "",
521
+ "aggregate checkpoint status",
522
+ cursor.aggregateCheckpointStatus.length > 0
523
+ ? cursor.aggregateCheckpointStatus.join(", ")
524
+ : "none",
525
+ "",
526
+ "active checkpoint",
527
+ cursor.activeCheckpoint || "none",
528
+ "",
529
+ "pending confirmation",
530
+ cursor.pendingConfirmation || "none",
531
+ "",
532
+ "last delivery event",
533
+ cursor.lastDeliveryEvent || "none",
534
+ "",
535
+ "boundary",
536
+ "No story lifecycle work was executed.",
537
+ ].join("\n")}\n`;
538
+ }
539
+
540
+ /**
541
+ * Python `_invalidate_for_coordinate_change`: a PENDING receipt is invalidated
542
+ * with the FIRST matching reason, in Python's order. A consumed or invalidated
543
+ * receipt is left alone, and child scope compares as a SET — so reordering
544
+ * children is not a change, while adding or removing one is.
545
+ */
546
+ export function invalidateForCoordinateChange(
547
+ previous: BuilderDeliveryCursor | null,
548
+ current: BuilderDeliveryCursor,
549
+ ): PlanPreauthorizationReceipt | null {
550
+ const receipt = current.planPreauthorization;
551
+ if (previous === null || receipt === null || receipt.status !== "pending") return receipt;
552
+ let reason: string | null = null;
553
+ if (previous.cursorGeneration !== current.cursorGeneration) {
554
+ reason = "cursor_generation_changed";
555
+ } else if (previous.activeItem !== current.activeItem) {
556
+ reason = "active_item_changed";
557
+ } else if (previous.activeItemLevel !== current.activeItemLevel) {
558
+ reason = "active_item_level_changed";
559
+ } else if (previous.navigatorFlowUnit !== current.navigatorFlowUnit) {
560
+ reason = "flow_unit_changed";
561
+ } else if (!sameSet(previous.childWorkItems, current.childWorkItems)) {
562
+ reason = "child_scope_changed";
563
+ }
564
+ if (reason === null) return receipt;
565
+ return { ...receipt, status: "invalidated", reason };
566
+ }
567
+
568
+ /** Python `set(a) != set(b)`. */
569
+ function sameSet(left: readonly string[], right: readonly string[]): boolean {
570
+ const leftSet = new Set(left);
571
+ const rightSet = new Set(right);
572
+ if (leftSet.size !== rightSet.size) return false;
573
+ for (const value of leftSet) {
574
+ if (!rightSet.has(value)) return false;
575
+ }
576
+ return true;
577
+ }