@fitlab-ai/agent-infra 0.9.14 → 0.10.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 (354) hide show
  1. package/README.md +10 -0
  2. package/README.zh-CN.md +10 -0
  3. package/bin/internal-cli.sh +45 -0
  4. package/dist/bin/internal-cli.js +93 -30
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +9 -4
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +43 -3
  8. package/dist/lib/defaults.json +103 -103
  9. package/dist/lib/fs/durable-write.js +39 -0
  10. package/dist/lib/fs/file-lock.js +111 -0
  11. package/dist/lib/host-control/audit.js +19 -0
  12. package/dist/lib/host-control/client.js +142 -0
  13. package/dist/lib/host-control/command.js +49 -0
  14. package/dist/lib/host-control/path.js +160 -0
  15. package/dist/lib/host-control/secure-fs.js +140 -0
  16. package/dist/lib/host-control/server.js +159 -0
  17. package/dist/lib/host-control/service.js +112 -0
  18. package/dist/lib/init.js +11 -2
  19. package/dist/lib/internal/cli-route-inventory.js +4 -2
  20. package/dist/lib/internal/codex-lifecycle.js +6 -6
  21. package/dist/lib/internal/demo-promotion.js +322 -0
  22. package/dist/lib/internal/demo-transcript.js +454 -0
  23. package/dist/lib/internal/host-control.js +28 -0
  24. package/dist/lib/internal/manual-validation.js +331 -0
  25. package/dist/lib/internal/platform-pr.js +18 -4
  26. package/dist/lib/internal/release-workflow.js +63 -84
  27. package/dist/lib/internal/sandbox-control.js +149 -2
  28. package/dist/lib/internal/task-artifact.js +16 -263
  29. package/dist/lib/internal/task-create.js +6 -5
  30. package/dist/lib/internal/task-event.js +37 -37
  31. package/dist/lib/internal/task-lifecycle.js +2 -2
  32. package/dist/lib/internal/task-operation-registry.js +101 -4
  33. package/dist/lib/internal/task-review.js +11 -56
  34. package/dist/lib/internal/task-validate.js +30 -10
  35. package/dist/lib/owned-path.js +33 -0
  36. package/dist/lib/platform/check-status.js +11 -0
  37. package/dist/lib/platform/comment-safety.js +4 -1
  38. package/dist/lib/platform/completion-backfill.js +1 -1
  39. package/dist/lib/platform/github-data.js +241 -0
  40. package/dist/lib/platform/github-provider.js +15 -20
  41. package/dist/lib/platform/issue-comments.js +18 -12
  42. package/dist/lib/platform/issues.js +2 -180
  43. package/dist/lib/platform/pr-change-report.js +5 -4
  44. package/dist/lib/platform/pr-checks.js +12 -65
  45. package/dist/lib/platform/pr-summary.js +117 -33
  46. package/dist/lib/platform/pull-requests.js +5 -108
  47. package/dist/lib/platform/snapshots.js +2 -0
  48. package/dist/lib/platform/task-identities.js +2 -9
  49. package/dist/lib/platform/verification-sync.js +132 -143
  50. package/dist/lib/pr-review/evidence-grading.js +4 -8
  51. package/dist/lib/release/workflow.js +1 -1
  52. package/dist/lib/sandbox/commands/create.js +7 -2
  53. package/dist/lib/sandbox/commands/rm.js +19 -1511
  54. package/dist/lib/sandbox/control/audit.js +203 -0
  55. package/dist/lib/sandbox/control/client.js +93 -54
  56. package/dist/lib/sandbox/control/executor.js +129 -65
  57. package/dist/lib/sandbox/control/finalization-response.js +27 -0
  58. package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
  59. package/dist/lib/sandbox/control/lifecycle.js +36 -2
  60. package/dist/lib/sandbox/control/native-file-lock.js +6 -98
  61. package/dist/lib/sandbox/control/protocol.js +55 -4
  62. package/dist/lib/sandbox/control/server.js +518 -127
  63. package/dist/lib/sandbox/control/state.js +64 -0
  64. package/dist/lib/sandbox/control/task-workflow.js +74 -0
  65. package/dist/lib/sandbox/control/workflow-executor.js +89 -0
  66. package/dist/lib/sandbox/index.js +1 -2
  67. package/dist/lib/sandbox/recovery.js +28 -4
  68. package/dist/lib/sandbox/removal.js +822 -0
  69. package/dist/lib/sandbox/task-cleanup.js +217 -0
  70. package/dist/lib/sandbox/workspace-identity.js +17 -1
  71. package/dist/lib/sandbox/workspace-view.js +13 -14
  72. package/dist/lib/task/activity-intent.js +2 -1
  73. package/dist/lib/task/activity-log.js +30 -24
  74. package/dist/lib/task/artifact-command.js +79 -0
  75. package/dist/lib/task/artifact-lifecycle.js +30 -28
  76. package/dist/lib/task/artifact-name.js +36 -0
  77. package/dist/lib/task/artifact-operations.js +29 -289
  78. package/dist/lib/task/artifact-receipts.js +3 -15
  79. package/dist/lib/task/artifact-recovery.js +430 -0
  80. package/dist/lib/task/artifact-repair-intent.js +134 -29
  81. package/dist/lib/task/artifact-schema.js +6 -14
  82. package/dist/lib/task/capabilities.js +12 -19
  83. package/dist/lib/task/codex-orchestration.js +71 -25
  84. package/dist/lib/task/commit-identity.js +28 -0
  85. package/dist/lib/task/commit-operation.js +33 -20
  86. package/dist/lib/task/control-authority.js +403 -6
  87. package/dist/lib/task/control-recovery.js +140 -0
  88. package/dist/lib/task/create-service.js +1 -8
  89. package/dist/lib/task/create.js +62 -9
  90. package/dist/lib/task/decision-details.js +2 -46
  91. package/dist/lib/task/delegation-receipts.js +64 -3
  92. package/dist/lib/task/events.js +275 -22
  93. package/dist/lib/task/finalization-state.js +9 -0
  94. package/dist/lib/task/human-override.js +8 -14
  95. package/dist/lib/task/implementation-inputs.js +3 -3
  96. package/dist/lib/task/intermediate-cleanup.js +441 -0
  97. package/dist/lib/task/invalidation.js +18 -65
  98. package/dist/lib/task/ledger-intents.js +7 -22
  99. package/dist/lib/task/ledger.js +1 -3
  100. package/dist/lib/task/lifecycle-recovery.js +549 -0
  101. package/dist/lib/task/lifecycle.js +31 -4
  102. package/dist/lib/task/local-artifact-finalization.js +197 -197
  103. package/dist/lib/task/manual-validation-completion.js +26 -0
  104. package/dist/lib/task/manual-validation-evidence.js +130 -0
  105. package/dist/lib/task/manual-validation-receipt.js +105 -0
  106. package/dist/lib/task/manual-validation-shared.js +40 -0
  107. package/dist/lib/task/manual-validation-transaction.js +243 -0
  108. package/dist/lib/task/markdown.js +47 -0
  109. package/dist/lib/task/orchestration.js +66 -19
  110. package/dist/lib/task/pr-delivery-fact.js +12 -56
  111. package/dist/lib/task/qualification-audit.js +20 -34
  112. package/dist/lib/task/recovery-warning.js +19 -0
  113. package/dist/lib/task/review-artifacts.js +2 -18
  114. package/dist/lib/task/review-command.js +26 -0
  115. package/dist/lib/task/review-finalization.js +175 -216
  116. package/dist/lib/task/review-fingerprint.js +2 -2
  117. package/dist/lib/task/sections.js +34 -56
  118. package/dist/lib/task/short-id.js +15 -7
  119. package/dist/lib/task/task-execution-lock.js +1 -1
  120. package/dist/lib/task/verification-engine.js +119 -337
  121. package/dist/lib/task/verification.js +1 -1
  122. package/dist/lib/task/workflow-command.js +16 -0
  123. package/dist/lib/task/workflow-warning-intents.js +0 -16
  124. package/dist/lib/task/workflow-warnings.js +28 -2
  125. package/dist/lib/update.js +2 -0
  126. package/dist/lib/version.js +1 -11
  127. package/dist/lifecycle-build-manifest.json +3 -3
  128. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
  129. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +10 -4
  130. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +55 -3
  131. package/lib/fs/durable-write.ts +30 -0
  132. package/lib/fs/file-lock.ts +113 -0
  133. package/lib/host-control/audit.ts +22 -0
  134. package/lib/host-control/client.ts +172 -0
  135. package/lib/host-control/command.ts +60 -0
  136. package/lib/host-control/path.ts +167 -0
  137. package/lib/host-control/secure-fs.ts +135 -0
  138. package/lib/host-control/server.ts +180 -0
  139. package/lib/host-control/service.ts +133 -0
  140. package/lib/init.ts +15 -2
  141. package/lib/internal/cli-route-inventory.ts +4 -2
  142. package/lib/internal/codex-lifecycle.ts +6 -6
  143. package/lib/internal/demo-promotion.ts +354 -0
  144. package/lib/internal/demo-transcript.ts +419 -0
  145. package/lib/internal/host-control.ts +28 -0
  146. package/lib/internal/manual-validation.ts +315 -0
  147. package/lib/internal/platform-pr.ts +15 -4
  148. package/lib/internal/release-workflow.ts +74 -82
  149. package/lib/internal/sandbox-control.ts +162 -1
  150. package/lib/internal/task-artifact.ts +14 -217
  151. package/lib/internal/task-create.ts +5 -5
  152. package/lib/internal/task-event.ts +20 -11
  153. package/lib/internal/task-lifecycle.ts +4 -3
  154. package/lib/internal/task-operation-registry.ts +111 -4
  155. package/lib/internal/task-review.ts +9 -54
  156. package/lib/internal/task-validate.ts +26 -10
  157. package/lib/owned-path.ts +26 -0
  158. package/lib/platform/check-status.ts +9 -0
  159. package/lib/platform/comment-safety.ts +5 -0
  160. package/lib/platform/completion-backfill.ts +1 -1
  161. package/lib/platform/github-data.ts +322 -0
  162. package/lib/platform/github-provider.ts +15 -20
  163. package/lib/platform/issue-comments.ts +21 -14
  164. package/lib/platform/issues.ts +5 -246
  165. package/lib/platform/pr-change-report.ts +7 -5
  166. package/lib/platform/pr-checks.ts +13 -77
  167. package/lib/platform/pr-summary.ts +123 -35
  168. package/lib/platform/pull-requests.ts +6 -173
  169. package/lib/platform/snapshots.ts +53 -0
  170. package/lib/platform/task-identities.ts +3 -14
  171. package/lib/platform/verification-sync.ts +150 -156
  172. package/lib/pr-review/evidence-grading.ts +4 -9
  173. package/lib/release/workflow.ts +3 -3
  174. package/lib/sandbox/commands/create.ts +7 -6
  175. package/lib/sandbox/commands/rm.ts +21 -1895
  176. package/lib/sandbox/control/audit.ts +253 -0
  177. package/lib/sandbox/control/client.ts +109 -66
  178. package/lib/sandbox/control/executor.ts +134 -74
  179. package/lib/sandbox/control/finalization-response.ts +29 -0
  180. package/lib/sandbox/control/identity-sentinel.ts +137 -0
  181. package/lib/sandbox/control/lifecycle.ts +45 -3
  182. package/lib/sandbox/control/native-file-lock.ts +6 -107
  183. package/lib/sandbox/control/protocol.ts +80 -6
  184. package/lib/sandbox/control/server.ts +567 -132
  185. package/lib/sandbox/control/state.ts +86 -0
  186. package/lib/sandbox/control/task-workflow.ts +95 -0
  187. package/lib/sandbox/control/workflow-executor.ts +92 -0
  188. package/lib/sandbox/index.ts +1 -2
  189. package/lib/sandbox/recovery.ts +28 -5
  190. package/lib/sandbox/removal.ts +1142 -0
  191. package/lib/sandbox/task-cleanup.ts +209 -0
  192. package/lib/sandbox/workspace-identity.ts +19 -3
  193. package/lib/sandbox/workspace-view.ts +17 -15
  194. package/lib/task/activity-intent.ts +2 -2
  195. package/lib/task/activity-log.ts +27 -19
  196. package/lib/task/artifact-command.ts +85 -0
  197. package/lib/task/artifact-lifecycle.ts +31 -26
  198. package/lib/task/artifact-name.ts +55 -0
  199. package/lib/task/artifact-operations.ts +34 -310
  200. package/lib/task/artifact-receipts.ts +3 -14
  201. package/lib/task/artifact-recovery.ts +519 -0
  202. package/lib/task/artifact-repair-intent.ts +162 -33
  203. package/lib/task/artifact-schema.ts +5 -34
  204. package/lib/task/capabilities.ts +13 -22
  205. package/lib/task/codex-orchestration.ts +85 -24
  206. package/lib/task/commit-identity.ts +31 -0
  207. package/lib/task/commit-operation.ts +37 -20
  208. package/lib/task/control-authority.ts +540 -10
  209. package/lib/task/control-recovery.ts +186 -0
  210. package/lib/task/create-service.ts +1 -10
  211. package/lib/task/create.ts +63 -8
  212. package/lib/task/decision-details.ts +3 -71
  213. package/lib/task/delegation-receipts.ts +73 -2
  214. package/lib/task/events.ts +309 -24
  215. package/lib/task/finalization-state.ts +11 -0
  216. package/lib/task/human-override.ts +8 -16
  217. package/lib/task/implementation-inputs.ts +3 -3
  218. package/lib/task/intermediate-cleanup.ts +493 -0
  219. package/lib/task/invalidation.ts +18 -62
  220. package/lib/task/ledger-intents.ts +7 -24
  221. package/lib/task/ledger.ts +1 -3
  222. package/lib/task/lifecycle-recovery.ts +691 -0
  223. package/lib/task/lifecycle.ts +30 -5
  224. package/lib/task/local-artifact-finalization.ts +244 -234
  225. package/lib/task/manual-validation-completion.ts +40 -0
  226. package/lib/task/manual-validation-evidence.ts +209 -0
  227. package/lib/task/manual-validation-receipt.ts +156 -0
  228. package/lib/task/manual-validation-shared.ts +50 -0
  229. package/lib/task/manual-validation-transaction.ts +304 -0
  230. package/lib/task/markdown.ts +70 -0
  231. package/lib/task/orchestration.ts +84 -19
  232. package/lib/task/pr-delivery-fact.ts +15 -62
  233. package/lib/task/qualification-audit.ts +22 -33
  234. package/lib/task/recovery-warning.ts +21 -0
  235. package/lib/task/review-artifacts.ts +1 -20
  236. package/lib/task/review-command.ts +24 -0
  237. package/lib/task/review-finalization.ts +183 -292
  238. package/lib/task/review-fingerprint.ts +2 -2
  239. package/lib/task/sections.ts +33 -54
  240. package/lib/task/short-id.ts +27 -5
  241. package/lib/task/task-execution-lock.ts +1 -1
  242. package/lib/task/verification-engine.ts +113 -383
  243. package/lib/task/verification-types.ts +15 -0
  244. package/lib/task/verification.ts +1 -1
  245. package/lib/task/workflow-command.ts +17 -0
  246. package/lib/task/workflow-warning-intents.ts +0 -13
  247. package/lib/task/workflow-warnings.ts +21 -2
  248. package/lib/update.ts +6 -0
  249. package/lib/version.ts +1 -15
  250. package/package.json +6 -6
  251. package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
  252. package/templates/.agents/rules/create-issue.en.md +1 -1
  253. package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
  254. package/templates/.agents/rules/issue-sync.en.md +4 -2
  255. package/templates/.agents/rules/issue-sync.zh-CN.md +4 -3
  256. package/templates/.agents/rules/local-artifact-repair.en.md +46 -39
  257. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +43 -36
  258. package/templates/.agents/rules/testing-discipline.en.md +2 -0
  259. package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
  260. package/templates/.agents/scripts/package.json +3 -0
  261. package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
  262. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
  263. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -1
  264. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
  265. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  266. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  267. package/templates/.agents/skills/block-task/config/verify.json +1 -1
  268. package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
  269. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
  270. package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
  271. package/templates/.agents/skills/code-task/SKILL.en.md +7 -7
  272. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +7 -7
  273. package/templates/.agents/skills/code-task/config/verify.en.json +1 -1
  274. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -1
  275. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
  276. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
  277. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +33 -21
  278. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +33 -21
  279. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  280. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  281. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
  282. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
  283. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
  284. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
  285. package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
  286. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
  287. package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
  288. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
  289. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
  290. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
  291. package/templates/.agents/skills/create-pr/SKILL.en.md +2 -2
  292. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -2
  293. package/templates/.agents/skills/create-pr/config/verify.json +1 -1
  294. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +2 -2
  295. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +2 -2
  296. package/templates/.agents/skills/create-task/SKILL.en.md +5 -5
  297. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -5
  298. package/templates/.agents/skills/create-task/config/verify.json +1 -1
  299. package/templates/.agents/skills/import-issue/SKILL.en.md +8 -8
  300. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +8 -8
  301. package/templates/.agents/skills/import-issue/config/verify.json +2 -2
  302. package/templates/.agents/skills/plan-task/SKILL.en.md +4 -4
  303. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -4
  304. package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
  305. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
  306. package/templates/.agents/skills/release/SKILL.en.md +7 -3
  307. package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
  308. package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
  309. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +2 -2
  310. package/templates/.agents/skills/restore-task/config/verify.json +1 -1
  311. package/templates/.agents/skills/review-analysis/SKILL.en.md +2 -2
  312. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
  313. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -1
  314. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -1
  315. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -7
  316. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +6 -6
  317. package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
  318. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
  319. package/templates/.agents/skills/review-code/config/verify.en.json +1 -1
  320. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -1
  321. package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -12
  322. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -12
  323. package/templates/.agents/skills/review-plan/SKILL.en.md +2 -2
  324. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
  325. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -1
  326. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -1
  327. package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -7
  328. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -7
  329. package/templates/.agents/skills/review-pr/config/verify.en.json +1 -1
  330. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -1
  331. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +3 -3
  332. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +3 -3
  333. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +20 -3
  334. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +20 -3
  335. package/templates/.agents/skills/run-manual-validation/config/verify.en.json +1 -1
  336. package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +1 -1
  337. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
  338. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
  339. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
  340. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
  341. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  342. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  343. package/templates/.agents/skills/test/SKILL.en.md +17 -6
  344. package/templates/.agents/skills/test/SKILL.zh-CN.md +17 -6
  345. package/templates/.agents/templates/task.en.md +1 -1
  346. package/templates/.agents/templates/task.zh-CN.md +1 -1
  347. package/templates/.claude/commands/import-issue.en.md +1 -1
  348. package/templates/.claude/commands/import-issue.zh-CN.md +1 -1
  349. package/templates/.claude/commands/restore-task.en.md +1 -1
  350. package/templates/.claude/commands/restore-task.zh-CN.md +1 -1
  351. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  352. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  353. package/dist/lib/platform/adapters.js +0 -56
  354. package/lib/platform/adapters.ts +0 -197
