@tacuchi/agent-workflow-cli 25.8.0 → 25.9.0

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 (502) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +5 -1
  3. package/dist/adapters/git-cli.js +225 -17
  4. package/dist/adapters/git-cli.js.map +1 -1
  5. package/dist/adapters/node-file-system.js +48 -0
  6. package/dist/adapters/node-file-system.js.map +1 -1
  7. package/dist/application/amend-service.js +51 -14
  8. package/dist/application/amend-service.js.map +1 -1
  9. package/dist/application/branch-resolver.js +6 -3
  10. package/dist/application/branch-resolver.js.map +1 -1
  11. package/dist/application/capability/design-handler.js +124 -43
  12. package/dist/application/capability/design-handler.js.map +1 -1
  13. package/dist/application/capability/dispatcher.js +4 -1
  14. package/dist/application/capability/dispatcher.js.map +1 -1
  15. package/dist/application/capability/durable-effect.js +3 -0
  16. package/dist/application/capability/durable-effect.js.map +1 -1
  17. package/dist/application/check-branch-service.js +158 -19
  18. package/dist/application/check-branch-service.js.map +1 -1
  19. package/dist/application/checkpoint/markdown.js +2 -2
  20. package/dist/application/checkpoint/markdown.js.map +1 -1
  21. package/dist/application/checkpoint/state-reader.js +12 -0
  22. package/dist/application/checkpoint/state-reader.js.map +1 -1
  23. package/dist/application/checkpoint-service.js +12 -4
  24. package/dist/application/checkpoint-service.js.map +1 -1
  25. package/dist/application/checkpoint-write-service.js +91 -17
  26. package/dist/application/checkpoint-write-service.js.map +1 -1
  27. package/dist/application/claims-ledger.js +137 -11
  28. package/dist/application/claims-ledger.js.map +1 -1
  29. package/dist/application/claims-recovery.js +85 -44
  30. package/dist/application/claims-recovery.js.map +1 -1
  31. package/dist/application/database-tool-catalog.js +33 -0
  32. package/dist/application/database-tool-catalog.js.map +1 -1
  33. package/dist/application/design/consumer-document.js +2 -2
  34. package/dist/application/design/consumer-document.js.map +1 -1
  35. package/dist/application/design/design-content-gate-service.js +20 -2
  36. package/dist/application/design/design-content-gate-service.js.map +1 -1
  37. package/dist/application/design/design-gate-service.js +2 -0
  38. package/dist/application/design/design-gate-service.js.map +1 -1
  39. package/dist/application/design/design-index-service.js +4 -0
  40. package/dist/application/design/design-index-service.js.map +1 -1
  41. package/dist/application/design/design-publish-service.js +1 -4
  42. package/dist/application/design/design-publish-service.js.map +1 -1
  43. package/dist/application/design/design-record-service.js +13 -7
  44. package/dist/application/design/design-record-service.js.map +1 -1
  45. package/dist/application/design/design-resolver-service.js +22 -3
  46. package/dist/application/design/design-resolver-service.js.map +1 -1
  47. package/dist/application/design/design-simple-service.js +24 -5
  48. package/dist/application/design/design-simple-service.js.map +1 -1
  49. package/dist/application/design/digest.js +41 -0
  50. package/dist/application/design/digest.js.map +1 -0
  51. package/dist/application/dev-only-services.js +166 -20
  52. package/dist/application/dev-only-services.js.map +1 -1
  53. package/dist/application/doc-branch-ledger.js +145 -0
  54. package/dist/application/doc-branch-ledger.js.map +1 -0
  55. package/dist/application/docs-canon-service.js +1 -1
  56. package/dist/application/docs-canon-service.js.map +1 -1
  57. package/dist/application/doctor/dist-integrity.js +113 -0
  58. package/dist/application/doctor/dist-integrity.js.map +1 -0
  59. package/dist/application/doctor/hosts.js +5 -2
  60. package/dist/application/doctor/hosts.js.map +1 -1
  61. package/dist/application/doctor/prepare.js +4 -1
  62. package/dist/application/doctor/prepare.js.map +1 -1
  63. package/dist/application/doctor/provider-installation.js +39 -19
  64. package/dist/application/doctor/provider-installation.js.map +1 -1
  65. package/dist/application/doctor/provider-mcps.js +9 -1
  66. package/dist/application/doctor/provider-mcps.js.map +1 -1
  67. package/dist/application/doctor/provider-skills.js +25 -0
  68. package/dist/application/doctor/provider-skills.js.map +1 -1
  69. package/dist/application/doctor/provider-visibility.js +76 -3
  70. package/dist/application/doctor/provider-visibility.js.map +1 -1
  71. package/dist/application/doctor/provider-workspace-block.js +156 -0
  72. package/dist/application/doctor/provider-workspace-block.js.map +1 -0
  73. package/dist/application/doctor/repair-runner.js +47 -3
  74. package/dist/application/doctor/repair-runner.js.map +1 -1
  75. package/dist/application/doctor/report.js +24 -4
  76. package/dist/application/doctor/report.js.map +1 -1
  77. package/dist/application/doctor/skills-toml-migrate.js +76 -0
  78. package/dist/application/doctor/skills-toml-migrate.js.map +1 -0
  79. package/dist/application/export-catalog-check.js +200 -0
  80. package/dist/application/export-catalog-check.js.map +1 -0
  81. package/dist/application/export-service.js +716 -79
  82. package/dist/application/export-service.js.map +1 -1
  83. package/dist/application/fix-git-service.js +285 -64
  84. package/dist/application/fix-git-service.js.map +1 -1
  85. package/dist/application/flow/advance.js +202 -25
  86. package/dist/application/flow/advance.js.map +1 -1
  87. package/dist/application/flow/annul-service.js +3 -1
  88. package/dist/application/flow/annul-service.js.map +1 -1
  89. package/dist/application/flow/batch-commit.js +189 -0
  90. package/dist/application/flow/batch-commit.js.map +1 -0
  91. package/dist/application/flow/checkout-observation.js +63 -10
  92. package/dist/application/flow/checkout-observation.js.map +1 -1
  93. package/dist/application/flow/close-artifacts.js +105 -0
  94. package/dist/application/flow/close-artifacts.js.map +1 -0
  95. package/dist/application/flow/close-at-boundary.js +25 -14
  96. package/dist/application/flow/close-at-boundary.js.map +1 -1
  97. package/dist/application/flow/close-document-guidance.js +70 -0
  98. package/dist/application/flow/close-document-guidance.js.map +1 -0
  99. package/dist/application/flow/flow-service.js +46 -3
  100. package/dist/application/flow/flow-service.js.map +1 -1
  101. package/dist/application/flow/internal-actions.js +300 -82
  102. package/dist/application/flow/internal-actions.js.map +1 -1
  103. package/dist/application/flow/internal-drive.js +24 -1
  104. package/dist/application/flow/internal-drive.js.map +1 -1
  105. package/dist/application/flow/plan-entry.js +82 -0
  106. package/dist/application/flow/plan-entry.js.map +1 -0
  107. package/dist/application/flow/prove.js +32 -1
  108. package/dist/application/flow/prove.js.map +1 -1
  109. package/dist/application/flow/quick-checkouts.js +48 -0
  110. package/dist/application/flow/quick-checkouts.js.map +1 -0
  111. package/dist/application/flow/reinfer-batch.js +177 -0
  112. package/dist/application/flow/reinfer-batch.js.map +1 -0
  113. package/dist/application/flow/reopen-run.js +12 -5
  114. package/dist/application/flow/reopen-run.js.map +1 -1
  115. package/dist/application/flow/run-state-service.js +2 -0
  116. package/dist/application/flow/run-state-service.js.map +1 -1
  117. package/dist/application/flow/submit.js +583 -116
  118. package/dist/application/flow/submit.js.map +1 -1
  119. package/dist/application/generate-launch-service.js +16 -6
  120. package/dist/application/generate-launch-service.js.map +1 -1
  121. package/dist/application/git-flow-service.js +34 -3
  122. package/dist/application/git-flow-service.js.map +1 -1
  123. package/dist/application/history-publications.js +24 -0
  124. package/dist/application/history-publications.js.map +1 -1
  125. package/dist/application/history-reconcile-service.js +45 -0
  126. package/dist/application/history-reconcile-service.js.map +1 -0
  127. package/dist/application/history-table.js +61 -12
  128. package/dist/application/history-table.js.map +1 -1
  129. package/dist/application/history-update-service.js +25 -4
  130. package/dist/application/history-update-service.js.map +1 -1
  131. package/dist/application/hook-branch-check.js +25 -3
  132. package/dist/application/hook-branch-check.js.map +1 -1
  133. package/dist/application/hook-git-commit-advisor.js +3 -4
  134. package/dist/application/hook-git-commit-advisor.js.map +1 -1
  135. package/dist/application/hook-turn-start.js +51 -0
  136. package/dist/application/hook-turn-start.js.map +1 -0
  137. package/dist/application/host-memory/codex.js +1 -1
  138. package/dist/application/host-memory/codex.js.map +1 -1
  139. package/dist/application/hub-registry.js +135 -0
  140. package/dist/application/hub-registry.js.map +1 -0
  141. package/dist/application/local-proposal.js +72 -19
  142. package/dist/application/local-proposal.js.map +1 -1
  143. package/dist/application/mcp-host-reader.js +1 -1
  144. package/dist/application/mcp-host-reader.js.map +1 -1
  145. package/dist/application/mcp-host-writer.js +16 -10
  146. package/dist/application/mcp-host-writer.js.map +1 -1
  147. package/dist/application/merge-state-service.js +150 -34
  148. package/dist/application/merge-state-service.js.map +1 -1
  149. package/dist/application/multiroot/claude.js +17 -10
  150. package/dist/application/multiroot/claude.js.map +1 -1
  151. package/dist/application/multiroot/codex.js +17 -11
  152. package/dist/application/multiroot/codex.js.map +1 -1
  153. package/dist/application/multiroot-service.js +52 -28
  154. package/dist/application/multiroot-service.js.map +1 -1
  155. package/dist/application/parsers/execution-batches.js +64 -0
  156. package/dist/application/parsers/execution-batches.js.map +1 -0
  157. package/dist/application/parsers/phases.js +23 -2
  158. package/dist/application/parsers/phases.js.map +1 -1
  159. package/dist/application/parsers/project-block.js +146 -17
  160. package/dist/application/parsers/project-block.js.map +1 -1
  161. package/dist/application/parsers/tasks.js +16 -2
  162. package/dist/application/parsers/tasks.js.map +1 -1
  163. package/dist/application/parsers/toml.js +19 -0
  164. package/dist/application/parsers/toml.js.map +1 -0
  165. package/dist/application/paths-service.js +13 -16
  166. package/dist/application/paths-service.js.map +1 -1
  167. package/dist/application/persist-service.js +118 -5
  168. package/dist/application/persist-service.js.map +1 -1
  169. package/dist/application/plan-exec-batch-service.js +162 -22
  170. package/dist/application/plan-exec-batch-service.js.map +1 -1
  171. package/dist/application/plan-exec-decision-service.js +61 -12
  172. package/dist/application/plan-exec-decision-service.js.map +1 -1
  173. package/dist/application/plan-exec-plan-diff.js +105 -0
  174. package/dist/application/plan-exec-plan-diff.js.map +1 -0
  175. package/dist/application/plan-lint-service.js +2 -0
  176. package/dist/application/plan-lint-service.js.map +1 -1
  177. package/dist/application/plan-passes.js +41 -0
  178. package/dist/application/plan-passes.js.map +1 -0
  179. package/dist/application/preparation-receipts.js +58 -0
  180. package/dist/application/preparation-receipts.js.map +1 -0
  181. package/dist/application/project-md-upsert-service.js +191 -52
  182. package/dist/application/project-md-upsert-service.js.map +1 -1
  183. package/dist/application/project-tab-data.js +40 -10
  184. package/dist/application/project-tab-data.js.map +1 -1
  185. package/dist/application/release-data/artifacts.js +15 -7
  186. package/dist/application/release-data/artifacts.js.map +1 -1
  187. package/dist/application/release-data/bundles.js +52 -6
  188. package/dist/application/release-data/bundles.js.map +1 -1
  189. package/dist/application/release-data/common.js +2 -2
  190. package/dist/application/release-data/common.js.map +1 -1
  191. package/dist/application/release-data/rollback.js +7 -0
  192. package/dist/application/release-data/rollback.js.map +1 -0
  193. package/dist/application/release-data/sessions.js +2 -2
  194. package/dist/application/release-data/sessions.js.map +1 -1
  195. package/dist/application/release-pass-ledger.js +1 -1
  196. package/dist/application/render/history-row.js +2 -0
  197. package/dist/application/render/history-row.js.map +1 -1
  198. package/dist/application/render/project-block.js +28 -11
  199. package/dist/application/render/project-block.js.map +1 -1
  200. package/dist/application/reseal-service.js +1 -0
  201. package/dist/application/reseal-service.js.map +1 -1
  202. package/dist/application/resume-service.js +55 -8
  203. package/dist/application/resume-service.js.map +1 -1
  204. package/dist/application/retirement/apply.js +25 -7
  205. package/dist/application/retirement/apply.js.map +1 -1
  206. package/dist/application/retirement/attribution.js +121 -48
  207. package/dist/application/retirement/attribution.js.map +1 -1
  208. package/dist/application/retirement/prepare.js +17 -11
  209. package/dist/application/retirement/prepare.js.map +1 -1
  210. package/dist/application/retirement/preview.js +2 -1
  211. package/dist/application/retirement/preview.js.map +1 -1
  212. package/dist/application/self/codex-plugin.js +1 -1
  213. package/dist/application/self/codex-plugin.js.map +1 -1
  214. package/dist/application/self/host-states.js +3 -3
  215. package/dist/application/self/host-states.js.map +1 -1
  216. package/dist/application/self/install-hooks.js +1 -1
  217. package/dist/application/self/install-hooks.js.map +1 -1
  218. package/dist/application/self/mcp-config.js +19 -11
  219. package/dist/application/self/mcp-config.js.map +1 -1
  220. package/dist/application/self/opencode-plugin.js +1 -0
  221. package/dist/application/self/opencode-plugin.js.map +1 -1
  222. package/dist/application/self/uninstall.js +1 -1
  223. package/dist/application/self/uninstall.js.map +1 -1
  224. package/dist/application/semantic-operation/protocol.js +66 -5
  225. package/dist/application/semantic-operation/protocol.js.map +1 -1
  226. package/dist/application/session-archive-service.js +113 -0
  227. package/dist/application/session-archive-service.js.map +1 -0
  228. package/dist/application/session-binding-service.js +35 -0
  229. package/dist/application/session-binding-service.js.map +1 -1
  230. package/dist/application/session-close-service.js +390 -54
  231. package/dist/application/session-close-service.js.map +1 -1
  232. package/dist/application/session-create-service.js +97 -5
  233. package/dist/application/session-create-service.js.map +1 -1
  234. package/dist/application/session-custody-recorder.js +15 -12
  235. package/dist/application/session-custody-recorder.js.map +1 -1
  236. package/dist/application/session-custody-service.js +1 -0
  237. package/dist/application/session-custody-service.js.map +1 -1
  238. package/dist/application/session-narrative.js +42 -12
  239. package/dist/application/session-narrative.js.map +1 -1
  240. package/dist/application/session-pause-service.js +46 -0
  241. package/dist/application/session-pause-service.js.map +1 -0
  242. package/dist/application/session-resolver.js +36 -11
  243. package/dist/application/session-resolver.js.map +1 -1
  244. package/dist/application/session-resume-service.js +134 -13
  245. package/dist/application/session-resume-service.js.map +1 -1
  246. package/dist/application/sessions-service.js +4 -0
  247. package/dist/application/sessions-service.js.map +1 -1
  248. package/dist/application/sessions-sweep-service.js +77 -0
  249. package/dist/application/sessions-sweep-service.js.map +1 -0
  250. package/dist/application/settle-service.js +3 -1
  251. package/dist/application/settle-service.js.map +1 -1
  252. package/dist/application/skills-resolver-service.js +1 -1
  253. package/dist/application/skills-resolver-service.js.map +1 -1
  254. package/dist/application/source-add-service.js +37 -0
  255. package/dist/application/source-add-service.js.map +1 -0
  256. package/dist/application/source-boundary-policy.js +97 -8
  257. package/dist/application/source-boundary-policy.js.map +1 -1
  258. package/dist/application/source-pipeline.js +68 -0
  259. package/dist/application/source-pipeline.js.map +1 -0
  260. package/dist/application/source-remove-service.js +15 -3
  261. package/dist/application/source-remove-service.js.map +1 -1
  262. package/dist/application/sources-service.js +71 -16
  263. package/dist/application/sources-service.js.map +1 -1
  264. package/dist/application/status-service.js +55 -5
  265. package/dist/application/status-service.js.map +1 -1
  266. package/dist/application/unit-dependencies.js +100 -0
  267. package/dist/application/unit-dependencies.js.map +1 -0
  268. package/dist/application/unit-membership.js +37 -0
  269. package/dist/application/unit-membership.js.map +1 -0
  270. package/dist/application/unit-residue.js +72 -0
  271. package/dist/application/unit-residue.js.map +1 -0
  272. package/dist/application/visibility-doctor-service.js +66 -4
  273. package/dist/application/visibility-doctor-service.js.map +1 -1
  274. package/dist/application/working-branch-service.js +6 -0
  275. package/dist/application/working-branch-service.js.map +1 -1
  276. package/dist/application/workline-graph.js +1 -1
  277. package/dist/application/workline-graph.js.map +1 -1
  278. package/dist/application/workline-index-service.js +118 -17
  279. package/dist/application/workline-index-service.js.map +1 -1
  280. package/dist/application/workspace-commit-service.js +218 -0
  281. package/dist/application/workspace-commit-service.js.map +1 -0
  282. package/dist/application/workspace-init-service.js +64 -34
  283. package/dist/application/workspace-init-service.js.map +1 -1
  284. package/dist/application/workspace-local-config.js +90 -0
  285. package/dist/application/workspace-local-config.js.map +1 -0
  286. package/dist/application/workspace-materialization-service.js +52 -3
  287. package/dist/application/workspace-materialization-service.js.map +1 -1
  288. package/dist/application/workspace-migrate/apply.js +525 -4
  289. package/dist/application/workspace-migrate/apply.js.map +1 -1
  290. package/dist/application/workspace-migrate/markers.js +9 -6
  291. package/dist/application/workspace-migrate/markers.js.map +1 -1
  292. package/dist/application/workspace-migrate/plan.js +97 -5
  293. package/dist/application/workspace-migrate/plan.js.map +1 -1
  294. package/dist/application/workspace-move-service.js +270 -0
  295. package/dist/application/workspace-move-service.js.map +1 -0
  296. package/dist/application/workspace-untrack-service.js +28 -0
  297. package/dist/application/workspace-untrack-service.js.map +1 -0
  298. package/dist/application/worktree-service.js +654 -105
  299. package/dist/application/worktree-service.js.map +1 -1
  300. package/dist/cli/ascii.js +2 -0
  301. package/dist/cli/ascii.js.map +1 -1
  302. package/dist/cli/commands/add-source.js +38 -0
  303. package/dist/cli/commands/add-source.js.map +1 -0
  304. package/dist/cli/commands/amend.js +14 -3
  305. package/dist/cli/commands/amend.js.map +1 -1
  306. package/dist/cli/commands/capability.js +11 -0
  307. package/dist/cli/commands/capability.js.map +1 -1
  308. package/dist/cli/commands/designs.js +14 -5
  309. package/dist/cli/commands/designs.js.map +1 -1
  310. package/dist/cli/commands/dev-only.js +19 -6
  311. package/dist/cli/commands/dev-only.js.map +1 -1
  312. package/dist/cli/commands/doc-branch.js +184 -0
  313. package/dist/cli/commands/doc-branch.js.map +1 -0
  314. package/dist/cli/commands/doctor.js +5 -1
  315. package/dist/cli/commands/doctor.js.map +1 -1
  316. package/dist/cli/commands/export.js +78 -18
  317. package/dist/cli/commands/export.js.map +1 -1
  318. package/dist/cli/commands/fix-git.js +89 -14
  319. package/dist/cli/commands/fix-git.js.map +1 -1
  320. package/dist/cli/commands/flow.js +120 -15
  321. package/dist/cli/commands/flow.js.map +1 -1
  322. package/dist/cli/commands/generate-launch.js +14 -2
  323. package/dist/cli/commands/generate-launch.js.map +1 -1
  324. package/dist/cli/commands/history-update.js +1 -1
  325. package/dist/cli/commands/history-update.js.map +1 -1
  326. package/dist/cli/commands/history.js +14 -0
  327. package/dist/cli/commands/history.js.map +1 -0
  328. package/dist/cli/commands/hook.js +15 -2
  329. package/dist/cli/commands/hook.js.map +1 -1
  330. package/dist/cli/commands/index.js +18 -1
  331. package/dist/cli/commands/index.js.map +1 -1
  332. package/dist/cli/commands/merge-state.js +2 -2
  333. package/dist/cli/commands/merge-state.js.map +1 -1
  334. package/dist/cli/commands/persist.js +6 -5
  335. package/dist/cli/commands/persist.js.map +1 -1
  336. package/dist/cli/commands/project-md-upsert.js +5 -3
  337. package/dist/cli/commands/project-md-upsert.js.map +1 -1
  338. package/dist/cli/commands/release-pass.js +3 -3
  339. package/dist/cli/commands/release-pass.js.map +1 -1
  340. package/dist/cli/commands/resume.js +12 -3
  341. package/dist/cli/commands/resume.js.map +1 -1
  342. package/dist/cli/commands/self.js +7 -2
  343. package/dist/cli/commands/self.js.map +1 -1
  344. package/dist/cli/commands/session-close.js +64 -9
  345. package/dist/cli/commands/session-close.js.map +1 -1
  346. package/dist/cli/commands/session-create.js +4 -2
  347. package/dist/cli/commands/session-create.js.map +1 -1
  348. package/dist/cli/commands/session-pause.js +14 -0
  349. package/dist/cli/commands/session-pause.js.map +1 -0
  350. package/dist/cli/commands/sessions.js +16 -3
  351. package/dist/cli/commands/sessions.js.map +1 -1
  352. package/dist/cli/commands/set-branch.js +28 -4
  353. package/dist/cli/commands/set-branch.js.map +1 -1
  354. package/dist/cli/commands/set-edit-mode.js +20 -0
  355. package/dist/cli/commands/set-edit-mode.js.map +1 -0
  356. package/dist/cli/commands/set-pipeline.js +35 -0
  357. package/dist/cli/commands/set-pipeline.js.map +1 -0
  358. package/dist/cli/commands/sources.js +4 -0
  359. package/dist/cli/commands/sources.js.map +1 -1
  360. package/dist/cli/commands/status.js +35 -2
  361. package/dist/cli/commands/status.js.map +1 -1
  362. package/dist/cli/commands/unknown-flags.js +1 -0
  363. package/dist/cli/commands/unknown-flags.js.map +1 -1
  364. package/dist/cli/commands/visibility.js +1 -0
  365. package/dist/cli/commands/visibility.js.map +1 -1
  366. package/dist/cli/commands/workspace-commit.js +62 -0
  367. package/dist/cli/commands/workspace-commit.js.map +1 -0
  368. package/dist/cli/commands/workspace-init.js +39 -2
  369. package/dist/cli/commands/workspace-init.js.map +1 -1
  370. package/dist/cli/commands/workspace-migrate.js +31 -3
  371. package/dist/cli/commands/workspace-migrate.js.map +1 -1
  372. package/dist/cli/commands/workspace-move.js +42 -0
  373. package/dist/cli/commands/workspace-move.js.map +1 -0
  374. package/dist/cli/commands/worktree.js +4 -4
  375. package/dist/cli/commands/worktree.js.map +1 -1
  376. package/dist/cli/context-id.js +6 -0
  377. package/dist/cli/context-id.js.map +1 -1
  378. package/dist/cli/full-cli.js +555 -0
  379. package/dist/cli/full-cli.js.map +1 -0
  380. package/dist/cli/help-groups.js +75 -16
  381. package/dist/cli/help-groups.js.map +1 -1
  382. package/dist/cli/hook-stdin-cache.js +12 -0
  383. package/dist/cli/hook-stdin-cache.js.map +1 -0
  384. package/dist/cli/main.js +47 -453
  385. package/dist/cli/main.js.map +1 -1
  386. package/dist/cli/parser.js +4 -0
  387. package/dist/cli/parser.js.map +1 -1
  388. package/dist/cli/tui/app.js +1 -1
  389. package/dist/cli/tui/app.js.map +1 -1
  390. package/dist/cli/tui/components/workspace-init-form.js +4 -1
  391. package/dist/cli/tui/components/workspace-init-form.js.map +1 -1
  392. package/dist/cli/tui/data/recommended-skills.js +2 -2
  393. package/dist/cli/tui/data/recommended-skills.js.map +1 -1
  394. package/dist/cli/tui/data/workflow-content.js +6 -1
  395. package/dist/cli/tui/data/workflow-content.js.map +1 -1
  396. package/dist/cli/tui/tabs/mcp-tab.js +4 -3
  397. package/dist/cli/tui/tabs/mcp-tab.js.map +1 -1
  398. package/dist/cli/tui/tabs/project-tab.js +7 -2
  399. package/dist/cli/tui/tabs/project-tab.js.map +1 -1
  400. package/dist/dist-manifest.json +4117 -0
  401. package/dist/domain/capability/protocol.js.map +1 -1
  402. package/dist/domain/decision-note.js +13 -0
  403. package/dist/domain/decision-note.js.map +1 -1
  404. package/dist/domain/design/artifact.js +22 -3
  405. package/dist/domain/design/artifact.js.map +1 -1
  406. package/dist/domain/design/capability.js +1 -0
  407. package/dist/domain/design/capability.js.map +1 -1
  408. package/dist/domain/design/expansion.js +1 -1
  409. package/dist/domain/design/expansion.js.map +1 -1
  410. package/dist/domain/design/maturity.js +16 -0
  411. package/dist/domain/design/maturity.js.map +1 -1
  412. package/dist/domain/design/reference.js +5 -0
  413. package/dist/domain/design/reference.js.map +1 -1
  414. package/dist/domain/doctor/operations.js +28 -2
  415. package/dist/domain/doctor/operations.js.map +1 -1
  416. package/dist/domain/effective-contract.js +3 -1
  417. package/dist/domain/effective-contract.js.map +1 -1
  418. package/dist/domain/elicitation.js +1 -1
  419. package/dist/domain/flow/answer.js +265 -130
  420. package/dist/domain/flow/answer.js.map +1 -1
  421. package/dist/domain/flow/authority.js +193 -34
  422. package/dist/domain/flow/authority.js.map +1 -1
  423. package/dist/domain/flow/batch-review.js +65 -0
  424. package/dist/domain/flow/batch-review.js.map +1 -0
  425. package/dist/domain/flow/decision-schema.js +47 -0
  426. package/dist/domain/flow/decision-schema.js.map +1 -0
  427. package/dist/domain/flow/directive.js +16 -0
  428. package/dist/domain/flow/directive.js.map +1 -1
  429. package/dist/domain/flow/execution-result.js +91 -2
  430. package/dist/domain/flow/execution-result.js.map +1 -1
  431. package/dist/domain/flow/run-state.js +352 -24
  432. package/dist/domain/flow/run-state.js.map +1 -1
  433. package/dist/domain/flow/test-run-evidence.js +285 -0
  434. package/dist/domain/flow/test-run-evidence.js.map +1 -0
  435. package/dist/domain/flow/unchanged-phase.js +15 -0
  436. package/dist/domain/flow/unchanged-phase.js.map +1 -0
  437. package/dist/domain/harnesses.js +14 -5
  438. package/dist/domain/harnesses.js.map +1 -1
  439. package/dist/domain/isolation-unit.js +2 -2
  440. package/dist/domain/isolation-unit.js.map +1 -1
  441. package/dist/domain/lineage.js +7 -2
  442. package/dist/domain/lineage.js.map +1 -1
  443. package/dist/domain/obligation-correspondence.js +4 -2
  444. package/dist/domain/obligation-correspondence.js.map +1 -1
  445. package/dist/domain/plan-handoff.js +5 -0
  446. package/dist/domain/plan-handoff.js.map +1 -0
  447. package/dist/domain/proposal.js +18 -7
  448. package/dist/domain/proposal.js.map +1 -1
  449. package/dist/domain/reservation.js +5 -3
  450. package/dist/domain/reservation.js.map +1 -1
  451. package/dist/domain/retirement/proposal.js +1 -0
  452. package/dist/domain/retirement/proposal.js.map +1 -1
  453. package/dist/domain/session/custody.js +10 -6
  454. package/dist/domain/session/custody.js.map +1 -1
  455. package/dist/domain/session/narrative.js +4 -1
  456. package/dist/domain/session/narrative.js.map +1 -1
  457. package/dist/domain/skills.js +2 -0
  458. package/dist/domain/skills.js.map +1 -1
  459. package/dist/domain/sql-lexer.js +1 -1
  460. package/dist/domain/sql-lexer.js.map +1 -1
  461. package/dist/runtime/namespace-resolver.js +4 -0
  462. package/dist/runtime/namespace-resolver.js.map +1 -1
  463. package/dist/runtime/workline-marker.js +35 -8
  464. package/dist/runtime/workline-marker.js.map +1 -1
  465. package/dist/runtime/workspace-resolution.js +123 -0
  466. package/dist/runtime/workspace-resolution.js.map +1 -0
  467. package/package.json +5 -4
  468. package/skills/w/SKILL.md +3 -1
  469. package/skills/w/artifacts/artifacts-core/CHECKPOINT.md +1 -1
  470. package/skills/w/artifacts/artifacts-core/SCRIPTS.rollback.sql +8 -0
  471. package/skills/w/artifacts/artifacts-core/SCRIPTS.sql +4 -8
  472. package/skills/w/commands/export-diagrams.md +3 -3
  473. package/skills/w/commands/export-manuals.md +6 -6
  474. package/skills/w/commands/export-reports.md +3 -3
  475. package/skills/w/commands/export-scripts.md +5 -5
  476. package/skills/w/commands/fix-git.md +12 -13
  477. package/skills/w/commands/persist.md +2 -2
  478. package/skills/w/commands/plan-exec.md +4 -2
  479. package/skills/w/commands/spec-new.md +1 -1
  480. package/skills/w/commands/workspace-init.md +2 -0
  481. package/skills/w/context/MANIFEST.json +1 -1
  482. package/skills/w/exports/export-diagrams/EXPORT.md +3 -3
  483. package/skills/w/exports/export-manuals/EXPORT.md +14 -16
  484. package/skills/w/exports/export-reports/EXPORT.md +3 -3
  485. package/skills/w/exports/export-scripts/EXPORT.md +45 -21
  486. package/skills/w/harness/HARNESS.md +2 -2
  487. package/skills/w/hooks/hooks.template.json +6 -4
  488. package/skills/w/loops/CHASSIS.md +11 -5
  489. package/skills/w/loops/CODE-POLICIES.md +7 -4
  490. package/skills/w/loops/plan-exec-loop/LOOP.md +23 -22
  491. package/skills/w/loops/plan-new-loop/LOOP.md +23 -31
  492. package/skills/w/loops/plan-refine-loop/LOOP.md +8 -2
  493. package/skills/w/loops/quick-loop/LOOP.md +6 -5
  494. package/skills/w/modules/DESIGN-REFERENCES.md +9 -11
  495. package/skills/w/modules/PLAN-EXECUTION-BATCHES.md +4 -4
  496. package/skills/w/modules/SESSION-NUMBERING.md +2 -0
  497. package/skills/w/roles/design/CONTRACT.md +6 -4
  498. package/skills/w/roles/design/ROLE.md +4 -2
  499. package/skills/w/roles/git/ROLE.md +16 -9
  500. package/skills/w/roles/research/ROLE.md +1 -1
  501. package/skills/w/roles/sql/ROLE.md +16 -14
  502. package/skills/w/schemas/design/ui-screen.v1.schema.json +9 -0
