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,408 @@
1
+ // CV22.DS7.US8 plateau 3 — Plan: the checkpoint that blocks implementation.
2
+ //
3
+ // Port of `plan_lifecycle_item` and `render_plan_checkpoint` from
4
+ // `src/memory/builder/lifecycle.py`.
5
+ //
6
+ // Three behaviors carry the meaning, and two of them are invisible in the surface:
7
+ //
8
+ // 1. **The receipt argument is always EXPLICIT.** Plan passes the receipt it
9
+ // resolved — new, or the existing one — which bypasses the cursor's
10
+ // coordinate-change invalidation. That is what lets a freshly recorded receipt
11
+ // survive the very write that records it; passing the KEEP sentinel here would
12
+ // invalidate it on arrival.
13
+ // 2. **The generation does NOT advance.** Plan is a checkpoint on the item Pull
14
+ // committed to, and a bump would invalidate the receipt it just wrote.
15
+ // 3. **The scaffold is composed here, from one vocabulary (CR111 D3).** Plan fills
16
+ // `PRODUCT_PLAN` with the caller's title, else the title Pull recorded, else the
17
+ // item's code (D5), and names the siblings the caller read from the roadmap as
18
+ // Non-Goals. A caller may set only the objective (`--objective`). Python took each
19
+ // section as an argument and fell back, per field, to a second set of sentences
20
+ // no product path wrote; the recorded corpus graded that set.
21
+ //
22
+ // CR111: the card prints what the runtime knows, and no section of the plan. It used
23
+ // to print the scaffold's sentences as the plan -- scope, acceptance, validation,
24
+ // checked off -- even over a plan.md a person had written. It now names the story
25
+ // files, each with its state and the sections it still needs, in the rows `build show`
26
+ // prints, judged once here, right after the write, so the command and the recorded
27
+ // corpus print the same card.
28
+ //
29
+ // CR082: the card names the package relative to the project, and the four `*_path=`
30
+ // lines, the agent's, stay absolute and print below the surface's end marker (D1).
31
+
32
+ import { dirname } from "node:path";
33
+ import type { WritableDatabase } from "#db/database.ts";
34
+ import { STORY_SCOPED_COMMIT_RULE, writeStoryPackage } from "./artifacts/planArtifacts.ts";
35
+ import {
36
+ fill,
37
+ fillPlanVocabulary,
38
+ type PlanVocabulary,
39
+ PRODUCT_PLAN,
40
+ SIBLING_NON_GOAL,
41
+ } from "./artifacts/scaffoldSections.ts";
42
+ import type { ArtifactVerdict } from "./artifacts/scaffoldState.ts";
43
+ import { judgeStoryFiles, storyFileLines } from "./artifacts/storyFiles.ts";
44
+ import { cardPrefixed, cardText, cardWrapped } from "./card.ts";
45
+ import { isImplementableByDefault, normalizeRequired } from "./cursorTransitions.ts";
46
+ import {
47
+ type BuilderDeliveryCursor,
48
+ type CursorWriteDeps,
49
+ getDeliveryCursor,
50
+ type PlanPreauthorizationReceipt,
51
+ setDeliveryCursor,
52
+ setTo,
53
+ } from "./deliveryCursor.ts";
54
+ import { effectiveNavigatorFlowUnit, FLOW_UNIT_STORY_BY_STORY } from "./flowUnit.ts";
55
+ import { LifecycleRefusal, refuseIfAlreadyComplete } from "./lifecycleRefusal.ts";
56
+ import { renderLifecycleRibbon } from "./lifecycleRibbon.ts";
57
+ import type { ContractDefinition, MethodDefinition } from "./methodDefinition.ts";
58
+ import {
59
+ createPlanPreauthorizationReceipt,
60
+ PREAUTHORIZATION_STOP,
61
+ STORY_PLAN_CONTRACT,
62
+ } from "./planPreauthorization.ts";
63
+ import { displayPath } from "./projectPaths.ts";
64
+ import { wrapAriadSurface } from "./surfaceProtocol.ts";
65
+
66
+ /** Python `BuilderPlanReport`. */
67
+ export interface BuilderPlanReport {
68
+ readonly journey: string;
69
+ readonly method: string;
70
+ readonly activeItem: string;
71
+ readonly activeItemTitle: string | null;
72
+ readonly activeItemLevel: string | null;
73
+ readonly implementableByDefault: boolean;
74
+ readonly planContract: ContractDefinition;
75
+ readonly implementContract: ContractDefinition;
76
+ readonly validationContract: ContractDefinition;
77
+ readonly localRules: readonly string[];
78
+ readonly cursor: BuilderDeliveryCursor;
79
+ readonly planArtifactPath: string | null;
80
+ /** The project Plan wrote under: the card names the package relative to it (CR082). */
81
+ readonly projectRoot: string | null;
82
+ /**
83
+ * The package's `index.md`, `plan.md`, and `test-guide.md`, judged right after Plan
84
+ * wrote what was missing (CR111); `null` when the journey has no project, so nothing
85
+ * was written.
86
+ */
87
+ readonly storyFiles: readonly ArtifactVerdict[] | null;
88
+ /**
89
+ * An `--objective` given where nothing is written: with no project there is no
90
+ * plan.md to hold it, and the card says so rather than drop it silently (CR111's
91
+ * handoff review, finding 2).
92
+ */
93
+ readonly unrecordedObjective: boolean;
94
+ readonly preauthorizationRecorded: boolean;
95
+ readonly nextEvent: string;
96
+ }
97
+
98
+ /** Python `_contract_for`: a missing contract is a method-definition defect, not a default. */
99
+ function contractFor(method: MethodDefinition, contractId: string): ContractDefinition {
100
+ const contract = method.contracts.find((candidate) => candidate.id === contractId);
101
+ if (contract === undefined) {
102
+ throw new Error(`method ${method.id} is missing contract ${contractId}`);
103
+ }
104
+ return contract;
105
+ }
106
+
107
+ /**
108
+ * Python `_cadence_preauthorizes_story_plan`.
109
+ *
110
+ * Keyed on the profile's POLICY, not on its name. `accelerated` is the profile that
111
+ * carries `bounded_story_authority` today, but a method that renames it or adds
112
+ * another profile with the same policy gets the same behavior — which is why the
113
+ * Python test asserts the policy rather than the profile id.
114
+ */
115
+ function cadencePreauthorizesStoryPlan(
116
+ method: MethodDefinition,
117
+ cadenceProfile: string | null,
118
+ ): boolean {
119
+ if (cadenceProfile === null) return false;
120
+ const profile = method.cadenceProfiles.find((candidate) => candidate.id === cadenceProfile);
121
+ return profile !== undefined && profile.planApprovalPolicy === "bounded_story_authority";
122
+ }
123
+
124
+ export interface PlanOptions {
125
+ readonly journey: string;
126
+ readonly method: MethodDefinition;
127
+ /** `--objective`: the one section a caller may write. Authored by construction. */
128
+ readonly objective?: string | null;
129
+ /**
130
+ * The item's title as the roadmap names it. Without one, the scaffold takes the
131
+ * title Pull recorded on the cursor, and only then the item's code (CR111 D5).
132
+ */
133
+ readonly title?: string | null;
134
+ /** The titles of the item's siblings in the roadmap, named as Non-Goals. */
135
+ readonly siblings?: readonly string[];
136
+ readonly localRules?: readonly string[];
137
+ readonly planArtifactPath?: string | null;
138
+ /** The project the package must stay inside (CR079). */
139
+ readonly projectRoot?: string | null;
140
+ readonly preauthorize?: boolean;
141
+ readonly stopBoundary?: string;
142
+ }
143
+
144
+ /**
145
+ * The sentences Plan writes where no file exists: `PRODUCT_PLAN`, filled with the
146
+ * caller's title, else the title Pull recorded, else the item's code (CR111 D5), with
147
+ * the roadmap's siblings as Non-Goals and the caller's objective when it gave one.
148
+ */
149
+ function composeScaffold(
150
+ options: PlanOptions,
151
+ recordedTitle: string | null,
152
+ code: string,
153
+ ): PlanVocabulary {
154
+ const vocabulary = fillPlanVocabulary(PRODUCT_PLAN, {
155
+ title: options.title || recordedTitle || code,
156
+ });
157
+ const siblings = (options.siblings ?? []).map((sibling) =>
158
+ fill(SIBLING_NON_GOAL, { title: sibling }),
159
+ );
160
+ return {
161
+ ...vocabulary,
162
+ objective: options.objective || vocabulary.objective,
163
+ nonGoals: siblings.length > 0 ? siblings : vocabulary.nonGoals,
164
+ };
165
+ }
166
+
167
+ /** Python `plan_lifecycle_item`. */
168
+ export function planLifecycleItem(
169
+ db: WritableDatabase,
170
+ options: PlanOptions,
171
+ deps: CursorWriteDeps,
172
+ ): BuilderPlanReport {
173
+ const journey = normalizeRequired(options.journey, "journey");
174
+ const method = options.method;
175
+
176
+ const existing = getDeliveryCursor(db, journey);
177
+ if (existing === null) throw new Error("delivery cursor is required before plan");
178
+ if (!existing.activeItem) throw new Error("active item is required before plan");
179
+ refuseIfAlreadyComplete("plan", existing);
180
+ if (existing.lastDeliveryEvent !== "prepare") {
181
+ throw new LifecycleRefusal("plan", "not_reached", "Prepare must be completed before Plan");
182
+ }
183
+
184
+ const implementable = isImplementableByDefault(existing.activeItemLevel);
185
+ if (!implementable) {
186
+ throw new LifecycleRefusal(
187
+ "plan",
188
+ "not_reached",
189
+ "Plan requires a User Story or Technical Story; expand the Delivery Story first",
190
+ );
191
+ }
192
+
193
+ const stopBoundary = options.stopBoundary ?? PREAUTHORIZATION_STOP;
194
+ const artifactPath = options.planArtifactPath ?? null;
195
+ let flowUnit = existing.navigatorFlowUnit;
196
+ let receipt: PlanPreauthorizationReceipt | null = existing.planPreauthorization;
197
+ const preauthorizationRecorded =
198
+ (options.preauthorize ?? false) ||
199
+ cadencePreauthorizesStoryPlan(method, existing.cadenceProfile);
200
+
201
+ if (preauthorizationRecorded) {
202
+ flowUnit = effectiveNavigatorFlowUnit(existing).flowUnit;
203
+ if (flowUnit !== FLOW_UNIT_STORY_BY_STORY) {
204
+ // Reachable only through a cursor written before CR117, when Pull carried a Delivery
205
+ // Story's unit to the story after it: the remedy is the Pull that no longer carries.
206
+ throw new Error(
207
+ "story Plan preauthorization requires story_by_story flow; the cursor holds " +
208
+ "delivery_story from a Delivery Story pulled before this story. Pull the story " +
209
+ "again to start it in story_by_story flow.",
210
+ );
211
+ }
212
+ receipt = createPlanPreauthorizationReceipt(
213
+ { ...existing, navigatorFlowUnit: flowUnit },
214
+ {
215
+ method: method.id,
216
+ planContractVersion: STORY_PLAN_CONTRACT,
217
+ stopBoundary,
218
+ },
219
+ );
220
+ }
221
+
222
+ const cursor = setDeliveryCursor(
223
+ db,
224
+ {
225
+ journey,
226
+ method: method.id,
227
+ activeItem: existing.activeItem,
228
+ activeItemTitle: existing.activeItemTitle,
229
+ activeItemLevel: existing.activeItemLevel,
230
+ activeCheckpoint: "after_plan",
231
+ pendingConfirmation: "navigator_approval",
232
+ lastDeliveryEvent: "plan",
233
+ cadenceProfile: existing.cadenceProfile,
234
+ cadenceLimits: existing.cadenceLimits,
235
+ granularityDecision: null,
236
+ navigatorFlowUnit: flowUnit,
237
+ childWorkItems: existing.childWorkItems,
238
+ aggregateCheckpointStatus: existing.aggregateCheckpointStatus,
239
+ cursorGeneration: existing.cursorGeneration,
240
+ planPreauthorization: setTo(receipt),
241
+ },
242
+ deps,
243
+ );
244
+
245
+ const report: Omit<BuilderPlanReport, "storyFiles" | "unrecordedObjective"> = {
246
+ journey,
247
+ method: method.id,
248
+ activeItem: existing.activeItem,
249
+ activeItemTitle: existing.activeItemTitle,
250
+ activeItemLevel: existing.activeItemLevel,
251
+ implementableByDefault: implementable,
252
+ planContract: contractFor(method, "plan_contract"),
253
+ implementContract: contractFor(method, "implement_contract"),
254
+ validationContract: contractFor(method, "validation_contract"),
255
+ localRules: options.localRules ?? [],
256
+ cursor,
257
+ planArtifactPath: artifactPath,
258
+ projectRoot: options.projectRoot ?? null,
259
+ preauthorizationRecorded,
260
+ nextEvent: "implement",
261
+ };
262
+
263
+ if (artifactPath !== null) {
264
+ const scaffold = composeScaffold(options, existing.activeItemTitle, existing.activeItem);
265
+ writeStoryPackage(dirname(artifactPath), options.projectRoot, {
266
+ activeItem: report.activeItem,
267
+ activeItemTitle: report.activeItemTitle,
268
+ activeItemLevel: report.activeItemLevel,
269
+ ...scaffold,
270
+ localRules: report.localRules,
271
+ implementContract: report.implementContract,
272
+ activeCheckpoint: cursor.activeCheckpoint,
273
+ pendingConfirmation: cursor.pendingConfirmation,
274
+ });
275
+ }
276
+ // Judged after the write, so the verdict describes the files as they now stand:
277
+ // the scaffold Plan just wrote, or the file a person wrote before it (CR111).
278
+ const storyFiles = artifactPath === null ? null : judgeStoryFiles(dirname(artifactPath));
279
+ return {
280
+ ...report,
281
+ storyFiles,
282
+ unrecordedObjective: artifactPath === null && Boolean(options.objective),
283
+ };
284
+ }
285
+
286
+ /** Python `_granularity_message`. */
287
+ function granularityMessage(report: BuilderPlanReport): string {
288
+ return report.implementableByDefault
289
+ ? `${report.activeItemLevel ?? "item"} is implementable by default.`
290
+ : "Delivery Story must expand into User Stories and/or Technical Stories before Plan.";
291
+ }
292
+
293
+ /**
294
+ * Python `_plan_next_action`, told by the file (CR111). The words are the skill's:
295
+ * the Driver authors and presents, the Navigator approves. None of them names passing
296
+ * the structural check as the goal. With no project nothing was written, so there is
297
+ * no file to name and the line stays what it was.
298
+ */
299
+ function planNextAction(report: BuilderPlanReport): string {
300
+ if (report.storyFiles === null) {
301
+ return report.preauthorizationRecorded
302
+ ? "Driver completes Plan and consumes bounded authority."
303
+ : "Navigator approves the Plan or requests changes.";
304
+ }
305
+ const authored = report.storyFiles.find((file) => file.name === "plan.md")?.state === "authored";
306
+ if (report.preauthorizationRecorded) {
307
+ return authored
308
+ ? "Driver consumes bounded authority."
309
+ : "Driver authors plan.md, then consumes bounded authority.";
310
+ }
311
+ return authored
312
+ ? "Navigator reads plan.md and approves it or requests changes."
313
+ : "Driver authors plan.md and presents it; the Navigator approves it or requests changes.";
314
+ }
315
+
316
+ /**
317
+ * The story files block: `build show`'s rows for the same files. With no project
318
+ * nothing was written; the package row says why, and this one says what that cost.
319
+ */
320
+ function storyFilesBlock(report: BuilderPlanReport): string[] {
321
+ if (report.storyFiles !== null) return report.storyFiles.flatMap(storyFileLines);
322
+ return cardWrapped(
323
+ report.unrecordedObjective
324
+ ? "none written, so the --objective given was recorded nowhere"
325
+ : "none written",
326
+ );
327
+ }
328
+
329
+ /** Python `_plan_boundary`. */
330
+ function planBoundary(report: BuilderPlanReport): string {
331
+ return report.preauthorizationRecorded
332
+ ? "No additional Navigator approval turn is expected."
333
+ : "Implementation remains blocked until approval.";
334
+ }
335
+
336
+ /**
337
+ * Python `render_plan_checkpoint`, without the plan (CR111).
338
+ *
339
+ * The card prints no section of the plan: not the objective, scope, non-goals,
340
+ * acceptance, or validation, which it used to fill from the scaffold's sentences.
341
+ * In their place it names the story files, each with its state and the sections it
342
+ * still needs, in the rows `build show` prints. The implementation contract stays:
343
+ * its lines are Ariad's rules and the project's own, true whatever the plan says.
344
+ *
345
+ * The card names the package relative to the project, as every card names a path
346
+ * (CR082). The four `*_path=` lines are the agent's: absolute, because an agent often
347
+ * works from a directory that is not the project, and printed only when an artifact
348
+ * path exists. They follow the surface's end marker, where the transport rule that
349
+ * copies a surface into the reply does not carry the owner's home directory (D1), the
350
+ * way `build load` prints `project_path=` outside its card.
351
+ */
352
+ export function renderPlanCheckpoint(report: BuilderPlanReport): string {
353
+ const written = report.planArtifactPath !== null;
354
+ const packagePath = written ? dirname(report.planArtifactPath as string) : "not written";
355
+
356
+ const card = [
357
+ "Delivery",
358
+ renderLifecycleRibbon("plan", report.activeItemLevel),
359
+ "",
360
+ "╭────────────────────────────────────────────────────────╮",
361
+ "│ 🧭■ PLAN CHECKPOINT │",
362
+ "│ │",
363
+ cardText("item"),
364
+ cardText(`🟦[${report.activeItem}]`),
365
+ cardText(`level: ${report.activeItemLevel ?? "unknown"}`),
366
+ "│ │",
367
+ cardText("story package"),
368
+ ...cardWrapped(
369
+ written
370
+ ? displayPath(packagePath, report.projectRoot)
371
+ : "none: the journey has no project path",
372
+ ),
373
+ "│ │",
374
+ cardText("story files"),
375
+ ...storyFilesBlock(report),
376
+ "│ │",
377
+ cardText("granularity"),
378
+ ...cardWrapped(granularityMessage(report)),
379
+ "│ │",
380
+ cardText("implementation contract"),
381
+ ...cardWrapped("TDD/characterization tests when behavior is testable."),
382
+ ...cardWrapped("Keep changes scoped to the active story."),
383
+ ...cardWrapped(STORY_SCOPED_COMMIT_RULE),
384
+ // A project without a guide adds nothing, not the card's usual `none` (CR019).
385
+ ...(report.localRules.length > 0 ? cardPrefixed(report.localRules, "✓") : []),
386
+ "│ │",
387
+ cardText("approval gate"),
388
+ cardText(`checkpoint: ${report.cursor.activeCheckpoint}`),
389
+ cardText(`pending: ${report.cursor.pendingConfirmation}`),
390
+ "│ │",
391
+ cardText("next action"),
392
+ ...cardWrapped(planNextAction(report)),
393
+ "│ │",
394
+ cardText("boundary"),
395
+ ...cardWrapped(planBoundary(report)),
396
+ "╰────────────────────────────────────────────────────────╯",
397
+ ].join("\n");
398
+
399
+ const trailer = written
400
+ ? [
401
+ `story_package_path=${packagePath}\n`,
402
+ `index_artifact_path=${packagePath}/index.md\n`,
403
+ `plan_artifact_path=${report.planArtifactPath as string}\n`,
404
+ `test_guide_artifact_path=${packagePath}/test-guide.md\n`,
405
+ ].join("")
406
+ : "";
407
+ return wrapAriadSurface("plan_checkpoint", card) + trailer;
408
+ }
@@ -0,0 +1,265 @@
1
+ // CV22.DS7.US8 plateau 3 — bounded authority for conditional Plan approval.
2
+ //
3
+ // Port of `src/memory/builder/plan_preauthorization.py`.
4
+ //
5
+ // This is the security-sensitive module of the plateau. A receipt lets the Driver
6
+ // approve its own Plan once, without another Navigator turn, so every property
7
+ // that bounds it is load-bearing:
8
+ //
9
+ // * the receipt is **payload-free** — it carries coordinates and a hash, never
10
+ // Plan prose, so it cannot smuggle scope past the Navigator;
11
+ // * it is **generation-bound**, so any cursor movement invalidates it;
12
+ // * it is **single-use**, consumed atomically by a compare-and-swap;
13
+ // * a mismatch **falls back to ordinary approval** rather than failing open.
14
+ //
15
+ // The fingerprint is an equality test on authority. If the two engines
16
+ // canonicalize its payload differently, a receipt Python recorded cannot be
17
+ // consumed by TypeScript — the Navigator is asked to approve a Plan they already
18
+ // authorized, which is the safe direction but still a defect. It never fails the
19
+ // other way, because a hash mismatch can only refuse.
20
+
21
+ import { createHash } from "node:crypto";
22
+
23
+ import type { WritableDatabase } from "#db/database.ts";
24
+ import { pythonJsonDumpsCanonical } from "#util/pyGenerators.ts";
25
+ import { pyStrip, sortByCodePoint } from "#util/pythonText.ts";
26
+ import {
27
+ type BuilderDeliveryCursor,
28
+ type CursorWriteDeps,
29
+ DeliveryCursorConflict,
30
+ type PlanPreauthorizationReceipt,
31
+ setDeliveryCursor,
32
+ setTo,
33
+ } from "./deliveryCursor.ts";
34
+
35
+ /** Python module constants. */
36
+ export const PREAUTHORIZATION_POLICY = "exact_scope";
37
+ export const PREAUTHORIZATION_STOP = "navigator_validation";
38
+ export const DELIVERY_STORY_PLAN_CONTRACT = "delivery_story_plan@1";
39
+ export const STORY_PLAN_CONTRACT = "story_plan@1";
40
+
41
+ /** Python `PlanPreauthorizationMismatch`. */
42
+ export class PlanPreauthorizationMismatch extends Error {
43
+ readonly reason: string;
44
+ /**
45
+ * For `plan_incomplete`, the sections still to author (CR112). The surface names
46
+ * them; the receipt keeps only the reason token, so a persisted cursor reads back as
47
+ * it always did.
48
+ */
49
+ readonly sections: readonly string[];
50
+
51
+ constructor(reason: string, sections: readonly string[] = []) {
52
+ super(reason);
53
+ this.name = "PlanPreauthorizationMismatch";
54
+ this.reason = reason;
55
+ this.sections = sections;
56
+ }
57
+ }
58
+
59
+ export interface ScopeFingerprintInput {
60
+ readonly journey: string;
61
+ readonly method: string;
62
+ readonly cursorGeneration: number;
63
+ readonly activeItem: string;
64
+ readonly activeItemLevel: string;
65
+ readonly flowUnit: string;
66
+ readonly childWorkItems: readonly string[];
67
+ readonly planContractVersion: string;
68
+ readonly stopBoundary: string;
69
+ }
70
+
71
+ /** Python `scope_fingerprint`: sha256 over canonical sorted-compact ASCII JSON. */
72
+ export function scopeFingerprint(input: ScopeFingerprintInput): string {
73
+ const payload = {
74
+ journey: input.journey,
75
+ method: input.method,
76
+ cursor_generation: input.cursorGeneration,
77
+ active_item: input.activeItem,
78
+ active_item_level: input.activeItemLevel,
79
+ flow_unit: input.flowUnit,
80
+ child_work_items: [...input.childWorkItems],
81
+ plan_contract_version: input.planContractVersion,
82
+ policy: PREAUTHORIZATION_POLICY,
83
+ stop_boundary: input.stopBoundary,
84
+ };
85
+ return createHash("sha256").update(pythonJsonDumpsCanonical(payload), "utf8").digest("hex");
86
+ }
87
+
88
+ /**
89
+ * Python `canonical_child_scope`: compare as a SET, store deterministically.
90
+ *
91
+ * Sorted and de-duplicated, so child ORDER is presentational while addition or
92
+ * removal is a mismatch — the distinction the Delivery Story flow depends on.
93
+ */
94
+ export function canonicalChildScope(items: readonly string[]): string[] {
95
+ const trimmed = new Set<string>();
96
+ for (const item of items) {
97
+ const normalized = pyStrip(item);
98
+ if (normalized) trimmed.add(normalized);
99
+ }
100
+ return sortByCodePoint([...trimmed]);
101
+ }
102
+
103
+ export interface CreateReceiptOptions {
104
+ readonly method: string;
105
+ readonly planContractVersion: string;
106
+ readonly childWorkItems?: readonly string[];
107
+ readonly stopBoundary?: string;
108
+ }
109
+
110
+ /** Python `create_plan_preauthorization_receipt`. */
111
+ export function createPlanPreauthorizationReceipt(
112
+ cursor: BuilderDeliveryCursor,
113
+ options: CreateReceiptOptions,
114
+ ): PlanPreauthorizationReceipt {
115
+ if (!cursor.activeItem || !cursor.activeItemLevel || !cursor.navigatorFlowUnit) {
116
+ throw new Error("complete active Plan coordinates are required");
117
+ }
118
+ const stopBoundary = options.stopBoundary ?? PREAUTHORIZATION_STOP;
119
+ if (stopBoundary !== PREAUTHORIZATION_STOP) {
120
+ throw new Error("unsupported Plan preauthorization stop boundary");
121
+ }
122
+ const children = canonicalChildScope(options.childWorkItems ?? []);
123
+ return {
124
+ journey: cursor.journey,
125
+ method: options.method,
126
+ cursorGeneration: cursor.cursorGeneration,
127
+ activeItem: cursor.activeItem,
128
+ activeItemLevel: cursor.activeItemLevel,
129
+ flowUnit: cursor.navigatorFlowUnit,
130
+ childWorkItems: children,
131
+ planContractVersion: options.planContractVersion,
132
+ policy: PREAUTHORIZATION_POLICY,
133
+ stopBoundary,
134
+ scopeFingerprint: scopeFingerprint({
135
+ journey: cursor.journey,
136
+ method: options.method,
137
+ cursorGeneration: cursor.cursorGeneration,
138
+ activeItem: cursor.activeItem,
139
+ activeItemLevel: cursor.activeItemLevel,
140
+ flowUnit: cursor.navigatorFlowUnit,
141
+ childWorkItems: children,
142
+ planContractVersion: options.planContractVersion,
143
+ stopBoundary,
144
+ }),
145
+ status: "pending",
146
+ reason: null,
147
+ };
148
+ }
149
+
150
+ export interface MismatchInput {
151
+ readonly journey: string;
152
+ readonly method: string;
153
+ readonly flowUnit: string;
154
+ readonly childWorkItems: readonly string[];
155
+ readonly planContractVersion: string;
156
+ readonly unfilledSections?: readonly string[];
157
+ }
158
+
159
+ /**
160
+ * Python `plan_preauthorization_mismatch_reason`: ONE bounded reason, or null.
161
+ *
162
+ * The checks run in Python's order and the FIRST failure wins, because the reason
163
+ * is Navigator-visible: a receipt that is both stale and cross-level reports
164
+ * `cursor_generation_changed`, not `active_item_changed`. The fingerprint is
165
+ * verified last, after the fields it hashes, so a tampered receipt whose fields
166
+ * still agree is caught by the hash rather than by a field comparison.
167
+ */
168
+ export function planPreauthorizationMismatchReason(
169
+ cursor: BuilderDeliveryCursor,
170
+ input: MismatchInput,
171
+ ): string | null {
172
+ const receipt = cursor.planPreauthorization;
173
+ if (receipt === null) return "authorization_missing";
174
+ if (receipt.status !== "pending") return receipt.reason || "authorization_not_pending";
175
+
176
+ const expectedChildren = canonicalChildScope(input.childWorkItems);
177
+ const checks: [boolean, string][] = [
178
+ [receipt.journey === input.journey, "journey_changed"],
179
+ [receipt.method === input.method, "method_changed"],
180
+ [receipt.cursorGeneration === cursor.cursorGeneration, "cursor_generation_changed"],
181
+ [receipt.activeItem === cursor.activeItem, "active_item_changed"],
182
+ [receipt.activeItemLevel === cursor.activeItemLevel, "active_item_level_changed"],
183
+ // Python's chained comparison: the receipt, the cursor, and the caller's flow
184
+ // unit must ALL agree, not just the receipt and the caller.
185
+ [
186
+ receipt.flowUnit === cursor.navigatorFlowUnit && cursor.navigatorFlowUnit === input.flowUnit,
187
+ "flow_unit_changed",
188
+ ],
189
+ [sameScope(receipt.childWorkItems, expectedChildren), "child_scope_changed"],
190
+ [receipt.planContractVersion === input.planContractVersion, "plan_contract_changed"],
191
+ [receipt.policy === PREAUTHORIZATION_POLICY, "policy_changed"],
192
+ [receipt.stopBoundary === PREAUTHORIZATION_STOP, "stop_boundary_changed"],
193
+ ];
194
+ for (const [matches, reason] of checks) {
195
+ if (!matches) return reason;
196
+ }
197
+
198
+ const expectedFingerprint = scopeFingerprint({
199
+ journey: receipt.journey,
200
+ method: receipt.method,
201
+ cursorGeneration: receipt.cursorGeneration,
202
+ activeItem: receipt.activeItem,
203
+ activeItemLevel: receipt.activeItemLevel,
204
+ flowUnit: receipt.flowUnit,
205
+ childWorkItems: receipt.childWorkItems,
206
+ planContractVersion: receipt.planContractVersion,
207
+ stopBoundary: receipt.stopBoundary,
208
+ });
209
+ if (receipt.scopeFingerprint !== expectedFingerprint) return "scope_fingerprint_changed";
210
+ if ((input.unfilledSections ?? []).length > 0) return "plan_incomplete";
211
+ return null;
212
+ }
213
+
214
+ function sameScope(left: readonly string[], right: readonly string[]): boolean {
215
+ return left.length === right.length && left.every((item, index) => item === right[index]);
216
+ }
217
+
218
+ /**
219
+ * Python `invalidate_plan_preauthorization`.
220
+ *
221
+ * Swallows a `DeliveryCursorConflict` and returns the cursor unchanged: this runs
222
+ * on a refusal path, and failing to record WHY authority was refused must not turn
223
+ * a clean refusal into an error.
224
+ */
225
+ export function invalidatePlanPreauthorization(
226
+ db: WritableDatabase,
227
+ cursor: BuilderDeliveryCursor,
228
+ reason: string,
229
+ deps: CursorWriteDeps,
230
+ ): BuilderDeliveryCursor {
231
+ const receipt = cursor.planPreauthorization;
232
+ if (receipt === null || receipt.status !== "pending") return cursor;
233
+ try {
234
+ return setDeliveryCursor(
235
+ db,
236
+ {
237
+ journey: cursor.journey,
238
+ method: cursor.method,
239
+ activeItem: cursor.activeItem,
240
+ activeItemTitle: cursor.activeItemTitle,
241
+ activeItemLevel: cursor.activeItemLevel,
242
+ activeCheckpoint: cursor.activeCheckpoint,
243
+ pendingConfirmation: cursor.pendingConfirmation,
244
+ lastDeliveryEvent: cursor.lastDeliveryEvent,
245
+ cadenceProfile: cursor.cadenceProfile,
246
+ cadenceLimits: cursor.cadenceLimits,
247
+ granularityDecision: cursor.granularityDecision,
248
+ navigatorFlowUnit: cursor.navigatorFlowUnit,
249
+ childWorkItems: cursor.childWorkItems,
250
+ aggregateCheckpointStatus: cursor.aggregateCheckpointStatus,
251
+ cursorGeneration: cursor.cursorGeneration,
252
+ planPreauthorization: setTo({ ...receipt, status: "invalidated", reason }),
253
+ expectedCursor: cursor,
254
+ },
255
+ deps,
256
+ );
257
+ } catch (error) {
258
+ if (error instanceof DeliveryCursorConflict) return cursor;
259
+ throw error;
260
+ }
261
+ }
262
+
263
+ // The sections a Plan approval requires, `PLAN_APPROVAL_SECTIONS`, and the reader that
264
+ // judges them live in `artifacts/` since CR111: one answer for every surface that says
265
+ // what a `plan.md` still needs, approval included.