@fitlab-ai/agent-infra 0.8.5 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (425) hide show
  1. package/bin/cli.ts +2 -2
  2. package/dist/bin/cli.js +2 -2
  3. package/dist/bin/internal-cli.js +98 -0
  4. package/dist/lib/decide.js +81 -90
  5. package/dist/lib/defaults.json +1 -0
  6. package/dist/lib/git/review-snapshot.js +79 -0
  7. package/dist/lib/git/workflow.js +101 -0
  8. package/dist/lib/internal/git-workflow.js +42 -0
  9. package/dist/lib/internal/platform-checks.js +119 -0
  10. package/dist/lib/internal/platform-comment.js +129 -0
  11. package/dist/lib/internal/platform-context.js +46 -0
  12. package/dist/lib/internal/platform-issue.js +142 -0
  13. package/dist/lib/internal/platform-pr.js +146 -0
  14. package/dist/lib/internal/release-workflow.js +217 -0
  15. package/dist/lib/internal/task-artifact.js +67 -0
  16. package/dist/lib/internal/task-context.js +43 -0
  17. package/dist/lib/internal/task-event.js +70 -0
  18. package/dist/lib/internal/task-ledger.js +114 -0
  19. package/dist/lib/internal/task-lifecycle.js +58 -0
  20. package/dist/lib/internal/task-short-id.js +83 -0
  21. package/dist/lib/internal/task-snapshot.js +47 -0
  22. package/dist/lib/internal/task-verify.js +50 -0
  23. package/dist/lib/internal/task-warning.js +74 -0
  24. package/dist/lib/platform/adapters.js +14 -0
  25. package/dist/lib/platform/context.js +146 -0
  26. package/dist/lib/platform/github-client.js +124 -0
  27. package/dist/lib/platform/issue-comments.js +409 -0
  28. package/dist/lib/platform/issue-metadata.js +183 -0
  29. package/dist/lib/platform/issues.js +617 -0
  30. package/dist/lib/platform/metadata-labels.js +28 -0
  31. package/dist/lib/platform/pr-checks.js +216 -0
  32. package/dist/lib/platform/pr-summary.js +88 -0
  33. package/dist/lib/platform/pull-request-metadata.js +38 -0
  34. package/dist/lib/platform/pull-requests.js +338 -0
  35. package/dist/lib/platform/releases.js +82 -0
  36. package/dist/lib/platform/types.js +22 -0
  37. package/dist/lib/platform/verification-required.js +78 -0
  38. package/dist/lib/platform/verification-sync.js +1014 -0
  39. package/dist/lib/platform/verification.js +6 -0
  40. package/dist/lib/release/channels.js +31 -0
  41. package/dist/lib/release/workflow.js +32 -0
  42. package/dist/lib/render.js +3 -2
  43. package/dist/lib/run/index.js +24 -7
  44. package/dist/lib/sandbox/capture.js +25 -3
  45. package/dist/lib/sandbox/commands/create.js +36 -22
  46. package/dist/lib/sandbox/commands/enter.js +34 -11
  47. package/dist/lib/sandbox/commands/list-running.js +10 -8
  48. package/dist/lib/sandbox/commands/show.js +2 -2
  49. package/dist/lib/sandbox/commands/start.js +53 -15
  50. package/dist/lib/sandbox/index.js +6 -6
  51. package/dist/lib/sandbox/mounts.js +36 -0
  52. package/dist/lib/sandbox/recovery.js +562 -0
  53. package/dist/lib/sandbox/task-resolver.js +5 -1
  54. package/dist/lib/sandbox/tools.js +19 -0
  55. package/dist/lib/server/protocol.js +5 -5
  56. package/dist/lib/task/activity-log.js +61 -0
  57. package/dist/lib/task/artifact-lifecycle.js +427 -0
  58. package/dist/lib/task/command-options.js +30 -0
  59. package/dist/lib/task/commands/cat.js +36 -6
  60. package/dist/lib/task/commands/decisions.js +80 -46
  61. package/dist/lib/task/commands/files.js +21 -7
  62. package/dist/lib/task/commands/grep.js +55 -7
  63. package/dist/lib/task/commands/issue-body.js +23 -14
  64. package/dist/lib/task/commands/log.js +27 -70
  65. package/dist/lib/task/commands/show.js +20 -7
  66. package/dist/lib/task/commands/status.js +18 -8
  67. package/dist/lib/task/decision-intents.js +154 -0
  68. package/dist/lib/task/decision-items.js +53 -0
  69. package/dist/lib/task/events.js +318 -0
  70. package/dist/lib/task/frontmatter.js +159 -1
  71. package/dist/lib/task/implementation-inputs.js +117 -0
  72. package/dist/lib/task/index.js +12 -9
  73. package/dist/lib/task/issue-form.js +19 -6
  74. package/dist/lib/task/ledger-intents.js +185 -0
  75. package/dist/lib/task/ledger.js +65 -35
  76. package/dist/lib/task/lifecycle.js +484 -0
  77. package/dist/lib/task/resolve-ref.js +152 -32
  78. package/dist/lib/task/review-artifacts.js +101 -0
  79. package/dist/lib/task/review-fingerprint.js +46 -0
  80. package/dist/lib/task/sections.js +358 -4
  81. package/dist/lib/task/short-id.js +292 -7
  82. package/dist/lib/task/snapshot.js +95 -0
  83. package/dist/lib/task/verification-checks.js +8 -0
  84. package/dist/lib/task/verification-config.js +14 -0
  85. package/dist/lib/task/verification-engine.js +906 -0
  86. package/dist/lib/task/verification-review.js +3 -0
  87. package/dist/lib/task/verification-types.js +2 -0
  88. package/dist/lib/task/verification.js +113 -0
  89. package/dist/lib/task/workflow-warning-intents.js +131 -0
  90. package/dist/lib/task/workflow-warnings.js +10 -78
  91. package/dist/lib/task/write.js +241 -0
  92. package/lib/decide.ts +57 -97
  93. package/lib/defaults.json +1 -0
  94. package/lib/git/review-snapshot.ts +73 -0
  95. package/lib/git/workflow.ts +101 -0
  96. package/lib/internal/git-workflow.ts +37 -0
  97. package/lib/internal/platform-checks.ts +101 -0
  98. package/lib/internal/platform-comment.ts +99 -0
  99. package/lib/internal/platform-context.ts +33 -0
  100. package/lib/internal/platform-issue.ts +116 -0
  101. package/lib/internal/platform-pr.ts +119 -0
  102. package/lib/internal/release-workflow.ts +186 -0
  103. package/lib/internal/task-artifact.ts +47 -0
  104. package/lib/internal/task-context.ts +35 -0
  105. package/lib/internal/task-event.ts +51 -0
  106. package/lib/internal/task-ledger.ts +86 -0
  107. package/lib/internal/task-lifecycle.ts +41 -0
  108. package/lib/internal/task-short-id.ts +55 -0
  109. package/lib/internal/task-snapshot.ts +32 -0
  110. package/lib/internal/task-verify.ts +33 -0
  111. package/lib/internal/task-warning.ts +50 -0
  112. package/lib/platform/adapters.ts +30 -0
  113. package/lib/platform/context.ts +164 -0
  114. package/lib/platform/github-client.ts +146 -0
  115. package/lib/platform/issue-comments.ts +479 -0
  116. package/lib/platform/issue-metadata.ts +250 -0
  117. package/lib/platform/issues.ts +691 -0
  118. package/lib/platform/metadata-labels.ts +33 -0
  119. package/lib/platform/pr-checks.ts +246 -0
  120. package/lib/platform/pr-summary.ts +90 -0
  121. package/lib/platform/pull-request-metadata.ts +69 -0
  122. package/lib/platform/pull-requests.ts +380 -0
  123. package/lib/platform/releases.ts +71 -0
  124. package/lib/platform/types.ts +74 -0
  125. package/lib/platform/verification-required.ts +83 -0
  126. package/{runtime/platform-adapters/platform-sync.github.js → lib/platform/verification-sync.ts} +198 -367
  127. package/lib/platform/verification.ts +11 -0
  128. package/lib/release/channels.ts +30 -0
  129. package/lib/release/workflow.ts +31 -0
  130. package/lib/render.ts +3 -2
  131. package/lib/run/index.ts +25 -7
  132. package/lib/sandbox/capture.ts +29 -3
  133. package/lib/sandbox/commands/create.ts +43 -25
  134. package/lib/sandbox/commands/enter.ts +38 -11
  135. package/lib/sandbox/commands/list-running.ts +10 -8
  136. package/lib/sandbox/commands/show.ts +2 -2
  137. package/lib/sandbox/commands/start.ts +50 -16
  138. package/lib/sandbox/index.ts +6 -6
  139. package/lib/sandbox/mounts.ts +55 -0
  140. package/lib/sandbox/recovery.ts +828 -0
  141. package/lib/sandbox/task-resolver.ts +5 -1
  142. package/lib/sandbox/tools.ts +31 -0
  143. package/lib/server/protocol.ts +5 -5
  144. package/lib/task/activity-log.ts +62 -0
  145. package/lib/task/artifact-lifecycle.ts +484 -0
  146. package/lib/task/command-options.ts +32 -0
  147. package/lib/task/commands/cat.ts +22 -9
  148. package/lib/task/commands/decisions.ts +49 -47
  149. package/lib/task/commands/files.ts +14 -6
  150. package/lib/task/commands/grep.ts +34 -7
  151. package/lib/task/commands/issue-body.ts +16 -16
  152. package/lib/task/commands/log.ts +17 -70
  153. package/lib/task/commands/show.ts +14 -6
  154. package/lib/task/commands/status.ts +17 -7
  155. package/lib/task/decision-intents.ts +163 -0
  156. package/lib/task/decision-items.ts +64 -0
  157. package/lib/task/events.ts +320 -0
  158. package/lib/task/frontmatter.ts +202 -2
  159. package/lib/task/implementation-inputs.ts +137 -0
  160. package/lib/task/index.ts +12 -9
  161. package/lib/task/issue-form.ts +20 -6
  162. package/lib/task/ledger-intents.ts +201 -0
  163. package/lib/task/ledger.ts +77 -31
  164. package/lib/task/lifecycle.ts +458 -0
  165. package/lib/task/resolve-ref.ts +197 -36
  166. package/{templates/.agents/scripts/lib/review-artifacts.js → lib/task/review-artifacts.ts} +11 -12
  167. package/{templates/.agents/scripts/lib/post-review-commit.js → lib/task/review-fingerprint.ts} +17 -12
  168. package/lib/task/sections.ts +486 -4
  169. package/lib/task/short-id.ts +403 -9
  170. package/lib/task/snapshot.ts +143 -0
  171. package/lib/task/verification-checks.ts +9 -0
  172. package/lib/task/verification-config.ts +20 -0
  173. package/{templates/.agents/scripts/validate-artifact.js → lib/task/verification-engine.ts} +199 -406
  174. package/lib/task/verification-review.ts +9 -0
  175. package/lib/task/verification-types.ts +29 -0
  176. package/lib/task/verification.ts +156 -0
  177. package/lib/task/workflow-warning-intents.ts +142 -0
  178. package/lib/task/workflow-warnings.ts +10 -78
  179. package/lib/task/write.ts +468 -0
  180. package/package.json +4 -2
  181. package/templates/.agents/README.en.md +4 -3
  182. package/templates/.agents/README.zh-CN.md +4 -3
  183. package/templates/.agents/rules/README.en.md +6 -5
  184. package/templates/.agents/rules/README.zh-CN.md +6 -5
  185. package/templates/.agents/rules/create-issue.en.md +1 -1
  186. package/templates/.agents/rules/create-issue.github.en.md +5 -184
  187. package/templates/.agents/rules/create-issue.github.zh-CN.md +12 -178
  188. package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
  189. package/templates/.agents/rules/human-decision-context.en.md +36 -0
  190. package/templates/.agents/rules/human-decision-context.zh-CN.md +36 -0
  191. package/templates/.agents/rules/issue-fields.github.en.md +4 -148
  192. package/templates/.agents/rules/issue-fields.github.zh-CN.md +9 -144
  193. package/templates/.agents/rules/issue-pr-commands.en.md +43 -3
  194. package/templates/.agents/rules/issue-pr-commands.github.en.md +15 -220
  195. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +23 -213
  196. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +45 -3
  197. package/templates/.agents/rules/issue-sync.en.md +1 -1
  198. package/templates/.agents/rules/issue-sync.github.en.md +14 -301
  199. package/templates/.agents/rules/issue-sync.github.zh-CN.md +36 -287
  200. package/templates/.agents/rules/issue-sync.zh-CN.md +1 -1
  201. package/templates/.agents/rules/milestone-inference.en.md +1 -1
  202. package/templates/.agents/rules/milestone-inference.github.en.md +7 -119
  203. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +8 -108
  204. package/templates/.agents/rules/milestone-inference.zh-CN.md +1 -1
  205. package/templates/.agents/rules/next-step-output.en.md +12 -11
  206. package/templates/.agents/rules/next-step-output.zh-CN.md +12 -11
  207. package/templates/.agents/rules/no-mid-flow-questions.en.md +3 -3
  208. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +3 -3
  209. package/templates/.agents/rules/pr-checks-commands.en.md +16 -3
  210. package/templates/.agents/rules/pr-checks-commands.github.en.md +10 -54
  211. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +19 -47
  212. package/templates/.agents/rules/pr-checks-commands.zh-CN.md +30 -3
  213. package/templates/.agents/rules/pr-sync.en.md +30 -3
  214. package/templates/.agents/rules/pr-sync.github.en.md +12 -115
  215. package/templates/.agents/rules/pr-sync.github.zh-CN.md +26 -104
  216. package/templates/.agents/rules/pr-sync.zh-CN.md +43 -3
  217. package/templates/.agents/rules/review-handshake.en.md +8 -5
  218. package/templates/.agents/rules/review-handshake.zh-CN.md +8 -5
  219. package/templates/.agents/rules/task-management.en.md +11 -2
  220. package/templates/.agents/rules/task-management.zh-CN.md +11 -3
  221. package/templates/.agents/rules/task-short-id.en.md +25 -29
  222. package/templates/.agents/rules/task-short-id.zh-CN.md +20 -23
  223. package/templates/.agents/scripts/lib/agent-infra-package.js +1 -9
  224. package/templates/.agents/scripts/task-short-id.js +71 -533
  225. package/templates/.agents/skills/analyze-task/SKILL.en.md +22 -55
  226. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +26 -59
  227. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -3
  228. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -3
  229. package/templates/.agents/skills/block-task/SKILL.en.md +21 -47
  230. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +23 -47
  231. package/templates/.agents/skills/block-task/config/verify.json +1 -2
  232. package/templates/.agents/skills/cancel-task/SKILL.en.md +16 -45
  233. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +16 -45
  234. package/templates/.agents/skills/cancel-task/config/verify.json +1 -2
  235. package/templates/.agents/skills/check-task/SKILL.en.md +6 -3
  236. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +6 -3
  237. package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -24
  238. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -24
  239. package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -24
  240. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -24
  241. package/templates/.agents/skills/code-task/SKILL.en.md +22 -35
  242. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +28 -44
  243. package/templates/.agents/skills/code-task/config/verify.en.json +3 -3
  244. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +3 -3
  245. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +13 -9
  246. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +13 -9
  247. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +1 -1
  248. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +1 -1
  249. package/templates/.agents/skills/code-task/reference/report-template.en.md +11 -1
  250. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +11 -1
  251. package/templates/.agents/skills/commit/SKILL.en.md +13 -11
  252. package/templates/.agents/skills/commit/SKILL.zh-CN.md +13 -11
  253. package/templates/.agents/skills/commit/config/verify.json +1 -2
  254. package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -11
  255. package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -11
  256. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +4 -8
  257. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +4 -8
  258. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +6 -18
  259. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +5 -17
  260. package/templates/.agents/skills/commit/reference/task-status-update.en.md +13 -0
  261. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +13 -0
  262. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +13 -36
  263. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +13 -36
  264. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -3
  265. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -3
  266. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +10 -54
  267. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +10 -79
  268. package/templates/.agents/skills/complete-task/SKILL.en.md +59 -63
  269. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +59 -63
  270. package/templates/.agents/skills/complete-task/config/verify.en.json +11 -7
  271. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +11 -7
  272. package/templates/.agents/skills/create-pr/SKILL.en.md +19 -44
  273. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +19 -43
  274. package/templates/.agents/skills/create-pr/config/verify.json +1 -2
  275. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +8 -12
  276. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +7 -11
  277. package/templates/.agents/skills/create-task/SKILL.en.md +12 -13
  278. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +12 -13
  279. package/templates/.agents/skills/create-task/config/verify.json +1 -2
  280. package/templates/.agents/skills/import-codescan/SKILL.en.md +2 -2
  281. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +2 -2
  282. package/templates/.agents/skills/import-codescan/config/verify.json +1 -2
  283. package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -2
  284. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +2 -2
  285. package/templates/.agents/skills/import-dependabot/config/verify.json +1 -2
  286. package/templates/.agents/skills/import-issue/SKILL.en.md +9 -13
  287. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +10 -14
  288. package/templates/.agents/skills/import-issue/config/verify.json +1 -2
  289. package/templates/.agents/skills/plan-task/SKILL.en.md +17 -42
  290. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +22 -49
  291. package/templates/.agents/skills/plan-task/config/verify.en.json +1 -3
  292. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -3
  293. package/templates/.agents/skills/post-release/SKILL.en.md +11 -81
  294. package/templates/.agents/skills/post-release/SKILL.zh-CN.md +8 -78
  295. package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
  296. package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
  297. package/templates/.agents/skills/release/SKILL.en.md +16 -163
  298. package/templates/.agents/skills/release/SKILL.zh-CN.md +15 -162
  299. package/templates/.agents/skills/restore-task/SKILL.en.md +11 -29
  300. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +11 -29
  301. package/templates/.agents/skills/restore-task/config/verify.json +1 -2
  302. package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -25
  303. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +13 -33
  304. package/templates/.agents/skills/review-analysis/config/verify.en.json +3 -4
  305. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +3 -4
  306. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +9 -12
  307. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +9 -12
  308. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +9 -2
  309. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +9 -2
  310. package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +7 -0
  311. package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +7 -0
  312. package/templates/.agents/skills/review-code/SKILL.en.md +22 -36
  313. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +23 -41
  314. package/templates/.agents/skills/review-code/config/verify.en.json +3 -4
  315. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +3 -4
  316. package/templates/.agents/skills/review-code/reference/output-templates.en.md +56 -18
  317. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +56 -18
  318. package/templates/.agents/skills/review-code/reference/report-template.en.md +11 -4
  319. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +11 -4
  320. package/templates/.agents/skills/review-code/reference/review-criteria.en.md +7 -0
  321. package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +7 -0
  322. package/templates/.agents/skills/review-plan/SKILL.en.md +13 -25
  323. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +13 -33
  324. package/templates/.agents/skills/review-plan/config/verify.en.json +3 -4
  325. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +3 -4
  326. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +9 -12
  327. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +9 -12
  328. package/templates/.agents/skills/review-plan/reference/report-template.en.md +9 -2
  329. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +9 -2
  330. package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +7 -0
  331. package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +7 -0
  332. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +1 -2
  333. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +1 -2
  334. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +3 -15
  335. package/templates/.agents/skills/watch-pr/SKILL.en.md +24 -14
  336. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +25 -15
  337. package/templates/.agents/skills/watch-pr/config/verify.json +1 -2
  338. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +2 -2
  339. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +2 -2
  340. package/templates/.agents/templates/task.en.md +6 -1
  341. package/templates/.agents/templates/task.zh-CN.md +6 -1
  342. package/templates/.claude/commands/analyze-task.en.md +1 -1
  343. package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
  344. package/templates/.claude/commands/block-task.en.md +1 -1
  345. package/templates/.claude/commands/block-task.zh-CN.md +1 -1
  346. package/templates/.claude/commands/cancel-task.en.md +1 -1
  347. package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
  348. package/templates/.claude/commands/check-task.en.md +1 -1
  349. package/templates/.claude/commands/check-task.zh-CN.md +1 -1
  350. package/templates/.claude/commands/code-task.en.md +1 -1
  351. package/templates/.claude/commands/code-task.zh-CN.md +1 -1
  352. package/templates/.claude/commands/commit.en.md +1 -0
  353. package/templates/.claude/commands/commit.zh-CN.md +1 -0
  354. package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
  355. package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
  356. package/templates/.claude/commands/complete-task.en.md +1 -1
  357. package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
  358. package/templates/.claude/commands/create-pr.en.md +1 -1
  359. package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
  360. package/templates/.claude/commands/plan-task.en.md +1 -1
  361. package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
  362. package/templates/.claude/commands/review-analysis.en.md +1 -1
  363. package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
  364. package/templates/.claude/commands/review-code.en.md +1 -1
  365. package/templates/.claude/commands/review-code.zh-CN.md +1 -1
  366. package/templates/.claude/commands/review-plan.en.md +1 -1
  367. package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
  368. package/templates/.claude/commands/watch-pr.en.md +1 -1
  369. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  370. package/templates/.gemini/commands/_project_/analyze-task.en.toml +2 -0
  371. package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +2 -0
  372. package/templates/.gemini/commands/_project_/block-task.en.toml +2 -0
  373. package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +2 -0
  374. package/templates/.gemini/commands/_project_/cancel-task.en.toml +2 -0
  375. package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +2 -0
  376. package/templates/.gemini/commands/_project_/check-task.en.toml +2 -0
  377. package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +2 -0
  378. package/templates/.gemini/commands/_project_/code-task.en.toml +2 -0
  379. package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +2 -0
  380. package/templates/.gemini/commands/_project_/commit.en.toml +4 -0
  381. package/templates/.gemini/commands/_project_/commit.zh-CN.toml +4 -0
  382. package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +2 -0
  383. package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +2 -0
  384. package/templates/.gemini/commands/_project_/complete-task.en.toml +2 -0
  385. package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +2 -0
  386. package/templates/.gemini/commands/_project_/create-pr.en.toml +2 -0
  387. package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +2 -0
  388. package/templates/.gemini/commands/_project_/plan-task.en.toml +2 -0
  389. package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +2 -0
  390. package/templates/.gemini/commands/_project_/review-analysis.en.toml +2 -0
  391. package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +2 -0
  392. package/templates/.gemini/commands/_project_/review-code.en.toml +2 -0
  393. package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +2 -0
  394. package/templates/.gemini/commands/_project_/review-plan.en.toml +2 -0
  395. package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +2 -0
  396. package/templates/.gemini/commands/_project_/watch-pr.en.toml +2 -0
  397. package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +2 -0
  398. package/templates/.git-hooks/check-large-files.cjs +92 -0
  399. package/templates/.git-hooks/check-version-format.sh +5 -1
  400. package/templates/.git-hooks/pre-commit +1 -0
  401. package/templates/.opencode/commands/analyze-task.en.md +1 -1
  402. package/templates/.opencode/commands/analyze-task.zh-CN.md +1 -1
  403. package/templates/.opencode/commands/check-task.en.md +1 -1
  404. package/templates/.opencode/commands/check-task.zh-CN.md +1 -1
  405. package/templates/.opencode/commands/code-task.en.md +1 -1
  406. package/templates/.opencode/commands/code-task.zh-CN.md +1 -1
  407. package/templates/.opencode/commands/commit.en.md +2 -0
  408. package/templates/.opencode/commands/commit.zh-CN.md +2 -0
  409. package/templates/.opencode/commands/complete-task.en.md +1 -1
  410. package/templates/.opencode/commands/complete-task.zh-CN.md +1 -1
  411. package/templates/.opencode/commands/plan-task.en.md +1 -1
  412. package/templates/.opencode/commands/plan-task.zh-CN.md +1 -1
  413. package/templates/.opencode/commands/review-analysis.en.md +1 -1
  414. package/templates/.opencode/commands/review-analysis.zh-CN.md +1 -1
  415. package/templates/.opencode/commands/review-code.en.md +1 -1
  416. package/templates/.opencode/commands/review-code.zh-CN.md +1 -1
  417. package/templates/.opencode/commands/review-plan.en.md +1 -1
  418. package/templates/.opencode/commands/review-plan.zh-CN.md +1 -1
  419. package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +0 -40
  420. package/templates/.agents/scripts/platform-adapters/platform-sync.js +0 -13
  421. package/templates/.agents/scripts/review-diff-fingerprint.js +0 -170
  422. package/templates/.agents/scripts/workflow-warnings.js +0 -290
  423. package/templates/.agents/skills/code-task/scripts/detect-mode.js +0 -257
  424. package/templates/.agents/skills/release/scripts/manage-milestones.github.sh +0 -80
  425. package/templates/.agents/skills/release/scripts/manage-milestones.sh +0 -6