@@ -25,35 +25,42 @@
25
25
  */
26
26
  import { join } from "node:path";
27
27
  import { dispositionOf } from "../../domain/flow/route.js";
28
- import { withPlanExecBatch, withPlanExecBatchBase, withPlanExecBatchLoop, withSettlement, } from "../../domain/flow/run-state.js";
29
- import { sealProposal } from "../../domain/proposal.js";
28
+ import { legacyPlanExecCommits, withPlanExecBatch, withPlanExecBatchBase, withPlanExecBatchLoop, withPlanExecBatchUpdate, withSettlement, } from "../../domain/flow/run-state.js";
29
+ import { approvedValidationOnly } from "../../domain/flow/unchanged-phase.js";
30
+ import { canonicalEol, sealProposal } from "../../domain/proposal.js";
30
31
  import { obligationExit } from "../../domain/reconciliation.js";
31
32
  import { runArtifactsCommand } from "../artifacts-service.js";
32
- import { appendClaimEvent, claimKey, claimShapedAmong, completedClaimsIn, ledgerPath, openClaimsOf, readClaimEvents, revokedAmong, } from "../claims-ledger.js";
33
+ import { appendClaimEvent, claimKey, claimShapedAmong, completedClaimsIn, ledgerPath, readClaimEvents, revokedAmong, } from "../claims-ledger.js";
33
34
  import { applyLocalProposal } from "../local-proposal.js";
