@fitlab-ai/agent-infra 0.9.2 → 0.9.4

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 (428) hide show
  1. package/README.md +12 -9
  2. package/README.zh-CN.md +12 -9
  3. package/bin/cli.ts +12 -0
  4. package/dist/bin/cli.js +14 -0
  5. package/dist/bin/internal-cli.js +134 -108
  6. package/dist/lib/agent-clients/adapter.js +137 -4
  7. package/dist/lib/agent-clients/adapters/antigravity-cli.js +64 -0
  8. package/dist/lib/{sandbox/credentials.js → agent-clients/adapters/claude-code-credentials.js} +4 -14
  9. package/dist/lib/agent-clients/adapters/claude-code-refresh.js +85 -0
  10. package/dist/lib/agent-clients/adapters/claude-code-sandbox.js +247 -0
  11. package/dist/lib/agent-clients/adapters/claude-code.js +27 -4
  12. package/dist/lib/agent-clients/adapters/codex.js +9 -0
  13. package/dist/lib/agent-clients/adapters/index.js +2 -2
  14. package/dist/lib/agent-clients/adapters/opencode-sandbox.js +108 -0
  15. package/dist/lib/agent-clients/adapters/opencode.js +24 -3
  16. package/dist/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
  17. package/dist/lib/agent-clients/config.js +42 -4
  18. package/dist/lib/agent-clients/next-steps.js +11 -2
  19. package/dist/lib/agent-clients/reconcile.js +2 -1
  20. package/dist/lib/agent-clients/registry.js +16 -2
  21. package/dist/lib/agent-clients/schema.js +18 -0
  22. package/dist/lib/agent-clients/tokens.js +49 -0
  23. package/dist/lib/agent-clients/types.js +1 -1
  24. package/dist/lib/defaults.json +1 -1
  25. package/dist/lib/git/workflow.js +54 -1
  26. package/dist/lib/internal/agent-client.js +61 -4
  27. package/dist/lib/internal/git-workflow.js +6 -1
  28. package/dist/lib/internal/platform-checks.js +3 -3
  29. package/dist/lib/internal/platform-comment.js +29 -2
  30. package/dist/lib/internal/platform-issue.js +7 -0
  31. package/dist/lib/internal/platform-pr-review.js +132 -0
  32. package/dist/lib/internal/platform-pr.js +20 -2
  33. package/dist/lib/internal/platform-release-notes.js +52 -16
  34. package/dist/lib/internal/pr-review-grade.js +140 -0
  35. package/dist/lib/internal/release-workflow.js +13 -6
  36. package/dist/lib/internal/sandbox-control.js +24 -0
  37. package/dist/lib/internal/task-activity.js +105 -0
  38. package/dist/lib/internal/task-event.js +17 -1
  39. package/dist/lib/internal/task-ledger.js +12 -3
  40. package/dist/lib/internal/task-lifecycle.js +26 -1
  41. package/dist/lib/internal/task-orchestration.js +167 -18
  42. package/dist/lib/internal/task-review.js +8 -3
  43. package/dist/lib/platform/adapters.js +8 -1
  44. package/dist/lib/platform/completion-backfill.js +122 -0
  45. package/dist/lib/platform/github-client.js +19 -2
  46. package/dist/lib/platform/issue-comments.js +3 -2
  47. package/dist/lib/platform/merged-pr-equivalence.js +115 -5
  48. package/dist/lib/platform/pr-checks.js +65 -42
  49. package/dist/lib/platform/pr-review.js +166 -0
  50. package/dist/lib/platform/pull-requests.js +371 -4
  51. package/dist/lib/platform/release-notes.js +85 -3
  52. package/dist/lib/platform/releases.js +13 -5
  53. package/dist/lib/pr-review/evidence-grading.js +229 -0
  54. package/dist/lib/process-data/index.js +239 -0
  55. package/dist/lib/process-data/normalize.js +198 -0
  56. package/dist/lib/process-data/privacy.js +33 -0
  57. package/dist/lib/process-data/reconcile.js +95 -0
  58. package/dist/lib/process-data/repair.js +58 -0
  59. package/dist/lib/process-data/sources.js +345 -0
  60. package/dist/lib/process-data/store.js +282 -0
  61. package/dist/lib/process-data/types.js +2 -0
  62. package/dist/lib/run/index.js +4 -3
  63. package/dist/lib/run/tui.js +10 -13
  64. package/dist/lib/sandbox/agent-client-reconciler.js +26 -2
  65. package/dist/lib/sandbox/capture.js +7 -2
  66. package/dist/lib/sandbox/commands/create.js +109 -248
  67. package/dist/lib/sandbox/commands/enter.js +15 -47
  68. package/dist/lib/sandbox/commands/list-running.js +16 -6
  69. package/dist/lib/sandbox/commands/ls.js +10 -6
  70. package/dist/lib/sandbox/commands/rebuild.js +4 -3
  71. package/dist/lib/sandbox/commands/refresh.js +4 -84
  72. package/dist/lib/sandbox/commands/rm.js +24 -3
  73. package/dist/lib/sandbox/commands/show.js +13 -4
  74. package/dist/lib/sandbox/commands/start.js +7 -4
  75. package/dist/lib/sandbox/config.js +2 -0
  76. package/dist/lib/sandbox/constants.js +6 -0
  77. package/dist/lib/sandbox/control/client.js +44 -0
  78. package/dist/lib/sandbox/control/protocol.js +35 -0
  79. package/dist/lib/sandbox/control/server.js +109 -0
  80. package/dist/lib/sandbox/dockerfile.js +36 -13
  81. package/dist/lib/sandbox/mounts.js +17 -4
  82. package/dist/lib/sandbox/recovery.js +128 -5
  83. package/dist/lib/sandbox/redaction.js +12 -0
  84. package/dist/lib/sandbox/runtimes/base.dockerfile +24 -74
  85. package/dist/lib/sandbox/workspace-identity.js +95 -0
  86. package/dist/lib/sandbox/workspace-view.js +103 -0
  87. package/dist/lib/task/activity-intent.js +307 -0
  88. package/dist/lib/task/artifact-lifecycle.js +2 -1
  89. package/dist/lib/task/commands/log.js +15 -14
  90. package/dist/lib/task/commit-finalization.js +267 -0
  91. package/dist/lib/task/commit-intent.js +165 -0
  92. package/dist/lib/task/decision-intents.js +31 -17
  93. package/dist/lib/task/delegation-receipts.js +25 -4
  94. package/dist/lib/task/events.js +67 -28
  95. package/dist/lib/task/finalization-artifacts.js +35 -0
  96. package/dist/lib/task/implementation-inputs.js +54 -7
  97. package/dist/lib/task/ledger-intents.js +35 -1
  98. package/dist/lib/task/lifecycle-execution.js +46 -0
  99. package/dist/lib/task/orchestration.js +753 -54
  100. package/dist/lib/task/resolve-ref.js +41 -1
  101. package/dist/lib/task/review-finalization.js +29 -9
  102. package/dist/lib/task/task-execution-lock.js +231 -0
  103. package/dist/lib/task/verification-checks.js +1 -1
  104. package/dist/lib/task/verification-engine.js +218 -2
  105. package/dist/lib/task/verification.js +4 -3
  106. package/dist/lib/task/workspace-snapshot.js +17 -5
  107. package/lib/agent-clients/adapter.ts +201 -4
  108. package/lib/agent-clients/adapters/antigravity-cli.ts +65 -0
  109. package/lib/{sandbox/credentials.ts → agent-clients/adapters/claude-code-credentials.ts} +3 -18
  110. package/lib/agent-clients/adapters/claude-code-refresh.ts +128 -0
  111. package/lib/agent-clients/adapters/claude-code-sandbox.ts +295 -0
  112. package/lib/agent-clients/adapters/claude-code.ts +31 -4
  113. package/lib/agent-clients/adapters/codex.ts +9 -0
  114. package/lib/agent-clients/adapters/index.ts +2 -2
  115. package/lib/agent-clients/adapters/opencode-sandbox.ts +136 -0
  116. package/lib/agent-clients/adapters/opencode.ts +27 -3
  117. package/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
  118. package/lib/agent-clients/config.ts +49 -4
  119. package/lib/agent-clients/next-steps.ts +15 -2
  120. package/lib/agent-clients/reconcile.ts +2 -1
  121. package/lib/agent-clients/registry.ts +19 -1
  122. package/lib/agent-clients/schema.ts +18 -0
  123. package/lib/agent-clients/tokens.ts +53 -0
  124. package/lib/agent-clients/types.ts +15 -1
  125. package/lib/defaults.json +1 -1
  126. package/lib/git/workflow.ts +66 -2
  127. package/lib/internal/agent-client.ts +59 -4
  128. package/lib/internal/git-workflow.ts +5 -1
  129. package/lib/internal/platform-checks.ts +4 -4
  130. package/lib/internal/platform-comment.ts +17 -2
  131. package/lib/internal/platform-issue.ts +4 -0
  132. package/lib/internal/platform-pr-review.ts +104 -0
  133. package/lib/internal/platform-pr.ts +14 -1
  134. package/lib/internal/platform-release-notes.ts +59 -16
  135. package/lib/internal/pr-review-grade.ts +130 -0
  136. package/lib/internal/release-workflow.ts +13 -6
  137. package/lib/internal/sandbox-control.ts +24 -0
  138. package/lib/internal/task-activity.ts +91 -0
  139. package/lib/internal/task-event.ts +9 -1
  140. package/lib/internal/task-ledger.ts +12 -3
  141. package/lib/internal/task-lifecycle.ts +26 -1
  142. package/lib/internal/task-orchestration.ts +123 -14
  143. package/lib/internal/task-review.ts +8 -3
  144. package/lib/platform/adapters.ts +29 -2
  145. package/lib/platform/completion-backfill.ts +134 -0
  146. package/lib/platform/github-client.ts +20 -2
  147. package/lib/platform/issue-comments.ts +3 -2
  148. package/lib/platform/merged-pr-equivalence.ts +127 -5
  149. package/lib/platform/pr-checks.ts +73 -46
  150. package/lib/platform/pr-review.ts +201 -0
  151. package/lib/platform/pull-requests.ts +444 -3
  152. package/lib/platform/release-notes.ts +100 -4
  153. package/lib/platform/releases.ts +16 -4
  154. package/lib/pr-review/evidence-grading.ts +318 -0
  155. package/lib/process-data/index.ts +224 -0
  156. package/lib/process-data/normalize.ts +202 -0
  157. package/lib/process-data/privacy.ts +41 -0
  158. package/lib/process-data/reconcile.ts +110 -0
  159. package/lib/process-data/repair.ts +64 -0
  160. package/lib/process-data/sources.ts +354 -0
  161. package/lib/process-data/store.ts +315 -0
  162. package/lib/process-data/types.ts +135 -0
  163. package/lib/run/index.ts +5 -3
  164. package/lib/run/tui.ts +14 -12
  165. package/lib/sandbox/agent-client-reconciler.ts +44 -2
  166. package/lib/sandbox/capture.ts +9 -2
  167. package/lib/sandbox/commands/create.ts +126 -280
  168. package/lib/sandbox/commands/enter.ts +13 -59
  169. package/lib/sandbox/commands/list-running.ts +23 -6
  170. package/lib/sandbox/commands/ls.ts +22 -6
  171. package/lib/sandbox/commands/rebuild.ts +4 -3
  172. package/lib/sandbox/commands/refresh.ts +4 -126
  173. package/lib/sandbox/commands/rm.ts +24 -3
  174. package/lib/sandbox/commands/show.ts +30 -4
  175. package/lib/sandbox/commands/start.ts +6 -4
  176. package/lib/sandbox/config.ts +4 -0
  177. package/lib/sandbox/constants.ts +8 -0
  178. package/lib/sandbox/control/client.ts +55 -0
  179. package/lib/sandbox/control/protocol.ts +75 -0
  180. package/lib/sandbox/control/server.ts +117 -0
  181. package/lib/sandbox/dockerfile.ts +66 -13
  182. package/lib/sandbox/mounts.ts +24 -4
  183. package/lib/sandbox/recovery.ts +163 -6
  184. package/lib/sandbox/redaction.ts +14 -0
  185. package/lib/sandbox/runtimes/base.dockerfile +24 -74
  186. package/lib/sandbox/tool-types.ts +16 -0
  187. package/lib/sandbox/workspace-identity.ts +123 -0
  188. package/lib/sandbox/workspace-view.ts +143 -0
  189. package/lib/server/server.schema.json +2 -2
  190. package/lib/task/activity-intent.ts +417 -0
  191. package/lib/task/artifact-lifecycle.ts +2 -1
  192. package/lib/task/commands/log.ts +15 -14
  193. package/lib/task/commit-finalization.ts +356 -0
  194. package/lib/task/commit-intent.ts +230 -0
  195. package/lib/task/decision-intents.ts +32 -12
  196. package/lib/task/delegation-receipts.ts +34 -5
  197. package/lib/task/events.ts +75 -30
  198. package/lib/task/finalization-artifacts.ts +58 -0
  199. package/lib/task/implementation-inputs.ts +62 -8
  200. package/lib/task/ledger-intents.ts +43 -3
  201. package/lib/task/lifecycle-execution.ts +106 -0
  202. package/lib/task/orchestration.ts +1019 -72
  203. package/lib/task/resolve-ref.ts +46 -1
  204. package/lib/task/review-finalization.ts +40 -9
  205. package/lib/task/task-execution-lock.ts +296 -0
  206. package/lib/task/verification-checks.ts +1 -1
  207. package/lib/task/verification-engine.ts +249 -2
  208. package/lib/task/verification.ts +5 -3
  209. package/lib/task/workspace-snapshot.ts +26 -5
  210. package/package.json +3 -3
  211. package/templates/.agents/QUICKSTART.en.md +25 -8
  212. package/templates/.agents/QUICKSTART.zh-CN.md +25 -8
  213. package/templates/.agents/README.en.md +10 -7
  214. package/templates/.agents/README.zh-CN.md +10 -7
  215. package/templates/.agents/hooks/lifecycle-delegation.js +15 -4
  216. package/templates/.agents/rules/README.en.md +1 -1
  217. package/templates/.agents/rules/README.zh-CN.md +1 -1
  218. package/templates/.agents/rules/create-issue.github.en.md +4 -2
  219. package/templates/.agents/rules/create-issue.github.zh-CN.md +4 -2
  220. package/templates/.agents/rules/human-decision-context.en.md +11 -2
  221. package/templates/.agents/rules/human-decision-context.zh-CN.md +11 -2
  222. package/templates/.agents/rules/issue-fields.github.en.md +3 -1
  223. package/templates/.agents/rules/issue-fields.github.zh-CN.md +3 -1
  224. package/templates/.agents/rules/issue-pr-commands.en.md +5 -3
  225. package/templates/.agents/rules/issue-pr-commands.github.en.md +5 -3
  226. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +5 -3
  227. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +5 -3
  228. package/templates/.agents/rules/issue-sync.github.en.md +7 -3
  229. package/templates/.agents/rules/issue-sync.github.zh-CN.md +8 -4
  230. package/templates/.agents/rules/lifecycle-orchestration.en.md +5 -2
  231. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +5 -2
  232. package/templates/.agents/rules/milestone-inference.github.en.md +4 -2
  233. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +4 -2
  234. package/templates/.agents/rules/next-step-output.en.md +3 -1
  235. package/templates/.agents/rules/next-step-output.zh-CN.md +3 -1
  236. package/templates/.agents/rules/no-mid-flow-questions.en.md +17 -0
  237. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +17 -0
  238. package/templates/.agents/rules/pr-checks-commands.en.md +3 -3
  239. package/templates/.agents/rules/pr-checks-commands.github.en.md +2 -2
  240. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +3 -3
  241. package/templates/.agents/rules/pr-checks-commands.zh-CN.md +3 -3
  242. package/templates/.agents/rules/pr-sync.en.md +3 -1
  243. package/templates/.agents/rules/pr-sync.github.en.md +11 -1
  244. package/templates/.agents/rules/pr-sync.github.zh-CN.md +11 -1
  245. package/templates/.agents/rules/pr-sync.zh-CN.md +3 -1
  246. package/templates/.agents/rules/release-commands.en.md +1 -1
  247. package/templates/.agents/rules/release-commands.github.en.md +12 -2
  248. package/templates/.agents/rules/release-commands.github.zh-CN.md +12 -2
  249. package/templates/.agents/rules/release-commands.zh-CN.md +1 -1
  250. package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
  251. package/templates/.agents/rules/task-management.en.md +13 -0
  252. package/templates/.agents/rules/task-management.zh-CN.md +13 -0
  253. package/templates/.agents/skills/analyze-task/SKILL.en.md +10 -7
  254. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +10 -7
  255. package/templates/.agents/skills/block-task/SKILL.en.md +7 -5
  256. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +7 -5
  257. package/templates/.agents/skills/cancel-task/SKILL.en.md +6 -4
  258. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +6 -4
  259. package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -1
  260. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -1
  261. package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -1
  262. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -1
  263. package/templates/.agents/skills/code-task/SKILL.en.md +10 -7
  264. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +12 -9
  265. package/templates/.agents/skills/commit/SKILL.en.md +23 -11
  266. package/templates/.agents/skills/commit/SKILL.zh-CN.md +29 -11
  267. package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
  268. package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
  269. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +49 -0
  270. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +55 -0
  271. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
  272. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
  273. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  274. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  275. package/templates/.agents/skills/commit/reference/task-status-update.en.md +4 -4
  276. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +4 -4
  277. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +6 -4
  278. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +6 -4
  279. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
  280. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
  281. package/templates/.agents/skills/complete-task/SKILL.en.md +22 -7
  282. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +22 -7
  283. package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
  284. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
  285. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +17 -0
  286. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +17 -0
  287. package/templates/.agents/skills/create-pr/SKILL.en.md +7 -5
  288. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -5
  289. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +1 -1
  290. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +1 -1
  291. package/templates/.agents/skills/create-release-note/SKILL.en.md +17 -16
  292. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +17 -16
  293. package/templates/.agents/skills/create-task/SKILL.en.md +5 -3
  294. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -3
  295. package/templates/.agents/skills/create-task/reference/context-capture.en.md +34 -0
  296. package/templates/.agents/skills/create-task/reference/context-capture.zh-CN.md +34 -0
  297. package/templates/.agents/skills/import-issue/SKILL.en.md +5 -3
  298. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -3
  299. package/templates/.agents/skills/plan-task/SKILL.en.md +8 -5
  300. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +8 -5
  301. package/templates/.agents/skills/post-release/SKILL.en.md +2 -0
  302. package/templates/.agents/skills/post-release/SKILL.zh-CN.md +2 -0
  303. package/templates/.agents/skills/release/SKILL.en.md +16 -10
  304. package/templates/.agents/skills/release/SKILL.zh-CN.md +16 -10
  305. package/templates/.agents/skills/restore-task/SKILL.en.md +4 -1
  306. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +4 -1
  307. package/templates/.agents/skills/review-analysis/SKILL.en.md +7 -4
  308. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +7 -4
  309. package/templates/.agents/skills/review-code/SKILL.en.md +11 -8
  310. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +11 -8
  311. package/templates/.agents/skills/review-code/reference/output-templates.en.md +1 -1
  312. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +1 -1
  313. package/templates/.agents/skills/review-plan/SKILL.en.md +7 -4
  314. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +7 -4
  315. package/templates/.agents/skills/review-pr/SKILL.en.md +186 -0
  316. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +186 -0
  317. package/templates/.agents/skills/review-pr/config/verify.en.json +37 -0
  318. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +37 -0
  319. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +65 -0
  320. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +65 -0
  321. package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +18 -0
  322. package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +18 -0
  323. package/templates/.agents/skills/review-pr/reference/report-template.en.md +85 -0
  324. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +85 -0
  325. package/templates/.agents/skills/run-task/SKILL.en.md +8 -8
  326. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +8 -8
  327. package/templates/.agents/skills/run-task/config/verify.en.json +2 -1
  328. package/templates/.agents/skills/run-task/config/verify.zh-CN.json +2 -1
  329. package/templates/.agents/skills/run-task/reference/host-validation.en.md +20 -0
  330. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +20 -0
  331. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +98 -107
  332. package/templates/.agents/skills/watch-pr/SKILL.en.md +13 -13
  333. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +13 -13
  334. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +27 -6
  335. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +26 -5
  336. package/templates/.agents/templates/task.en.md +2 -2
  337. package/templates/.agents/templates/task.zh-CN.md +2 -2
  338. package/templates/.agents/workflows/bug-fix.en.yaml +3 -3
  339. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +3 -3
  340. package/templates/.agents/workflows/code-review.en.yaml +1 -1
  341. package/templates/.agents/workflows/code-review.zh-CN.yaml +1 -1
  342. package/templates/.agents/workflows/feature-development.en.yaml +4 -4
  343. package/templates/.agents/workflows/feature-development.zh-CN.yaml +4 -4
  344. package/templates/.agents/workflows/refactoring.en.yaml +3 -3
  345. package/templates/.agents/workflows/refactoring.zh-CN.yaml +3 -3
  346. package/templates/.claude/commands/post-release.en.md +1 -0
  347. package/templates/.claude/commands/post-release.zh-CN.md +1 -0
  348. package/templates/.claude/commands/review-pr.en.md +8 -0
  349. package/templates/.claude/commands/review-pr.zh-CN.md +8 -0
  350. package/templates/.claude/commands/watch-pr.en.md +1 -1
  351. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  352. package/templates/.claude/rules/tool-preferences.en.md +20 -0
  353. package/templates/.claude/rules/tool-preferences.zh-CN.md +20 -0
  354. package/templates/.opencode/commands/post-release.en.md +2 -0
  355. package/templates/.opencode/commands/post-release.zh-CN.md +2 -0
  356. package/templates/.opencode/commands/review-pr.en.md +11 -0
  357. package/templates/.opencode/commands/review-pr.zh-CN.md +11 -0
  358. package/templates/.opencode/commands/watch-pr.en.md +1 -1
  359. package/templates/.opencode/commands/watch-pr.zh-CN.md +1 -1
  360. package/dist/lib/agent-clients/adapters/gemini-cli.js +0 -64
  361. package/lib/agent-clients/adapters/gemini-cli.ts +0 -65
  362. package/templates/.gemini/commands/_project_/analyze-task.en.toml +0 -10
  363. package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +0 -10
  364. package/templates/.gemini/commands/_project_/archive-tasks.en.toml +0 -10
  365. package/templates/.gemini/commands/_project_/archive-tasks.zh-CN.toml +0 -10
  366. package/templates/.gemini/commands/_project_/block-task.en.toml +0 -10
  367. package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +0 -10
  368. package/templates/.gemini/commands/_project_/cancel-task.en.toml +0 -10
  369. package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +0 -10
  370. package/templates/.gemini/commands/_project_/check-task.en.toml +0 -10
  371. package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +0 -10
  372. package/templates/.gemini/commands/_project_/close-codescan.en.toml +0 -8
  373. package/templates/.gemini/commands/_project_/close-codescan.zh-CN.toml +0 -8
  374. package/templates/.gemini/commands/_project_/close-dependabot.en.toml +0 -8
  375. package/templates/.gemini/commands/_project_/close-dependabot.zh-CN.toml +0 -8
  376. package/templates/.gemini/commands/_project_/code-task.en.toml +0 -10
  377. package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +0 -10
  378. package/templates/.gemini/commands/_project_/commit.en.toml +0 -10
  379. package/templates/.gemini/commands/_project_/commit.zh-CN.toml +0 -10
  380. package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +0 -10
  381. package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +0 -10
  382. package/templates/.gemini/commands/_project_/complete-task.en.toml +0 -10
  383. package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +0 -10
  384. package/templates/.gemini/commands/_project_/create-pr.en.toml +0 -10
  385. package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +0 -10
  386. package/templates/.gemini/commands/_project_/create-release-note.en.toml +0 -8
  387. package/templates/.gemini/commands/_project_/create-release-note.zh-CN.toml +0 -8
  388. package/templates/.gemini/commands/_project_/create-task.en.toml +0 -8
  389. package/templates/.gemini/commands/_project_/create-task.zh-CN.toml +0 -8
  390. package/templates/.gemini/commands/_project_/import-codescan.en.toml +0 -8
  391. package/templates/.gemini/commands/_project_/import-codescan.zh-CN.toml +0 -8
  392. package/templates/.gemini/commands/_project_/import-dependabot.en.toml +0 -8
  393. package/templates/.gemini/commands/_project_/import-dependabot.zh-CN.toml +0 -8
  394. package/templates/.gemini/commands/_project_/import-issue.en.toml +0 -8
  395. package/templates/.gemini/commands/_project_/import-issue.zh-CN.toml +0 -8
  396. package/templates/.gemini/commands/_project_/init-labels.en.toml +0 -8
  397. package/templates/.gemini/commands/_project_/init-labels.zh-CN.toml +0 -8
  398. package/templates/.gemini/commands/_project_/init-milestones.en.toml +0 -10
  399. package/templates/.gemini/commands/_project_/init-milestones.zh-CN.toml +0 -10
  400. package/templates/.gemini/commands/_project_/plan-task.en.toml +0 -10
  401. package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +0 -10
  402. package/templates/.gemini/commands/_project_/post-release.en.toml +0 -6
  403. package/templates/.gemini/commands/_project_/post-release.zh-CN.toml +0 -6
  404. package/templates/.gemini/commands/_project_/refine-title.en.toml +0 -8
  405. package/templates/.gemini/commands/_project_/refine-title.zh-CN.toml +0 -8
  406. package/templates/.gemini/commands/_project_/release.en.toml +0 -8
  407. package/templates/.gemini/commands/_project_/release.zh-CN.toml +0 -8
  408. package/templates/.gemini/commands/_project_/restore-task.en.toml +0 -8
  409. package/templates/.gemini/commands/_project_/restore-task.zh-CN.toml +0 -8
  410. package/templates/.gemini/commands/_project_/review-analysis.en.toml +0 -10
  411. package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +0 -10
  412. package/templates/.gemini/commands/_project_/review-code.en.toml +0 -10
  413. package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +0 -10
  414. package/templates/.gemini/commands/_project_/review-plan.en.toml +0 -10
  415. package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +0 -10
  416. package/templates/.gemini/commands/_project_/run-task.en.toml +0 -8
  417. package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +0 -8
  418. package/templates/.gemini/commands/_project_/test-integration.en.toml +0 -6
  419. package/templates/.gemini/commands/_project_/test-integration.zh-CN.toml +0 -6
  420. package/templates/.gemini/commands/_project_/test.en.toml +0 -6
  421. package/templates/.gemini/commands/_project_/test.zh-CN.toml +0 -6
  422. package/templates/.gemini/commands/_project_/update-agent-infra.en.toml +0 -6
  423. package/templates/.gemini/commands/_project_/update-agent-infra.zh-CN.toml +0 -6
  424. package/templates/.gemini/commands/_project_/upgrade-dependency.en.toml +0 -8
  425. package/templates/.gemini/commands/_project_/upgrade-dependency.zh-CN.toml +0 -8
  426. package/templates/.gemini/commands/_project_/watch-pr.en.toml +0 -10
  427. package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +0 -10
  428. package/templates/.gemini/settings.json +0 -3
