@tacuchi/agent-workflow-cli 25.7.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 (580) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/README.md +5 -1
  3. package/dist/adapters/git-cli.js +311 -24
  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/capability/readiness.js +26 -10
  18. package/dist/application/capability/readiness.js.map +1 -1
  19. package/dist/application/capability/wrapper.js +10 -3
  20. package/dist/application/capability/wrapper.js.map +1 -1
  21. package/dist/application/check-branch-service.js +158 -19
  22. package/dist/application/check-branch-service.js.map +1 -1
  23. package/dist/application/checkpoint/markdown.js +2 -2
  24. package/dist/application/checkpoint/markdown.js.map +1 -1
  25. package/dist/application/checkpoint/state-reader.js +12 -0
  26. package/dist/application/checkpoint/state-reader.js.map +1 -1
  27. package/dist/application/checkpoint-service.js +12 -4
  28. package/dist/application/checkpoint-service.js.map +1 -1
  29. package/dist/application/checkpoint-write-service.js +91 -17
  30. package/dist/application/checkpoint-write-service.js.map +1 -1
  31. package/dist/application/claims-ledger.js +137 -11
  32. package/dist/application/claims-ledger.js.map +1 -1
  33. package/dist/application/claims-recovery.js +85 -44
  34. package/dist/application/claims-recovery.js.map +1 -1
  35. package/dist/application/database-tool-catalog.js +33 -0
  36. package/dist/application/database-tool-catalog.js.map +1 -1
  37. package/dist/application/design/consumer-document.js +2 -2
  38. package/dist/application/design/consumer-document.js.map +1 -1
  39. package/dist/application/design/design-content-gate-service.js +20 -2
  40. package/dist/application/design/design-content-gate-service.js.map +1 -1
  41. package/dist/application/design/design-gate-service.js +2 -0
  42. package/dist/application/design/design-gate-service.js.map +1 -1
  43. package/dist/application/design/design-index-service.js +4 -0
  44. package/dist/application/design/design-index-service.js.map +1 -1
  45. package/dist/application/design/design-publish-service.js +1 -4
  46. package/dist/application/design/design-publish-service.js.map +1 -1
  47. package/dist/application/design/design-record-service.js +13 -7
  48. package/dist/application/design/design-record-service.js.map +1 -1
  49. package/dist/application/design/design-resolver-service.js +22 -3
  50. package/dist/application/design/design-resolver-service.js.map +1 -1
  51. package/dist/application/design/design-simple-service.js +24 -5
  52. package/dist/application/design/design-simple-service.js.map +1 -1
  53. package/dist/application/design/digest.js +41 -0
  54. package/dist/application/design/digest.js.map +1 -0
  55. package/dist/application/dev-only-services.js +171 -27
  56. package/dist/application/dev-only-services.js.map +1 -1
  57. package/dist/application/doc-branch-ledger.js +145 -0
  58. package/dist/application/doc-branch-ledger.js.map +1 -0
  59. package/dist/application/docs-canon-service.js +1 -1
  60. package/dist/application/docs-canon-service.js.map +1 -1
  61. package/dist/application/doctor/dist-integrity.js +113 -0
  62. package/dist/application/doctor/dist-integrity.js.map +1 -0
  63. package/dist/application/doctor/hosts.js +5 -2
  64. package/dist/application/doctor/hosts.js.map +1 -1
  65. package/dist/application/doctor/prepare.js +4 -1
  66. package/dist/application/doctor/prepare.js.map +1 -1
  67. package/dist/application/doctor/provider-installation.js +39 -19
  68. package/dist/application/doctor/provider-installation.js.map +1 -1
  69. package/dist/application/doctor/provider-mcps.js +10 -2
  70. package/dist/application/doctor/provider-mcps.js.map +1 -1
  71. package/dist/application/doctor/provider-skills.js +25 -0
  72. package/dist/application/doctor/provider-skills.js.map +1 -1
  73. package/dist/application/doctor/provider-visibility.js +76 -3
  74. package/dist/application/doctor/provider-visibility.js.map +1 -1
  75. package/dist/application/doctor/provider-workspace-block.js +156 -0
  76. package/dist/application/doctor/provider-workspace-block.js.map +1 -0
  77. package/dist/application/doctor/repair-runner.js +47 -3
  78. package/dist/application/doctor/repair-runner.js.map +1 -1
  79. package/dist/application/doctor/report.js +24 -4
  80. package/dist/application/doctor/report.js.map +1 -1
  81. package/dist/application/doctor/skills-toml-migrate.js +76 -0
  82. package/dist/application/doctor/skills-toml-migrate.js.map +1 -0
  83. package/dist/application/export-catalog-check.js +200 -0
  84. package/dist/application/export-catalog-check.js.map +1 -0
  85. package/dist/application/export-service.js +716 -79
  86. package/dist/application/export-service.js.map +1 -1
  87. package/dist/application/fix-git-service.js +285 -64
  88. package/dist/application/fix-git-service.js.map +1 -1
  89. package/dist/application/flow/advance.js +241 -30
  90. package/dist/application/flow/advance.js.map +1 -1
  91. package/dist/application/flow/annul-service.js +3 -1
  92. package/dist/application/flow/annul-service.js.map +1 -1
  93. package/dist/application/flow/batch-commit.js +189 -0
  94. package/dist/application/flow/batch-commit.js.map +1 -0
  95. package/dist/application/flow/checkout-observation.js +110 -8
  96. package/dist/application/flow/checkout-observation.js.map +1 -1
  97. package/dist/application/flow/close-artifacts.js +105 -0
  98. package/dist/application/flow/close-artifacts.js.map +1 -0
  99. package/dist/application/flow/close-at-boundary.js +155 -0
  100. package/dist/application/flow/close-at-boundary.js.map +1 -0
  101. package/dist/application/flow/close-document-guidance.js +70 -0
  102. package/dist/application/flow/close-document-guidance.js.map +1 -0
  103. package/dist/application/flow/flow-service.js +49 -5
  104. package/dist/application/flow/flow-service.js.map +1 -1
  105. package/dist/application/flow/internal-actions.js +361 -80
  106. package/dist/application/flow/internal-actions.js.map +1 -1
  107. package/dist/application/flow/internal-drive.js +47 -19
  108. package/dist/application/flow/internal-drive.js.map +1 -1
  109. package/dist/application/flow/plan-entry.js +82 -0
  110. package/dist/application/flow/plan-entry.js.map +1 -0
  111. package/dist/application/flow/prove.js +32 -1
  112. package/dist/application/flow/prove.js.map +1 -1
  113. package/dist/application/flow/quick-checkouts.js +48 -0
  114. package/dist/application/flow/quick-checkouts.js.map +1 -0
  115. package/dist/application/flow/reinfer-batch.js +177 -0
  116. package/dist/application/flow/reinfer-batch.js.map +1 -0
  117. package/dist/application/flow/reopen-run.js +159 -0
  118. package/dist/application/flow/reopen-run.js.map +1 -0
  119. package/dist/application/flow/run-journey.js +11 -8
  120. package/dist/application/flow/run-journey.js.map +1 -1
  121. package/dist/application/flow/run-projection.js +16 -5
  122. package/dist/application/flow/run-projection.js.map +1 -1
  123. package/dist/application/flow/run-state-service.js +3 -1
  124. package/dist/application/flow/run-state-service.js.map +1 -1
  125. package/dist/application/flow/submit.js +731 -154
  126. package/dist/application/flow/submit.js.map +1 -1
  127. package/dist/application/generate-launch-service.js +16 -6
  128. package/dist/application/generate-launch-service.js.map +1 -1
  129. package/dist/application/git-flow-service.js +34 -3
  130. package/dist/application/git-flow-service.js.map +1 -1
  131. package/dist/application/history-publications.js +24 -0
  132. package/dist/application/history-publications.js.map +1 -1
  133. package/dist/application/history-reconcile-service.js +45 -0
  134. package/dist/application/history-reconcile-service.js.map +1 -0
  135. package/dist/application/history-table.js +61 -12
  136. package/dist/application/history-table.js.map +1 -1
  137. package/dist/application/history-update-service.js +25 -4
  138. package/dist/application/history-update-service.js.map +1 -1
  139. package/dist/application/hook-branch-check.js +25 -3
  140. package/dist/application/hook-branch-check.js.map +1 -1
  141. package/dist/application/hook-git-commit-advisor.js +3 -4
  142. package/dist/application/hook-git-commit-advisor.js.map +1 -1
  143. package/dist/application/hook-sql-mutation-guard.js +147 -12
  144. package/dist/application/hook-sql-mutation-guard.js.map +1 -1
  145. package/dist/application/hook-turn-start.js +51 -0
  146. package/dist/application/hook-turn-start.js.map +1 -0
  147. package/dist/application/host-memory/codex.js +1 -1
  148. package/dist/application/host-memory/codex.js.map +1 -1
  149. package/dist/application/hub-registry.js +135 -0
  150. package/dist/application/hub-registry.js.map +1 -0
  151. package/dist/application/local-proposal.js +72 -19
  152. package/dist/application/local-proposal.js.map +1 -1
  153. package/dist/application/mcp-host-reader.js +1 -1
  154. package/dist/application/mcp-host-reader.js.map +1 -1
  155. package/dist/application/mcp-host-writer.js +16 -10
  156. package/dist/application/mcp-host-writer.js.map +1 -1
  157. package/dist/application/merge-state-service.js +150 -34
  158. package/dist/application/merge-state-service.js.map +1 -1
  159. package/dist/application/multiroot/claude.js +17 -10
  160. package/dist/application/multiroot/claude.js.map +1 -1
  161. package/dist/application/multiroot/codex.js +17 -11
  162. package/dist/application/multiroot/codex.js.map +1 -1
  163. package/dist/application/multiroot-service.js +52 -28
  164. package/dist/application/multiroot-service.js.map +1 -1
  165. package/dist/application/parsers/execution-batches.js +64 -0
  166. package/dist/application/parsers/execution-batches.js.map +1 -0
  167. package/dist/application/parsers/phases.js +23 -2
  168. package/dist/application/parsers/phases.js.map +1 -1
  169. package/dist/application/parsers/project-block.js +146 -17
  170. package/dist/application/parsers/project-block.js.map +1 -1
  171. package/dist/application/parsers/tasks.js +16 -2
  172. package/dist/application/parsers/tasks.js.map +1 -1
  173. package/dist/application/parsers/toml.js +19 -0
  174. package/dist/application/parsers/toml.js.map +1 -0
  175. package/dist/application/paths-service.js +13 -16
  176. package/dist/application/paths-service.js.map +1 -1
  177. package/dist/application/persist-service.js +118 -5
  178. package/dist/application/persist-service.js.map +1 -1
  179. package/dist/application/plan-exec-batch-service.js +162 -22
  180. package/dist/application/plan-exec-batch-service.js.map +1 -1
  181. package/dist/application/plan-exec-decision-service.js +61 -12
  182. package/dist/application/plan-exec-decision-service.js.map +1 -1
  183. package/dist/application/plan-exec-plan-diff.js +105 -0
  184. package/dist/application/plan-exec-plan-diff.js.map +1 -0
  185. package/dist/application/plan-lint-service.js +2 -0
  186. package/dist/application/plan-lint-service.js.map +1 -1
  187. package/dist/application/plan-passes.js +41 -0
  188. package/dist/application/plan-passes.js.map +1 -0
  189. package/dist/application/preparation-receipts.js +58 -0
  190. package/dist/application/preparation-receipts.js.map +1 -0
  191. package/dist/application/project-md-upsert-service.js +191 -52
  192. package/dist/application/project-md-upsert-service.js.map +1 -1
  193. package/dist/application/project-tab-data.js +40 -10
  194. package/dist/application/project-tab-data.js.map +1 -1
  195. package/dist/application/release-data/artifacts.js +15 -7
  196. package/dist/application/release-data/artifacts.js.map +1 -1
  197. package/dist/application/release-data/bundles.js +52 -6
  198. package/dist/application/release-data/bundles.js.map +1 -1
  199. package/dist/application/release-data/common.js +2 -2
  200. package/dist/application/release-data/common.js.map +1 -1
  201. package/dist/application/release-data/rollback.js +7 -0
  202. package/dist/application/release-data/rollback.js.map +1 -0
  203. package/dist/application/release-data/sessions.js +2 -2
  204. package/dist/application/release-data/sessions.js.map +1 -1
  205. package/dist/application/release-pass-ledger.js +1 -1
  206. package/dist/application/render/history-row.js +2 -0
  207. package/dist/application/render/history-row.js.map +1 -1
  208. package/dist/application/render/project-block.js +28 -11
  209. package/dist/application/render/project-block.js.map +1 -1
  210. package/dist/application/reseal-service.js +1 -0
  211. package/dist/application/reseal-service.js.map +1 -1
  212. package/dist/application/resume-service.js +55 -8
  213. package/dist/application/resume-service.js.map +1 -1
  214. package/dist/application/retirement/apply.js +25 -7
  215. package/dist/application/retirement/apply.js.map +1 -1
  216. package/dist/application/retirement/attribution.js +121 -48
  217. package/dist/application/retirement/attribution.js.map +1 -1
  218. package/dist/application/retirement/prepare.js +17 -11
  219. package/dist/application/retirement/prepare.js.map +1 -1
  220. package/dist/application/retirement/preview.js +2 -1
  221. package/dist/application/retirement/preview.js.map +1 -1
  222. package/dist/application/self/codex-plugin.js +1 -1
  223. package/dist/application/self/codex-plugin.js.map +1 -1
  224. package/dist/application/self/host-states.js +3 -3
  225. package/dist/application/self/host-states.js.map +1 -1
  226. package/dist/application/self/install-hooks.js +1 -1
  227. package/dist/application/self/install-hooks.js.map +1 -1
  228. package/dist/application/self/install-skill.js +35 -11
  229. package/dist/application/self/install-skill.js.map +1 -1
  230. package/dist/application/self/install-targets.js +73 -2
  231. package/dist/application/self/install-targets.js.map +1 -1
  232. package/dist/application/self/mcp-config.js +19 -11
  233. package/dist/application/self/mcp-config.js.map +1 -1
  234. package/dist/application/self/opencode-plugin.js +1 -0
  235. package/dist/application/self/opencode-plugin.js.map +1 -1
  236. package/dist/application/self/uninstall.js +22 -8
  237. package/dist/application/self/uninstall.js.map +1 -1
  238. package/dist/application/semantic-operation/protocol.js +66 -5
  239. package/dist/application/semantic-operation/protocol.js.map +1 -1
  240. package/dist/application/session-archive-service.js +113 -0
  241. package/dist/application/session-archive-service.js.map +1 -0
  242. package/dist/application/session-binding-service.js +35 -0
  243. package/dist/application/session-binding-service.js.map +1 -1
  244. package/dist/application/session-close-service.js +390 -54
  245. package/dist/application/session-close-service.js.map +1 -1
  246. package/dist/application/session-create-service.js +97 -5
  247. package/dist/application/session-create-service.js.map +1 -1
  248. package/dist/application/session-custody-recorder.js +15 -12
  249. package/dist/application/session-custody-recorder.js.map +1 -1
  250. package/dist/application/session-custody-service.js +1 -0
  251. package/dist/application/session-custody-service.js.map +1 -1
  252. package/dist/application/session-narrative.js +51 -12
  253. package/dist/application/session-narrative.js.map +1 -1
  254. package/dist/application/session-pause-service.js +46 -0
  255. package/dist/application/session-pause-service.js.map +1 -0
  256. package/dist/application/session-resolver.js +36 -11
  257. package/dist/application/session-resolver.js.map +1 -1
  258. package/dist/application/session-resume-service.js +190 -20
  259. package/dist/application/session-resume-service.js.map +1 -1
  260. package/dist/application/sessions-service.js +4 -0
  261. package/dist/application/sessions-service.js.map +1 -1
  262. package/dist/application/sessions-sweep-service.js +77 -0
  263. package/dist/application/sessions-sweep-service.js.map +1 -0
  264. package/dist/application/settle-service.js +3 -1
  265. package/dist/application/settle-service.js.map +1 -1
  266. package/dist/application/skills-resolver-service.js +1 -1
  267. package/dist/application/skills-resolver-service.js.map +1 -1
  268. package/dist/application/source-add-service.js +37 -0
  269. package/dist/application/source-add-service.js.map +1 -0
  270. package/dist/application/source-boundary-policy.js +97 -8
  271. package/dist/application/source-boundary-policy.js.map +1 -1
  272. package/dist/application/source-pipeline.js +68 -0
  273. package/dist/application/source-pipeline.js.map +1 -0
  274. package/dist/application/source-remove-service.js +15 -3
  275. package/dist/application/source-remove-service.js.map +1 -1
  276. package/dist/application/sources-service.js +71 -16
  277. package/dist/application/sources-service.js.map +1 -1
  278. package/dist/application/status-service.js +55 -5
  279. package/dist/application/status-service.js.map +1 -1
  280. package/dist/application/unit-dependencies.js +100 -0
  281. package/dist/application/unit-dependencies.js.map +1 -0
  282. package/dist/application/unit-membership.js +37 -0
  283. package/dist/application/unit-membership.js.map +1 -0
  284. package/dist/application/unit-residue.js +72 -0
  285. package/dist/application/unit-residue.js.map +1 -0
  286. package/dist/application/visibility-doctor-service.js +66 -4
  287. package/dist/application/visibility-doctor-service.js.map +1 -1
  288. package/dist/application/working-branch-service.js +6 -0
  289. package/dist/application/working-branch-service.js.map +1 -1
  290. package/dist/application/workline-graph.js +1 -1
  291. package/dist/application/workline-graph.js.map +1 -1
  292. package/dist/application/workline-index-service.js +118 -17
  293. package/dist/application/workline-index-service.js.map +1 -1
  294. package/dist/application/workspace-commit-service.js +218 -0
  295. package/dist/application/workspace-commit-service.js.map +1 -0
  296. package/dist/application/workspace-init-service.js +64 -34
  297. package/dist/application/workspace-init-service.js.map +1 -1
  298. package/dist/application/workspace-local-config.js +90 -0
  299. package/dist/application/workspace-local-config.js.map +1 -0
  300. package/dist/application/workspace-materialization-service.js +52 -3
  301. package/dist/application/workspace-materialization-service.js.map +1 -1
  302. package/dist/application/workspace-migrate/apply.js +525 -4
  303. package/dist/application/workspace-migrate/apply.js.map +1 -1
  304. package/dist/application/workspace-migrate/markers.js +9 -6
  305. package/dist/application/workspace-migrate/markers.js.map +1 -1
  306. package/dist/application/workspace-migrate/plan.js +97 -5
  307. package/dist/application/workspace-migrate/plan.js.map +1 -1
  308. package/dist/application/workspace-move-service.js +270 -0
  309. package/dist/application/workspace-move-service.js.map +1 -0
  310. package/dist/application/workspace-untrack-service.js +28 -0
  311. package/dist/application/workspace-untrack-service.js.map +1 -0
  312. package/dist/application/worktree-service.js +654 -105
  313. package/dist/application/worktree-service.js.map +1 -1
  314. package/dist/cli/ascii.js +86 -0
  315. package/dist/cli/ascii.js.map +1 -0
  316. package/dist/cli/commands/add-source.js +38 -0
  317. package/dist/cli/commands/add-source.js.map +1 -0
  318. package/dist/cli/commands/amend.js +14 -2
  319. package/dist/cli/commands/amend.js.map +1 -1
  320. package/dist/cli/commands/capability.js +26 -0
  321. package/dist/cli/commands/capability.js.map +1 -1
  322. package/dist/cli/commands/check-branch.js +1 -0
  323. package/dist/cli/commands/check-branch.js.map +1 -1
  324. package/dist/cli/commands/checkpoint-read.js +1 -0
  325. package/dist/cli/commands/checkpoint-read.js.map +1 -1
  326. package/dist/cli/commands/checkpoint-write.js +4 -1
  327. package/dist/cli/commands/checkpoint-write.js.map +1 -1
  328. package/dist/cli/commands/claims.js +1 -0
  329. package/dist/cli/commands/claims.js.map +1 -1
  330. package/dist/cli/commands/code-scan.js +1 -0
  331. package/dist/cli/commands/code-scan.js.map +1 -1
  332. package/dist/cli/commands/context-budget.js +1 -0
  333. package/dist/cli/commands/context-budget.js.map +1 -1
  334. package/dist/cli/commands/context-plan.js +1 -0
  335. package/dist/cli/commands/context-plan.js.map +1 -1
  336. package/dist/cli/commands/cut-intent.js +1 -0
  337. package/dist/cli/commands/cut-intent.js.map +1 -1
  338. package/dist/cli/commands/designs.js +15 -5
  339. package/dist/cli/commands/designs.js.map +1 -1
  340. package/dist/cli/commands/dev-only.js +22 -5
  341. package/dist/cli/commands/dev-only.js.map +1 -1
  342. package/dist/cli/commands/doc-branch.js +184 -0
  343. package/dist/cli/commands/doc-branch.js.map +1 -0
  344. package/dist/cli/commands/doctor.js +9 -0
  345. package/dist/cli/commands/doctor.js.map +1 -1
  346. package/dist/cli/commands/export.js +89 -16
  347. package/dist/cli/commands/export.js.map +1 -1
  348. package/dist/cli/commands/fix-git.js +89 -13
  349. package/dist/cli/commands/fix-git.js.map +1 -1
  350. package/dist/cli/commands/flow.js +129 -13
  351. package/dist/cli/commands/flow.js.map +1 -1
  352. package/dist/cli/commands/generate-launch.js +15 -2
  353. package/dist/cli/commands/generate-launch.js.map +1 -1
  354. package/dist/cli/commands/git-flow.js +1 -0
  355. package/dist/cli/commands/git-flow.js.map +1 -1
  356. package/dist/cli/commands/history-update.js +7 -9
  357. package/dist/cli/commands/history-update.js.map +1 -1
  358. package/dist/cli/commands/history.js +14 -0
  359. package/dist/cli/commands/history.js.map +1 -0
  360. package/dist/cli/commands/hook.js +26 -5
  361. package/dist/cli/commands/hook.js.map +1 -1
  362. package/dist/cli/commands/host-doctor.js +1 -0
  363. package/dist/cli/commands/host-doctor.js.map +1 -1
  364. package/dist/cli/commands/host-memory.js +2 -9
  365. package/dist/cli/commands/host-memory.js.map +1 -1
  366. package/dist/cli/commands/index.js +18 -1
  367. package/dist/cli/commands/index.js.map +1 -1
  368. package/dist/cli/commands/mcp.js +30 -4
  369. package/dist/cli/commands/mcp.js.map +1 -1
  370. package/dist/cli/commands/merge-state.js +3 -2
  371. package/dist/cli/commands/merge-state.js.map +1 -1
  372. package/dist/cli/commands/multiroot.js +16 -0
  373. package/dist/cli/commands/multiroot.js.map +1 -1
  374. package/dist/cli/commands/persist.js +7 -5
  375. package/dist/cli/commands/persist.js.map +1 -1
  376. package/dist/cli/commands/plan.js +1 -0
  377. package/dist/cli/commands/plan.js.map +1 -1
  378. package/dist/cli/commands/plugin-cache.js +4 -0
  379. package/dist/cli/commands/plugin-cache.js.map +1 -1
  380. package/dist/cli/commands/plugin-doctor.js +3 -0
  381. package/dist/cli/commands/plugin-doctor.js.map +1 -1
  382. package/dist/cli/commands/project-md-upsert.js +8 -3
  383. package/dist/cli/commands/project-md-upsert.js.map +1 -1
  384. package/dist/cli/commands/release-data.js +12 -0
  385. package/dist/cli/commands/release-data.js.map +1 -1
  386. package/dist/cli/commands/release-pass.js +13 -3
  387. package/dist/cli/commands/release-pass.js.map +1 -1
  388. package/dist/cli/commands/remove-source.js +1 -0
  389. package/dist/cli/commands/remove-source.js.map +1 -1
  390. package/dist/cli/commands/reseal.js +1 -0
  391. package/dist/cli/commands/reseal.js.map +1 -1
  392. package/dist/cli/commands/resume-summary.js +5 -0
  393. package/dist/cli/commands/resume-summary.js.map +1 -1
  394. package/dist/cli/commands/resume.js +13 -3
  395. package/dist/cli/commands/resume.js.map +1 -1
  396. package/dist/cli/commands/retirement.js +1 -0
  397. package/dist/cli/commands/retirement.js.map +1 -1
  398. package/dist/cli/commands/self.js +39 -1
  399. package/dist/cli/commands/self.js.map +1 -1
  400. package/dist/cli/commands/session-artifacts.js +1 -0
  401. package/dist/cli/commands/session-artifacts.js.map +1 -1
  402. package/dist/cli/commands/session-close.js +185 -31
  403. package/dist/cli/commands/session-close.js.map +1 -1
  404. package/dist/cli/commands/session-create.js +4 -1
  405. package/dist/cli/commands/session-create.js.map +1 -1
  406. package/dist/cli/commands/session-pause.js +14 -0
  407. package/dist/cli/commands/session-pause.js.map +1 -0
  408. package/dist/cli/commands/session-resume.js +1 -0
  409. package/dist/cli/commands/session-resume.js.map +1 -1
  410. package/dist/cli/commands/sessions.js +16 -2
  411. package/dist/cli/commands/sessions.js.map +1 -1
  412. package/dist/cli/commands/set-branch.js +29 -4
  413. package/dist/cli/commands/set-branch.js.map +1 -1
  414. package/dist/cli/commands/set-edit-mode.js +20 -0
  415. package/dist/cli/commands/set-edit-mode.js.map +1 -0
  416. package/dist/cli/commands/set-pipeline.js +35 -0
  417. package/dist/cli/commands/set-pipeline.js.map +1 -0
  418. package/dist/cli/commands/settle.js +1 -0
  419. package/dist/cli/commands/settle.js.map +1 -1
  420. package/dist/cli/commands/skill-index.js +1 -0
  421. package/dist/cli/commands/skill-index.js.map +1 -1
  422. package/dist/cli/commands/skills.js +1 -0
  423. package/dist/cli/commands/skills.js.map +1 -1
  424. package/dist/cli/commands/sources.js +5 -0
  425. package/dist/cli/commands/sources.js.map +1 -1
  426. package/dist/cli/commands/stack.js +1 -0
  427. package/dist/cli/commands/stack.js.map +1 -1
  428. package/dist/cli/commands/status.js +36 -2
  429. package/dist/cli/commands/status.js.map +1 -1
  430. package/dist/cli/commands/tool.js +5 -21
  431. package/dist/cli/commands/tool.js.map +1 -1
  432. package/dist/cli/commands/unknown-flags.js +68 -16
  433. package/dist/cli/commands/unknown-flags.js.map +1 -1
  434. package/dist/cli/commands/visibility.js +2 -0
  435. package/dist/cli/commands/visibility.js.map +1 -1
  436. package/dist/cli/commands/workspace-commit.js +62 -0
  437. package/dist/cli/commands/workspace-commit.js.map +1 -0
  438. package/dist/cli/commands/workspace-init.js +51 -2
  439. package/dist/cli/commands/workspace-init.js.map +1 -1
  440. package/dist/cli/commands/workspace-migrate.js +31 -14
  441. package/dist/cli/commands/workspace-migrate.js.map +1 -1
  442. package/dist/cli/commands/workspace-move.js +42 -0
  443. package/dist/cli/commands/workspace-move.js.map +1 -0
  444. package/dist/cli/commands/worktree.js +5 -4
  445. package/dist/cli/commands/worktree.js.map +1 -1
  446. package/dist/cli/context-id.js +6 -0
  447. package/dist/cli/context-id.js.map +1 -1
  448. package/dist/cli/full-cli.js +555 -0
  449. package/dist/cli/full-cli.js.map +1 -0
  450. package/dist/cli/help-groups.js +108 -14
  451. package/dist/cli/help-groups.js.map +1 -1
  452. package/dist/cli/hook-stdin-cache.js +12 -0
  453. package/dist/cli/hook-stdin-cache.js.map +1 -0
  454. package/dist/cli/main.js +47 -471
  455. package/dist/cli/main.js.map +1 -1
  456. package/dist/cli/output-mode.js +11 -2
  457. package/dist/cli/output-mode.js.map +1 -1
  458. package/dist/cli/parser.js +18 -5
  459. package/dist/cli/parser.js.map +1 -1
  460. package/dist/cli/registry.js.map +1 -1
  461. package/dist/cli/render.js +30 -1
  462. package/dist/cli/render.js.map +1 -1
  463. package/dist/cli/tui/app.js +1 -1
  464. package/dist/cli/tui/app.js.map +1 -1
  465. package/dist/cli/tui/components/workspace-init-form.js +4 -1
  466. package/dist/cli/tui/components/workspace-init-form.js.map +1 -1
  467. package/dist/cli/tui/data/recommended-skills.js +2 -2
  468. package/dist/cli/tui/data/recommended-skills.js.map +1 -1
  469. package/dist/cli/tui/data/workflow-content.js +6 -1
  470. package/dist/cli/tui/data/workflow-content.js.map +1 -1
  471. package/dist/cli/tui/tabs/mcp-tab.js +4 -3
  472. package/dist/cli/tui/tabs/mcp-tab.js.map +1 -1
  473. package/dist/cli/tui/tabs/project-tab.js +7 -2
  474. package/dist/cli/tui/tabs/project-tab.js.map +1 -1
  475. package/dist/dist-manifest.json +4117 -0
  476. package/dist/domain/capability/protocol.js.map +1 -1
  477. package/dist/domain/decision-note.js +13 -0
  478. package/dist/domain/decision-note.js.map +1 -1
  479. package/dist/domain/design/artifact.js +22 -3
  480. package/dist/domain/design/artifact.js.map +1 -1
  481. package/dist/domain/design/capability.js +1 -0
  482. package/dist/domain/design/capability.js.map +1 -1
  483. package/dist/domain/design/expansion.js +1 -1
  484. package/dist/domain/design/expansion.js.map +1 -1
  485. package/dist/domain/design/maturity.js +16 -0
  486. package/dist/domain/design/maturity.js.map +1 -1
  487. package/dist/domain/design/reference.js +5 -0
  488. package/dist/domain/design/reference.js.map +1 -1
  489. package/dist/domain/doctor/operations.js +28 -2
  490. package/dist/domain/doctor/operations.js.map +1 -1
  491. package/dist/domain/effective-contract.js +3 -1
  492. package/dist/domain/effective-contract.js.map +1 -1
  493. package/dist/domain/elicitation.js +1 -1
  494. package/dist/domain/flow/answer.js +273 -130
  495. package/dist/domain/flow/answer.js.map +1 -1
  496. package/dist/domain/flow/authority.js +330 -44
  497. package/dist/domain/flow/authority.js.map +1 -1
  498. package/dist/domain/flow/batch-review.js +65 -0
  499. package/dist/domain/flow/batch-review.js.map +1 -0
  500. package/dist/domain/flow/decision-schema.js +47 -0
  501. package/dist/domain/flow/decision-schema.js.map +1 -0
  502. package/dist/domain/flow/directive.js +16 -0
  503. package/dist/domain/flow/directive.js.map +1 -1
  504. package/dist/domain/flow/execution-result.js +169 -9
  505. package/dist/domain/flow/execution-result.js.map +1 -1
  506. package/dist/domain/flow/route.js.map +1 -1
  507. package/dist/domain/flow/rules.js +5 -6
  508. package/dist/domain/flow/rules.js.map +1 -1
  509. package/dist/domain/flow/run-state.js +531 -47
  510. package/dist/domain/flow/run-state.js.map +1 -1
  511. package/dist/domain/flow/test-run-evidence.js +285 -0
  512. package/dist/domain/flow/test-run-evidence.js.map +1 -0
  513. package/dist/domain/flow/unchanged-phase.js +15 -0
  514. package/dist/domain/flow/unchanged-phase.js.map +1 -0
  515. package/dist/domain/harnesses.js +22 -6
  516. package/dist/domain/harnesses.js.map +1 -1
  517. package/dist/domain/isolation-unit.js +2 -2
  518. package/dist/domain/isolation-unit.js.map +1 -1
  519. package/dist/domain/lineage.js +7 -2
  520. package/dist/domain/lineage.js.map +1 -1
  521. package/dist/domain/obligation-correspondence.js +4 -2
  522. package/dist/domain/obligation-correspondence.js.map +1 -1
  523. package/dist/domain/plan-handoff.js +5 -0
  524. package/dist/domain/plan-handoff.js.map +1 -0
  525. package/dist/domain/proposal.js +18 -7
  526. package/dist/domain/proposal.js.map +1 -1
  527. package/dist/domain/reservation.js +5 -3
  528. package/dist/domain/reservation.js.map +1 -1
  529. package/dist/domain/retirement/proposal.js +1 -0
  530. package/dist/domain/retirement/proposal.js.map +1 -1
  531. package/dist/domain/session/custody.js +10 -6
  532. package/dist/domain/session/custody.js.map +1 -1
  533. package/dist/domain/session/narrative.js +4 -1
  534. package/dist/domain/session/narrative.js.map +1 -1
  535. package/dist/domain/skills.js +2 -0
  536. package/dist/domain/skills.js.map +1 -1
  537. package/dist/domain/sql-lexer.js +15 -2
  538. package/dist/domain/sql-lexer.js.map +1 -1
  539. package/dist/runtime/namespace-resolver.js +4 -0
  540. package/dist/runtime/namespace-resolver.js.map +1 -1
  541. package/dist/runtime/workline-marker.js +35 -8
  542. package/dist/runtime/workline-marker.js.map +1 -1
  543. package/dist/runtime/workspace-resolution.js +123 -0
  544. package/dist/runtime/workspace-resolution.js.map +1 -0
  545. package/package.json +5 -4
  546. package/skills/w/SKILL.md +3 -1
  547. package/skills/w/artifacts/artifacts-core/CHECKPOINT.md +1 -1
  548. package/skills/w/artifacts/artifacts-core/SCRIPTS.rollback.sql +8 -0
  549. package/skills/w/artifacts/artifacts-core/SCRIPTS.sql +4 -8
  550. package/skills/w/commands/export-diagrams.md +3 -3
  551. package/skills/w/commands/export-manuals.md +6 -6
  552. package/skills/w/commands/export-reports.md +3 -3
  553. package/skills/w/commands/export-scripts.md +5 -5
  554. package/skills/w/commands/fix-git.md +12 -13
  555. package/skills/w/commands/persist.md +2 -2
  556. package/skills/w/commands/plan-exec.md +4 -2
  557. package/skills/w/commands/spec-new.md +1 -1
  558. package/skills/w/commands/workspace-init.md +2 -0
  559. package/skills/w/context/MANIFEST.json +1 -1
  560. package/skills/w/exports/export-diagrams/EXPORT.md +3 -3
  561. package/skills/w/exports/export-manuals/EXPORT.md +14 -16
  562. package/skills/w/exports/export-reports/EXPORT.md +3 -3
  563. package/skills/w/exports/export-scripts/EXPORT.md +45 -21
  564. package/skills/w/harness/HARNESS.md +5 -3
  565. package/skills/w/hooks/hooks.template.json +6 -4
  566. package/skills/w/loops/CHASSIS.md +12 -6
  567. package/skills/w/loops/CODE-POLICIES.md +7 -4
  568. package/skills/w/loops/plan-exec-loop/LOOP.md +23 -22
  569. package/skills/w/loops/plan-new-loop/LOOP.md +23 -31
  570. package/skills/w/loops/plan-refine-loop/LOOP.md +8 -2
  571. package/skills/w/loops/quick-loop/LOOP.md +6 -5
  572. package/skills/w/modules/DESIGN-REFERENCES.md +9 -11
  573. package/skills/w/modules/PLAN-EXECUTION-BATCHES.md +4 -4
  574. package/skills/w/modules/SESSION-NUMBERING.md +2 -0
  575. package/skills/w/roles/design/CONTRACT.md +16 -7
  576. package/skills/w/roles/design/ROLE.md +4 -2
  577. package/skills/w/roles/git/ROLE.md +16 -9
  578. package/skills/w/roles/research/ROLE.md +1 -1
  579. package/skills/w/roles/sql/ROLE.md +16 -14
  580. package/skills/w/schemas/design/ui-screen.v1.schema.json +9 -0