34
35
  import { parseMdSectionBilingual } from "../markdown.js";
35
36
  import { parsePhases } from "../parsers/phases.js";
36
- import { parseTasks } from "../parsers/tasks.js";
37
37
  import { resolveWorkspaceRoot } from "../paths-service.js";
38
- import { inferPlanExecBatch, preparePlanExecDoneSeal, publishPlanExecBatch, } from "../plan-exec-batch-service.js";
38
+ import { inferNextPlanExecBatch, preparePlanExecDoneSeal, publishPlanExecBatch, } from "../plan-exec-batch-service.js";
39
39
  import { settlePlanExecObligations } from "../plan-exec-decision-service.js";
40
+ import { sealedPlanPath } from "../plan-exec-plan-diff.js";
40
41
  import { readSessionArtifacts } from "../release-data/artifacts.js";
41
42
  import { canonicalJson } from "../semantic-operation/protocol.js";
43
+ import { canonicalArtifactPath } from "../session-artifacts.js";
42
44
  import { runSessionClose } from "../session-close-service.js";
43
45
  import { recordPublication } from "../session-custody-recorder.js";
46
+ import { readCustody } from "../session-custody-service.js";
44
47
  import { runStatusCommand } from "../status-service.js";
45
48
  import { buildWorklineIndex } from "../workline-index-service.js";