@@ -132,6 +132,43 @@ function enumerateTaskDirs(repoRoot: string): { taskId: string; taskDir: string
132
132
  return out;
133
133
  }
134
134
 
135
+ /**
136
+ * Enumerate every task directory, including the cold YYYY/MM/DD archive.
137
+ * Invalid directory names, symlinks and entries without task.md are ignored;
138
+ * duplicate task ids fail closed because their identity would be ambiguous.
139
+ */
140
+ function enumerateAllTaskDirs(repoRoot: string): { taskId: string; taskDir: string; state: TaskWorkspaceState }[] {
141
+ const out: { taskId: string; taskDir: string; state: TaskWorkspaceState }[] = [];
142
+ const seen = new Set<string>();
143
+ const add = (taskId: string, taskDir: string, state: TaskWorkspaceState) => {
144
+ if (!TASK_ID_RE.test(taskId)) return;
145
+ const stat = fs.lstatSync(taskDir);
146
+ if (!stat.isDirectory() || stat.isSymbolicLink() || !fs.existsSync(path.join(taskDir, 'task.md'))) return;
147
+ if (seen.has(taskId)) throw new Error(`duplicate task identity: ${taskId}`);
148
+ seen.add(taskId);
149
+ out.push({ taskId, taskDir, state });
150
+ };
151
+
152
+ for (const state of FLAT_WORKSPACE_DIRS) {
153
+ const base = path.join(repoRoot, '.agents', 'workspace', state);
154
+ if (!fs.existsSync(base)) continue;
155
+ for (const taskId of fs.readdirSync(base).sort()) add(taskId, path.join(base, taskId), state);
156
+ }
157
+
158
+ const archive = path.join(repoRoot, '.agents', 'workspace', 'archive');
159
+ for (const year of listSortedNumeric(archive, 4).reverse()) {
160
+ const yearDir = path.join(archive, year);
161
+ for (const month of listSortedNumeric(yearDir, 2).reverse()) {
162
+ const monthDir = path.join(yearDir, month);
163
+ for (const day of listSortedNumeric(monthDir, 2).reverse()) {
164
+ const dayDir = path.join(monthDir, day);
165
+ for (const taskId of fs.readdirSync(dayDir).sort()) add(taskId, path.join(dayDir, taskId), 'archive');
166
+ }
167
+ }
168
+ }
169
+ return out;
170
+ }
171
+
135
172
  function locateHotTaskDirs(
136
173
  repoRoot: string,
137
174
  taskId: string
@@ -312,7 +349,15 @@ function resolveTaskContext(
312
349
  return { ok: true, repoRoot, ...match, state: 'active' };
313
350
  }
314
351
 
315
- export { resolveTaskRef, resolveTaskContext, detectRepoRoot, enumerateTaskDirs, locateHotTaskDirs, TASK_ID_RE };
352
+ export {
353
+ resolveTaskRef,
354
+ resolveTaskContext,
355
+ detectRepoRoot,
356
+ enumerateTaskDirs,
357
+ enumerateAllTaskDirs,
358
+ locateHotTaskDirs,
359
+ TASK_ID_RE
360
+ };
316
361
  export type {
317
362
  ResolveRefResult,
318
363
  ResolveTaskRefErrorCode,
@@ -8,7 +8,8 @@ import { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.
8
8
  import type { LedgerStageStatus, ReviewStage } from './ledger.ts';
9
9
  import { finalizeReviewSummaryContent } from './review-artifacts.ts';
10
10
  import { resolveTaskRef } from './resolve-ref.ts';
11
- import { validateOrchestrationStage } from './orchestration.ts';
11
+ import { validateLifecycleExecution } from './lifecycle-execution.ts';
12
+ import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
12
13
  import type { ResolveTaskRefErrorCode } from './resolve-ref.ts';
13
14
 
14
15
  type ReviewFinalizationErrorCode =
@@ -31,6 +32,7 @@ type ReviewFinalizationRequest = {
31
32
  taskRef: string;
32
33
  stage: string;
33
34
  artifact: string;
35
+ orchestrated?: boolean;
34
36
  dryRun?: boolean;
35
37
  };
36
38
  type ReviewFinalizationResult = {
@@ -113,7 +115,7 @@ function cleanupTemp(fileSystem: ReviewFileSystem, tempPath: string): void {
113
115
  }
114
116
  }
115
117
 
116
- function finalizeReviewSummary(
118
+ function finalizeReviewSummaryUnlocked(
117
119
  request: ReviewFinalizationRequest,
118
120
  options: ReviewFinalizationOptions = {}
119
121
  ): ReviewFinalizationResult {
@@ -167,17 +169,21 @@ function finalizeReviewSummary(
167
169
  resolved.taskId
168
170
  );
169
171
  }
170
- const provenance = validateOrchestrationStage(request.taskRef, {
171
- stage: spec.family,
172
- round: parsedArtifact.round,
173
- artifact: request.artifact,
174
- role: 'reviewer'
172
+ const execution = validateLifecycleExecution(request.taskRef, {
173
+ mode: request.orchestrated ? 'orchestrated' : 'standalone',
174
+ identity: {
175
+ stage: spec.family,
176
+ round: parsedArtifact.round,
177
+ artifact: request.artifact,
178
+ role: 'reviewer'
179
+ },
180
+ dryRun: request.dryRun
175
181
  }, { repoRoot: options.repoRoot });
176
- if (provenance.status === 'failed' || provenance.status === 'paused') {
182
+ if (!execution.ok) {
177
183
  return failed(
178
184
  request,
179
185
  'REVIEW_PROVENANCE_INVALID',
180
- provenance.error?.message ?? provenance.run?.pause?.message ?? 'orchestration provenance validation failed',
186
+ `${execution.error?.code ?? 'ORCHESTRATION_PROVENANCE_MISMATCH'}: ${execution.error?.message ?? 'orchestration provenance validation failed'}`,
181
187
  resolved.taskId
182
188
  );
183
189
  }
@@ -265,6 +271,31 @@ function finalizeReviewSummary(
265
271
  };
266
272
  }
267
273
 
274
+ function finalizeReviewSummary(
275
+ request: ReviewFinalizationRequest,
276
+ options: ReviewFinalizationOptions = {}
277
+ ): ReviewFinalizationResult {
278
+ if (request.dryRun) return finalizeReviewSummaryUnlocked(request, options);
279
+ const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
280
+ if (!resolved.ok) return finalizeReviewSummaryUnlocked(request, options);
281
+ try {
282
+ return withTaskExecutionLock(
283
+ resolved.repoRoot,
284
+ resolved.taskId,
285
+ 'task-review.finalize-summary',
286
+ () => finalizeReviewSummaryUnlocked(request, options)
287
+ );
288
+ } catch (error) {
289
+ if (!(error instanceof TaskExecutionLockError)) throw error;
290
+ return failed(
291
+ request,
292
+ 'REVIEW_PROVENANCE_INVALID',
293
+ `${error.code}: ${error.message}`,
294
+ resolved.taskId
295
+ );
296
+ }
297
+ }
298
+
268
299
  export { finalizeReviewSummary };
269
300
  export type {
270
301
  ReviewFinalizationError,
@@ -0,0 +1,296 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { createHash, randomUUID } from 'node:crypto';
5
+
6
+ import { getProcessStartTime, processIdentityMatches } from '../server/process-state.ts';
7
+
8
+ type TaskExecutionLockErrorCode =
9
+ | 'ORCHESTRATION_LOCK_BUSY'
10
+ | 'ORCHESTRATION_LOCK_UNSUPPORTED'
11
+ | 'ORCHESTRATION_LOCK_FAILED';
12
+
13
+ type TaskExecutionLockOwner = Readonly<{
14
+ version: 1;
15
+ pid: number;
16
+ startTime: string;
17
+ token: string;
18
+ owner: string;
19
+ canonicalRepoRoot: string;
20
+ taskId: string;
21
+ acquiredAt: string;
22
+ }>;
23
+
24
+ type LinkOperation = 'acquire' | 'quarantine';
25
+ type LinkDisposition = 'exists' | 'missing' | TaskExecutionLockError;
26
+
27
+ type TaskExecutionLockOptions = Readonly<{
28
+ lockRoot?: string;
29
+ token?: () => string;
30
+ now?: () => string;
31
+ getStartTime?: (pid: number) => string | null;
32
+ identityMatches?: (record: { version: 1; pid: number; startTime: string }) => boolean;
33
+ linkSync?: (existingPath: string, newPath: string) => void;
34
+ }>;
35
+
36
+ class TaskExecutionLockError extends Error {
37
+ readonly code: TaskExecutionLockErrorCode;
38
+ readonly detail: Readonly<{ operation: string; errno: string | null; key: string }>;
39
+
40
+ constructor(
41
+ code: TaskExecutionLockErrorCode,
42
+ message: string,
43
+ detail: Readonly<{ operation: string; errno: string | null; key: string }>
44
+ ) {
45
+ super(message);
46
+ this.name = 'TaskExecutionLockError';
47
+ this.code = code;
48
+ this.detail = detail;
49
+ }
50
+ }
51
+
52
+ function errno(error: unknown): string | null {
53
+ const value = (error as NodeJS.ErrnoException | null)?.code;
54
+ return typeof value === 'string' ? value : null;
55
+ }
56
+
57
+ function mapLinkError(operation: LinkOperation, error: unknown, key: string): LinkDisposition {
58
+ const code = errno(error);
59
+ if (code === 'EEXIST') return 'exists';
60
+ if (operation === 'quarantine' && code === 'ENOENT') return 'missing';
61
+ const detail = { operation, errno: code, key };
62
+ if (['EPERM', 'EACCES', 'ENOSYS', 'ENOTSUP', 'EOPNOTSUPP', 'EXDEV'].includes(code ?? '')) {
63
+ return new TaskExecutionLockError(
64
+ 'ORCHESTRATION_LOCK_UNSUPPORTED',
65
+ `lifecycle lock hard-link operation '${operation}' is unsupported`,
66
+ detail
67
+ );
68
+ }
69
+ return new TaskExecutionLockError(
70
+ 'ORCHESTRATION_LOCK_FAILED',
71
+ `lifecycle lock hard-link operation '${operation}' failed`,
72
+ detail
73
+ );
74
+ }
75
+
76
+ function lockKey(repoRoot: string, taskId: string): Readonly<{ canonicalRepoRoot: string; key: string }> {
77
+ const canonicalRepoRoot = fs.realpathSync.native(repoRoot);
78
+ const key = createHash('sha256').update(`${canonicalRepoRoot}\0${taskId}`).digest('hex');
79
+ return { canonicalRepoRoot, key };
80
+ }
81
+
82
+ function parseOwner(raw: string, key: string): TaskExecutionLockOwner {
83
+ try {
84
+ const value = JSON.parse(raw) as Partial<TaskExecutionLockOwner> | null;
85
+ if (
86
+ value !== null
87
+ && value.version === 1
88
+ && Number.isSafeInteger(value.pid)
89
+ && (value.pid ?? 0) > 0
90
+ && typeof value.startTime === 'string'
91
+ && value.startTime.length > 0
92
+ && typeof value.token === 'string'
93
+ && value.token.length > 0
94
+ && typeof value.owner === 'string'
95
+ && typeof value.canonicalRepoRoot === 'string'
96
+ && typeof value.taskId === 'string'
97
+ && typeof value.acquiredAt === 'string'
98
+ ) {
99
+ return value as TaskExecutionLockOwner;
100
+ }
101
+ } catch {
102
+ // Fall through to the stable lock failure below.
103
+ }
104
+ throw new TaskExecutionLockError(
105
+ 'ORCHESTRATION_LOCK_FAILED',
106
+ 'lifecycle lock owner record is invalid',
107
+ { operation: 'read', errno: null, key }
108
+ );
109
+ }
110
+
111
+ function unlinkIfPresent(file: string): void {
112
+ try {
113
+ fs.unlinkSync(file);
114
+ } catch (error) {
115
+ if (errno(error) !== 'ENOENT') throw error;
116
+ }
117
+ }
118
+
119
+ function sameInode(left: fs.Stats, right: fs.Stats): boolean {
120
+ return left.dev === right.dev && left.ino === right.ino;
121
+ }
122
+
123
+ function reclaimStaleLock(
124
+ fixed: string,
125
+ quarantine: string,
126
+ snapshot: string,
127
+ key: string,
128
+ link: (existingPath: string, newPath: string) => void
129
+ ): void {
130
+ let linked = false;
131
+ try {
132
+ link(fixed, quarantine);
133
+ linked = true;
134
+ } catch (error) {
135
+ const disposition = mapLinkError('quarantine', error, key);
136
+ if (disposition === 'missing') return;
137
+ if (disposition === 'exists') {
138
+ try {
139
+ const quarantineSnapshot = fs.readFileSync(quarantine, 'utf8');
140
+ const fixedStat = fs.statSync(fixed);
141
+ const quarantineStat = fs.statSync(quarantine);
142
+ if (quarantineSnapshot === snapshot && sameInode(fixedStat, quarantineStat)) {
143
+ unlinkIfPresent(quarantine);
144
+ }
145
+ } catch (readError) {
146
+ if (errno(readError) !== 'ENOENT') throw readError;
147
+ }
148
+ return;
149
+ }
150
+ if (disposition instanceof TaskExecutionLockError) throw disposition;
151
+ }
152
+ try {
153
+ const fixedSnapshot = fs.readFileSync(fixed, 'utf8');
154
+ const fixedStat = fs.statSync(fixed);
155
+ const quarantineStat = fs.statSync(quarantine);
156
+ if (fixedSnapshot === snapshot && sameInode(fixedStat, quarantineStat)) unlinkIfPresent(fixed);
157
+ } catch (error) {
158
+ if (errno(error) !== 'ENOENT') throw error;
159
+ } finally {
160
+ if (linked) unlinkIfPresent(quarantine);
161
+ }
162
+ }
163
+
164
+ function cleanStaleCandidates(
165
+ lockRoot: string,
166
+ key: string,
167
+ identityMatches: (record: { version: 1; pid: number; startTime: string }) => boolean
168
+ ): void {
169
+ const prefix = `${key}.candidate.`;
170
+ for (const name of fs.readdirSync(lockRoot)) {
171
+ if (!name.startsWith(prefix)) continue;
172
+ const candidate = path.join(lockRoot, name);
173
+ try {
174
+ const owner = parseOwner(fs.readFileSync(candidate, 'utf8'), key);
175
+ if (!identityMatches(owner)) unlinkIfPresent(candidate);
176
+ } catch {
177
+ // A malformed candidate never owns the fixed lock and is left for manual diagnosis.
178
+ }
179
+ }
180
+ }
181
+
182
+ function withTaskExecutionLock<T>(
183
+ repoRoot: string,
184
+ taskId: string,
185
+ ownerName: string,
186
+ callback: () => T,
187
+ options: TaskExecutionLockOptions = {}
188
+ ): T {
189
+ let identity: Readonly<{ canonicalRepoRoot: string; key: string }>;
190
+ try {
191
+ identity = lockKey(repoRoot, taskId);
192
+ } catch (error) {
193
+ throw new TaskExecutionLockError(
194
+ 'ORCHESTRATION_LOCK_FAILED',
195
+ 'failed to canonicalize the repository for lifecycle locking',
196
+ { operation: 'canonicalize', errno: errno(error), key: 'unresolved' }
197
+ );
198
+ }
199
+ const { canonicalRepoRoot, key } = identity;
200
+ const lockRoot = options.lockRoot ?? path.join(os.tmpdir(), 'agent-infra-lifecycle-locks');
201
+ const token = (options.token ?? randomUUID)();
202
+ const candidate = path.join(lockRoot, `${key}.candidate.${process.pid}.${token}`);
203
+ const fixed = path.join(lockRoot, `${key}.lock`);
204
+ const link = options.linkSync ?? fs.linkSync;
205
+ const startTime = (options.getStartTime ?? getProcessStartTime)(process.pid);
206
+ if (startTime === null) {
207
+ throw new TaskExecutionLockError(
208
+ 'ORCHESTRATION_LOCK_FAILED',
209
+ 'current process start time is unavailable',
210
+ { operation: 'identity', errno: null, key }
211
+ );
212
+ }
213
+ const owner: TaskExecutionLockOwner = {
214
+ version: 1,
215
+ pid: process.pid,
216
+ startTime,
217
+ token,
218
+ owner: ownerName,
219
+ canonicalRepoRoot,
220
+ taskId,
221
+ acquiredAt: (options.now ?? (() => new Date().toISOString()))()
222
+ };
223
+ const serialized = `${JSON.stringify(owner)}\n`;
224
+ let ownsFixed = false;
225
+ try {
226
+ fs.mkdirSync(lockRoot, { recursive: true });
227
+ cleanStaleCandidates(lockRoot, key, options.identityMatches ?? processIdentityMatches);
228
+ const descriptor = fs.openSync(candidate, 'wx', 0o600);
229
+ try {
230
+ fs.writeFileSync(descriptor, serialized);
231
+ fs.fsyncSync(descriptor);
232
+ } finally {
233
+ fs.closeSync(descriptor);
234
+ }
235
+ for (let attempt = 0; attempt < 16; attempt += 1) {
236
+ try {
237
+ link(candidate, fixed);
238
+ ownsFixed = true;
239
+ break;
240
+ } catch (error) {
241
+ const disposition = mapLinkError('acquire', error, key);
242
+ if (disposition instanceof TaskExecutionLockError) throw disposition;
243
+ let snapshot: string;
244
+ let current: TaskExecutionLockOwner;
245
+ try {
246
+ snapshot = fs.readFileSync(fixed, 'utf8');
247
+ current = parseOwner(snapshot, key);
248
+ } catch (readError) {
249
+ if (errno(readError) === 'ENOENT') continue;
250
+ throw readError;
251
+ }
252
+ if ((options.identityMatches ?? processIdentityMatches)(current)) {
253
+ throw new TaskExecutionLockError(
254
+ 'ORCHESTRATION_LOCK_BUSY',
255
+ 'another lifecycle operation holds the task lock',
256
+ { operation: 'acquire', errno: 'EEXIST', key }
257
+ );
258
+ }
259
+ reclaimStaleLock(fixed, `${fixed}.quarantine.${current.token}`, snapshot, key, link);
260
+ }
261
+ }
262
+ if (!ownsFixed) {
263
+ throw new TaskExecutionLockError(
264
+ 'ORCHESTRATION_LOCK_BUSY',
265
+ 'lifecycle task lock could not be acquired after stale recovery',
266
+ { operation: 'acquire', errno: 'EEXIST', key }
267
+ );
268
+ }
269
+ unlinkIfPresent(candidate);
270
+ return callback();
271
+ } catch (error) {
272
+ if (error instanceof TaskExecutionLockError) throw error;
273
+ throw new TaskExecutionLockError(
274
+ 'ORCHESTRATION_LOCK_FAILED',
275
+ 'lifecycle task lock operation failed',
276
+ { operation: ownsFixed ? 'callback' : 'acquire', errno: errno(error), key }
277
+ );
278
+ } finally {
279
+ try {
280
+ unlinkIfPresent(candidate);
281
+ } catch {
282
+ // Candidate cleanup is best-effort and must not replace the primary result.
283
+ }
284
+ if (ownsFixed) {
285
+ try {
286
+ const current = parseOwner(fs.readFileSync(fixed, 'utf8'), key);
287
+ if (current.token === token) unlinkIfPresent(fixed);
288
+ } catch {
289
+ // A missing or replaced fixed lock is not owned by this invocation.
290
+ }
291
+ }
292
+ }
293
+ }
294
+
295
+ export { TaskExecutionLockError, lockKey, mapLinkError, withTaskExecutionLock };
296
+ export type { LinkDisposition, LinkOperation, TaskExecutionLockErrorCode, TaskExecutionLockOptions, TaskExecutionLockOwner };
@@ -1,6 +1,6 @@
1
1
  const LOCAL_VERIFICATION_CHECKS = [
2
2
  'task-meta', 'artifact', 'implementation-input', 'activity-log', 'completion-checklist',
3
- 'orchestration-state'
3
+ 'orchestration-state', 'orchestration-evidence'
4
4
  ] as const;
5
5
 
6
6
  function isLocalVerificationCheck(value: string): value is typeof LOCAL_VERIFICATION_CHECKS[number] {