@@ -2,19 +2,21 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { execFileSync } from 'node:child_process';
4
4
  import { commandForEngine } from '../../sandbox/shell.ts';
5
- import { resolveTaskRef } from '../resolve-ref.ts';
5
+ import { parseTaskScope } from '../command-options.ts';
6
+ import { resolveTaskContext, resolveTaskRef } from '../resolve-ref.ts';
6
7
  import { enumerateArtifacts, type Artifact } from '../artifacts.ts';
7
8
  import { parseTaskFrontmatter, extractTitle, type Frontmatter } from '../frontmatter.ts';
8
9
  import { loadShortIdByTaskId } from '../short-id.ts';
9
10
  import { getOpenWorkflowWarnings, formatWorkflowWarningSummary, type WorkflowWarning } from '../workflow-warnings.ts';
10
- import { parseActivityLog, pairEntries } from './log.ts';
11
+ import { parseActivityLog, pairEntries } from '../activity-log.ts';
11
12
  import { statusCard, type DisplayMessage } from '../../server/display.ts';
12
13
 
13
- const USAGE = `Usage: ai task status <N | #N | TASK-id>
14
+ const USAGE = `Usage: ai task status [<N | TASK-id> | --task <ref> | -t <ref>]
14
15
 
15
16
  Prints an aggregated "health check" view for a task: header, metadata,