46
- import { runWorktree } from "../worktree-service.js";
47
- import { observeScopedFingerprints } from "./checkout-observation.js";
49
+ import { runWorkspaceCommit } from "../workspace-commit-service.js";
50
+ import { classifyListedUnits, runWorktree } from "../worktree-service.js";
51
+ import { commitBatch, verifyBatchGitState } from "./batch-commit.js";
52
+ import { observeScopedFingerprints, resolveCheckoutCandidates } from "./checkout-observation.js";
53
+ import { preserveBoundaryClose } from "./close-artifacts.js";
54
+ import { closeDocumentGuidance } from "./close-document-guidance.js";
48
55
  import { projectRun } from "./run-projection.js";
49
56
  import { applyUnderLock, locateRun, readRun } from "./run-state-service.js";
50
57
  export function internalActionExecutor(deps) {
51
58
  return async (plan, run) => {
52
59
  switch (plan.operation) {
53
60
  case "workspace.board":
54
- return board(deps);
61
+ return board(deps, run);
55
62
  case "session.artifacts":
56
- return artifacts(deps, run, plan.dump ?? null);
63
+ return artifacts(deps, run, plan.dump ?? null, run.session.endsWith("-plan-refine"));
57
64
  case "session.close":
58
65
  return close(deps, run);
59
66
  case "worktree.ensure":
@@ -64,6 +71,8 @@ export function internalActionExecutor(deps) {
64
71
  return inferBatch(deps, run);
65
72
  case "plan-exec.batch-close":
66
73
  return closeBatch(deps, run);
74
+ case "plan-exec.batch-commit":
75
+ return commitBatch(deps, run);
67
76
  case "plan-exec.plan-done":
68
77
  return sealPlanDone(deps, run);
69
78
  case "plan-exec.settlement-publish":
@@ -91,7 +100,7 @@ async function ensureUnits(deps, run) {
91
100
  return refusal("worktree.ensure", "la corrida no fijó qué fuentes edita: no hay unidad que adquirir", canonicalJson({ scope: null }));
92
101
  }
93
102
  const acquired = [];
94
- for (const alias of scope.sources) {
103
+ for (const alias of scope.isolation === "in-place" ? [] : scope.sources) {
95
104
  // `workspace` is the documentary/control checkout itself. It is a valid
96
105
  // source-bounded proof surface, but never a source repository that needs a
97
106
  // per-session Git worktree.
@@ -115,12 +124,14 @@ async function ensureUnits(deps, run) {
115
124
  }
116
125
  return {
117
126
  ok: true,
118
- summary: `unidades de ${run.session}: ${acquired.map((unit) => `${unit.alias} → ${unit.branch}`).join(", ")}`,
127
+ summary: scope.isolation === "in-place"
128
+ ? "in-place: sin unidad"
129
+ : `unidades de ${run.session}: ${acquired.map((unit) => `${unit.alias} → ${unit.branch}`).join(", ")}`,
119
130
  output: canonicalJson({ plan: scope.plan, units: acquired }),
120
131
  // The tree IS there, however it got there — the same reading `proposal.publish`
121
132
  // makes of a re-entry that finds the bytes already written. Crediting nothing
122
133
  // when `created` is false would refuse the resumption this row is idempotent for.
123
- effects: ["local_additive"],
134
+ effects: ["local_additive", "mutate_overwrite"],
124
135
  ...(based.state === null ? {} : { state: based.state }),
125
136
  };
126
137
  }
@@ -148,6 +159,55 @@ async function recordBatchBase(deps, run, sources) {
148
159
  const observed = await observeScopedFingerprints(deps.fs, deps.git, deps.paths, run.session, sources);
149
160
  if (!observed.ok)
150
161
  return observed;
162
+ const legacy = live.ok && legacyPlanExecCommits(live.state);
163
+ const candidates = legacy
164
+ ? []
165
+ : await resolveCheckoutCandidates(deps.fs, deps.paths, run.session);
166
+ const snapshot = {};
167
+ for (const alias of legacy ? [] : sources) {
168
+ if (alias === "workspace")
169
+ continue;
170
+ const root = candidates.find((candidate) => candidate.source === alias)?.root;
171
+ if (root === undefined)
172
+ return {
173
+ ok: false,
174
+ failure: {
175
+ code: "PLAN_EXEC_BATCH_UNIT_MISSING",
176
+ message: `falta la unidad de ${alias}`,
177
+ action: "adquirí la unidad antes de iniciar el lote",
178
+ },
179
+ };
180
+ try {
181
+ const [head, branch, dirty] = await Promise.all([
182
+ deps.git.head(root),
183
+ deps.git.currentBranch(root),
184
+ deps.git.dirtyPaths(root),
185
+ ]);
186
+ if (head === null ||
187
+ branch === undefined ||
188
+ (run.scope?.isolation !== "in-place" && dirty.length > 0)) {
189
+ return {
190
+ ok: false,
191
+ failure: {
192
+ code: "PLAN_EXEC_BATCH_UNIT_DIRTY",
193
+ message: `${alias}: no puede iniciar el lote con HEAD/rama ausente o rutas sucias: ${dirty.map((entry) => entry.path).join(", ")}`,
194
+ action: "cerrá los cambios del lote anterior antes de iniciar éste",
195
+ },
196
+ };
197
+ }
198
+ snapshot[alias] = { head, branch, dirty };
199
+ }
200
+ catch (error) {
201
+ return {
202
+ ok: false,
203
+ failure: {
204
+ code: "PLAN_EXEC_BATCH_GIT_UNOBSERVABLE",
205
+ message: `${alias}: ${String(error)}`,
206
+ action: "restaurá la lectura de git y reintentá la adquisición",
207
+ },
208
+ };
209
+ }
210
+ }
151
211
  const recorded = await applyUnderLock(deps.fs, locateRun(deps.paths, run.session), (current) => {
152
212
  if (current === null) {
153
213
  return {
@@ -168,7 +228,9 @@ async function recordBatchBase(deps, run, sources) {
168
228
  // base is the one the archived run sealed before that work, not today's tree.
169
229
  const inherited = current.inherited_bases?.find((entry) => entry.phases.length === batch.phases.length &&
170
230
  entry.phases.every((phase, index) => phase === batch.phases[index]));
171
- const next = withPlanExecBatchBase(current, batch.id, inherited?.base ?? observed.base);
231
+ let next = withPlanExecBatchBase(current, batch.id, inherited?.base ?? observed.base);
232
+ if (batch.snapshot === undefined && !legacyPlanExecCommits(current))
233
+ next = withPlanExecBatchUpdate(next, batch.id, (item) => ({ ...item, snapshot }));
172
234
  return { ok: true, state: next, value: null, persist: next !== current };
173
235
  }, run.state_digest === undefined ? {} : { expectDigest: run.state_digest });
174
236
  if (!recorded.ok)
@@ -215,7 +277,7 @@ async function publish(deps, run) {
215
277
  ok: true,
216
278
  summary: result.already_applied
217
279
  ? `la propuesta ya estaba aplicada: ${destinations.join(", ")} sin cambios`
218
- : `publicado: ${result.written.join(", ")}`,
280
+ : `publicado: ${result.written.join(", ")}${result.notice === undefined ? "" : `; aviso: ${result.notice}`}`,
219
281
  output: canonicalJson({ ...result, digest: proposal.digest, destinations, claims }),
220
282
  // Only what really landed. A re-entry that found the bytes already there
221
283
  // credits the same classes — the effect IS applied — and the summary is what
@@ -233,8 +295,8 @@ async function publish(deps, run) {
233
295
  * never held up by a ledger it does not depend on — an unreadable ledger must not
234
296
  * stop every loop in the system from saving.
235
297
  */
236
- async function revokedFence(deps, owner, destinations) {
237
- const read = await readClaimEvents(deps.fs, deps.paths);
298
+ export async function revokedFence(deps, owner, destinations) {
299
+ const read = await readClaimEvents(deps.fs, deps.paths, { lockHeld: true });
238
300
  const blocked = revokedAmong(read.events, owner, destinations);
239
301
  if (blocked.length > 0) {
240
302
  return {
@@ -244,13 +306,10 @@ async function revokedFence(deps, owner, destinations) {
244
306
  };
245
307
  }
246
308
  if (read.unreadable > 0) {
247
- // Scoped to claims this owner ACTUALLY holds in the ledger, not to every
248
- // numbered destination. Otherwise one unparseable line — and this file is a
249
- // committed append-only JSONL, so a merge conflict produces exactly that —
250
- // would refuse every SPEC, PLAN and QUICK save in the workspace, which is a
251
- // workspace-wide outage rather than the narrow fail-closed this needs to be.
252
- const mine = new Set(openClaimsOf(read.events, owner).map((claim) => claimKey(claim)));
253
- const atRisk = claimShapedAmong(owner, destinations).filter((claim) => mine.has(claimKey(claim)));
309
+ // An illegible revoked row may have removed the very open claim a narrowed
310
+ // check would rely on. Every numbered destination is at risk; loose files
311
+ // outside the claim namespace remain unaffected.
312
+ const atRisk = claimShapedAmong(owner, destinations);
254
313
  if (atRisk.length > 0) {
255
314
  return {
256
315
  code: "CLAIM_LEDGER_UNREADABLE",
@@ -286,6 +345,8 @@ async function recordCompletedClaims(deps, owner, result, destinations) {
286
345
  const recorded = [];
287
346
  try {
288
347
  const read = await readClaimEvents(deps.fs, deps.paths);
348
+ if (read.unreadable > 0)
349
+ return { recorded, ledger_unreadable: read.unreadable };
289
350
  const completed = completedClaimsIn(read.events, owner, {
290
351
  written: result.written,
291
352
  already_applied: result.already_applied,
@@ -312,13 +373,23 @@ async function recordCompletedClaims(deps, owner, result, destinations) {
312
373
  };
313
374
  }
314
375
  }
315
- async function board(deps) {
376
+ async function board(deps, run) {
316
377
  const data = await runStatusCommand(deps.fs, deps.env, deps.paths, { git: deps.git });
317
378
  const counts = data.counts;
379
+ const custody = run.session.endsWith("-plan-new")
380
+ ? await readCustody(deps.fs, locateRun(deps.paths, run.session).dir)
381
+ : null;
382
+ const superseded = custody?.status === "present"
383
+ ? data.specs.find((spec) => spec.status === "superseded" &&
384
+ custody.custody.artifacts.some((artifact) => artifact.role === "input" && artifact.path === spec.file))
385
+ : undefined;
386
+ const warning = superseded === undefined
387
+ ? null
388
+ : `la spec ${superseded.number} fue reemplazada${superseded.superseded_by ? ` por ${superseded.superseded_by}` : ""}: plan-new debe usar el reemplazo`;
318
389
  return {
319
390
  ok: true,
320
- summary: `tablero: ${counts.specs} specs, ${counts.plans} planes, ${counts.sessions_active} sesiones activas, ${counts.pending} pendientes`,
321
- output: canonicalJson(data),
391
+ summary: `tablero: ${counts.specs} specs, ${counts.plans} planes, ${counts.sessions_active} sesiones activas, ${counts.pending} pendientes${warning ? `; aviso: ${warning}` : ""}`,
392
+ output: canonicalJson(warning ? { ...data, spec_readiness_warning: warning } : data),
322
393
  effects: ["read_only"],
323
394
  };
324
395
  }
@@ -332,10 +403,13 @@ async function board(deps) {
332
403
  * says the specific artifacts have content. A dump alone would pass a session
333
404
  * whose criteria were never seeded.
334
405
  */
335
- async function artifacts(deps, run, dump) {
406
+ async function artifacts(deps, run, dump, planRefine = false) {
336
407
  // The presence report, without the narrative: this operation checks that the
337
408
  // artifacts are THERE, and projecting the session's whole reading to answer
338
409
  // that would be work nobody asked for on every advance.
410
+ if (dump?.includes("objetivo") && dump.includes("checkpoint")) {
411
+ await seedMissingCheckpoint(deps, run);
412
+ }
339
413
  const report = await runArtifactsCommand(deps.fs, deps.env, deps.paths, {
340
414
  code: run.code,
341
415
  noNarrative: true,
@@ -346,22 +420,27 @@ async function artifacts(deps, run, dump) {
346
420
  if (report.artifacts.session === null) {
347
421
  return refusal("session.artifacts", `la sesión '${report.session}' no tiene su SESSION.md`, canonicalJson(report));
348
422
  }
423
+ const destination = planRefine ? await refineDestination(deps, run, canonicalJson(report)) : "";
424
+ if (typeof destination !== "string")
425
+ return destination;
426
+ const legacyWarning = destination;
349
427
  if (dump === null) {
350
428
  return {
351
429
  ok: true,
352
- summary: `sesión ${report.session}: SESSION.md presente, ${report.artifacts.session.criterios_count} criterios, ${report.artifacts.decisiones_count} decisiones`,
430
+ summary: `sesión ${report.session}: SESSION.md presente, ${report.artifacts.session.criterios_count} criterios, ${report.artifacts.decisiones_count} decisiones${legacyWarning}`,
353
431
  output: canonicalJson(report),
354
432
  effects: SEEDED_EFFECTS,
355
433
  };
356
434
  }
357
435
  const dumped = await readSessionArtifacts(deps.fs, deps.paths, run.code, [...dump], deps.runtime);
358
- const output = canonicalJson({ report, dump: dumped });
436
+ const script = dump.includes("scripts") ? await rootScript(deps, run) : null;
437
+ const output = canonicalJson({ report, dump: dumped, root_script: script });
359
438
  if (dumped.error !== undefined) {
360
439
  return refusal("session.artifacts", String(dumped.hint ?? dumped.error), output);
361
440
  }
362
- const empty = dump.filter((kind) => !hasContent(dumped[kind]));
441
+ const empty = dump.filter((kind) => !hasContent(dumped[kind]) && !(kind === "scripts" && hasContent(script)));
363
442
  if (empty.length > 0) {
364
- return refusal("session.artifacts", `sin contenido: ${empty.join(", ")}`, output);
443
+ return refusal("session.artifacts", `sin contenido: ${empty.join(", ")}${empty.includes("scripts") ? " (SCRIPTS.sql o scripts/*.sql)" : ""}`, output);
365
444
  }
366
445
  // `objetivo` is the artifact that carries the success criteria, so demanding it
367
446
  // is demanding them: a SESSION.md with a criteria heading and nothing under it
@@ -373,11 +452,46 @@ async function artifacts(deps, run, dump) {
373
452
  }
374
453
  return {
375
454
  ok: true,
376
- summary: `sesión ${report.session}: ${dump.join(", ")} con contenido, ${report.artifacts.session.criterios_count} criterios`,
455
+ summary: `sesión ${report.session}: ${dump.join(", ")} con contenido, ${report.artifacts.session.criterios_count} criterios${legacyWarning}`,
377
456
  output,
378
457
  effects: SEEDED_EFFECTS,
379
458
  };
380
459
  }
460
+ async function seedMissingCheckpoint(deps, run) {
461
+ const folder = join(deps.paths.cwdSessionsDir(), run.session);
462
+ const checkpoint = canonicalArtifactPath(folder, "checkpoint");
463
+ if (await deps.fs.exists(checkpoint))
464
+ return;
465
+ const session = canonicalArtifactPath(folder, "session");
466
+ if (!(await deps.fs.exists(session)))
467
+ return;
468
+ const objective = parseMdSectionBilingual(await deps.fs.readText(session), "Objective")?.trim();
469
+ if (!objective || objective.includes("_[AI:"))
470
+ return;
471
+ await deps.fs.publishTextExclusive(checkpoint, `# CHECKPOINT\n\n## Completed\n\n## Pending / Next\n\n${objective}\n`);
472
+ }
473
+ /** Only this evidence reader accepts the root script; exports keep their dump. */
474
+ async function rootScript(deps, run) {
475
+ const path = canonicalArtifactPath(join(deps.paths.cwdSessionsDir(), run.session), "scripts_sql");
476
+ return (await deps.fs.exists(path)) ? { path, content: await deps.fs.readText(path) } : null;
477
+ }
478
+ /** A new refine must be bound to an existing plan; only pre-custody sessions degrade. */
479
+ async function refineDestination(deps, run, report) {
480
+ const custody = await readCustody(deps.fs, locateRun(deps.paths, run.session).dir);
481
+ if (custody.status === "unreadable") {
482
+ return refusal("session.artifacts", `custodia ilegible: ${custody.reason}`, report);
483
+ }
484
+ if (custody.status === "absent") {
485
+ return "; sesión legada sin custodia: no se puede verificar su plan destino";
486
+ }
487
+ for (const artifact of custody.custody.artifacts) {
488
+ if (artifact.role !== "input" || !/^docs\/plans\/\d{3,}-plan-[^/]+\.md$/.test(artifact.path))
489
+ continue;
490
+ if (await deps.fs.exists(join(deps.paths.workspaceDir(), artifact.path)))
491
+ return "";
492
+ }
493
+ return refusal("session.artifacts", "plan-refine exige un plan destino existente: creá la sesión con --input docs/plans/PPP-plan-<slug>.md o el descriptor <slug>-plan-refine, del que session-create deriva el plan", canonicalJson({ report, custody: custody.custody.artifacts.map((artifact) => artifact.path) }));
494
+ }
381
495
  /**
382
496
  * What a satisfied artifact reading really attests.
383
497
  *
@@ -456,6 +570,16 @@ async function inferBatch(deps, run) {
456
570
  }
457
571
  const active = (current.batches ?? []).find((batch) => batch.published_plan_digest === undefined);
458
572
  if (active !== undefined) {
573
+ if (active.commit_result !== undefined && Object.keys(active.commit_result).length > 0) {
574
+ return {
575
+ ok: false,
576
+ failure: {
577
+ code: "PLAN_EXEC_BATCH_REINFER_COMMITTED",
578
+ message: `${active.id} ya tiene recibos de commit y no puede re-inferirse`,
579
+ action: `revisá los recibos; para reabrir su trabajo usá aw flow annul --session ${run.session} --from ${active.id}`,
580
+ },
581
+ };
582
+ }
459
583
  return {
460
584
  ok: true,
461
585
  state: current,
@@ -477,7 +601,7 @@ async function inferBatch(deps, run) {
477
601
  },
478
602
  };
479
603
  }
480
- const next = inferNextBatch(text, current);
604
+ const next = inferNextPlanExecBatch(text, current);
481
605
  if (!next.ok) {
482
606
  if (next.failure.code !== "PLAN_EXEC_BATCH_NONE_OPEN") {
483
607
  return { ok: false, failure: next.failure };
@@ -494,6 +618,27 @@ async function inferBatch(deps, run) {
494
618
  value: { batch: null, created: false, no_work: true },
495
619
  };
496
620
  }
621
+ // Store the exact plan text BEFORE publishing the inferred state. A retry
622
+ // either sees the same addressed copy or refuses rather than losing its base.
623
+ try {
624
+ const snapshot = sealedPlanPath(location.dir, next.batch.plan_digest);
625
+ await deps.fs.mkdirp(join(location.dir, ".plan-seals"));
626
+ const saved = await deps.fs.publishTextExclusive(snapshot, text);
627
+ if (!saved.created &&
628
+ canonicalEol(await deps.fs.readText(snapshot)) !== canonicalEol(text)) {
629
+ throw new Error("la copia direccionada no coincide con el plan inferido");
630
+ }
631
+ }
632
+ catch (error) {
633
+ return {
634
+ ok: false,
635
+ failure: {
636
+ code: "PLAN_EXEC_BATCH_SNAPSHOT_UNAVAILABLE",
637
+ message: `no se pudo guardar la copia sellada: ${String(error)}`,
638
+ action: "revisá el almacenamiento de la sesión y reintentá la inferencia",
639
+ },
640
+ };
641
+ }
497
642
  return {
498
643
  ok: true,
499
644
  state: withPlanExecBatch(current, next.batch),
@@ -508,7 +653,7 @@ async function inferBatch(deps, run) {
508
653
  summary: inferred.value.no_work
509
654
  ? "el plan ya no tiene tareas abiertas: se omite el batch vacío y se expone la validación final"
510
655
  : inferred.value.created
511
- ? `batch ${inferred.value.batch?.id ?? "nuevo"} inferido y sellado antes de implementar`
656
+ ? `batch ${inferred.value.batch?.id ?? "nuevo"} inferido y sellado antes de implementar; partición ${canonicalJson(inferred.value.batch?.partition ?? null)}`
512
657
  : `batch ${inferred.value.batch?.id ?? "actual"} ya estaba inferido; se conserva su snapshot sellado`,
513
658
  output: canonicalJson({ batch: inferred.value.batch, created: inferred.value.created }),
514
659
  effects: [],
@@ -643,12 +788,26 @@ async function closeBatch(deps, run) {
643
788
  return refusal("plan-exec.batch-close", "no hay un batch inferido para cerrar: la corrida no puede acreditar tareas que no selló antes de implementarlas", canonicalJson({ code: "PLAN_EXEC_BATCH_NOT_INFERRED", plan: run.scope.plan }));
644
789
  }
645
790
  // A batch born with a base was meant to be credited at its phase validation.
791
+ if (validationOnlyCloseMissing(live.state, batch)) {
792
+ return refusal("plan-exec.batch-close", "el lote de sólo validación carece de aprobación o de evidencia acreditada", canonicalJson({ code: "PLAN_VALIDATION_ONLY_NOT_APPROVED", batch: batch.id }));
793
+ }
646
794
  // Reaching the close without that credit — an exhausted validation degraded
647
795
  // past, a route that skipped it — would publish its tasks as done on no proof.
648
796
  if (batch.base !== undefined && batch.credit === undefined) {
649
797
  return refusal("plan-exec.batch-close", `el batch ${batch.id} no tiene acreditación: su validación de fase nunca aceptó una prueba propia del checkout de sus fuentes`, canonicalJson({ code: "PLAN_EXEC_BATCH_UNCREDITED", batch: batch.id, plan: run.scope.plan }));
650
798
  }
651
799
  const phaseUpdates = phaseUpdatesForClosedBatch(text, batch);
800
+ if (batch.review === undefined && batch.published_plan_digest === undefined) {
801
+ return refusal("plan-exec.batch-close", `el batch ${batch.id} no tiene revisión registrada; completá review-findings con un revisor distinto y las correcciones revisadas`, canonicalJson({ code: "PLAN_EXEC_BATCH_REVIEW_MISSING", batch: batch.id }));
802
+ }
803
+ if (!legacyPlanExecCommits(live.state) && batch.commit_result === undefined) {
804
+ return refusal("plan-exec.batch-close", `el batch ${batch.id} no tiene resultado de commits verificado`, canonicalJson({ code: "PLAN_EXEC_BATCH_COMMIT_MISSING", batch: batch.id }));
805
+ }
806
+ if (!legacyPlanExecCommits(live.state)) {
807
+ const gitFailure = await verifyBatchGitState(deps, run, batch, run.scope?.isolation === "in-place");
808
+ if (gitFailure !== null)
809
+ return refusal("plan-exec.batch-close", gitFailure.message, canonicalJson(gitFailure));
810
+ }
652
811
  if (!phaseUpdates.ok) {
653
812
  return refusal("plan-exec.batch-close", `${phaseUpdates.failure.message} — ${phaseUpdates.failure.action}`, canonicalJson({ failure: phaseUpdates.failure }));
654
813
  }
@@ -676,11 +835,12 @@ async function closeBatch(deps, run) {
676
835
  ok: true,
677
836
  summary: published.already_applied
678
837
  ? `batch ${published.batch.id} ya estaba publicado en ${run.scope.plan}`
679
- : `batch ${published.batch.id} publicado: ${published.written.join(", ")}`,
838
+ : `batch ${published.batch.id} publicado: ${published.written.join(", ")}${published.notice === undefined ? "" : `; aviso: ${published.notice}`}`,
680
839
  output: canonicalJson({
681
840
  batch: published.batch,
682
841
  written: published.written,
683
842
  already_applied: published.already_applied,
843
+ ...(published.notice === undefined ? {} : { notice: published.notice }),
684
844
  }),
685
845
  effects: ["mutate_overwrite"],
686
846
  state,
@@ -697,6 +857,10 @@ async function closeBatch(deps, run) {
697
857
  * already names the NEXT batch — or nothing, when it was the last one. The
698
858
  * publisher then confirms it through its own already-applied path.
699
859
  */
860
+ function validationOnlyCloseMissing(state, batch) {
861
+ return (batch.kind === "validation-only" &&
862
+ (!approvedValidationOnly(state, batch) || batch.credit === undefined));
863
+ }
700
864
  function batchToClose(state) {
701
865
  const batches = state.batches ?? [];
702
866
  const open = batches.find((batch) => batch.published_plan_digest === undefined);
@@ -756,11 +920,21 @@ async function sealPlanDone(deps, run) {
756
920
  return refusal("plan-exec.plan-done", "la corrida cambió mientras se preparaba el sello final", canonicalJson({ expected: run.state_digest, actual: live.state.digest }));
757
921
  }
758
922
  const missing = PLAN_DONE_REQUIRED_TRANSITIONS.filter((transition) => {
923
+ if (transition === "plan-exec.final-validation") {
924
+ return (!live.state.applied.includes(transition) ||
925
+ live.state.skipped.includes(transition) ||
926
+ (live.state.degraded ?? []).some((item) => item.transition === transition) ||
927
+ dispositionOf(live.state.route_decisions, transition)?.disposition === "substitute");
928
+ }
759
929
  if (live.state.applied.includes(transition))
760
930
  return false;
761
931
  const disposition = dispositionOf(live.state.route_decisions, transition)?.disposition;
762
932
  return disposition !== "omit" && disposition !== "substitute";
763
933
  });
934
+ if (!legacyPlanExecCommits(live.state) &&
935
+ (live.state.batches ?? []).some((batch) => batch.commit_result === undefined)) {
936
+ return refusal("plan-exec.plan-done", "hay lotes sin recibo o resultado de commit", canonicalJson({ code: "PLAN_EXEC_DONE_BATCH_COMMIT_MISSING" }));
937
+ }
764
938
  if (missing.length > 0) {
765
939
  return refusal("plan-exec.plan-done", `todavía falta evidencia de ${missing.join(", ")}`, canonicalJson({ code: "PLAN_EXEC_DONE_PREREQUISITE", missing }));
766
940
  }
@@ -828,6 +1002,12 @@ async function sealPlanDone(deps, run) {
828
1002
  }
829
1003
  /** The plan seal says exactly which evidence was absent or substituted. */
830
1004
  function planDoneClosure(state, session) {
1005
+ const destination = state.scope?.isolation === "in-place"
1006
+ ? `commits en el checkout (${(state.batches ?? [])
1007
+ .flatMap((batch) => Object.values(batch.commit_result ?? {}).map((receipt) => receipt.branch))
1008
+ .filter((branch) => branch !== null)
1009
+ .join(", ")}); sin integración`
1010
+ : "commits e integración";
831
1011
  const decisions = state.route_decisions ?? [];
832
1012
  const controls = new Map((state.route_proposal?.controls ?? []).map((control) => [control.transition, control]));
833
1013
  const omitted = decisions
@@ -841,15 +1021,15 @@ function planDoneClosure(state, session) {
841
1021
  .map((decision) => decision.substitution)
842
1022
  .filter((substitution) => substitution !== null);
843
1023
  if (state.assurance === "unverified_accepted") {
844
- return `evidencia omitida en la propuesta aceptada (${omitted.join("; ") || "sin detalle"}); commits e integración acreditados por la corrida ${session}; no se afirma que la validación pasó`;
1024
+ return `evidencia omitida en la propuesta aceptada (${omitted.join("; ") || "sin detalle"}); ${destination} acreditados por la corrida ${session}; no se afirma que la validación pasó`;
845
1025
  }
846
1026
  if (state.assurance === "partially_verified") {
847
- return `validación sustituta pendiente (${substitutions.map((substitution) => substitution.validation).join(", ") || "sin detalle"}); riesgo aceptado: ${substitutions.map((substitution) => substitution.risk).join(", ") || "assurance parcial"}; commits e integración acreditados por la corrida ${session}`;
1027
+ return `validación sustituta pendiente (${substitutions.map((substitution) => substitution.validation).join(", ") || "sin detalle"}); riesgo aceptado: ${substitutions.map((substitution) => substitution.risk).join(", ") || "assurance parcial"}; ${destination} acreditados por la corrida ${session}`;
848
1028
  }
849
1029
  if (substitutions.length > 0) {
850
- return `validación sustituta ejecutada (${substitutions.map((substitution) => substitution.validation).join(", ")}); commits e integración acreditados por la corrida ${session}`;
1030
+ return `validación sustituta ejecutada (${substitutions.map((substitution) => substitution.validation).join(", ")}); ${destination} acreditados por la corrida ${session}`;
851
1031
  }
852
- return `validación final, commits e integración acreditados por la corrida ${session}`;
1032
+ return `validación final, ${destination} acreditados por la corrida ${session}`;
853
1033
  }
854
1034
  /** The documentary checks that must pass in the same critical section as done. */
855
1035
  export async function planDonePrecondition(deps, path, session) {
@@ -958,40 +1138,6 @@ function phaseUpdatesForClosedBatch(text, batch) {
958
1138
  updates: batch.phases.map((phase) => ({ phase, state: "validada" })),
959
1139
  };
960
1140
  }
961
- function inferNextBatch(text, state) {
962
- // `inferPlanExecBatch` itself validates that the phase has real, uniquely
963
- // labelled Tn.m tasks. We only choose the first still-open phase from the
964
- // document, which is a deterministic batch boundary rather than a claimed one.
965
- const openPhase = parseTasks(text).items.find((task) => task.status === "open" && task.phase !== undefined)?.phase;
966
- if (openPhase === undefined) {
967
- const unresolved = parsePhases(text).items.find((phase) => phase.state !== "validada");
968
- if (unresolved !== undefined) {
969
- return {
970
- ok: false,
971
- failure: {
972
- code: "PLAN_EXEC_BATCH_PHASE_UNRESOLVED",
973
- message: `F${unresolved.n} sigue '${unresolved.state}' pero no tiene tareas abiertas acreditables`,
974
- action: "normalizá la fase con plan-refine; no se salta a la validación final sobre una fase no validada",
975
- },
976
- };
977
- }
978
- return {
979
- ok: false,
980
- failure: {
981
- code: "PLAN_EXEC_BATCH_NONE_OPEN",
982
- message: "el plan no tiene una fase con tareas abiertas que este batch pueda acreditar",
983
- action: "el batch ya está cerrado: reanudá la corrida para que exponga la validación final",
984
- },
985
- };
986
- }
987
- const iteration = Math.max(0, ...(state.batches ?? []).map((batch) => batch.iteration)) + 1;
988
- return inferPlanExecBatch(text, {
989
- id: `batch-${iteration}`,
990
- iteration,
991
- mode: "continuous",
992
- phases: [openPhase],
993
- });
994
- }
995
1141
  /**
996
1142
  * Close the run's session — and refuse to, while it still holds a unit.
997
1143
  *
@@ -1003,14 +1149,74 @@ function inferNextBatch(text, state) {
1003
1149
  * notice behind a `.closed` marker that also makes the remedy stop resolving.
1004
1150
  */
1005
1151
  async function close(deps, run) {
1006
- const result = await runSessionClose(deps.fs, deps.paths, { code: run.code, requireIntegrated: true }, async () => {
1007
- const listed = await runWorktree({ fs: deps.fs, env: deps.env, git: deps.git, paths: deps.paths }, { action: "list" });
1008
- // A list that did not come back is NOT "no units": the close refuses on it,
1009
- // which is the whole point of asking before writing the marker.
1010
- if (!("units" in listed))
1011
- throw new Error(JSON.stringify(listed));
1012
- return listed.units;
1013
- });
1152
+ const read = await readRun(deps.fs, locateRun(deps.paths, run.session));
1153
+ if (!read.ok && read.failure.code !== "FLOW_RUN_ABSENT") {
1154
+ return refusal("session.close", read.failure.message, canonicalJson(read.failure));
1155
+ }
1156
+ const boundaryClose = read.ok && read.state.reentries?.at(-1)?.kind === "close";
1157
+ const listed = await runWorktree({ fs: deps.fs, env: deps.env, git: deps.git, paths: deps.paths }, { action: "list" });
1158
+ if (!("units" in listed)) {
1159
+ return refusal("session.close", "no se pudieron leer las unidades; revisá 'aw worktree list' y reintentá con 'aw flow advance'", canonicalJson(listed));
1160
+ }
1161
+ const classified = await classifyListedUnits(deps, listed.units);
1162
+ const pending = boundaryClose
1163
+ ? await preserveBoundaryClose(deps.fs, deps.paths, deps.git, read.state, classified, listed.unreadable ?? [])
1164
+ : [];
1165
+ const documents = read.ok ? await closeDocumentGuidance(deps.fs, deps.paths, read.state) : [];
1166
+ const result = await runSessionClose(deps.fs, deps.paths, {
1167
+ code: run.code,
1168
+ withEvidence: read.ok &&
1169
+ read.state.events.some((event) => event.kind === "executed" && event.operation === "workspace.evidence-approved"),
1170
+ // `Cerrar` and a consented QUICK escalation are explicit human exits:
1171
+ // incomplete criteria travel in BACKLOG, not as an invented completion.
1172
+ force: boundaryClose ||
1173
+ (read.ok &&
1174
+ read.state.flow === "quick" &&
1175
+ read.state.applied.includes("quick.escalation-destination") &&
1176
+ !read.state.skipped.includes("quick.escalation-destination")),
1177
+ requireIntegrated: !boundaryClose,
1178
+ final: !boundaryClose,
1179
+ preserveReservations: boundaryClose
1180
+ ? (read.state.proposal?.artifacts.filter((a) => a.reserved).map((a) => a.path) ?? [])
1181
+ : [],
1182
+ }, async () => ({ units: classified, unreadable: listed.unreadable ?? [] }), deps.git, undefined, (alias, folder) => releaseClassifiedUnit(deps, alias, folder));
1183
+ if ("sessionClose" in result) {
1184
+ const approval = read.ok
1185
+ ? [...read.state.events]
1186
+ .reverse()
1187
+ .find((event) => event.kind === "executed" && event.operation === "workspace.commit-approved")
1188
+ : undefined;
1189
+ if (approval?.kind === "executed") {
1190
+ if (result.sessionClose.archive_error !== undefined ||
1191
+ result.sessionClose.history_error !== undefined) {
1192
+ result.sessionClose.commit_error = `no se ejecutó el commit: ${result.sessionClose.archive_error ?? result.sessionClose.history_error}`;
1193
+ }
1194
+ else {
1195
+ try {
1196
+ const accepted = JSON.parse(approval.summary);
1197
+ if (typeof accepted === "object" &&
1198
+ accepted !== null &&
1199
+ "approval" in accepted &&
1200
+ typeof accepted.approval === "string") {
1201
+ const commit = await runWorkspaceCommit(deps.fs, deps.git, undefined, deps.paths, {
1202
+ code: run.session,
1203
+ approval: accepted.approval,
1204
+ withEvidence: read.ok &&
1205
+ read.state.events.some((event) => event.kind === "executed" && event.operation === "workspace.evidence-approved"),
1206
+ });
1207
+ if ("committed" in commit && commit.committed)
1208
+ result.sessionClose.commit_receipt = commit.committed;
1209
+ else
1210
+ result.sessionClose.commit_error =
1211
+ "error" in commit ? commit.error : "el commit aprobado no se produjo";
1212
+ }
1213
+ }
1214
+ catch (error) {
1215
+ result.sessionClose.commit_error = error instanceof Error ? error.message : String(error);
1216
+ }
1217
+ }
1218
+ }
1219
+ }
1014
1220
  if ("sessionHeld" in result) {
1015
1221
  const held = result.sessionHeld;
1016
1222
  return refusal("session.close", `${held.reason} — integralas con '${held.integrate}'`, canonicalJson(result));
@@ -1020,15 +1226,27 @@ async function close(deps, run) {
1020
1226
  return refusal("session.close", `la sesión no cerró: ${why}`, canonicalJson(result));
1021
1227
  }
1022
1228
  const closed = result.sessionClose;
1229
+ if (documents.length > 0)
1230
+ closed.outdated_documents = documents;
1231
+ if (boundaryClose) {
1232
+ closed.pending_work = pending;
1233
+ closed.reopen = `aw session-resume --code ${run.session} --reopen`;
1234
+ }
1023
1235
  return {
1024
1236
  ok: closed.closed,
1025
- summary: `sesión ${closed.folder} cerrada${closed.history === undefined ? " (sin fila de HISTORY)" : ` · HISTORY ${closed.history.action}`}`,
1237
+ summary: `sesión ${closed.folder} cerrada${closed.history === undefined ? " (sin fila de HISTORY)" : ` · HISTORY ${closed.history.action}`}${closed.sql_pending_export === undefined ? "" : ` · sql_pending_export: ${closed.sql_pending_export.files.join(", ")} → ${closed.sql_pending_export.command}`}${pending.length === 0 ? "" : ` · ${pending.join(" ")} · ${closed.reopen}`}${documents.length === 0 ? "" : ` · ${documents.join(" · ")}`}${!boundaryClose && closed.unverifiable_sources?.length ? ` · no verificable: ${closed.unverifiable_sources.map((item) => `${item.alias}: ${item.reason}`).join("; ")}` : ""}${closed.empty_units?.length ? ` · vacías conservadas: ${closed.empty_units.map((item) => item.alias).join(", ")}` : ""}${closed.preserved_units?.length ? ` · preservadas conservadas: ${closed.preserved_units.map((item) => item.alias).join(", ")}` : ""}${closed.released_empty?.length ? ` · vacías liberadas: ${closed.released_empty.map((item) => item.alias).join(", ")}` : ""}${closed.released_preserved?.length ? ` · preservadas liberadas: ${closed.released_preserved.map((item) => item.alias).join(", ")}` : ""}${closed.released_integrated?.length ? ` · integradas liberadas: ${closed.released_integrated.map((item) => item.alias).join(", ")}` : ""}${closed.commit_receipt ? ` · commit ${closed.commit_receipt.after}` : closed.commit_error ? ` · commit pendiente: ${closed.commit_error}` : ""}`,
1026
1238
  output: canonicalJson(result),
1027
1239
  // Closing ensures the CHECKPOINT exists and rewrites the session's marker plus
1028
1240
  // its HISTORY row: additive and overwriting, both real.
1029
1241
  effects: closed.closed ? ["local_additive", "mutate_overwrite"] : [],
1030
1242
  };
1031
1243
  }
1244
+ async function releaseClassifiedUnit(deps, alias, folder) {
1245
+ const result = await runWorktree({ fs: deps.fs, env: deps.env, git: deps.git, paths: deps.paths }, { action: "release", alias, sessionCode: folder });
1246
+ if ("error" in result || "released" in result)
1247
+ return result;
1248
+ return { error: "unit_unresolved", message: `no se pudo liberar ${alias} de ${folder}` };
1249
+ }
1032
1250
  function refusal(operation, message, output) {
1033
1251
  return { ok: false, summary: `${operation}: ${message}`, output, effects: [] };
1034
1252
  }