@@ -1,16 +1,36 @@
1
1
  import fs from 'node:fs';
2
2
 
3
- import { inspectDecisionDetailDuplicates, scanVisibleMarkdown } from './decision-details.ts';
3
+ import { inspectDecisionDetailDuplicates } from './decision-details.ts';
4
+ import { scanVisibleMarkdown } from './markdown.ts';
5
+ import { parseArtifactName } from './artifact-name.ts';
4
6
  import {
5
- parseArtifactName,
7
+ hasOpenArtifactRound,
8
+ parseCodePlanInputReference,
9
+ validateArtifactPublication,
6
10
  validateCompletedArtifact
7
11
  } from './artifact-lifecycle.ts';
8
12
  import { resolveTaskRef } from './resolve-ref.ts';
9
13
  import { expectedQualificationRelations, validateQualificationAudit } from './qualification-audit.ts';
10
14
  import { canonicalSemanticDigest, inspectArtifactPatterns, inspectArtifactStructure, sha256Content } from './artifact-operations.ts';
11
15
  import { getArtifactSchema } from './artifact-schema.ts';
12
- import { readArtifactRepairIntent, writeArtifactRepairIntent } from './artifact-repair-intent.ts';
13
- import type { ArtifactRepairIntent } from './artifact-repair-intent.ts';
16
+ import { readArtifactRecoveryIntent } from './artifact-repair-intent.ts';
17
+ import type { ArtifactRecoveryIntent } from './artifact-repair-intent.ts';
18
+ import {
19
+ beginArtifactRecovery,
20
+ commitArtifactRecovery,
21
+ consumeArtifactRecovery,
22
+ prepareArtifactRecoveryCommit,
23
+ recordArtifactRecoveryPassed,
24
+ recoveryContextFromIntent,
25
+ stageArtifactCandidate
26
+ } from './artifact-recovery.ts';
27
+ import type { ArtifactRecoveryContext } from './artifact-recovery.ts';
28
+ import {
29
+ consumeLifecycleRecoveryAttestation,
30
+ lifecycleRecoveryAttestationDigest,
31
+ validateLifecycleRecoveryAttestation
32
+ } from './control-authority.ts';
33
+ import type { LifecycleRecoveryAttestationV1 } from './control-authority.ts';
14
34
 