@@ -25,34 +25,42 @@
25
25
  */
26
26
  import { join } from "node:path";
27
27
  import { dispositionOf } from "../../domain/flow/route.js";
28
- import { withPlanExecBatch, 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";
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";
47
55
  import { projectRun } from "./run-projection.js";
48
56
  import { applyUnderLock, locateRun, readRun } from "./run-state-service.js";
49
57
  export function internalActionExecutor(deps) {
50
58
  return async (plan, run) => {
51
59
  switch (plan.operation) {
52
60
  case "workspace.board":
53
- return board(deps);
61
+ return board(deps, run);
54
62
  case "session.artifacts":
55
- return artifacts(deps, run, plan.dump ?? null);
63
+ return artifacts(deps, run, plan.dump ?? null, run.session.endsWith("-plan-refine"));
56
64
  case "session.close":
57
65
  return close(deps, run);
58
66
  case "worktree.ensure":
@@ -63,6 +71,8 @@ export function internalActionExecutor(deps) {
63
71
  return inferBatch(deps, run);
64
72
  case "plan-exec.batch-close":
65
73
  return closeBatch(deps, run);
74
+ case "plan-exec.batch-commit":
75
+ return commitBatch(deps, run);
66
76
  case "plan-exec.plan-done":
67
77
  return sealPlanDone(deps, run);
68
78
  case "plan-exec.settlement-publish":
@@ -90,7 +100,7 @@ async function ensureUnits(deps, run) {
90
100
  return refusal("worktree.ensure", "la corrida no fijó qué fuentes edita: no hay unidad que adquirir", canonicalJson({ scope: null }));
91
101
  }
92
102
  const acquired = [];
93
- for (const alias of scope.sources) {
103
+ for (const alias of scope.isolation === "in-place" ? [] : scope.sources) {
94
104
  // `workspace` is the documentary/control checkout itself. It is a valid
95
105
  // source-bounded proof surface, but never a source repository that needs a
96
106
  // per-session Git worktree.
@@ -108,16 +118,125 @@ async function ensureUnits(deps, run) {
108
118
  }
109
119
  acquired.push(result);
110
120
  }
121
+ const based = await recordBatchBase(deps, run, scope.sources);
122
+ if (!based.ok) {
123
+ return refusal("worktree.ensure", `${based.failure.message} — ${based.failure.action}`, canonicalJson({ failure: based.failure, acquired }));
124
+ }
111
125
  return {
112
126
  ok: true,
113
- 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(", ")}`,
114
130
  output: canonicalJson({ plan: scope.plan, units: acquired }),
115
131
  // The tree IS there, however it got there — the same reading `proposal.publish`
116
132
  // makes of a re-entry that finds the bytes already written. Crediting nothing
117
133
  // when `created` is false would refuse the resumption this row is idempotent for.
118
- effects: ["local_additive"],
134
+ effects: ["local_additive", "mutate_overwrite"],
135
+ ...(based.state === null ? {} : { state: based.state }),
119
136
  };
120
137
  }
138
+ /** The batch this iteration walks, while it still has no base. */
139
+ function batchAwaitingBase(state) {
140
+ const iteration = state.batch_loop?.iteration ?? null;
141
+ const batch = (state.batches ?? []).find((entry) => entry.iteration === iteration);
142
+ return batch?.base === undefined ? batch : undefined;
143
+ }
144
+ /**
145
+ * Seal, on the batch this iteration walks, what each of its sources looked like
146
+ * before its work — the base it will have to have changed to be credited.
147
+ *
148
+ * Taken here because this row runs once per iteration, after the previous
149
+ * batch's close and commit: comparing against the previous CREDIT instead would
150
+ * let the plan that close published, or the review's fixes, pass as the next
151
+ * batch's work. The first batch's base is the run's start.
152
+ */
153
+ async function recordBatchBase(deps, run, sources) {
154
+ // Measuring hashes whole trees, so it happens only when a batch is waiting for
155
+ // its base; the lock below re-checks, and a run that moved fails its CAS.
156
+ const live = await readRun(deps.fs, locateRun(deps.paths, run.session));
157
+ if (live.ok && batchAwaitingBase(live.state) === undefined)
158
+ return { ok: true, state: null };
159
+ const observed = await observeScopedFingerprints(deps.fs, deps.git, deps.paths, run.session, sources);
160
+ if (!observed.ok)
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
+ }
211
+ const recorded = await applyUnderLock(deps.fs, locateRun(deps.paths, run.session), (current) => {
212
+ if (current === null) {
213
+ return {
214
+ ok: false,
215
+ failure: {
216
+ code: "FLOW_RUN_ABSENT",
217
+ message: "la corrida desapareció antes de sellar la base del batch",
218
+ action: "reanudá la corrida con 'aw flow advance'",
219
+ },
220
+ };
221
+ }
222
+ const batch = batchAwaitingBase(current);
223
+ // Outside plan-exec, before any batch was inferred, or with its base already
224
+ // sealed, there is nothing to seal.
225
+ if (batch === undefined)
226
+ return { ok: true, state: current, value: null, persist: false };
227
+ // A reseated run already holds the work of the batches it re-infers: their
228
+ // base is the one the archived run sealed before that work, not today's tree.
229
+ const inherited = current.inherited_bases?.find((entry) => entry.phases.length === batch.phases.length &&
230
+ entry.phases.every((phase, index) => phase === batch.phases[index]));
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 }));
234
+ return { ok: true, state: next, value: null, persist: next !== current };
235
+ }, run.state_digest === undefined ? {} : { expectDigest: run.state_digest });
236
+ if (!recorded.ok)
237
+ return recorded;
238
+ return { ok: true, state: recorded.state };
239
+ }
121
240
  /**
122
241
  * Write the run's approved proposal — all of it, or none of it.
123
242
  *
@@ -158,7 +277,7 @@ async function publish(deps, run) {
158
277
  ok: true,
159
278
  summary: result.already_applied
160
279
  ? `la propuesta ya estaba aplicada: ${destinations.join(", ")} sin cambios`
161
- : `publicado: ${result.written.join(", ")}`,
280
+ : `publicado: ${result.written.join(", ")}${result.notice === undefined ? "" : `; aviso: ${result.notice}`}`,
162
281
  output: canonicalJson({ ...result, digest: proposal.digest, destinations, claims }),
163
282
  // Only what really landed. A re-entry that found the bytes already there
164
283
  // credits the same classes — the effect IS applied — and the summary is what
@@ -176,8 +295,8 @@ async function publish(deps, run) {
176
295
  * never held up by a ledger it does not depend on — an unreadable ledger must not
177
296
  * stop every loop in the system from saving.
178
297
  */
179
- async function revokedFence(deps, owner, destinations) {
180
- 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 });
181
300
  const blocked = revokedAmong(read.events, owner, destinations);
182
301
  if (blocked.length > 0) {
183
302
  return {
@@ -187,13 +306,10 @@ async function revokedFence(deps, owner, destinations) {
187
306
  };
188
307
  }
189
308
  if (read.unreadable > 0) {
190
- // Scoped to claims this owner ACTUALLY holds in the ledger, not to every
191
- // numbered destination. Otherwise one unparseable line — and this file is a
192
- // committed append-only JSONL, so a merge conflict produces exactly that —
193
- // would refuse every SPEC, PLAN and QUICK save in the workspace, which is a
194
- // workspace-wide outage rather than the narrow fail-closed this needs to be.
195
- const mine = new Set(openClaimsOf(read.events, owner).map((claim) => claimKey(claim)));
196
- 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);
197
313
  if (atRisk.length > 0) {
198
314
  return {
199
315
  code: "CLAIM_LEDGER_UNREADABLE",
@@ -229,6 +345,8 @@ async function recordCompletedClaims(deps, owner, result, destinations) {
229
345
  const recorded = [];
230
346
  try {
231
347
  const read = await readClaimEvents(deps.fs, deps.paths);
348
+ if (read.unreadable > 0)
349
+ return { recorded, ledger_unreadable: read.unreadable };
232
350
  const completed = completedClaimsIn(read.events, owner, {
233
351
  written: result.written,
234
352
  already_applied: result.already_applied,
@@ -255,13 +373,23 @@ async function recordCompletedClaims(deps, owner, result, destinations) {
255
373
  };
256
374
  }
257
375
  }
258
- async function board(deps) {
376
+ async function board(deps, run) {
259
377
  const data = await runStatusCommand(deps.fs, deps.env, deps.paths, { git: deps.git });
260
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`;
261
389
  return {
262
390
  ok: true,
263
- summary: `tablero: ${counts.specs} specs, ${counts.plans} planes, ${counts.sessions_active} sesiones activas, ${counts.pending} pendientes`,
264
- 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),
265
393
  effects: ["read_only"],
266
394
  };
267
395
  }
@@ -275,10 +403,13 @@ async function board(deps) {
275
403
  * says the specific artifacts have content. A dump alone would pass a session
276
404
  * whose criteria were never seeded.
277
405
  */
278
- async function artifacts(deps, run, dump) {
406
+ async function artifacts(deps, run, dump, planRefine = false) {
279
407
  // The presence report, without the narrative: this operation checks that the
280
408
  // artifacts are THERE, and projecting the session's whole reading to answer
281
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
+ }
282
413
  const report = await runArtifactsCommand(deps.fs, deps.env, deps.paths, {
283
414
  code: run.code,
284
415
  noNarrative: true,
@@ -289,22 +420,27 @@ async function artifacts(deps, run, dump) {
289
420
  if (report.artifacts.session === null) {
290
421
  return refusal("session.artifacts", `la sesión '${report.session}' no tiene su SESSION.md`, canonicalJson(report));
291
422
  }
423
+ const destination = planRefine ? await refineDestination(deps, run, canonicalJson(report)) : "";
424
+ if (typeof destination !== "string")
425
+ return destination;
426
+ const legacyWarning = destination;
292
427
  if (dump === null) {
293
428
  return {
294
429
  ok: true,
295
- 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}`,
296
431
  output: canonicalJson(report),
297
432
  effects: SEEDED_EFFECTS,
298
433
  };
299
434
  }
300
435
  const dumped = await readSessionArtifacts(deps.fs, deps.paths, run.code, [...dump], deps.runtime);
301
- 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 });
302
438
  if (dumped.error !== undefined) {
303
439
  return refusal("session.artifacts", String(dumped.hint ?? dumped.error), output);
304
440
  }
305
- const empty = dump.filter((kind) => !hasContent(dumped[kind]));
441
+ const empty = dump.filter((kind) => !hasContent(dumped[kind]) && !(kind === "scripts" && hasContent(script)));
306
442
  if (empty.length > 0) {
307
- 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);
308
444
  }
309
445
  // `objetivo` is the artifact that carries the success criteria, so demanding it
310
446
  // is demanding them: a SESSION.md with a criteria heading and nothing under it
@@ -316,11 +452,46 @@ async function artifacts(deps, run, dump) {
316
452
  }
317
453
  return {
318
454
  ok: true,
319
- 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}`,
320
456
  output,
321
457
  effects: SEEDED_EFFECTS,
322
458
  };
323
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
+ }
324
495
  /**
325
496
  * What a satisfied artifact reading really attests.
326
497
  *
@@ -399,6 +570,16 @@ async function inferBatch(deps, run) {
399
570
  }
400
571
  const active = (current.batches ?? []).find((batch) => batch.published_plan_digest === undefined);
401
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
+ }
402
583
  return {
403
584
  ok: true,
404
585
  state: current,
@@ -420,7 +601,7 @@ async function inferBatch(deps, run) {
420
601
  },
421
602
  };
422
603
  }
423
- const next = inferNextBatch(text, current);
604
+ const next = inferNextPlanExecBatch(text, current);
424
605
  if (!next.ok) {
425
606
  if (next.failure.code !== "PLAN_EXEC_BATCH_NONE_OPEN") {
426
607
  return { ok: false, failure: next.failure };
@@ -437,6 +618,27 @@ async function inferBatch(deps, run) {
437
618
  value: { batch: null, created: false, no_work: true },
438
619
  };
439
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
+ }
440
642
  return {
441
643
  ok: true,
442
644
  state: withPlanExecBatch(current, next.batch),
@@ -451,7 +653,7 @@ async function inferBatch(deps, run) {
451
653
  summary: inferred.value.no_work
452
654
  ? "el plan ya no tiene tareas abiertas: se omite el batch vacío y se expone la validación final"
453
655
  : inferred.value.created
454
- ? `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)}`
455
657
  : `batch ${inferred.value.batch?.id ?? "actual"} ya estaba inferido; se conserva su snapshot sellado`,
456
658
  output: canonicalJson({ batch: inferred.value.batch, created: inferred.value.created }),
457
659
  effects: [],
@@ -585,7 +787,27 @@ async function closeBatch(deps, run) {
585
787
  if (batch === undefined) {
586
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 }));
587
789
  }
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
+ }
794
+ // Reaching the close without that credit — an exhausted validation degraded
795
+ // past, a route that skipped it — would publish its tasks as done on no proof.
796
+ if (batch.base !== undefined && batch.credit === undefined) {
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 }));
798
+ }
588
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
+ }
589
811
  if (!phaseUpdates.ok) {
590
812
  return refusal("plan-exec.batch-close", `${phaseUpdates.failure.message} — ${phaseUpdates.failure.action}`, canonicalJson({ failure: phaseUpdates.failure }));
591
813
  }
@@ -613,11 +835,12 @@ async function closeBatch(deps, run) {
613
835
  ok: true,
614
836
  summary: published.already_applied
615
837
  ? `batch ${published.batch.id} ya estaba publicado en ${run.scope.plan}`
616
- : `batch ${published.batch.id} publicado: ${published.written.join(", ")}`,
838
+ : `batch ${published.batch.id} publicado: ${published.written.join(", ")}${published.notice === undefined ? "" : `; aviso: ${published.notice}`}`,
617
839
  output: canonicalJson({
618
840
  batch: published.batch,
619
841
  written: published.written,
620
842
  already_applied: published.already_applied,
843
+ ...(published.notice === undefined ? {} : { notice: published.notice }),
621
844
  }),
622
845
  effects: ["mutate_overwrite"],
623
846
  state,
@@ -634,6 +857,10 @@ async function closeBatch(deps, run) {
634
857
  * already names the NEXT batch — or nothing, when it was the last one. The
635
858
  * publisher then confirms it through its own already-applied path.
636
859
  */
860
+ function validationOnlyCloseMissing(state, batch) {
861
+ return (batch.kind === "validation-only" &&
862
+ (!approvedValidationOnly(state, batch) || batch.credit === undefined));
863
+ }
637
864
  function batchToClose(state) {
638
865
  const batches = state.batches ?? [];
639
866
  const open = batches.find((batch) => batch.published_plan_digest === undefined);
@@ -693,11 +920,21 @@ async function sealPlanDone(deps, run) {
693
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 }));
694
921
  }
695
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
+ }
696
929
  if (live.state.applied.includes(transition))
697
930
  return false;
698
931
  const disposition = dispositionOf(live.state.route_decisions, transition)?.disposition;
699
932
  return disposition !== "omit" && disposition !== "substitute";
700
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
+ }
701
938
  if (missing.length > 0) {
702
939
  return refusal("plan-exec.plan-done", `todavía falta evidencia de ${missing.join(", ")}`, canonicalJson({ code: "PLAN_EXEC_DONE_PREREQUISITE", missing }));
703
940
  }
@@ -765,6 +1002,12 @@ async function sealPlanDone(deps, run) {
765
1002
  }
766
1003
  /** The plan seal says exactly which evidence was absent or substituted. */
767
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";
768
1011
  const decisions = state.route_decisions ?? [];
769
1012
  const controls = new Map((state.route_proposal?.controls ?? []).map((control) => [control.transition, control]));
770
1013
  const omitted = decisions
@@ -778,15 +1021,15 @@ function planDoneClosure(state, session) {
778
1021
  .map((decision) => decision.substitution)
779
1022
  .filter((substitution) => substitution !== null);
780
1023
  if (state.assurance === "unverified_accepted") {
781
- 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ó`;
782
1025
  }
783
1026
  if (state.assurance === "partially_verified") {
784
- 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}`;
785
1028
  }
786
1029
  if (substitutions.length > 0) {
787
- 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}`;
788
1031
  }
789
- return `validación final, commits e integración acreditados por la corrida ${session}`;
1032
+ return `validación final, ${destination} acreditados por la corrida ${session}`;
790
1033
  }
791
1034
  /** The documentary checks that must pass in the same critical section as done. */
792
1035
  export async function planDonePrecondition(deps, path, session) {
@@ -895,40 +1138,6 @@ function phaseUpdatesForClosedBatch(text, batch) {
895
1138
  updates: batch.phases.map((phase) => ({ phase, state: "validada" })),
896
1139
  };
897
1140
  }
898
- function inferNextBatch(text, state) {
899
- // `inferPlanExecBatch` itself validates that the phase has real, uniquely
900
- // labelled Tn.m tasks. We only choose the first still-open phase from the
901
- // document, which is a deterministic batch boundary rather than a claimed one.
902
- const openPhase = parseTasks(text).items.find((task) => task.status === "open" && task.phase !== undefined)?.phase;
903
- if (openPhase === undefined) {
904
- const unresolved = parsePhases(text).items.find((phase) => phase.state !== "validada");
905
- if (unresolved !== undefined) {
906
- return {
907
- ok: false,
908
- failure: {
909
- code: "PLAN_EXEC_BATCH_PHASE_UNRESOLVED",
910
- message: `F${unresolved.n} sigue '${unresolved.state}' pero no tiene tareas abiertas acreditables`,
911
- action: "normalizá la fase con plan-refine; no se salta a la validación final sobre una fase no validada",
912
- },
913
- };
914
- }
915
- return {
916
- ok: false,
917
- failure: {
918
- code: "PLAN_EXEC_BATCH_NONE_OPEN",
919
- message: "el plan no tiene una fase con tareas abiertas que este batch pueda acreditar",
920
- action: "el batch ya está cerrado: reanudá la corrida para que exponga la validación final",
921
- },
922
- };
923
- }
924
- const iteration = Math.max(0, ...(state.batches ?? []).map((batch) => batch.iteration)) + 1;
925
- return inferPlanExecBatch(text, {
926
- id: `batch-${iteration}`,
927
- iteration,
928
- mode: "continuous",
929
- phases: [openPhase],
930
- });
931
- }
932
1141
  /**
933
1142
  * Close the run's session — and refuse to, while it still holds a unit.
934
1143
  *
@@ -940,14 +1149,74 @@ function inferNextBatch(text, state) {
940
1149
  * notice behind a `.closed` marker that also makes the remedy stop resolving.
941
1150
  */
942
1151
  async function close(deps, run) {
943
- const result = await runSessionClose(deps.fs, deps.paths, { code: run.code, requireIntegrated: true }, async () => {
944
- const listed = await runWorktree({ fs: deps.fs, env: deps.env, git: deps.git, paths: deps.paths }, { action: "list" });
945
- // A list that did not come back is NOT "no units": the close refuses on it,
946
- // which is the whole point of asking before writing the marker.
947
- if (!("units" in listed))
948
- throw new Error(JSON.stringify(listed));
949
- return listed.units;
950
- });
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
+ }
951
1220
  if ("sessionHeld" in result) {
952
1221
  const held = result.sessionHeld;
953
1222
  return refusal("session.close", `${held.reason} — integralas con '${held.integrate}'`, canonicalJson(result));
@@ -957,15 +1226,27 @@ async function close(deps, run) {
957
1226
  return refusal("session.close", `la sesión no cerró: ${why}`, canonicalJson(result));
958
1227
  }
959
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
+ }
960
1235
  return {
961
1236
  ok: closed.closed,
962
- 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}` : ""}`,
963
1238
  output: canonicalJson(result),
964
1239
  // Closing ensures the CHECKPOINT exists and rewrites the session's marker plus
965
1240
  // its HISTORY row: additive and overwriting, both real.
966
1241
  effects: closed.closed ? ["local_additive", "mutate_overwrite"] : [],
967
1242
  };
968
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
+ }
969
1250
  function refusal(operation, message, output) {
970
1251
  return { ok: false, summary: `${operation}: ${message}`, output, effects: [] };
971
1252
  }