16
17
  artifacts, workflow/runtime execution state, and git branch state.
17
- <ref> Bare numeric / '#N' short id, or a full TASK-YYYYMMDD-HHMMSS id.
18
+ Omit <ref> Resolve the unique active task for the current branch.
19
+ <ref> Bare numeric short id, or a full TASK-YYYYMMDD-HHMMSS id.
18
20
 
19
21
  Git rows are best-effort: a failed git call degrades that row to '-' without
20
22
  failing the command.
@@ -489,15 +491,23 @@ function statusModelToDisplay(model: StatusModel): DisplayMessage {
489
491
  }
490
492
 
491
493
  function status(args: string[] = []): void {
492
- if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
494
+ if (args[0] === '--help' || args[0] === '-h') {
493
495
  process.stdout.write(USAGE);
494
- if (args.length === 0) process.exitCode = 1;
495
496
  return;
496
497
  }
497
498
 
498
499
  let model: StatusModel;
499
500
  try {
500
- model = buildStatusModel(args[0]!);
501
+ const scope = parseTaskScope(args);
502
+ if (scope.positionals.length > 1 || (scope.explicit && scope.positionals.length > 0)) {
503
+ throw new Error('task ref must be provided once');
504
+ }
505
+ const resolved = resolveTaskContext(scope.taskRef ?? scope.positionals[0]);
506
+ if (!resolved.ok) throw new Error(resolved.message);
507
+ model = buildFromResolved({
508
+ taskId: resolved.taskId, taskDir: resolved.taskDir, taskMdPath: resolved.taskMdPath,
509
+ repoRoot: resolved.repoRoot
510
+ });
501
511
  } catch (error) {
502
512
  process.stderr.write(`ai task status: ${error instanceof Error ? error.message : String(error)}\n`);
503
513
  process.exitCode = 1;
@@ -0,0 +1,163 @@
1
+ import fs from 'node:fs';
2
+
3
+ import { appendActivityEntry, locateActivityLog } from './activity-log.ts';
4
+ import { isDecisionItem, listDecisionItems, selectDecisionItem } from './decision-items.ts';
5
+ import {
6
+ createImplementationInput,
7
+ IMPLEMENTATION_INPUT_ALIASES,
8
+ parseImplementationInputs,
9
+ renderImplementationInputs
10
+ } from './implementation-inputs.ts';
11
+ import { LEDGER_COLUMNS, LEDGER_HEADINGS, parseLedger } from './ledger.ts';
12
+ import type { LedgerRow } from './ledger.ts';
13
+ import { resolveTaskContext } from './resolve-ref.ts';
14
+ import { extractSection, extractSubSection, findSectionHeading } from './sections.ts';
15
+ import { captureTaskWriteMetadata, writeTask } from './write.ts';
16
+ import type { TaskMutation, TaskOperationSummary, TaskWriteOptions } from './write.ts';
17
+
18
+ type HumanDecisionRequest = {
19
+ taskRef?: string;
20
+ selector: string;
21
+ decision: string;
22
+ needsImplementation?: boolean;
23
+ dryRun?: boolean;
24
+ };
25
+
26
+ type HumanDecisionResult = {
27
+ status: 'planned' | 'applied' | 'no-op' | 'failed'; changed: boolean;
28
+ taskId: string | null; ledgerId: string | null; recordId: string | null;
29
+ implementationInputId: string | null; operations: readonly TaskOperationSummary[];
30
+ error: { code: string; message: string } | null;
31
+ };
32
+
33
+ const DECISION_ALIASES = ['人工裁决', 'Human Rulings', 'Human Decisions', 'Human Decision'] as const;
34
+ const ACTIVITY_ALIASES = ['活动日志', 'Activity Log'] as const;
35
+
36
+ function failed(code: string, message: string, taskId: string | null = null, ledgerId: string | null = null): HumanDecisionResult {
37
+ return { status: 'failed', changed: false, taskId, ledgerId, recordId: null, implementationInputId: null, operations: [], error: { code, message } };
38
+ }
39
+
40
+ function nextRecordId(content: string): string {
41
+ let max = 0;
42
+ for (const match of content.matchAll(/^###\s+HDR-(\d+)\s*$/gm)) max = Math.max(max, Number(match[1]));
43
+ return `HDR-${max + 1}`;
44
+ }
45
+
46
+ function classifyMissing(rows: readonly LedgerRow[], selector: string): string {
47
+ const matches = rows.filter((row) => row.id.toUpperCase() === selector.toUpperCase());
48
+ const valid = matches.filter(isDecisionItem);
49
+ if (valid.some((row) => row.status === 'human-decided')) return `${selector} is already decided`;
50
+ if (valid.length > 0) return `${selector} is not a pending review decision`;
51
+ if (matches.length > 0 || !/^(AN|PL|CD|HD)-\d+$/i.test(selector)) return `${selector} is an invalid decision item`;
52
+ return `${selector} not found in review ledger`;
53
+ }
54
+
55
+ function prependBlock(body: string, block: string): string {
56
+ return body ? `${block}\n\n${body}` : block;
57
+ }
58
+
59
+ function existingDecision(
60
+ content: string,
61
+ row: LedgerRow,
62
+ decision: string,
63
+ needsImplementation: boolean | undefined
64
+ ): { recordId: string; implementationInputId: string | null } | { conflict: string } {
65
+ const match = /^task\.md#(HDR-[1-9]\d*)$/.exec(row.evidence);
66
+ if (!match) return { conflict: `decided row '${row.id}' has invalid decision evidence` };
67
+ const recordId = match[1]!;
68
+ const block = extractSubSection(content, recordId);
69
+ if (!block || !block.includes(`**原账本 ID**:${row.id}`) || !block.includes(`**裁决结果**:${decision}`)) {
70
+ return { conflict: `decision for '${row.id}' conflicts with ${recordId}` };
71
+ }
72
+ if (row.stage !== 'code') return needsImplementation === undefined
73
+ ? { recordId, implementationInputId: null }
74
+ : { conflict: '--needs-implementation is only valid for code-stage decisions' };
75
+ const inputs = parseImplementationInputs(content).rows.filter((input) => input.ledgerId === row.id && input.decisionEvidence === row.evidence);
76
+ if (inputs.length !== 1 || inputs[0]!.needsImplementation !== needsImplementation) {
77
+ return { conflict: `implementation intent for '${row.id}' conflicts with the existing decision` };
78
+ }
79
+ return { recordId, implementationInputId: inputs[0]!.id };
80
+ }
81
+
82
+ function applyHumanDecision(request: HumanDecisionRequest, options: TaskWriteOptions = {}): HumanDecisionResult {
83
+ if (!request.selector || !request.decision.trim()) return failed('DECISION_PAYLOAD_INVALID', 'selector and decision are required');
84
+ const resolved = resolveTaskContext(request.taskRef, { repoRoot: options.repoRoot });
85
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
86
+ if (resolved.state !== 'active') return failed('TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
87
+ let content: string;
88
+ let rows: LedgerRow[];
89
+ try { content = fs.readFileSync(resolved.taskMdPath, 'utf8'); rows = parseLedger(content); }
90
+ catch (error) { return failed('DECISION_DOCUMENT_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId); }
91
+
92
+ const direct = rows.filter((row) => row.id.toUpperCase() === request.selector.toUpperCase());
93
+ if (!/^-?\d+$/.test(request.selector) && direct.length === 1 && direct[0]!.status === 'human-decided' && isDecisionItem(direct[0]!)) {
94
+ try {
95
+ const replay = existingDecision(content, direct[0]!, request.decision.trim(), request.needsImplementation);
96
+ if ('conflict' in replay) return failed('DECISION_CONFLICT', replay.conflict, resolved.taskId, direct[0]!.id);
97
+ return { status: 'no-op', changed: false, taskId: resolved.taskId, ledgerId: direct[0]!.id, recordId: replay.recordId, implementationInputId: replay.implementationInputId, operations: [], error: null };
98
+ } catch (error) { return failed('DECISION_DOCUMENT_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId, direct[0]!.id); }
99
+ }
100
+
101
+ const candidates = listDecisionItems(rows);
102
+ const selected = selectDecisionItem(candidates, request.selector);
103
+ if (!selected.ok) {
104
+ const message = selected.code === 'not-found' && !/^-?\d+$/.test(request.selector)
105
+ ? classifyMissing(rows, request.selector) : selected.message;
106
+ return failed('DECISION_TARGET_INVALID', message, resolved.taskId);
107
+ }
108
+ const row = selected.row;
109
+ if (row.stage === 'code' && request.needsImplementation === undefined) return failed('DECISION_PAYLOAD_INVALID', 'code-stage decisions require --needs-implementation true|false', resolved.taskId, row.id);
110
+ if (row.stage !== 'code' && request.needsImplementation !== undefined) return failed('DECISION_PAYLOAD_INVALID', '--needs-implementation is only valid for code-stage decisions', resolved.taskId, row.id);
111
+
112
+ let metadata;
113
+ try { metadata = (options.metadataProvider ?? captureTaskWriteMetadata)(); }
114
+ catch (error) { return failed('METADATA_CAPTURE_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId, row.id); }
115
+ const recordId = nextRecordId(content);
116
+ const evidence = `task.md#${recordId}`;
117
+ const decision = request.decision.trim();
118
+ const record = `### ${recordId}\n\n- **原账本 ID**:${row.id}\n- **裁决时间**:${metadata.timestamp}\n- **裁决结果**:${decision}`;
119
+ const activity = locateActivityLog(content);
120
+ if (!activity) return failed('DECISION_ACTIVITY_MISSING', 'activity log section is missing or ambiguous', resolved.taskId, row.id);
121
+ const mutations: TaskMutation[] = [
122
+ {
123
+ kind: 'table-row', action: 'upsert', sectionAliases: LEDGER_HEADINGS, columns: LEDGER_COLUMNS,
124
+ keyColumn: 'id', key: row.id,
125
+ values: { stage: row.stage, round: row.round, severity: row.severity, status: 'human-decided', evidence }
126
+ },
127
+ {
128
+ kind: 'section', aliases: DECISION_ALIASES,
129
+ heading: findSectionHeading(content, [...DECISION_ALIASES]),
130
+ body: prependBlock(extractSection(content, [...DECISION_ALIASES]), record)
131
+ }
132
+ ];
133
+ let implementationInputId: string | null = null;
134
+ if (row.stage === 'code') {
135
+ let parsed;
136
+ try { parsed = parseImplementationInputs(content); }
137
+ catch (error) { return failed('DECISION_DOCUMENT_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId, row.id); }
138
+ const input = createImplementationInput(parsed.rows, {
139
+ ledgerId: row.id, decisionEvidence: evidence,
140
+ needsImplementation: request.needsImplementation!, decidedAt: metadata.timestamp
141
+ });
142
+ implementationInputId = input.id;
143
+ mutations.push({
144
+ kind: 'section', aliases: IMPLEMENTATION_INPUT_ALIASES,
145
+ heading: findSectionHeading(content, [...IMPLEMENTATION_INPUT_ALIASES]),
146
+ body: renderImplementationInputs([...parsed.rows, input])
147
+ });
148
+ }
149
+ mutations.push({
150
+ kind: 'section', aliases: ACTIVITY_ALIASES, heading: activity.heading,
151
+ body: appendActivityEntry(activity, { time: metadata.timestamp, step: 'Human Decision', agent: 'human', note: `${row.id} decided → ${recordId}` })
152
+ });
153
+ const writeResult = writeTask({ taskRef: resolved.taskId, expectedState: 'active', mutations, dryRun: request.dryRun }, {
154
+ ...options,
155
+ taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state },
156
+ metadataProvider: () => metadata
157
+ });
158
+ if (writeResult.status === 'failed') return failed(writeResult.error.code, writeResult.error.message, writeResult.taskId, row.id);
159
+ return { status: writeResult.status, changed: writeResult.changed, taskId: writeResult.taskId, ledgerId: row.id, recordId, implementationInputId, operations: writeResult.operations, error: null };
160
+ }
161
+
162
+ export { applyHumanDecision };
163
+ export type { HumanDecisionRequest, HumanDecisionResult };
@@ -0,0 +1,64 @@
1
+ import { isReviewStage, type LedgerRow, type ReviewStage } from './ledger.ts';
2
+
3
+ type DecisionSelectorErrorCode =
4
+ | 'invalid-selector'
5
+ | 'not-found'
6
+ | 'ambiguous'
7
+ | 'ordinal-out-of-range';
8
+
9
+ type DecisionSelectorResult =
10
+ | { ok: true; row: LedgerRow }
11
+ | { ok: false; code: DecisionSelectorErrorCode; message: string };
12
+
13
+ function isDecisionItem(row: LedgerRow): boolean {
14
+ if (!isReviewStage(row.stage)) return false;
15
+ if (/^HD-\d+$/i.test(row.id)) return true;
16
+ if (row.stage === 'analysis') return /^AN-\d+$/i.test(row.id);
17
+ if (row.stage === 'plan') return /^PL-\d+$/i.test(row.id);
18
+ return /^CD-\d+$/i.test(row.id);
19
+ }
20
+
21
+ function listDecisionItems(
22
+ rows: readonly LedgerRow[],
23
+ options: { includeDecided?: boolean; stage?: ReviewStage } = {}
24
+ ): LedgerRow[] {
25
+ return rows.filter((row) => {
26
+ if (!isDecisionItem(row)) return false;
27
+ if (options.stage !== undefined && row.stage !== options.stage) return false;
28
+ return options.includeDecided
29
+ ? row.status === 'needs-human-decision' || row.status === 'human-decided'
30
+ : row.status === 'needs-human-decision';
31
+ });
32
+ }
33
+
34
+ function selectDecisionItem(rows: readonly LedgerRow[], selector: string): DecisionSelectorResult {
35
+ if (/^-?\d+$/.test(selector)) {
36
+ const ordinal = Number.parseInt(selector, 10);
37
+ if (ordinal < 1 || ordinal > rows.length) {
38
+ return {
39
+ ok: false,
40
+ code: 'ordinal-out-of-range',
41
+ message: `ordinal '${selector}' out of range (1..${rows.length})`
42
+ };
43
+ }
44
+ return { ok: true, row: rows[ordinal - 1]! };
45
+ }
46
+ if (!/^[A-Za-z]+-\d+$/.test(selector)) {
47
+ return { ok: false, code: 'invalid-selector', message: `invalid selector '${selector}'` };
48
+ }
49
+ const matches = rows.filter((row) => row.id.toUpperCase() === selector.toUpperCase());
50
+ if (matches.length === 0) {
51
+ return { ok: false, code: 'not-found', message: `no decision item matches '${selector}'` };
52
+ }
53
+ if (matches.length > 1) {
54
+ return {
55
+ ok: false,
56
+ code: 'ambiguous',
57
+ message: `duplicate id '${selector}' in ledger; select by ordinal instead`
58
+ };
59
+ }
60
+ return { ok: true, row: matches[0]! };
61
+ }
62
+
63
+ export { isDecisionItem, listDecisionItems, selectDecisionItem };
64
+ export type { DecisionSelectorErrorCode, DecisionSelectorResult };
@@ -0,0 +1,320 @@
1
+ import fs from 'node:fs';
2
+
3
+ import { appendActivityEntry, locateActivityLog, pairEntries } from './activity-log.ts';
4
+ import {
5
+ buildArtifactLinkSection,
6
+ artifactName,
7
+ parseArtifactName,
8
+ resolveArtifactContext,
9
+ validateCompletedArtifact
10
+ } from './artifact-lifecycle.ts';
11
+ import type { ArtifactContextResult, ArtifactErrorCode, ArtifactFamily, ArtifactIdentity } from './artifact-lifecycle.ts';
12
+ import { parseTypedTaskFrontmatter } from './frontmatter.ts';
13
+ import {
14
+ consumeImplementationInput,
15
+ IMPLEMENTATION_INPUT_ALIASES,
16
+ parseImplementationInputs,
17
+ renderImplementationInputs
18
+ } from './implementation-inputs.ts';
19
+ import { resolveTaskRef } from './resolve-ref.ts';
20
+ import { findSectionHeading } from './sections.ts';
21
+ import { captureTaskWriteMetadata, writeTask } from './write.ts';
22
+ import type { TaskOperationSummary, TaskWriteErrorCode, TaskWriteOptions } from './write.ts';
23
+
24
+ const eventCatalog = [
25
+ 'analyze.started', 'analyze.awaiting-input', 'analyze.completed',
26
+ 'review-analysis.started', 'review-analysis.completed',
27
+ 'plan.started', 'plan.completed',
28
+ 'review-plan.started', 'review-plan.completed',
29
+ 'code.started', 'code.completed',
30
+ 'review-code.started', 'review-code.completed',
31
+ 'manual-validation.started', 'manual-validation.completed'
32
+ ] as const;
33
+ type TaskEventName = (typeof eventCatalog)[number];
34
+ type Verdict = 'approved' | 'changes-requested' | 'rejected';
35
+ type TaskEventErrorCode =
36
+ | 'EVENT_UNKNOWN' | 'EVENT_PAYLOAD_INVALID' | 'EVENT_TRANSITION_INVALID'
37
+ | 'EVENT_LOG_MISSING' | 'EVENT_START_MISSING' | 'EVENT_ALREADY_COMPLETED'
38
+ | 'EVENT_LOG_CONFLICT' | 'EVENT_ARTIFACT_CONFLICT' | ArtifactErrorCode | TaskWriteErrorCode;
39
+ type TaskEventRequest = {
40
+ taskRef: string; event: TaskEventName | string; agent: string; dryRun?: boolean;
41
+ round?: number; question?: number; artifact?: string; fixFor?: string; implementationInput?: string;
42
+ verdict?: Verdict; blockers?: number; major?: number; minor?: number;
43
+ manualValidation?: number; filesModified?: number; testsPassed?: number;
44
+ summaryResult?: string;
45
+ };
46
+ type TaskEventError = { code: TaskEventErrorCode; message: string };
47
+ type TaskEventResult = {
48
+ status: 'planned' | 'applied' | 'no-op' | 'failed'; changed: boolean;
49
+ event: string; requestRef: string; taskId: string | null; taskMdPath: string | null;
50
+ fromStep: string | null; toStep: string | null; action: string | null;
51
+ phase: 'started' | 'waiting' | 'completed' | null; round: number | null;
52
+ artifact: string | null; fixFor: string | null; implementationInput: string | null;
53
+ artifactContext: ArtifactContextResult | null;
54
+ timestamp: string | null; agentInfraVersion: string | null;
55
+ operations: readonly TaskOperationSummary[]; error: TaskEventError | null;
56
+ };
57
+
58
+ const BASE_FIELDS = new Set(['taskRef', 'event', 'agent', 'dryRun']);
59
+ const SCHEMAS: Record<TaskEventName, { required?: string[]; optional?: string[] }> = {
60
+ 'analyze.started': { optional: ['round'] },
61
+ 'analyze.awaiting-input': { required: ['question'] },
62
+ 'analyze.completed': { required: ['artifact'], optional: ['round'] },
63
+ 'review-analysis.started': { optional: ['round'] },
64
+ 'review-analysis.completed': { required: ['artifact', 'verdict', 'blockers', 'major', 'minor', 'manualValidation'], optional: ['round'] },
65
+ 'plan.started': { optional: ['round'] },
66
+ 'plan.completed': { required: ['artifact'], optional: ['round'] },
67
+ 'review-plan.started': { optional: ['round'] },
68
+ 'review-plan.completed': { required: ['artifact', 'verdict', 'blockers', 'major', 'minor', 'manualValidation'], optional: ['round'] },
69
+ 'code.started': { optional: ['round', 'fixFor', 'implementationInput'] },
70
+ 'code.completed': { required: ['artifact'], optional: ['round', 'fixFor', 'implementationInput', 'filesModified', 'testsPassed', 'blockers', 'major', 'minor', 'manualValidation'] },
71
+ 'review-code.started': { optional: ['round'] },
72
+ 'review-code.completed': { required: ['artifact', 'verdict', 'blockers', 'major', 'minor', 'manualValidation'], optional: ['round'] },
73
+ 'manual-validation.started': { optional: ['round'] },
74
+ 'manual-validation.completed': { required: ['artifact', 'summaryResult'], optional: ['round'] }
75
+ };
76
+
77
+ function validateTaskEventRequest(request: TaskEventRequest): TaskEventError | null {
78
+ if (!eventCatalog.includes(request.event as TaskEventName)) return { code: 'EVENT_UNKNOWN', message: `unknown task event '${request.event}'` };
79
+ if (!request.taskRef || !request.agent) return { code: 'EVENT_PAYLOAD_INVALID', message: 'taskRef and agent are required' };
80
+ const schema = SCHEMAS[request.event as TaskEventName];
81
+ const required = schema.required ?? [];
82
+ const allowed = new Set([...BASE_FIELDS, ...required, ...(schema.optional ?? [])]);
83
+ for (const [key, value] of Object.entries(request)) {
84
+ if (value !== undefined && !allowed.has(key)) return { code: 'EVENT_PAYLOAD_INVALID', message: `${request.event} does not accept '${key}'` };
85
+ }
86
+ for (const key of required) if (request[key as keyof TaskEventRequest] === undefined) return { code: 'EVENT_PAYLOAD_INVALID', message: `${request.event} requires '${key}'` };
87
+ for (const key of ['round', 'question', 'blockers', 'major', 'minor', 'manualValidation', 'filesModified', 'testsPassed'] as const) {
88
+ const value = request[key];
89
+ if (value !== undefined && (!Number.isInteger(value) || value < (key === 'round' || key === 'question' ? 1 : 0))) return { code: 'EVENT_PAYLOAD_INVALID', message: `'${key}' must be a ${key === 'round' || key === 'question' ? 'positive' : 'non-negative'} integer` };
90
+ }
91
+ if (request.verdict && !['approved', 'changes-requested', 'rejected'].includes(request.verdict)) return { code: 'EVENT_PAYLOAD_INVALID', message: 'verdict is invalid' };
92
+ if (request.event === 'code.completed') {
93
+ const fix = request.fixFor !== undefined || ['blockers', 'major', 'minor', 'manualValidation'].some((key) => request[key as keyof TaskEventRequest] !== undefined);
94
+ const modeRequired = fix ? ['fixFor', 'blockers', 'major', 'minor', 'manualValidation'] : ['filesModified', 'testsPassed'];
95
+ const forbidden = fix ? ['filesModified', 'testsPassed'] : ['fixFor', 'blockers', 'major', 'minor', 'manualValidation'];
96
+ if (modeRequired.some((key) => request[key as keyof TaskEventRequest] === undefined) || forbidden.some((key) => request[key as keyof TaskEventRequest] !== undefined)) return { code: 'EVENT_PAYLOAD_INVALID', message: 'code.completed requires either initial or fix completion payload' };
97
+ }
98
+ if (request.fixFor && !/^review-code(?:-r(?:[2-9]|[1-9]\d+))?\.md$/.test(request.fixFor)) return { code: 'EVENT_PAYLOAD_INVALID', message: 'fixFor must reference a canonical review-code artifact' };
99
+ if (request.implementationInput && !/^II-[1-9]\d*$/.test(request.implementationInput)) return { code: 'EVENT_PAYLOAD_INVALID', message: 'implementationInput must be a canonical II-N id' };
100
+ if (request.fixFor && request.implementationInput) return { code: 'EVENT_PAYLOAD_INVALID', message: 'fixFor and implementationInput are mutually exclusive' };
101
+ if (request.summaryResult !== undefined && (!request.summaryResult.trim() || /[\r\n]/.test(request.summaryResult))) return { code: 'EVENT_PAYLOAD_INVALID', message: 'summaryResult must be a non-empty single line' };
102
+ return null;
103
+ }
104
+
105
+ const FAMILY = {
106
+ analyze: { artifact: 'analysis', started: ['requirement-analysis', 'requirement-analysis-review', 'code'], completed: ['requirement-analysis', 'requirement-analysis-review', 'code'], target: 'requirement-analysis', label: 'Analyze Task' },
107
+ 'review-analysis': { artifact: 'review-analysis', started: ['requirement-analysis', 'requirement-analysis-review'], completed: ['requirement-analysis', 'requirement-analysis-review'], target: 'requirement-analysis-review', label: 'Review Analysis' },
108
+ plan: { artifact: 'plan', started: ['requirement-analysis-review', 'technical-design-review'], completed: ['requirement-analysis-review', 'technical-design-review'], target: 'technical-design', label: 'Plan Task' },
109
+ 'review-plan': { artifact: 'review-plan', started: ['technical-design', 'technical-design-review'], completed: ['technical-design', 'technical-design-review'], target: 'technical-design-review', label: 'Review Plan' },
110
+ code: { artifact: 'code', started: ['technical-design-review', 'code-review'], completed: ['technical-design-review', 'code-review'], target: 'code', label: 'Code Task' },
111
+ 'review-code': { artifact: 'review-code', started: ['code', 'code-review', 'commit'], completed: ['code', 'code-review', 'commit'], target: 'code-review', label: 'Review Code' },
112
+ 'manual-validation': { artifact: 'manual-validation', started: ['code-review'], completed: ['code-review'], target: null, label: 'Complete Manual Validation' }
113
+ } as const;
114
+ type EventFamily = keyof typeof FAMILY;
115
+
116
+ function eventParts(event: string): { family: EventFamily; phase: 'started' | 'waiting' | 'completed' } {
117
+ const [family, suffix] = event.split('.') as [EventFamily, string];
118
+ return { family, phase: suffix === 'started' ? 'started' : suffix === 'awaiting-input' ? 'waiting' : 'completed' };
119
+ }
120
+
121
+ function identity(request: TaskEventRequest) {
122
+ const { family, phase } = eventParts(request.event);
123
+ if (phase === 'waiting') return { family, phase, action: 'Analyze Task (Brainstorming)', note: `Asked Q${request.question}, awaiting answer`, target: FAMILY.analyze.target } as const;
124
+ const spec = FAMILY[family];
125
+ if (family === 'manual-validation') {
126
+ return {
127
+ family, phase, action: spec.label,
128
+ note: phase === 'started' ? 'started' : `Manual validation passed → ${request.artifact}; ${request.summaryResult}`,
129
+ target: null
130
+ } as const;
131
+ }
132
+ const qualifier = request.fixFor
133
+ ? `, fix for ${request.fixFor}`
134
+ : request.implementationInput ? `, decision ${request.implementationInput}` : '';
135
+ const action = `${spec.label} (Round ${request.round}${qualifier})`;
136
+ if (phase === 'started') return { family, phase, action, note: 'started', target: null } as const;
137
+ let note = '';
138
+ if (family === 'analyze') note = `Analysis completed → ${request.artifact}`;
139
+ else if (family === 'plan') note = `Plan completed, awaiting human review → ${request.artifact}`;
140
+ else if (family === 'code' && request.fixFor) note = `Fixed ${request.blockers} blockers, ${request.major} major, ${request.minor} minor issues${request.manualValidation ? `, skipped ${request.manualValidation} manual-validation` : ''} → ${request.artifact}`;
141
+ else if (family === 'code') note = `Code implemented, ${request.filesModified} files modified, ${request.testsPassed} tests passed → ${request.artifact}`;
142
+ else {
143
+ const verdict = request.verdict === 'approved' ? 'Approved' : request.verdict === 'changes-requested' ? 'Changes Requested' : 'Rejected';
144
+ note = `Verdict: ${verdict}, blockers: ${request.blockers}, major: ${request.major}, minor: ${request.minor}, Manual-validation: ${request.manualValidation} → ${request.artifact}`;
145
+ }
146
+ return { family, phase, action, note, target: spec.target } as const;
147
+ }
148
+
149
+ function failed(request: TaskEventRequest, error: TaskEventError, extra: Partial<TaskEventResult> = {}): TaskEventResult {
150
+ return {
151
+ status: 'failed', changed: false, event: request.event, requestRef: request.taskRef,
152
+ taskId: null, taskMdPath: null, fromStep: null, toStep: null, action: null,
153
+ phase: null, round: request.round ?? null, artifact: request.artifact ?? null,
154
+ fixFor: request.fixFor ?? null, implementationInput: request.implementationInput ?? null,
155
+ artifactContext: null, timestamp: null,
156
+ agentInfraVersion: null, operations: [], error, ...extra
157
+ };
158
+ }
159
+
160
+ function normalizeStarted(request: TaskEventRequest, repoRoot: string): { request: TaskEventRequest; context: ArtifactContextResult } | { error: TaskEventError; context: ArtifactContextResult } {
161
+ const family = eventParts(request.event).family;
162
+ const context = resolveArtifactContext(request.taskRef, FAMILY[family].artifact, { repoRoot });
163
+ if (context.status !== 'ready' || !context.next) {
164
+ return { error: { code: context.error?.code ?? 'EVENT_ARTIFACT_CONFLICT', message: context.error?.message ?? context.codeMode?.message ?? 'artifact context is not writable' }, context };
165
+ }
166
+ const round = context.next.round;
167
+ if (request.round !== undefined && request.round !== round) return { error: { code: 'EVENT_ARTIFACT_CONFLICT', message: `round ${request.round} conflicts with expected round ${round}` }, context };
168
+ const expectedFix = family === 'code' && context.codeMode?.mode === 'fix' ? context.codeMode.reviewArtifact ?? undefined : undefined;
169
+ const expectedImplementation = family === 'code' && context.codeMode?.mode === 'decision'
170
+ ? context.codeMode.implementationInput ?? undefined : undefined;
171
+ if (request.fixFor !== undefined && request.fixFor !== expectedFix) return { error: { code: 'EVENT_ARTIFACT_CONFLICT', message: `fixFor '${request.fixFor}' conflicts with artifact context` }, context };
172
+ if (request.implementationInput !== expectedImplementation) return { error: { code: 'EVENT_ARTIFACT_CONFLICT', message: `implementationInput '${request.implementationInput ?? ''}' conflicts with artifact context` }, context };
173
+ return { request: { ...request, round, artifact: context.next.name, fixFor: expectedFix, implementationInput: expectedImplementation }, context };
174
+ }
175
+
176
+ function openStartedIdentity(rows: ReturnType<typeof pairEntries>, family: EventFamily) {
177
+ const spec = FAMILY[family];
178
+ if (family === 'manual-validation') {
179
+ const row = rows.filter((item) => item.step === spec.label && item.started && !item.done).at(-1);
180
+ const completed = rows.filter((item) => item.step === spec.label && item.done).length;
181
+ return row ? { row, round: completed + 1, fixFor: undefined, implementationInput: undefined } : null;
182
+ }
183
+ const escaped = spec.label.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
184
+ const pattern = new RegExp(`^${escaped} \\(Round (\\d+)(?:(?:, fix for (review-code(?:-r\\d+)?\\.md))|(?:, decision (II-[1-9]\\d*)))?\\)$`);
185
+ const matches = rows.flatMap((row) => {
186
+ if (!row.started || row.done) return [];
187
+ const match = pattern.exec(row.step);
188
+ return match ? [{ row, round: Number(match[1]), fixFor: match[2], implementationInput: match[3] }] : [];
189
+ });
190
+ return matches.length === 1 ? matches[0] : matches.length > 1 ? { conflict: true as const } : null;
191
+ }
192
+
193
+ function applyTaskEvent(request: TaskEventRequest, options: TaskWriteOptions = {}): TaskEventResult {
194
+ const invalid = validateTaskEventRequest(request);
195
+ if (invalid) return failed(request, invalid);
196
+ const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
197
+ if (!resolved.ok) return failed(request, { code: resolved.code, message: resolved.message }, { taskId: resolved.taskId });
198
+ if (resolved.state !== 'active') return failed(request, { code: 'TASK_STATE_MISMATCH', message: `task ${resolved.taskId} is ${resolved.state}, expected active` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
199
+ let content: string;
200
+ try { content = fs.readFileSync(resolved.taskMdPath, 'utf8'); }
201
+ catch (error) { return failed(request, { code: 'TASK_READ_FAILED', message: String(error) }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath }); }
202
+ let frontmatter;
203
+ try { frontmatter = parseTypedTaskFrontmatter(content); }
204
+ catch (error) { return failed(request, { code: 'TASK_DOCUMENT_INVALID', message: error instanceof Error ? error.message : String(error) }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath }); }
205
+ const section = locateActivityLog(content);
206
+ if (!section) return failed(request, { code: 'EVENT_LOG_MISSING', message: 'task has no unique Activity Log section' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
207
+ const rows = pairEntries(section.entries);
208
+ let normalized = request;
209
+ let artifactContext: ArtifactContextResult | null = null;
210
+ const initialParts = eventParts(request.event);
211
+ if (initialParts.phase === 'started') {
212
+ const openIdentity = openStartedIdentity(rows, initialParts.family);
213
+ if (openIdentity && 'conflict' in openIdentity) return failed(request, { code: 'EVENT_LOG_CONFLICT', message: 'artifact family has more than one open started event' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
214
+ if (openIdentity) {
215
+ if (openIdentity.row.agent !== request.agent) return failed(request, { code: 'EVENT_LOG_CONFLICT', message: 'open started event has a different agent' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
216
+ if (request.round !== undefined && request.round !== openIdentity.round) return failed(request, { code: 'EVENT_ARTIFACT_CONFLICT', message: `round ${request.round} conflicts with open round ${openIdentity.round}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
217
+ if (request.fixFor !== undefined && request.fixFor !== openIdentity.fixFor) return failed(request, { code: 'EVENT_ARTIFACT_CONFLICT', message: `fixFor '${request.fixFor}' conflicts with open event` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
218
+ if (request.implementationInput !== openIdentity.implementationInput) return failed(request, { code: 'EVENT_ARTIFACT_CONFLICT', message: `implementationInput '${request.implementationInput ?? ''}' conflicts with open event` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
219
+ normalized = { ...request, round: openIdentity.round, artifact: artifactName(FAMILY[initialParts.family].artifact, openIdentity.round), fixFor: openIdentity.fixFor, implementationInput: openIdentity.implementationInput };
220
+ return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, typeof frontmatter.current_step === 'string' ? frontmatter.current_step : '', identity(normalized), openIdentity.row.started, frontmatter, null);
221
+ }
222
+ const result = normalizeStarted(request, resolved.repoRoot);
223
+ artifactContext = result.context;
224
+ if ('error' in result) return failed(request, result.error, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, artifactContext });
225
+ normalized = result.request;
226
+ } else if (initialParts.phase === 'completed') {
227
+ const identity = request.artifact ? parseArtifactName(request.artifact) : null;
228
+ if (identity?.family === FAMILY[initialParts.family].artifact) normalized = { ...request, round: identity.round };
229
+ }
230
+ const eventIdentity = identity(normalized);
231
+ const currentStep = typeof frontmatter.current_step === 'string' ? frontmatter.current_step : '';
232
+ const matchingRows = rows.filter((item) => item.step === eventIdentity.action);
233
+ const manual = eventIdentity.family === 'manual-validation';
234
+ if (!manual && matchingRows.length > 1) return failed(normalized, { code: 'EVENT_LOG_CONFLICT', message: 'event identity appears more than once' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
235
+ const openRows = matchingRows.filter((item) => item.started && !item.done);
236
+ const completedRows = matchingRows.filter((item) => item.done);
237
+ const row = manual ? openRows.at(-1) : matchingRows[0];
238
+ let completedArtifact: ArtifactIdentity | null = null;
239
+ if (eventIdentity.phase === 'started' && row) {
240
+ if (row.agent !== normalized.agent) return failed(normalized, { code: 'EVENT_LOG_CONFLICT', message: 'open started event has a different agent' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
241
+ return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, currentStep, eventIdentity, row.started, frontmatter, artifactContext);
242
+ }
243
+ if (eventIdentity.phase === 'started' && !manual && completedRows.length > 0) return failed(normalized, { code: 'EVENT_ALREADY_COMPLETED', message: 'event identity is already completed' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
244
+ const done = completedRows.find((item) => item.note === eventIdentity.note);
245
+ if (eventIdentity.phase === 'completed' && done) return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, currentStep, eventIdentity, done.done, frontmatter, artifactContext);
246
+ if (eventIdentity.phase === 'completed' && completedRows.length > 0 && openRows.length === 0) return failed(normalized, { code: 'EVENT_LOG_CONFLICT', message: 'event identity is already completed with a different payload' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
247
+ if (eventIdentity.phase === 'completed' && !row?.started) return failed(normalized, { code: 'EVENT_START_MISSING', message: 'completion requires one open matching started event' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
248
+ if (eventIdentity.phase === 'completed') {
249
+ const validated = validateCompletedArtifact(resolved.taskDir, FAMILY[eventIdentity.family].artifact, normalized.artifact!, normalized.round);
250
+ if (!validated.ok) return failed(normalized, validated.error, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
251
+ completedArtifact = validated.artifact;
252
+ }
253
+ if (eventIdentity.phase === 'waiting' && section.entries.some((entry) => entry.step === eventIdentity.action && entry.note === eventIdentity.note)) {
254
+ const existing = section.entries.find((entry) => entry.step === eventIdentity.action && entry.note === eventIdentity.note)!;
255
+ return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, currentStep, eventIdentity, existing.time, frontmatter, artifactContext);
256
+ }
257
+ const allowed = FAMILY[eventIdentity.family][eventIdentity.phase === 'started' ? 'started' : 'completed'];
258
+ if (!(allowed as readonly string[]).includes(currentStep)) return failed(normalized, { code: 'EVENT_TRANSITION_INVALID', message: `${normalized.event} is not allowed from '${currentStep}'` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
259
+ let metadata;
260
+ try { metadata = (options.metadataProvider ?? captureTaskWriteMetadata)(); }
261
+ catch (error) { return failed(normalized, { code: 'METADATA_CAPTURE_FAILED', message: String(error) }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath }); }
262
+ const step = eventIdentity.phase === 'started' || eventIdentity.target === null ? currentStep : eventIdentity.target;
263
+ const logStep = eventIdentity.phase === 'started' ? `${eventIdentity.action} [started]` : eventIdentity.action;
264
+ const body = appendActivityEntry(section, { time: metadata.timestamp, step: logStep, agent: normalized.agent, note: eventIdentity.note });
265
+ const frontmatterSet: Record<string, string> = { current_step: step, assigned_to: normalized.agent };
266
+ if (eventIdentity.phase === 'started' && normalized.implementationInput) frontmatterSet.last_reviewed_commit = '';
267
+ const mutations: Parameters<typeof writeTask>[0]['mutations'][number][] = [
268
+ { kind: 'frontmatter', set: frontmatterSet }
269
+ ];
270
+ if (completedArtifact) {
271
+ const link = buildArtifactLinkSection(content, completedArtifact);
272
+ mutations.push({ kind: 'section', aliases: link.aliases, heading: link.heading, body: link.body });
273
+ }
274
+ if (eventIdentity.phase === 'completed' && normalized.implementationInput) {
275
+ let implementationRows;
276
+ try {
277
+ implementationRows = consumeImplementationInput(
278
+ parseImplementationInputs(content).rows,
279
+ normalized.implementationInput,
280
+ normalized.artifact!
281
+ );
282
+ } catch (error) {
283
+ return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: error instanceof Error ? error.message : String(error) }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
284
+ }
285
+ mutations.push({
286
+ kind: 'section', aliases: IMPLEMENTATION_INPUT_ALIASES,
287
+ heading: findSectionHeading(content, [...IMPLEMENTATION_INPUT_ALIASES]),
288
+ body: renderImplementationInputs(implementationRows)
289
+ });
290
+ }
291
+ mutations.push({ kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: section.heading, body });
292
+ const result = writeTask({ taskRef: normalized.taskRef, expectedState: 'active', dryRun: normalized.dryRun, mutations }, { ...options, metadataProvider: () => metadata });
293
+ if (result.status === 'failed') return failed(normalized, result.error, { taskId: result.taskId, taskMdPath: result.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, timestamp: result.timestamp, agentInfraVersion: result.agentInfraVersion, operations: result.operations, artifactContext });
294
+ return {
295
+ status: result.status, changed: result.changed, event: normalized.event,
296
+ requestRef: normalized.taskRef, taskId: result.taskId, taskMdPath: result.taskMdPath,
297
+ fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase,
298
+ round: normalized.round ?? null, artifact: normalized.artifact ?? null,
299
+ fixFor: normalized.fixFor ?? null, implementationInput: normalized.implementationInput ?? null,
300
+ artifactContext, timestamp: result.timestamp,
301
+ agentInfraVersion: result.agentInfraVersion, operations: result.operations, error: null
302
+ };
303
+ }
304
+
305
+ function successNoOp(
306
+ request: TaskEventRequest, taskId: string, taskMdPath: string, currentStep: string,
307
+ eventIdentity: ReturnType<typeof identity>, timestamp: string,
308
+ frontmatter: Record<string, unknown>, artifactContext: ArtifactContextResult | null
309
+ ): TaskEventResult {
310
+ return {
311
+ ...failed(request, { code: 'EVENT_LOG_CONFLICT', message: '' }), status: 'no-op', error: null,
312
+ taskId, taskMdPath, fromStep: currentStep, toStep: currentStep,
313
+ action: eventIdentity.action, phase: eventIdentity.phase, timestamp,
314
+ agentInfraVersion: typeof frontmatter.agent_infra_version === 'string' ? frontmatter.agent_infra_version : null,
315
+ artifactContext
316
+ };
317
+ }
318
+
319
+ export { eventCatalog, validateTaskEventRequest, applyTaskEvent };
320
+ export type { TaskEventName, TaskEventRequest, TaskEventResult, TaskEventError, TaskEventErrorCode, Verdict };