15
35
  type LocalArtifactFamily = 'analysis' | 'plan' | 'code';
16
36
 
@@ -29,18 +49,14 @@ type LocalArtifactDiagnosticCode =
29
49
  | 'LOCAL_REQUIRED_PATTERN_MISSING'
30
50
  | 'LOCAL_STRUCTURAL_INVALID'
31
51
  | 'LOCAL_SECTION_HEADING_TRAILING_PUNCTUATION'
32
- | 'LOCAL_REPAIR_PROVENANCE_CONFLICT'
33
- | 'LOCAL_REPAIR_BASELINE_MISMATCH'
52
+ | 'LOCAL_RECOVERY_PROVENANCE_CONFLICT'
53
+ | 'LOCAL_RECOVERY_BASELINE_MISMATCH'
34
54
  | 'LOCAL_QUALIFICATION_AUDIT_INVALID';
35
55
 
36
56
  type LocalArtifactDiagnostic = {
37
57
  code: LocalArtifactDiagnosticCode;
38
58
  message: string;
39
- repairable: boolean;
40
59
  line: number | null;
41
- from?: string;
42
- to?: string;
43
- operation?: 'replace-line';
44
60
  };
45
61
 
46
62
  type LocalArtifactValidationOptions = {
@@ -54,7 +70,6 @@ type LocalArtifactValidationResult = {
54
70
  ok: boolean;
55
71
  family: LocalArtifactFamily;
56
72
  semanticDigest: string;
57
- repairable: boolean;
58
73
  diagnostics: readonly LocalArtifactDiagnostic[];
59
74
  };
60
75
 
@@ -64,49 +79,76 @@ type LocalArtifactFinalizationRequest = {
64
79
  artifact: string;
65
80
  repoRoot?: string;
66
81
  requiredSections?: readonly string[];
82
+ recoveryId?: string;
83
+ lockAlreadyHeld?: boolean;
67
84
  };
68
85
 
86
+ type LocalArtifactRecoveryInfo = Readonly<{
87
+ recoveryId: string;
88
+ candidatePath: string;
89
+ baselineSha256: string;
90
+ baselineSemanticDigest: string;
91
+ }>;
92
+
69
93
  type LocalArtifactFinalizationResult = {
70
94
  status: 'passed' | 'failed';
71
- changed: false;
95
+ changed: boolean;
72
96
  taskId: string | null;
73
97
  taskDir: string | null;
74
98
  family: LocalArtifactFamily;
75
99
  artifact: string;
76
100
  artifactSha256: string | null;
77
101
  semanticDigest: string | null;
78
- repairable: boolean;
79
102
  diagnostics: readonly LocalArtifactDiagnostic[];
103
+ recovery?: LocalArtifactRecoveryInfo;
80
104
  error: { code: string; message: string } | null;
81
105
  };
82
106
 
83
- type LocalArtifactFinalizationIntent = ArtifactRepairIntent;
107
+ type LocalArtifactFinalizationIntent = ArtifactRecoveryIntent;
84
108
 
85
- function readLocalArtifactFinalizationIntent(
86
- repoRoot: string,
87
- taskId: string,
88
- family: LocalArtifactFamily,
89
- artifact: string
90
- ): LocalArtifactFinalizationIntent | null {
91
- return readArtifactRepairIntent(repoRoot, taskId, family, artifact);
109
+ function authorityDigest(authority: LifecycleRecoveryAttestationV1): string {
110
+ return lifecycleRecoveryAttestationDigest(authority);
92
111
  }
93
112
 
94
- function writeLocalArtifactFinalizationIntent(repoRoot: string, value: LocalArtifactFinalizationIntent): void {
95
- writeArtifactRepairIntent(repoRoot, value);
113
+ function validFinalizerAuthority(
114
+ authority: LifecycleRecoveryAttestationV1 | undefined,
115
+ request: LocalArtifactFinalizationRequest,
116
+ taskId: string
117
+ ): boolean {
118
+ if (!authority) return false;
119
+ try { validateLifecycleRecoveryAttestation(authority); }
120
+ catch { return false; }
121
+ return authority.version === 1
122
+ && authority.phase === 'artifact.finalize-local'
123
+ && authority.taskId === taskId
124
+ && authority.family === request.family
125
+ && authority.artifact === request.artifact
126
+ && authority.round === (parseArtifactName(request.artifact)?.round ?? 0)
127
+ && authority.requestId.length > 0
128
+ && authority.operationId.length > 0;
129
+ }
130
+
131
+ function recoveryInfo(context: ArtifactRecoveryContext): LocalArtifactRecoveryInfo {
132
+ return {
133
+ recoveryId: context.recoveryId,
134
+ candidatePath: context.stagingPath,
135
+ baselineSha256: context.baselineSha256,
136
+ baselineSemanticDigest: context.baselineSemanticDigest
137
+ };
96
138
  }
97
139
 
98
140
  function consumeLocalArtifactFinalizationIntent(
99
141
  repoRoot: string,
100
- intent: LocalArtifactFinalizationIntent
142
+ intent: LocalArtifactFinalizationIntent,
143
+ options: Readonly<{ lockAlreadyHeld?: boolean }> = {}
101
144
  ): LocalArtifactFinalizationIntent {
102
145
  if (intent.state === 'consumed') return intent;
103
- if (intent.state !== 'passed') throw new Error('LOCAL_FINALIZATION_INTENT_INVALID: only passed provenance can be consumed');
104
- const consumed = { ...intent, state: 'consumed' as const };
105
- writeLocalArtifactFinalizationIntent(repoRoot, consumed);
106
- return consumed;
146
+ const resolved = resolveTaskRef(intent.taskId, { repoRoot });
147
+ if (!resolved.ok) throw new Error(resolved.message);
148
+ return consumeArtifactRecovery(recoveryContextFromIntent(repoRoot, resolved.taskDir, intent), options);
107
149
  }
108
150
 
109
- function localDiagnosticCode(code: string): LocalArtifactDiagnosticCode | null {
151
+ function localDiagnosticCode(code: string): LocalArtifactDiagnosticCode {
110
152
  const map: Record<string, LocalArtifactDiagnosticCode> = {
111
153
  ARTIFACT_EMPTY: 'LOCAL_ARTIFACT_EMPTY',
112
154
  ARTIFACT_MISSING_SECTION: 'LOCAL_ARTIFACT_MISSING_SECTION',
@@ -116,20 +158,8 @@ function localDiagnosticCode(code: string): LocalArtifactDiagnosticCode | null {
116
158
  return map[code] ?? 'LOCAL_STRUCTURAL_INVALID';
117
159
  }
118
160
 
119
- function localDiagnostic(
120
- item: { code: string; message: string; line: number | null; repairable: boolean; operation?: { kind: string; from: string; to: string } }
121
- ): LocalArtifactDiagnostic | null {
122
- const code = localDiagnosticCode(item.code);
123
- if (!code) return null;
124
- return {
125
- code,
126
- message: item.message,
127
- repairable: item.repairable,
128
- line: item.line,
129
- ...(item.operation?.kind === 'replace-line'
130
- ? { from: item.operation.from, to: item.operation.to, operation: 'replace-line' as const }
131
- : {})
132
- };
161
+ function localDiagnostic(item: { code: string; message: string; line: number | null }): LocalArtifactDiagnostic {
162
+ return { code: localDiagnosticCode(item.code), message: item.message, line: item.line };
133
163
  }
134
164
 
135
165
  function isStatusPattern(pattern: string): boolean {
@@ -141,14 +171,10 @@ function validateLocalArtifact(
141
171
  options: LocalArtifactValidationOptions
142
172
  ): LocalArtifactValidationResult {
143
173
  const schema = getArtifactSchema(options.family)!;
144
- const patterns = schema.requiredPatterns;
145
174
  const diagnostics: LocalArtifactDiagnostic[] = [];
146
175
  const scanned = scanVisibleMarkdown(content);
147
176
  const structure = inspectArtifactStructure(content, schema);
148
- for (const item of structure.diagnostics) {
149
- const mapped = localDiagnostic(item);
150
- if (mapped) diagnostics.push(mapped);
151
- }
177
+ for (const item of structure.diagnostics) diagnostics.push(localDiagnostic(item));
152
178
 
153
179
  const statusSection = schema.sections.find((section) => section.id === 'state-check');
154
180
  if (statusSection) {
@@ -160,9 +186,9 @@ function validateLocalArtifact(
160
186
  if (statusHeading) {
161
187
  const next = scanned.headings.find((candidate) => candidate.start > statusHeading.start && candidate.level <= 2);
162
188
  const body = content.slice(statusHeading.end, next?.start ?? content.length);
163
- for (const pattern of patterns.filter(isStatusPattern)) {
189
+ for (const pattern of schema.requiredPatterns.filter(isStatusPattern)) {
164
190
  if (!new RegExp(pattern, 'm').test(body)) {
165
- diagnostics.push({ code: 'LOCAL_STATUS_COMMAND_MISSING', message: `status section '${statusHeading.text}' is missing required command output`, repairable: false, line: content.slice(0, statusHeading.start).split('\n').length });
191
+ diagnostics.push({ code: 'LOCAL_STATUS_COMMAND_MISSING', message: `status section '${statusHeading.text}' is missing required command output`, line: content.slice(0, statusHeading.start).split('\n').length });
166
192
  }
167
193
  }
168
194
  }
@@ -170,40 +196,28 @@ function validateLocalArtifact(
170
196
 
171
197
  const patternInspection = inspectArtifactPatterns(content, {
172
198
  ...schema,
173
- requiredPatterns: patterns.filter((item) => !isStatusPattern(item))
199
+ requiredPatterns: schema.requiredPatterns.filter((item) => !isStatusPattern(item))
174
200
  });
175
- for (const item of patternInspection.diagnostics) {
176
- diagnostics.push({ code: 'LOCAL_REQUIRED_PATTERN_MISSING', message: item.message, repairable: false, line: null });
177
- }
201
+ for (const item of patternInspection.diagnostics) diagnostics.push({ code: 'LOCAL_REQUIRED_PATTERN_MISSING', message: item.message, line: null });
178
202
 
179
203
  const decisionDetails = inspectDecisionDetailDuplicates(content);
180
- if (!decisionDetails.ok) {
181
- diagnostics.push({ code: 'LOCAL_DECISION_DETAIL_DUPLICATE', message: decisionDetails.message, repairable: false, line: null });
182
- }
204
+ if (!decisionDetails.ok) diagnostics.push({ code: 'LOCAL_DECISION_DETAIL_DUPLICATE', message: decisionDetails.message, line: null });
183
205
 
184
206
  if (options.taskContent !== undefined) {
185
207
  const expected = expectedQualificationRelations(options.taskContent, options.family);
186
- if (!expected.ok) {
187
- diagnostics.push({ code: 'LOCAL_QUALIFICATION_AUDIT_INVALID', message: `${expected.code}: ${expected.message}`, repairable: false, line: null });
188
- }
208
+ if (!expected.ok) diagnostics.push({ code: 'LOCAL_QUALIFICATION_AUDIT_INVALID', message: `${expected.code}: ${expected.message}`, line: null });
189
209
  const qualification = validateQualificationAudit(options.taskContent, content, {
190
- family: options.family === 'analysis' ? 'analysis' : options.family === 'plan' ? 'plan' : 'code',
210
+ family: options.family,
191
211
  artifact: options.artifact,
192
212
  expectedUpstreamRelations: expected.ok ? expected.relations : undefined
193
213
  });
194
- if (!qualification.ok) {
195
- diagnostics.push({ code: 'LOCAL_QUALIFICATION_AUDIT_INVALID', message: `${qualification.code}: ${qualification.message}`, repairable: false, line: null });
196
- }
214
+ if (!qualification.ok) diagnostics.push({ code: 'LOCAL_QUALIFICATION_AUDIT_INVALID', message: `${qualification.code}: ${qualification.message}`, line: null });
197
215
  }
198
216
 
199
- const repairable = structure.repair !== null && diagnostics.length === 1 && diagnostics[0]?.repairable === true;
200
- const semanticDigestValue = canonicalSemanticDigest(content, structure.repair);
201
-
202
217
  return {
203
218
  ok: diagnostics.length === 0,
204
219
  family: options.family,
205
- semanticDigest: semanticDigestValue,
206
- repairable,
220
+ semanticDigest: canonicalSemanticDigest(content),
207
221
  diagnostics
208
222
  };
209
223
  }
@@ -218,204 +232,198 @@ function failedFinalization(
218
232
  taskId: null, taskDir: null,
219
233
  family: request.family, artifact: request.artifact,
220
234
  artifactSha256: null, semanticDigest: null,
221
- repairable: false, diagnostics: [], error, ...extra
235
+ diagnostics: [], error, ...extra
222
236
  };
223
237
  }
224
238
 
225
- function provenanceFailure(
239
+ type LocalArtifactPreparation = Readonly<{
240
+ result: LocalArtifactFinalizationResult;
241
+ content: string;
242
+ repoRoot?: string;
243
+ lockAlreadyHeld?: boolean;
244
+ recovery?: ArtifactRecoveryContext;
245
+ authority?: LifecycleRecoveryAttestationV1;
246
+ }>;
247
+
248
+ function tupleFor(
226
249
  request: LocalArtifactFinalizationRequest,
227
- resolved: { taskId: string; taskDir: string },
228
- content: string,
229
- artifactSha256: string,
230
- semanticDigestValue: string,
231
- code: LocalArtifactDiagnosticCode,
232
- message: string
233
- ): LocalArtifactFinalizationResult {
234
- return failedFinalization(request, { code, message }, {
235
- taskId: resolved.taskId,
236
- taskDir: resolved.taskDir,
237
- artifactSha256,
238
- semanticDigest: semanticDigestValue,
239
- diagnostics: [{ code, message, repairable: false, line: null }]
240
- });
250
+ taskId: string,
251
+ authority?: LifecycleRecoveryAttestationV1
252
+ ) {
253
+ const round = parseArtifactName(request.artifact)?.round ?? 0;
254
+ return {
255
+ taskId,
256
+ family: request.family,
257
+ artifact: request.artifact,
258
+ round,
259
+ requestId: authority?.lifecycleRequestId ?? `local-finalize:${taskId}:${request.family}:${round}`,
260
+ phase: authority?.phase ?? 'artifact.finalize-local' as const,
261
+ authorityDigest: authority ? authorityDigest(authority) : null
262
+ };
241
263
  }
242
264
 
243
- function finalizeLocalArtifact(request: LocalArtifactFinalizationRequest): LocalArtifactFinalizationResult {
265
+ function prepareLocalArtifact(
266
+ request: LocalArtifactFinalizationRequest,
267
+ candidate?: string,
268
+ authority?: LifecycleRecoveryAttestationV1
269
+ ): LocalArtifactPreparation {
270
+ const failed = (code: string, message: string, extra: Partial<LocalArtifactFinalizationResult> = {}): LocalArtifactPreparation => ({
271
+ result: failedFinalization(request, { code, message }, extra), content: candidate ?? ''
272
+ });
244
273
  const resolved = resolveTaskRef(request.taskRef, { repoRoot: request.repoRoot });
245
- if (!resolved.ok) return failedFinalization(request, { code: resolved.code, message: resolved.message });
274
+ if (!resolved.ok) return failed(resolved.code, resolved.message);
246
275
  const parsed = parseArtifactName(request.artifact);
247
- if (!parsed || parsed.family !== request.family) {
248
- return failedFinalization(request, {
249
- code: 'ARTIFACT_IDENTITY_INVALID',
250
- message: `artifact '${request.artifact}' does not match ${request.family}`
251
- }, { taskId: resolved.taskId, taskDir: resolved.taskDir });
252
- }
253
- const validated = validateCompletedArtifact(resolved.taskDir, request.family, request.artifact, parsed.round);
254
- if (!validated.ok) {
255
- return failedFinalization(request, validated.error, { taskId: resolved.taskId, taskDir: resolved.taskDir });
256
- }
257
- let content: string;
258
- try { content = fs.readFileSync(validated.artifact.path, 'utf8'); }
259
- catch (error) {
260
- return failedFinalization(request, { code: 'ARTIFACT_NOT_READABLE', message: String(error) }, {
261
- taskId: resolved.taskId, taskDir: resolved.taskDir
262
- });
276
+ if (!parsed || parsed.family !== request.family) return failed('ARTIFACT_IDENTITY_INVALID', `artifact '${request.artifact}' does not match ${request.family}`);
277
+ if (authority && !validFinalizerAuthority(authority, request, resolved.taskId)) {
278
+ return failed('LOCAL_EXECUTION_AUTHORITY_INVALID', 'finalizer authority does not match the artifact tuple');
263
279
  }
280
+
264
281
  let taskContent: string;
265
- try { taskContent = fs.readFileSync(resolved.taskMdPath, 'utf8'); }
266
- catch (error) {
267
- return failedFinalization(request, { code: 'TASK_READ_FAILED', message: error instanceof Error ? error.message : String(error) }, {
268
- taskId: resolved.taskId, taskDir: resolved.taskDir
269
- });
282
+ let content: string;
283
+ let recovery: ArtifactRecoveryContext | undefined;
284
+ try {
285
+ taskContent = fs.readFileSync(resolved.taskMdPath, 'utf8');
286
+ if (request.recoveryId) {
287
+ const intent = readArtifactRecoveryIntent(resolved.repoRoot, resolved.taskId, request.family, request.artifact);
288
+ if (!intent || intent.recoveryOperationId !== request.recoveryId) return failed('LOCAL_RECOVERY_PROVENANCE_CONFLICT', 'recovery id does not match the artifact journal');
289
+ if (!['awaiting-recovery', 'finalize-ready', 'commit-started'].includes(intent.state)) return failed('LOCAL_RECOVERY_PROVENANCE_CONFLICT', `recovery journal is in '${intent.state}' state`);
290
+ recovery = recoveryContextFromIntent(resolved.repoRoot, resolved.taskDir, intent);
291
+ content = fs.existsSync(recovery.stagingPath)
292
+ ? fs.readFileSync(recovery.stagingPath, 'utf8')
293
+ : fs.readFileSync(recovery.formalPath, 'utf8');
294
+ } else {
295
+ const validated = validateCompletedArtifact(resolved.taskDir, request.family, request.artifact, parsed.round);
296
+ if (!validated.ok) return failed(validated.error.code, validated.error.message);
297
+ content = candidate ?? fs.readFileSync(validated.artifact.path, 'utf8');
298
+ }
299
+ } catch (error) { return failed('ARTIFACT_NOT_READABLE', String(error)); }
300
+
301
+ if (resolved.state !== 'active' || !hasOpenArtifactRound(taskContent, request.family, parsed.round)) {
302
+ return failed('ARTIFACT_IDENTITY_INVALID', 'candidate must match one open started round in an active task');
270
303
  }
271
- const result = validateLocalArtifact(content, {
272
- family: request.family,
273
- requiredSections: request.requiredSections,
274
- taskContent,
275
- artifact: request.artifact
304
+ const validation = validateLocalArtifact(content, {
305
+ family: request.family, requiredSections: request.requiredSections, taskContent, artifact: request.artifact
276
306
  });
277
307
  const artifactSha256 = sha256Content(content);
278
- let intent: LocalArtifactFinalizationIntent | null;
279
- try {
280
- intent = readLocalArtifactFinalizationIntent(resolved.repoRoot, resolved.taskId, request.family, request.artifact);
281
- } catch (error) {
282
- return failedFinalization(request, {
283
- code: 'LOCAL_FINALIZATION_INTENT_INVALID',
284
- message: error instanceof Error ? error.message : String(error)
285
- }, { taskId: resolved.taskId, taskDir: resolved.taskDir, artifactSha256, semanticDigest: result.semanticDigest });
308
+ const result = failedFinalization(request, {
309
+ code: 'LOCAL_ARTIFACT_INVALID',
310
+ message: validation.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; ')
311
+ }, {
312
+ taskId: resolved.taskId, taskDir: resolved.taskDir, artifactSha256,
313
+ semanticDigest: validation.semanticDigest, diagnostics: validation.diagnostics
314
+ });
315
+
316
+ if (request.recoveryId) {
317
+ if (!validation.ok) return { result, content, repoRoot: resolved.repoRoot, recovery, authority, lockAlreadyHeld: request.lockAlreadyHeld };
318
+ return {
319
+ result: { ...result, status: 'passed', error: null, recovery: recoveryInfo(recovery!) },
320
+ content, repoRoot: resolved.repoRoot, recovery, authority, lockAlreadyHeld: request.lockAlreadyHeld
321
+ };
286
322
  }
287
- if (result.repairable) {
288
- if (intent && (intent.state !== 'awaiting-repair' || intent.baselineSemanticDigest !== result.semanticDigest)) {
289
- return provenanceFailure(
290
- request, resolved, content, artifactSha256, result.semanticDigest,
291
- 'LOCAL_REPAIR_PROVENANCE_CONFLICT',
292
- 'a different local repair baseline is already recorded for this artifact'
293
- );
294
- }
295
- if (!intent) {
296
- try {
297
- writeLocalArtifactFinalizationIntent(resolved.repoRoot, {
298
- version: 1,
299
- taskId: resolved.taskId,
300
- family: request.family,
301
- artifact: request.artifact,
302
- state: 'awaiting-repair',
303
- baselineSemanticDigest: result.semanticDigest,
304
- artifactSha256,
305
- semanticDigest: result.semanticDigest
306
- });
307
- } catch (error) {
308
- return failedFinalization(request, {
309
- code: 'LOCAL_FINALIZATION_INTENT_WRITE_FAILED',
310
- message: error instanceof Error ? error.message : String(error)
311
- }, { taskId: resolved.taskId, taskDir: resolved.taskDir, artifactSha256, semanticDigest: result.semanticDigest });
312
- }
323
+
324
+ const existing = readArtifactRecoveryIntent(resolved.repoRoot, resolved.taskId, request.family, request.artifact);
325
+ if (existing?.state === 'passed' || existing?.state === 'consumed') {
326
+ if (existing.finalArtifactSha256 !== artifactSha256 || existing.finalSemanticDigest !== validation.semanticDigest) {
327
+ return { ...failed('LOCAL_RECOVERY_PROVENANCE_CONFLICT', 'formal artifact does not match its completed recovery journal'), content, repoRoot: resolved.repoRoot, authority, lockAlreadyHeld: request.lockAlreadyHeld };
313
328
  }
329
+ const context = recoveryContextFromIntent(resolved.repoRoot, resolved.taskDir, existing);
314
330
  return {
315
- status: 'failed',
316
- changed: false,
317
- taskId: resolved.taskId,
318
- taskDir: resolved.taskDir,
319
- family: request.family,
320
- artifact: request.artifact,
321
- artifactSha256,
322
- semanticDigest: result.semanticDigest,
323
- repairable: true,
324
- diagnostics: result.diagnostics,
325
- error: {
326
- code: 'LOCAL_ARTIFACT_INVALID',
327
- message: result.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; ')
328
- }
331
+ result: { ...result, status: 'passed', error: null, recovery: recoveryInfo(context) },
332
+ content, repoRoot: resolved.repoRoot, recovery: context, authority, lockAlreadyHeld: request.lockAlreadyHeld
329
333
  };
330
334
  }
331
- if (!result.ok) {
332
- return {
333
- status: 'failed',
334
- changed: false,
335
- taskId: resolved.taskId,
335
+
336
+ try {
337
+ if (validation.ok) {
338
+ const context = recordArtifactRecoveryPassed(tupleFor(request, resolved.taskId, authority), {
339
+ repoRoot: resolved.repoRoot,
340
+ taskDir: resolved.taskDir,
341
+ expectedFinalSha256: artifactSha256,
342
+ expectedFinalSemanticDigest: validation.semanticDigest,
343
+ ...(authority ? { recoveryId: authority.operationId } : {}),
344
+ lockAlreadyHeld: request.lockAlreadyHeld
345
+ });
346
+ recovery = context;
347
+ return { result: { ...result, status: 'passed', error: null, recovery: recoveryInfo(context) }, content, repoRoot: resolved.repoRoot, recovery, authority, lockAlreadyHeld: request.lockAlreadyHeld };
348
+ }
349
+ const context = beginArtifactRecovery(tupleFor(request, resolved.taskId, authority), Buffer.from(content, 'utf8'), {
350
+ repoRoot: resolved.repoRoot,
336
351
  taskDir: resolved.taskDir,
337
- family: request.family,
338
- artifact: request.artifact,
339
- artifactSha256,
340
- semanticDigest: result.semanticDigest,
341
- repairable: false,
342
- diagnostics: result.diagnostics,
343
- error: {
344
- code: 'LOCAL_ARTIFACT_INVALID',
345
- message: result.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; ')
346
- }
352
+ ...(authority ? { recoveryId: authority.operationId } : {}),
353
+ lockAlreadyHeld: request.lockAlreadyHeld
354
+ });
355
+ recovery = context;
356
+ return {
357
+ result: { ...result, recovery: recoveryInfo(context) },
358
+ content, repoRoot: resolved.repoRoot, recovery, authority, lockAlreadyHeld: request.lockAlreadyHeld
347
359
  };
360
+ } catch (error) {
361
+ return { ...failed('LOCAL_RECOVERY_PROVENANCE_CONFLICT', String(error)), content, repoRoot: resolved.repoRoot, authority, lockAlreadyHeld: request.lockAlreadyHeld };
348
362
  }
349
- if (intent?.state === 'awaiting-repair' && intent.baselineSemanticDigest !== result.semanticDigest) {
350
- return provenanceFailure(
351
- request, resolved, content, artifactSha256, result.semanticDigest,
352
- 'LOCAL_REPAIR_BASELINE_MISMATCH',
353
- 'the repaired artifact semantic digest does not match the recorded repair baseline'
354
- );
355
- }
356
- if ((intent?.state === 'passed' || intent?.state === 'consumed')
357
- && (intent.artifactSha256 !== artifactSha256 || intent.semanticDigest !== result.semanticDigest)) {
358
- return provenanceFailure(
359
- request, resolved, content, artifactSha256, result.semanticDigest,
360
- 'LOCAL_REPAIR_PROVENANCE_CONFLICT',
361
- 'the artifact changed after its finalization provenance was recorded'
362
- );
363
- }
364
- if (intent?.state === 'consumed') {
363
+ }
364
+
365
+ function commitLocalArtifactProvenance(prepared: LocalArtifactPreparation): LocalArtifactFinalizationResult {
366
+ if (!prepared.recovery || !prepared.repoRoot || prepared.result.status === 'failed' && !prepared.result.recovery) return prepared.result;
367
+ if (prepared.result.status === 'failed') return prepared.result;
368
+ try {
369
+ const intent = readArtifactRecoveryIntent(prepared.repoRoot, prepared.recovery.taskId, prepared.recovery.family, prepared.recovery.artifact);
370
+ if (!intent) throw new Error('ARTIFACT_RECOVERY_INTENT_MISSING');
371
+ let committed;
372
+ if (intent.state === 'passed' || intent.state === 'consumed') {
373
+ return {
374
+ ...prepared.result,
375
+ artifactSha256: intent.finalArtifactSha256,
376
+ semanticDigest: intent.finalSemanticDigest,
377
+ error: null
378
+ };
379
+ } else if (intent.state === 'finalize-ready' || intent.state === 'commit-started') {
380
+ committed = commitArtifactRecovery(prepared.recovery, { lockAlreadyHeld: prepared.lockAlreadyHeld });
381
+ } else {
382
+ const staged = stageArtifactCandidate(prepared.recovery, Buffer.from(prepared.content, 'utf8'), { lockAlreadyHeld: prepared.lockAlreadyHeld });
383
+ prepareArtifactRecoveryCommit(prepared.recovery, staged.candidateSha256, staged.semanticDigest, { lockAlreadyHeld: prepared.lockAlreadyHeld });
384
+ committed = commitArtifactRecovery(prepared.recovery, { lockAlreadyHeld: prepared.lockAlreadyHeld });
385
+ }
386
+ if (committed.state !== 'passed') throw new Error(`ARTIFACT_RECOVERY_STATE_INVALID: commit ended in '${committed.state}'`);
365
387
  return {
388
+ ...prepared.result,
366
389
  status: 'passed',
367
- changed: false,
368
- taskId: resolved.taskId,
369
- taskDir: resolved.taskDir,
370
- family: request.family,
371
- artifact: request.artifact,
372
- artifactSha256,
373
- semanticDigest: result.semanticDigest,
374
- repairable: false,
375
- diagnostics: result.diagnostics,
390
+ artifactSha256: committed.finalArtifactSha256,
391
+ semanticDigest: committed.finalSemanticDigest,
376
392
  error: null
377
393
  };
378
- }
379
- try {
380
- writeLocalArtifactFinalizationIntent(resolved.repoRoot, {
381
- version: 1,
382
- taskId: resolved.taskId,
383
- family: request.family,
384
- artifact: request.artifact,
385
- state: 'passed',
386
- baselineSemanticDigest: intent?.baselineSemanticDigest ?? null,
387
- artifactSha256,
388
- semanticDigest: result.semanticDigest
389
- });
390
394
  } catch (error) {
391
- return failedFinalization(request, {
392
- code: 'LOCAL_FINALIZATION_INTENT_WRITE_FAILED',
393
- message: error instanceof Error ? error.message : String(error)
394
- }, { taskId: resolved.taskId, taskDir: resolved.taskDir, artifactSha256, semanticDigest: result.semanticDigest });
395
+ return { ...prepared.result, status: 'failed', error: {
396
+ code: 'LOCAL_RECOVERY_COMMIT_FAILED', message: String(error)
397
+ } };
395
398
  }
396
- return {
397
- status: result.ok ? 'passed' : 'failed',
398
- changed: false,
399
- taskId: resolved.taskId,
400
- taskDir: resolved.taskDir,
401
- family: request.family,
402
- artifact: request.artifact,
403
- artifactSha256,
404
- semanticDigest: result.semanticDigest,
405
- repairable: result.repairable,
406
- diagnostics: result.diagnostics,
407
- error: result.ok ? null : {
408
- code: 'LOCAL_ARTIFACT_INVALID',
409
- message: result.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; ')
399
+ }
400
+
401
+ function finalizeLocalArtifact(
402
+ request: LocalArtifactFinalizationRequest,
403
+ authority?: LifecycleRecoveryAttestationV1,
404
+ options: Readonly<{ deferLifecycleRecoveryConsumption?: boolean }> = {}
405
+ ): LocalArtifactFinalizationResult {
406
+ const prepared = prepareLocalArtifact(request, undefined, authority);
407
+ const result = commitLocalArtifactProvenance(prepared);
408
+ if (authority && !options.deferLifecycleRecoveryConsumption && result.status === 'passed') {
409
+ try { consumeLifecycleRecoveryAttestation(authority); }
410
+ catch (error) {
411
+ return { ...result, status: 'failed', error: {
412
+ code: 'LOCAL_EXECUTION_AUTHORITY_CONSUME_FAILED',
413
+ message: error instanceof Error ? error.message : String(error)
414
+ } };
410
415
  }
411
- };
416
+ }
417
+ return result;
412
418
  }
413
419
 
414
420
  export {
415
421
  LOCAL_ARTIFACT_REQUIRED_SECTIONS,
416
422
  consumeLocalArtifactFinalizationIntent,
417
423
  finalizeLocalArtifact,
418
- readLocalArtifactFinalizationIntent,
424
+ prepareLocalArtifact,
425
+ commitLocalArtifactProvenance,
426
+ readArtifactRecoveryIntent as readLocalArtifactFinalizationIntent,
419
427
  canonicalSemanticDigest as semanticDigest,
420
428
  sha256Content,
421
429
  validateLocalArtifact
@@ -427,6 +435,8 @@ export type {
427
435
  LocalArtifactFinalizationRequest,
428
436
  LocalArtifactFinalizationResult,
429
437
  LocalArtifactFinalizationIntent,
438
+ LocalArtifactPreparation,
430
439
  LocalArtifactValidationOptions,
431
- LocalArtifactValidationResult
440
+ LocalArtifactValidationResult,
441
+ LocalArtifactRecoveryInfo
432
442
  };