@fitlab-ai/agent-infra 0.9.9 → 0.9.11

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 (309) hide show
  1. package/README.md +13 -3
  2. package/README.zh-CN.md +13 -3
  3. package/bin/cli.ts +17 -4
  4. package/dist/bin/cli.js +19 -4
  5. package/dist/bin/internal-cli.js +55 -4
  6. package/dist/lib/agent-client.js +0 -1
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +17 -10
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +11 -31
  9. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +15 -33
  10. package/dist/lib/agent-clients/adapters/codex-sandbox.js +72 -11
  11. package/dist/lib/agent-clients/adapters/index.js +3 -1
  12. package/dist/lib/agent-clients/adapters/opencode-sandbox.js +0 -4
  13. package/dist/lib/agent-clients/adapters/traecli.js +65 -0
  14. package/dist/lib/agent-clients/config.js +14 -99
  15. package/dist/lib/agent-clients/reconcile.js +1 -11
  16. package/dist/lib/agent-clients/schema.js +5 -4
  17. package/dist/lib/agent-clients/tokens.js +7 -17
  18. package/dist/lib/agent-clients/types.js +2 -1
  19. package/dist/lib/decide.js +14 -30
  20. package/dist/lib/defaults.json +9 -0
  21. package/dist/lib/init.js +2 -2
  22. package/dist/lib/internal/codex-lifecycle.js +2 -2
  23. package/dist/lib/internal/codex-sandbox-controller.js +4 -3
  24. package/dist/lib/internal/release-workflow.js +0 -1
  25. package/dist/lib/internal/sandbox-control.js +2 -2
  26. package/dist/lib/internal/task-finalization.js +19 -9
  27. package/dist/lib/internal/task-ledger.js +8 -2
  28. package/dist/lib/internal/task-lifecycle.js +17 -93
  29. package/dist/lib/internal/task-orchestration.js +30 -216
  30. package/dist/lib/{task/commands/validate.js → internal/task-validate.js} +77 -43
  31. package/dist/lib/merge.js +205 -103
  32. package/dist/lib/process-data/normalize.js +2 -9
  33. package/dist/lib/run/host.js +46 -16
  34. package/dist/lib/run/tui.js +5 -2
  35. package/dist/lib/sandbox/agent-client-reconciler.js +4 -12
  36. package/dist/lib/sandbox/commands/create.js +3 -3
  37. package/dist/lib/sandbox/commands/enter.js +18 -4
  38. package/dist/lib/sandbox/config.js +3 -12
  39. package/dist/lib/sandbox/control/controller-registration.js +4 -2
  40. package/dist/lib/sandbox/control/executor.js +135 -40
  41. package/dist/lib/sandbox/control/lifecycle.js +29 -20
  42. package/dist/lib/sandbox/control/server.js +18 -16
  43. package/dist/lib/sandbox/mounts.js +3 -2
  44. package/dist/lib/sandbox/recovery.js +123 -22
  45. package/dist/lib/sandbox/workspace-identity.js +14 -0
  46. package/dist/lib/sandbox/workspace-view.js +8 -9
  47. package/dist/lib/self-update.js +278 -0
  48. package/dist/lib/server/protocol.js +5 -5
  49. package/dist/lib/task/activity-log.js +4 -1
  50. package/dist/lib/task/artifact-lifecycle.js +4 -2
  51. package/dist/lib/task/codex-orchestration.js +32 -35
  52. package/dist/lib/task/command-options.js +1 -3
  53. package/dist/lib/task/commands/cat.js +0 -5
  54. package/dist/lib/task/commands/decisions.js +76 -92
  55. package/dist/lib/task/commands/files.js +6 -6
  56. package/dist/lib/task/commands/grep.js +25 -61
  57. package/dist/lib/task/commands/issue-body.js +6 -6
  58. package/dist/lib/task/commands/log.js +25 -10
  59. package/dist/lib/task/commands/show.js +5 -6
  60. package/dist/lib/task/commands/status.js +8 -8
  61. package/dist/lib/task/commit-operation.js +21 -25
  62. package/dist/lib/task/commit-policy.js +1 -1
  63. package/dist/lib/task/control-authority.js +431 -0
  64. package/dist/lib/task/create.js +6 -1
  65. package/dist/lib/task/current-contract.js +35 -0
  66. package/dist/lib/task/decision-details.js +263 -0
  67. package/dist/lib/task/decision-intents.js +5 -2
  68. package/dist/lib/task/delegation-receipts.js +8 -7
  69. package/dist/lib/task/events.js +11 -5
  70. package/dist/lib/task/finalization.js +72 -46
  71. package/dist/lib/task/index.js +19 -29
  72. package/dist/lib/task/ledger-intents.js +8 -9
  73. package/dist/lib/task/ledger.js +13 -6
  74. package/dist/lib/task/lifecycle.js +16 -0
  75. package/dist/lib/task/orchestration.js +51 -14
  76. package/dist/lib/task/review-artifacts.js +31 -12
  77. package/dist/lib/task/review-finalization.js +19 -6
  78. package/dist/lib/task/verification-engine.js +38 -18
  79. package/dist/lib/task/verification-review.js +1 -1
  80. package/dist/lib/task/workspace-snapshot.js +5 -11
  81. package/dist/lib/task/write.js +11 -1
  82. package/dist/lib/update.js +6 -37
  83. package/dist/lib/version.js +5 -1
  84. package/dist/lifecycle-build-manifest.json +3 -3
  85. package/lib/agent-client.ts +0 -1
  86. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +25 -10
  87. package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +16 -33
  88. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +16 -35
  89. package/lib/agent-clients/adapters/codex-sandbox.ts +89 -14
  90. package/lib/agent-clients/adapters/index.ts +3 -1
  91. package/lib/agent-clients/adapters/opencode-sandbox.ts +0 -5
  92. package/lib/agent-clients/adapters/traecli.ts +66 -0
  93. package/lib/agent-clients/config.ts +14 -144
  94. package/lib/agent-clients/reconcile.ts +1 -12
  95. package/lib/agent-clients/schema.ts +5 -4
  96. package/lib/agent-clients/tokens.ts +7 -20
  97. package/lib/agent-clients/types.ts +2 -1
  98. package/lib/decide.ts +12 -22
  99. package/lib/defaults.json +9 -0
  100. package/lib/init.ts +2 -2
  101. package/lib/internal/codex-lifecycle.ts +2 -2
  102. package/lib/internal/codex-sandbox-controller.ts +4 -3
  103. package/lib/internal/release-workflow.ts +0 -1
  104. package/lib/internal/sandbox-control.ts +2 -2
  105. package/lib/internal/task-finalization.ts +27 -13
  106. package/lib/internal/task-ledger.ts +8 -2
  107. package/lib/internal/task-lifecycle.ts +33 -76
  108. package/lib/internal/task-orchestration.ts +33 -190
  109. package/lib/{task/commands/validate.ts → internal/task-validate.ts} +77 -41
  110. package/lib/merge.ts +236 -120
  111. package/lib/process-data/normalize.ts +2 -8
  112. package/lib/run/host.ts +63 -14
  113. package/lib/run/tui.ts +5 -3
  114. package/lib/sandbox/agent-client-reconciler.ts +5 -15
  115. package/lib/sandbox/commands/create.ts +4 -4
  116. package/lib/sandbox/commands/enter.ts +24 -4
  117. package/lib/sandbox/config.ts +2 -14
  118. package/lib/sandbox/control/container-identity.ts +2 -2
  119. package/lib/sandbox/control/controller-registration.ts +8 -3
  120. package/lib/sandbox/control/executor.ts +156 -53
  121. package/lib/sandbox/control/lifecycle.ts +34 -28
  122. package/lib/sandbox/control/protocol.ts +1 -3
  123. package/lib/sandbox/control/server.ts +15 -12
  124. package/lib/sandbox/control/state.ts +7 -7
  125. package/lib/sandbox/mounts.ts +4 -3
  126. package/lib/sandbox/recovery.ts +162 -34
  127. package/lib/sandbox/tools.ts +1 -1
  128. package/lib/sandbox/workspace-identity.ts +28 -0
  129. package/lib/sandbox/workspace-view.ts +14 -10
  130. package/lib/self-update.ts +341 -0
  131. package/lib/server/protocol.ts +5 -5
  132. package/lib/task/activity-log.ts +6 -1
  133. package/lib/task/artifact-lifecycle.ts +5 -3
  134. package/lib/task/codex-orchestration.ts +40 -45
  135. package/lib/task/command-options.ts +1 -2
  136. package/lib/task/commands/cat.ts +0 -4
  137. package/lib/task/commands/decisions.ts +80 -75
  138. package/lib/task/commands/files.ts +6 -6
  139. package/lib/task/commands/grep.ts +23 -51
  140. package/lib/task/commands/issue-body.ts +6 -6
  141. package/lib/task/commands/log.ts +22 -10
  142. package/lib/task/commands/show.ts +5 -6
  143. package/lib/task/commands/status.ts +8 -8
  144. package/lib/task/commit-operation.ts +25 -30
  145. package/lib/task/commit-policy.ts +1 -2
  146. package/lib/task/control-authority.ts +563 -0
  147. package/lib/task/create.ts +6 -1
  148. package/lib/task/current-contract.ts +49 -0
  149. package/lib/task/decision-details.ts +340 -0
  150. package/lib/task/decision-intents.ts +7 -2
  151. package/lib/task/delegation-receipts.ts +8 -6
  152. package/lib/task/events.ts +10 -5
  153. package/lib/task/finalization.ts +84 -42
  154. package/lib/task/index.ts +19 -28
  155. package/lib/task/ledger-intents.ts +6 -8
  156. package/lib/task/ledger.ts +17 -7
  157. package/lib/task/lifecycle.ts +20 -0
  158. package/lib/task/orchestration.ts +42 -16
  159. package/lib/task/review-artifacts.ts +53 -15
  160. package/lib/task/review-finalization.ts +33 -7
  161. package/lib/task/verification-engine.ts +33 -21
  162. package/lib/task/verification-review.ts +1 -1
  163. package/lib/task/workspace-snapshot.ts +4 -13
  164. package/lib/task/write.ts +20 -1
  165. package/lib/update.ts +6 -40
  166. package/lib/version.ts +6 -1
  167. package/package.json +1 -1
  168. package/templates/.agents/QUICKSTART.en.md +2 -3
  169. package/templates/.agents/QUICKSTART.zh-CN.md +2 -3
  170. package/templates/.agents/README.en.md +5 -3
  171. package/templates/.agents/README.zh-CN.md +5 -3
  172. package/templates/.agents/rules/README.en.md +1 -0
  173. package/templates/.agents/rules/README.zh-CN.md +1 -0
  174. package/templates/.agents/rules/compatibility-policy.en.md +40 -0
  175. package/templates/.agents/rules/compatibility-policy.zh-CN.md +40 -0
  176. package/templates/.agents/rules/create-issue.github.en.md +1 -1
  177. package/templates/.agents/rules/create-issue.github.zh-CN.md +1 -1
  178. package/templates/.agents/rules/human-decision-context.en.md +9 -7
  179. package/templates/.agents/rules/human-decision-context.zh-CN.md +9 -7
  180. package/templates/.agents/rules/issue-fields.github.en.md +1 -1
  181. package/templates/.agents/rules/issue-fields.github.zh-CN.md +1 -1
  182. package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
  183. package/templates/.agents/rules/issue-pr-commands.github.en.md +1 -1
  184. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +1 -1
  185. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  186. package/templates/.agents/rules/issue-sync.github.en.md +1 -1
  187. package/templates/.agents/rules/issue-sync.github.zh-CN.md +2 -2
  188. package/templates/.agents/rules/lifecycle-orchestration.en.md +4 -4
  189. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +4 -4
  190. package/templates/.agents/rules/milestone-inference.github.en.md +1 -1
  191. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +1 -1
  192. package/templates/.agents/rules/next-step-output.en.md +3 -3
  193. package/templates/.agents/rules/next-step-output.zh-CN.md +3 -3
  194. package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -1
  195. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -1
  196. package/templates/.agents/rules/pr-sync.en.md +2 -2
  197. package/templates/.agents/rules/pr-sync.github.en.md +3 -2
  198. package/templates/.agents/rules/pr-sync.github.zh-CN.md +3 -1
  199. package/templates/.agents/rules/pr-sync.zh-CN.md +2 -1
  200. package/templates/.agents/rules/release-commands.github.en.md +6 -0
  201. package/templates/.agents/rules/release-commands.github.zh-CN.md +6 -0
  202. package/templates/.agents/rules/review-handshake.en.md +1 -1
  203. package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
  204. package/templates/.agents/rules/review-method.en.md +1 -0
  205. package/templates/.agents/rules/review-method.zh-CN.md +1 -0
  206. package/templates/.agents/rules/task-management.en.md +1 -2
  207. package/templates/.agents/rules/task-management.zh-CN.md +1 -2
  208. package/templates/.agents/skills/analyze-task/SKILL.en.md +4 -3
  209. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +4 -3
  210. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  211. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  212. package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
  213. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
  214. package/templates/.agents/skills/check-task/SKILL.en.md +2 -2
  215. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +2 -2
  216. package/templates/.agents/skills/close-codescan/SKILL.en.md +1 -1
  217. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +1 -1
  218. package/templates/.agents/skills/close-dependabot/SKILL.en.md +1 -1
  219. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +1 -1
  220. package/templates/.agents/skills/code-task/SKILL.en.md +4 -3
  221. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +5 -3
  222. package/templates/.agents/skills/commit/SKILL.en.md +27 -7
  223. package/templates/.agents/skills/commit/SKILL.zh-CN.md +3 -4
  224. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -5
  225. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -5
  226. package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
  227. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
  228. package/templates/.agents/skills/create-pr/SKILL.en.md +15 -11
  229. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +15 -11
  230. package/templates/.agents/skills/create-pr/reference/change-report.en.md +46 -0
  231. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +46 -0
  232. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +203 -0
  233. package/templates/.agents/skills/create-release-note/SKILL.en.md +5 -6
  234. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +5 -6
  235. package/templates/.agents/skills/create-task/SKILL.en.md +1 -1
  236. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -1
  237. package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
  238. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
  239. package/templates/.agents/skills/plan-task/SKILL.en.md +5 -4
  240. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -4
  241. package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
  242. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
  243. package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -3
  244. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -3
  245. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -0
  246. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -0
  247. package/templates/.agents/skills/review-code/SKILL.en.md +5 -3
  248. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -3
  249. package/templates/.agents/skills/review-code/config/verify.en.json +1 -0
  250. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -0
  251. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +9 -6
  252. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +9 -6
  253. package/templates/.agents/skills/review-plan/SKILL.en.md +5 -3
  254. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -3
  255. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -0
  256. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -0
  257. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +7 -3
  258. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +7 -3
  259. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +1 -1
  260. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +1 -1
  261. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -2
  262. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -2
  263. package/templates/.agents/skills/run-task/SKILL.en.md +6 -2
  264. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +6 -2
  265. package/templates/.agents/skills/run-task/reference/host-validation.en.md +2 -2
  266. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +2 -2
  267. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +2 -2
  268. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +2 -2
  269. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +159 -104
  270. package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
  271. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
  272. package/templates/.agents/templates/task.en.md +1 -1
  273. package/templates/.agents/templates/task.zh-CN.md +1 -1
  274. package/templates/.claude/commands/analyze-task.en.md +1 -1
  275. package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
  276. package/templates/.claude/commands/block-task.en.md +1 -1
  277. package/templates/.claude/commands/block-task.zh-CN.md +1 -1
  278. package/templates/.claude/commands/cancel-task.en.md +1 -1
  279. package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
  280. package/templates/.claude/commands/check-task.en.md +1 -1
  281. package/templates/.claude/commands/check-task.zh-CN.md +1 -1
  282. package/templates/.claude/commands/code-task.en.md +1 -1
  283. package/templates/.claude/commands/code-task.zh-CN.md +1 -1
  284. package/templates/.claude/commands/commit.en.md +1 -1
  285. package/templates/.claude/commands/commit.zh-CN.md +1 -1
  286. package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
  287. package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
  288. package/templates/.claude/commands/complete-task.en.md +1 -1
  289. package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
  290. package/templates/.claude/commands/create-pr.en.md +1 -1
  291. package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
  292. package/templates/.claude/commands/plan-task.en.md +1 -1
  293. package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
  294. package/templates/.claude/commands/review-analysis.en.md +1 -1
  295. package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
  296. package/templates/.claude/commands/review-code.en.md +1 -1
  297. package/templates/.claude/commands/review-code.zh-CN.md +1 -1
  298. package/templates/.claude/commands/review-plan.en.md +1 -1
  299. package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
  300. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  301. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  302. package/templates/.claude/commands/run-task.en.md +1 -1
  303. package/templates/.claude/commands/run-task.zh-CN.md +1 -1
  304. package/templates/.claude/commands/watch-pr.en.md +1 -1
  305. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  306. package/templates/.traecli/commands/update-agent-infra.en.md +7 -0
  307. package/templates/.traecli/commands/update-agent-infra.zh-CN.md +7 -0
  308. package/dist/lib/builtin-tuis.js +0 -40
  309. package/lib/builtin-tuis.ts +0 -56
@@ -12,18 +12,25 @@ const LEDGER_TERMINAL = new Set(['confirmed', 'closed', 'human-decided']);
12
12
  // Statuses that represent a decision row awaiting or carrying a human ruling.
13
13
  const HUMAN_DECISION_STATUSES = new Set(['needs-human-decision', 'human-decided']);
14
14
  const FINDING_STATUSES = new Set(['open', 'accepted', 'adjusted', 'refuted', 'cannot-judge', 'confirmed', 'needs-human-decision', 'closed', 'human-decided']);
15
+ const LEDGER_SECTION_MISSING_CODE = 'LEDGER_SECTION_MISSING';
16
+ const LEDGER_SECTION_MISSING_MESSAGE = 'review disagreement ledger section is missing';
15
17
  function isReviewStage(stage) {
16
18
  return stage === 'analysis' || stage === 'plan' || stage === 'code';
17
19
  }
18
20
  // Parse all rows of the disagreement ledger table. Skips the heading, the
19
21
  // header row (`| id | ... |`) and the `|---|` separator; ignores non-`|` lines.
20
22
  // Rows with fewer than 6 columns are skipped (mirrors the JS gate parser).
21
- function parseLedger(content) {
23
+ function parseLedgerDocument(content) {
22
24
  const table = parseTable(content, { sectionAliases: LEDGER_HEADINGS, columns: LEDGER_COLUMNS });
23
- return table?.rows.map(({ values, sourceLine }) => ({
24
- id: values.id, stage: values.stage, round: values.round, severity: values.severity,
25
- status: values.status, evidence: values.evidence, sourceLine
26
- })) ?? [];
25
+ if (!table)
26
+ return { present: false, rows: [] };
27
+ return {
28
+ present: true,
29
+ rows: table.rows.map(({ values, sourceLine }) => ({
30
+ id: values.id, stage: values.stage, round: values.round, severity: values.severity,
31
+ status: values.status, evidence: values.evidence, sourceLine
32
+ }))
33
+ };
27
34
  }
28
35
  function validateLedgerRows(rows) {
29
36
  const seen = new Set();
@@ -104,5 +111,5 @@ function nextHdId(rows) {
104
111
  }
105
112
  return `HD-${max + 1}`;
106
113
  }
107
- export { LEDGER_HEADINGS, LEDGER_COLUMNS, parseLedger, nextHdId, isReviewStage, validateLedgerRows, summarizeLedgerStage, LEDGER_TERMINAL, HUMAN_DECISION_STATUSES };
114
+ export { LEDGER_HEADINGS, LEDGER_COLUMNS, parseLedgerDocument, nextHdId, isReviewStage, validateLedgerRows, summarizeLedgerStage, LEDGER_TERMINAL, HUMAN_DECISION_STATUSES, LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE };
108
115
  //# sourceMappingURL=ledger.js.map
@@ -7,6 +7,8 @@ import { parseTypedTaskFrontmatter } from './frontmatter.js';
7
7
  import { locateHotTaskDirs, resolveTaskRef, TASK_ID_RE } from './resolve-ref.js';
8
8
  import { configuredShortIdLength, executeShortIdCommand, loadShortIdByTaskId, mutateShortIdRegistry } from './short-id.js';
9
9
  import { captureTaskWriteMetadata, writeTask } from './write.js';
10
+ import { validateCurrentTaskContract } from './current-contract.js';
11
+ import { isValidAgentInfraVersion } from '../version.js';
10
12
  const lifecycleIntentCatalog = [
11
13
  'block', 'activate', 'cancel', 'complete', 'close-codescan', 'close-dependabot', 'restore'
12
14
  ];
@@ -57,6 +59,9 @@ function failed(request, error, extra = {}) {
57
59
  error, ...extra
58
60
  };
59
61
  }
62
+ export function taskLifecycleFailure(request, error, taskId = null) {
63
+ return failed(request, error, { taskId });
64
+ }
60
65
  function normalizedRequest(request) {
61
66
  if (!request || typeof request !== 'object' || !lifecycleIntentCatalog.includes(request.intent)) {
62
67
  return { code: 'LIFECYCLE_PAYLOAD_INVALID', message: 'intent is not in the lifecycle catalog' };
@@ -366,6 +371,11 @@ function applyTaskLifecycle(requestInput, options = {}) {
366
371
  return failed(request, { code: 'LIFECYCLE_IDENTITY_INVALID', message: 'task.md id does not match its lifecycle task id' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
367
372
  if (!locateActivityLog(content) && !allowsManualOverride(options, 'LIFECYCLE_LOG_MISSING'))
368
373
  return failed(request, { code: 'LIFECYCLE_LOG_MISSING', message: 'task has no unique Activity Log section' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
374
+ if (spec.target === 'active') {
375
+ const contract = validateCurrentTaskContract(content);
376
+ if (!contract.ok)
377
+ return failed(request, { code: 'LIFECYCLE_DOCUMENT_INVALID', message: contract.message }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
378
+ }
369
379
  const registryRequest = executeShortIdCommand({
370
380
  operation: 'list', activeDir: path.join(repoRoot, '.agents', 'workspace', 'active'),
371
381
  shortIdLength: configuredShortIdLength(repoRoot)
@@ -394,6 +404,9 @@ function applyTaskLifecycle(requestInput, options = {}) {
394
404
  }
395
405
  if (journal.taskId !== taskId || journal.intent !== request.intent)
396
406
  return failed(request, { code: 'LIFECYCLE_INTENT_CONFLICT', message: 'an in-progress lifecycle journal belongs to a different request' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath });
407
+ if (spec.target === 'active' && !isValidAgentInfraVersion(journal.metadata.agentInfraVersion)) {
408
+ return failed(request, { code: 'LIFECYCLE_DOCUMENT_INVALID', message: `journal metadata agentInfraVersion must be a valid v-prefixed semver (received ${journal.metadata.agentInfraVersion})` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath });
409
+ }
397
410
  if (request.dryRun) {
398
411
  if (!journal.failure)
399
412
  return failed(request, { code: 'LIFECYCLE_JOURNAL_INVALID', message: 'existing lifecycle journal has no durable failure evidence for a read-only probe' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath });
@@ -411,6 +424,9 @@ function applyTaskLifecycle(requestInput, options = {}) {
411
424
  catch (error) {
412
425
  return failed(request, { code: 'LIFECYCLE_METADATA_FAILED', message: String(error) }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
413
426
  }
427
+ if (spec.target === 'active' && !isValidAgentInfraVersion(metadata.agentInfraVersion)) {
428
+ return failed(request, { code: 'LIFECYCLE_DOCUMENT_INVALID', message: `metadata agentInfraVersion must be a valid v-prefixed semver (received ${metadata.agentInfraVersion})` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
429
+ }
414
430
  journal = { version: 1, taskId, intent: request.intent, intentDigest: digest, sourceState, targetState: spec.target, metadata, completedSteps: [] };
415
431
  if (request.dryRun) {
416
432
  const plannedMutations = mutationsFor(request, content, metadata, restoredFiles, allowsManualOverride(options, 'LIFECYCLE_LOG_MISSING'));
@@ -2,9 +2,10 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { randomUUID } from 'node:crypto';
4
4
  import { parseTypedTaskFrontmatter } from './frontmatter.js';
5
- import { parseReviewSummary } from './review-artifacts.js';
5
+ import { validateCurrentTaskContract } from './current-contract.js';
6
+ import { parseReviewSummary, resolveCanonicalVerdict } from './review-artifacts.js';
6
7
  import { inspectArtifactDirectory } from './artifact-lifecycle.js';
7
- import { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
8
+ import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
8
9
  import { resolveTaskRef } from './resolve-ref.js';
9
10
  import { activateDelegation, abortPreparedDelegation, completeDelegationStage, consumeDelegation, dispatchDelegation, foldBlankToNull, isDelegationReceipt, managedDelegationRole, prepareDelegation, sealDelegation } from './delegation-receipts.js';
10
11
  import { normalizeAgentClients } from '../agent-clients/config.js';
@@ -25,7 +26,7 @@ function supportsLifecycleDelegation(client) {
25
26
  function orchestrationPath(taskDir) {
26
27
  return path.join(taskDir, 'orchestration.json');
27
28
  }
28
- const ORCHESTRATION_STATE_INVALID_MESSAGE = 'orchestration.json does not match the current runtime structure; finish or clear active runs before upgrading';
29
+ const ORCHESTRATION_STATE_INVALID_MESSAGE = 'orchestration.json does not match the current runtime structure; the file was left unchanged; rebuild the sandbox or manually repair the state before retrying';
29
30
  class OrchestrationStateError extends Error {
30
31
  code = 'ORCHESTRATION_STATE_INVALID';
31
32
  taskId;
@@ -350,9 +351,21 @@ function hasReviewAfterArtifact(taskDir, family, reviewFamily) {
350
351
  function latestReviewApproved(taskDir, family) {
351
352
  const round = highestRound(taskDir, family);
352
353
  if (round === 0)
353
- return false;
354
- const parsed = parseReviewSummary(fs.readFileSync(path.join(taskDir, artifactName(family, round)), 'utf8'));
355
- return parsed.ok && parsed.summary.verdict === 'Approved' && parsed.summary.counts !== null;
354
+ return { approved: false, error: null };
355
+ let content;
356
+ try {
357
+ content = fs.readFileSync(path.join(taskDir, artifactName(family, round)), 'utf8');
358
+ }
359
+ catch (error) {
360
+ return { approved: false, error: String(error) };
361
+ }
362
+ const parsed = parseReviewSummary(content);
363
+ if (!parsed.ok)
364
+ return { approved: false, error: `${parsed.code}: ${parsed.message}` };
365
+ const verdict = resolveCanonicalVerdict(parsed.summary);
366
+ if (!verdict.ok)
367
+ return { approved: false, error: `${verdict.code}: ${verdict.message}` };
368
+ return { approved: verdict.verdict === 'Approved', error: null };
356
369
  }
357
370
  function routeFromFacts(taskDir) {
358
371
  const analysisRound = highestRound(taskDir, 'analysis');
@@ -369,7 +382,10 @@ function routeFromFacts(taskDir) {
369
382
  const round = analysisReviewRound + 1;
370
383
  return { action: 'review-analysis', role: 'reviewer', stage: 'review-analysis', round, artifact: artifactName('review-analysis', round) };
371
384
  }
372
- if (!latestReviewApproved(taskDir, 'review-analysis')) {
385
+ const analysisVerdict = latestReviewApproved(taskDir, 'review-analysis');
386
+ if (analysisVerdict.error)
387
+ return { error: analysisVerdict.error };
388
+ if (!analysisVerdict.approved) {
373
389
  const round = analysisRound + 1;
374
390
  return { action: 'analyze-task', role: 'executor', stage: 'analysis', round, artifact: artifactName('analysis', round) };
375
391
  }
@@ -381,7 +397,10 @@ function routeFromFacts(taskDir) {
381
397
  const round = planReviewRound + 1;
382
398
  return { action: 'review-plan', role: 'reviewer', stage: 'review-plan', round, artifact: artifactName('review-plan', round) };
383
399
  }
384
- if (!latestReviewApproved(taskDir, 'review-plan')) {
400
+ const planVerdict = latestReviewApproved(taskDir, 'review-plan');
401
+ if (planVerdict.error)
402
+ return { error: planVerdict.error };
403
+ if (!planVerdict.approved) {
385
404
  const round = planRound + 1;
386
405
  return { action: 'plan-task', role: 'executor', stage: 'plan', round, artifact: artifactName('plan', round) };
387
406
  }
@@ -393,7 +412,10 @@ function routeFromFacts(taskDir) {
393
412
  const round = codeReviewRound + 1;
394
413
  return { action: 'review-code', role: 'reviewer', stage: 'review-code', round, artifact: artifactName('review-code', round) };
395
414
  }
396
- if (!latestReviewApproved(taskDir, 'review-code')) {
415
+ const codeVerdict = latestReviewApproved(taskDir, 'review-code');
416
+ if (codeVerdict.error)
417
+ return { error: codeVerdict.error };
418
+ if (!codeVerdict.approved) {
397
419
  const round = codeRound + 1;
398
420
  return { action: 'code-task', role: 'executor', stage: 'code', round, artifact: artifactName('code', round) };
399
421
  }
@@ -418,13 +440,25 @@ function routeOrchestration(taskRef, options = {}) {
418
440
  return failed('ORCHESTRATION_TASK_READ_FAILED', String(error), resolved.taskId);
419
441
  }
420
442
  let metadata;
421
- try {
422
- metadata = parseTypedTaskFrontmatter(content);
443
+ let currentLedger = null;
444
+ if (resolved.state === 'active') {
445
+ const contract = validateCurrentTaskContract(content);
446
+ if (!contract.ok)
447
+ return failed('ORCHESTRATION_CURRENT_CONTRACT_INVALID', contract.message, resolved.taskId);
448
+ metadata = contract.metadata;
449
+ currentLedger = contract.ledger;
423
450
  }
424
- catch (error) {
425
- return failed('ORCHESTRATION_TASK_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
451
+ else {
452
+ try {
453
+ metadata = parseTypedTaskFrontmatter(content);
454
+ }
455
+ catch (error) {
456
+ return failed('ORCHESTRATION_TASK_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
457
+ }
426
458
  }
427
459
  const routed = routeFromFacts(resolved.taskDir);
460
+ if (routed && 'error' in routed)
461
+ return failed('ORCHESTRATION_REVIEW_INVALID', routed.error, resolved.taskId);
428
462
  if (!routed)
429
463
  return failed('ORCHESTRATION_ROUTE_UNKNOWN', 'cannot determine a unique lifecycle action', resolved.taskId);
430
464
  const run = readRun(resolved.taskDir);
@@ -440,7 +474,10 @@ function routeOrchestration(taskRef, options = {}) {
440
474
  if (!review.ok || review.summary.manualValidation === null) {
441
475
  return failed('ORCHESTRATION_REVIEW_INVALID', 'latest code review has no numeric manual-validation count', resolved.taskId);
442
476
  }
443
- const rows = parseLedger(content);
477
+ const ledger = currentLedger ?? parseLedgerDocument(content);
478
+ if (!ledger.present)
479
+ return failed('ORCHESTRATION_CURRENT_CONTRACT_INVALID', `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`, resolved.taskId);
480
+ const rows = ledger.rows;
444
481
  const ledgerError = validateLedgerRows(rows);
445
482
  if (ledgerError)
446
483
  return failed('ORCHESTRATION_LEDGER_INVALID', ledgerError.message, resolved.taskId);
@@ -159,27 +159,46 @@ function finalizeReviewSummaryContent(content, counts) {
159
159
  content: content.slice(0, parsed.findingsStart) + line + content.slice(parsed.findingsEnd)
160
160
  };
161
161
  }
162
+ function resolveCanonicalVerdict(summary) {
163
+ if (summary.counts === null) {
164
+ return {
165
+ ok: false,
166
+ verdict: null,
167
+ code: 'REVIEW_FINDING_COUNTS_NOT_FINALIZED',
168
+ message: 'review summary finding counts are not finalized'
169
+ };
170
+ }
171
+ if (summary.verdict === 'Approved' && !equalCounts(summary.counts, { blocker: 0, major: 0, minor: 0 })) {
172
+ return {
173
+ ok: false,
174
+ verdict: null,
175
+ code: 'REVIEW_VERDICT_FINDING_MISMATCH',
176
+ message: 'Approved verdict requires zero finalized findings'
177
+ };
178
+ }
179
+ return { ok: true, verdict: summary.verdict };
180
+ }
162
181
  function parseVerdict(reviewPath) {
163
182
  if (!fs.existsSync(reviewPath)) {
164
- return { ok: false, verdict: null, message: `Review artifact not found: ${path.basename(reviewPath)}` };
183
+ return { ok: false, verdict: null, code: 'REVIEW_ARTIFACT_NOT_FOUND', message: `Review artifact not found: ${path.basename(reviewPath)}` };
184
+ }
185
+ let content;
186
+ try {
187
+ content = fs.readFileSync(reviewPath, 'utf8');
188
+ }
189
+ catch (error) {
190
+ return { ok: false, verdict: null, code: 'REVIEW_ARTIFACT_READ_FAILED', message: `Review artifact could not be read: ${String(error)}` };
165
191
  }
166
- const parsed = parseReviewSummary(fs.readFileSync(reviewPath, 'utf8'));
192
+ const parsed = parseReviewSummary(content);
167
193
  if (!parsed.ok) {
168
194
  return {
169
195
  ok: false,
170
196
  verdict: null,
197
+ code: parsed.code === 'REVIEW_SUMMARY_PLACEHOLDER_INVALID' ? parsed.code : 'REVIEW_SUMMARY_NOT_FOUND',
171
198
  message: `cannot parse review summary in ${path.basename(reviewPath)}: ${parsed.message}`
172
199
  };
173
200
  }
174
- const { verdict, counts } = parsed.summary;
175
- if (verdict !== 'Approved')
176
- return { ok: true, verdict };
177
- return {
178
- ok: true,
179
- verdict: counts && equalCounts(counts, { blocker: 0, major: 0, minor: 0 })
180
- ? 'Approved'
181
- : 'Approved-with-issues'
182
- };
201
+ return resolveCanonicalVerdict(parsed.summary);
183
202
  }
184
- export { artifactName, equalCounts, escapeRegExp, extractSection, finalizeReviewSummaryContent, maxRound, normalizeVerdict, parseReviewSummary, parseVerdict };
203
+ export { artifactName, equalCounts, escapeRegExp, extractSection, finalizeReviewSummaryContent, maxRound, normalizeVerdict, parseReviewSummary, parseVerdict, resolveCanonicalVerdict };
185
204
  //# sourceMappingURL=review-artifacts.js.map
@@ -1,10 +1,11 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { randomUUID } from 'node:crypto';
4
- import { locateActivityLog, pairEntries } from './activity-log.js';
4
+ import { locateActivityLog, pairEntries, startedBackedRows } from './activity-log.js';
5
5
  import { parseArtifactName, validateCompletedArtifact } from './artifact-lifecycle.js';
6
- import { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
6
+ import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
7
7
  import { finalizeReviewSummaryContent } from './review-artifacts.js';
8
+ import { inspectDecisionDetailDuplicates, repairDecisionDetailDuplicates } from './decision-details.js';
8
9
  import { resolveTaskRef } from './resolve-ref.js';
9
10
  import { validateLifecycleExecution } from './lifecycle-execution.js';
10
11
  import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
@@ -40,7 +41,7 @@ function openReviewRound(content, action, round) {
40
41
  if (!section)
41
42
  return false;
42
43
  const expected = `${action} (Round ${round})`;
43
- return pairEntries(section.entries).filter((row) => (row.step === expected && Boolean(row.started) && !row.done)).length === 1;
44
+ return startedBackedRows(pairEntries(section.entries)).filter((row) => (row.step === expected && !row.done)).length === 1;
44
45
  }
45
46
  function cleanupTemp(fileSystem, tempPath) {
46
47
  try {
@@ -103,7 +104,11 @@ function finalizeReviewSummaryUnlocked(request, options = {}) {
103
104
  }
104
105
  let rows;
105
106
  try {
106
- rows = parseLedger(taskContent);
107
+ const ledger = parseLedgerDocument(taskContent);
108
+ if (!ledger.present) {
109
+ return failed(request, 'REVIEW_LEDGER_INVALID', `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`, resolved.taskId);
110
+ }
111
+ rows = ledger.rows;
107
112
  }
108
113
  catch (error) {
109
114
  return failed(request, 'REVIEW_LEDGER_INVALID', String(error), resolved.taskId);
@@ -113,11 +118,19 @@ function finalizeReviewSummaryUnlocked(request, options = {}) {
113
118
  return failed(request, 'REVIEW_LEDGER_INVALID', `${ledgerError.code}: ${ledgerError.message}`, resolved.taskId);
114
119
  }
115
120
  const stageStatus = summarizeLedgerStage(rows, stage);
116
- const transformed = finalizeReviewSummaryContent(artifactContent, stageStatus.unresolvedFindingCounts);
121
+ const repaired = repairDecisionDetailDuplicates(artifactContent);
122
+ if (!repaired.ok) {
123
+ return failed(request, 'REVIEW_DECISION_DETAIL_INVALID', `${repaired.code}: ${repaired.message}`, resolved.taskId, stageStatus);
124
+ }
125
+ const detailInspection = inspectDecisionDetailDuplicates(repaired.content);
126
+ if (!detailInspection.ok) {
127
+ return failed(request, 'REVIEW_DECISION_DETAIL_INVALID', `${detailInspection.code}: ${detailInspection.message}`, resolved.taskId, stageStatus);
128
+ }
129
+ const transformed = finalizeReviewSummaryContent(repaired.content, stageStatus.unresolvedFindingCounts);
117
130
  if (!transformed.ok) {
118
131
  return failed(request, transformed.code, transformed.message, resolved.taskId, stageStatus);
119
132
  }
120
- if (!transformed.changed) {
133
+ if (!repaired.changed && !transformed.changed) {
121
134
  return {
122
135
  ...failed(request, 'REVIEW_ARTIFACT_CONFLICT', '', resolved.taskId, stageStatus),
123
136
  status: 'no-op',
@@ -12,8 +12,10 @@ import { resolveMaterializedReviewedHeadRelation } from "../platform/change-requ
12
12
  import { resolveReviewedHeadRelation } from "../platform/merged-pr-equivalence.js";
13
13
  import { resolveLocalReviewedCommitRelation } from "../git/reviewed-commit-equivalence.js";
14
14
  import { parseTypedTaskFrontmatter } from "./frontmatter.js";
15
- import { parseLedger, summarizeLedgerStage, validateLedgerRows } from "./ledger.js";
15
+ import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from "./ledger.js";
16
+ import { isValidAgentInfraVersion } from "../version.js";
16
17
  import { equalCounts, parseReviewSummary } from "./review-artifacts.js";
18
+ import { inspectDecisionDetailDuplicates } from "./decision-details.js";
17
19
  import { loadVerificationConfig } from "./verification-config.js";
18
20
  import { snapshotReview } from "../git/review-snapshot.js";
19
21
  import { OrchestrationStateError, readRun } from "./orchestration.js";
@@ -34,7 +36,6 @@ const DEFAULT_REQUIRED_FIELDS = [
34
36
  "assigned_to"
35
37
  ];
36
38
  const DATE_TIME_PATTERN = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:[+-]\d{2}:\d{2})?$/;
37
- const AGENT_INFRA_VERSION_PATTERN = /^v\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
38
39
  const ACTIVITY_LOG_PATTERN = /^- (\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:[+-]\d{2}:\d{2})?) — \*\*(.+?)\*\* by (.+?) — (.+)$/;
39
40
  // Start markers (action suffixed with ` [started]`) are excluded from the
40
41
  // "latest action" / freshness computation so a step's in-flight marker never
@@ -89,6 +90,8 @@ function runCheck(type, context, shared) {
89
90
  return checkTaskMeta(context);
90
91
  case "artifact":
91
92
  return checkArtifact(context);
93
+ case "decision-details":
94
+ return checkDecisionDetails(context);
92
95
  case "implementation-input":
93
96
  return checkImplementationInput(context);
94
97
  case "activity-log":
@@ -308,9 +311,6 @@ function checkOrchestrationEvidence({ taskDir }) {
308
311
  if (!provenance
309
312
  || (activatedCodex && (host?.kind !== 'codex-lifecycle-v2'
310
313
  || host.protocolVersion !== provenance.protocolVersion
311
- || host.packageVersion !== provenance.packageVersion
312
- || host.internalExecutableBuildHash !== provenance.internalExecutableBuildHash
313
- || host.lifecycleContractHash !== provenance.lifecycleContractHash
314
314
  || host.hookDefinitionHash !== provenance.hookDefinitionHash
315
315
  || host.hookSource !== provenance.hookSource
316
316
  || host.hookSourcePathDigest !== provenance.hookSourcePathDigest
@@ -355,18 +355,17 @@ function checkTaskMeta({ taskDir, config }) {
355
355
  const metadata = task.metadata;
356
356
  const requiredFields = config.required_fields || DEFAULT_REQUIRED_FIELDS;
357
357
  const missingFields = requiredFields.filter((field) => isBlank(metadata[field]));
358
- const blockingMissingFields = missingFields.filter((field) => field !== "agent_infra_version");
358
+ const currentTask = metadata.status === 'active';
359
+ const blockingMissingFields = missingFields.filter((field) => field !== "agent_infra_version" || currentTask);
359
360
  const warnings = [];
360
- if (missingFields.includes("agent_infra_version")) {
361
+ if (missingFields.includes("agent_infra_version") && !currentTask) {
361
362
  warnings.push("field 'agent_infra_version' missing — historical task or skipped version stamp");
362
363
  }
363
364
  if (blockingMissingFields.length > 0) {
364
365
  return failResult("task-meta", `Missing required fields: ${blockingMissingFields.join(", ")}`);
365
366
  }
366
- if (!isBlank(metadata.agent_infra_version) &&
367
- metadata.agent_infra_version !== "unknown" &&
368
- !AGENT_INFRA_VERSION_PATTERN.test(metadata.agent_infra_version)) {
369
- return failResult("task-meta", `Invalid agent_infra_version: ${metadata.agent_infra_version}`);
367
+ if (currentTask && !isValidAgentInfraVersion(metadata.agent_infra_version)) {
368
+ return failResult("task-meta", `Invalid agent_infra_version: ${String(metadata.agent_infra_version ?? 'missing')}`);
370
369
  }
371
370
  const invalidDates = ["created_at", "updated_at", "completed_at", "blocked_at", "cancelled_at"]
372
371
  .filter((field) => !isBlank(metadata[field]) && !DATE_TIME_PATTERN.test(metadata[field]));
@@ -530,6 +529,21 @@ function checkArtifact({ taskDir, config, artifactFile }) {
530
529
  }
531
530
  return passResult("artifact", `${path.basename(artifactPath)} passed (${requiredSections.length} sections)`);
532
531
  }
532
+ function checkDecisionDetails({ taskDir, artifactFile }) {
533
+ const resolvedArtifact = resolveArtifactPath(taskDir, "", artifactFile);
534
+ if (!resolvedArtifact.ok)
535
+ return failResult("decision-details", resolvedArtifact.message);
536
+ const artifactPath = resolvedArtifact.path;
537
+ const stat = safeStat(artifactPath);
538
+ if (!stat?.isFile())
539
+ return failResult("decision-details", `Artifact not found: ${path.basename(artifactPath)}`);
540
+ if (stat.size === 0)
541
+ return passResult("decision-details", `${path.basename(artifactPath)} contains no decision details`);
542
+ const inspection = inspectDecisionDetailDuplicates(fs.readFileSync(artifactPath, "utf8"));
543
+ if (!inspection.ok)
544
+ return failResult("decision-details", `${path.basename(artifactPath)}: ${inspection.message}`);
545
+ return passResult("decision-details", `${path.basename(artifactPath)} has no duplicate decision detail ids`);
546
+ }
533
547
  function checkReviewSummary({ taskDir, config, artifactFile }) {
534
548
  const stage = String(config.stage || "");
535
549
  if (!["analysis", "plan", "code"].includes(stage)) {
@@ -550,7 +564,10 @@ function checkReviewSummary({ taskDir, config, artifactFile }) {
550
564
  return failResult("review-summary", task.message);
551
565
  let rows;
552
566
  try {
553
- rows = parseLedger(task.content);
567
+ const ledger = parseLedgerDocument(task.content);
568
+ if (!ledger.present)
569
+ return failResult("review-summary", `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`);
570
+ rows = ledger.rows;
554
571
  }
555
572
  catch (error) {
556
573
  return failResult("review-summary", `Invalid disagreement ledger: ${String(error)}`);
@@ -820,13 +837,12 @@ function checkReviewLedger({ taskDir, config }) {
820
837
  if (!task.ok) {
821
838
  return failResult("review-ledger", task.message);
822
839
  }
823
- const section = getSectionContent(task.content, LEDGER_SECTION_NAMES);
824
- if (!section.trim()) {
825
- return passResult("review-ledger", "No disagreement ledger section; treated as no open disagreements");
826
- }
827
840
  let rows;
828
841
  try {
829
- rows = parseLedger(task.content).map((row) => [
842
+ const ledger = parseLedgerDocument(task.content);
843
+ if (!ledger.present)
844
+ return failResult("review-ledger", `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`);
845
+ rows = ledger.rows.map((row) => [
830
846
  row.id, row.stage, row.round, row.severity, row.status, row.evidence
831
847
  ]);
832
848
  }
@@ -1071,7 +1087,11 @@ function formatPostReviewExemptions(exemptions) {
1071
1087
  }
1072
1088
  function resolvePostReviewExemption(content) {
1073
1089
  try {
1074
- const rows = parseLedger(content);
1090
+ const ledger = parseLedgerDocument(content);
1091
+ if (!ledger.present) {
1092
+ return { ok: false, code: "POST_REVIEW_EXEMPTION_INVALID", message: `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}` };
1093
+ }
1094
+ const rows = ledger.rows;
1075
1095
  const invalid = validateLedgerRows(rows);
1076
1096
  if (invalid) {
1077
1097
  return {
@@ -1,3 +1,3 @@
1
1
  export { extractReviewBaseline, extractReviewDiffBase, extractReviewDiffFingerprint, extractReviewedSnapshotTree, findAuthoritativeReviewCodeArtifact, loadPostReviewConfig, parseReviewVerdict, resolvePostReviewGlobs } from './review-fingerprint.js';
2
- export { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
2
+ export { parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
3
3
  //# sourceMappingURL=verification-review.js.map
@@ -26,12 +26,6 @@ function captureWorktreeTree(repoRoot, forcedPath) {
26
26
  fs.rmSync(tempDir, { recursive: true, force: true });
27
27
  }
28
28
  }
29
- function captureLegacyWorkspaceSnapshot(repoRoot, taskId) {
30
- const workspaceRelative = taskId
31
- ? `.agents/workspace/active/${taskId}`
32
- : '.agents/workspace/active';
33
- return captureWorktreeTree(repoRoot, workspaceRelative);
34
- }
35
29
  function taskFileFingerprints(stateRoot, taskId) {
36
30
  const activeRoot = path.join(stateRoot, '.agents', 'workspace', 'active');
37
31
  const roots = taskId ? [path.join(activeRoot, taskId)] : [activeRoot];
@@ -46,6 +40,8 @@ function taskFileFingerprints(stateRoot, taskId) {
46
40
  }
47
41
  if (!stat.isFile() && !stat.isSymbolicLink())
48
42
  return;
43
+ if (relative.startsWith('.agents/workspace/active/') && path.posix.basename(relative) === 'orchestration.json')
44
+ return;
49
45
  const content = stat.isSymbolicLink()
50
46
  ? Buffer.from(fs.readlinkSync(absolute))
51
47
  : fs.readFileSync(absolute);
@@ -81,13 +77,11 @@ function decodeWorkspaceFingerprint(value) {
81
77
  throw new Error(`ORCHESTRATION_SNAPSHOT_FAILED: ${error instanceof Error ? error.message : String(error)}`);
82
78
  }
83
79
  }
84
- function captureWorkspaceSnapshot(contextOrRoot, taskId) {
85
- if (typeof contextOrRoot === 'string')
86
- return captureLegacyWorkspaceSnapshot(contextOrRoot, taskId ?? null);
80
+ function captureWorkspaceSnapshot(context) {
87
81
  return encodeWorkspaceFingerprint({
88
82
  version: 2,
89
- gitTree: captureWorktreeTree(contextOrRoot.gitRoot, null),
90
- taskFiles: taskFileFingerprints(contextOrRoot.stateRoot, contextOrRoot.taskId)
83
+ gitTree: captureWorktreeTree(context.gitRoot, null),
84
+ taskFiles: taskFileFingerprints(context.stateRoot, context.taskId)
91
85
  });
92
86
  }
93
87
  function captureRepositorySnapshot(repoRoot) {
@@ -1,10 +1,11 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { VERSION } from '../version.js';
3
+ import { isValidAgentInfraVersion, VERSION } from '../version.js';
4
4
  import { parseTypedTaskFrontmatter, updateTaskFrontmatter } from './frontmatter.js';
5
5
  import { resolveTaskRef } from './resolve-ref.js';
6
6
  import { allowsManualOverride } from './guard-override.js';
7
7
  import { mutateTableRow, upsertSection } from './sections.js';
8
+ import { validateCurrentTaskContract } from './current-contract.js';
8
9
  const DEFAULT_FILE_SYSTEM = {
9
10
  readFileSync: (file) => fs.readFileSync(file, 'utf8'),
10
11
  statModeSync: (file) => fs.statSync(file).mode,
@@ -84,6 +85,12 @@ function writeTask(request, options = {}) {
84
85
  catch (error) {
85
86
  return failure(request, identity, 'TASK_READ_FAILED', error instanceof Error ? error.message : String(error));
86
87
  }
88
+ if (resolved.state === 'active') {
89
+ const contract = validateCurrentTaskContract(original);
90
+ if (!contract.ok) {
91
+ return failure(request, identity, contract.code, contract.message);
92
+ }
93
+ }
87
94
  let candidate = original;
88
95
  const operations = [];
89
96
  try {
@@ -154,6 +161,9 @@ function writeTask(request, options = {}) {
154
161
  catch (error) {
155
162
  return failure(request, identity, 'METADATA_CAPTURE_FAILED', error instanceof Error ? error.message : String(error), operations);
156
163
  }
164
+ if (resolved.state === 'active' && !isValidAgentInfraVersion(metadata.agentInfraVersion)) {
165
+ return failure(request, identity, 'TASK_CURRENT_CONTRACT_INVALID', `metadata agentInfraVersion must be a valid v-prefixed semver (received ${metadata.agentInfraVersion})`, operations, metadata);
166
+ }
157
167
  if (candidate === original) {
158
168
  return {
159
169
  status: 'no-op',
@@ -8,27 +8,9 @@ import { renderFile, copySkillDir } from './render.js';
8
8
  const defaults = JSON.parse(fs.readFileSync(new URL('./defaults.json', import.meta.url), 'utf8'));
9
9
  const CONFIG_DIR = '.agents';
10
10
  const CONFIG_PATH = path.join(CONFIG_DIR, '.airc.json');
11
- // One-time migration of the legacy project-level PR switch to the three-state
12
- // `prFlow` preference. `true` (the old default / "PR flow on") maps to the
13
- // strong constraint `required`; `false` maps to `disabled`. A missing or
14
- // already-migrated config is left untouched (idempotent). Returns the new
15
- // prFlow value when a migration happened, otherwise null.
16
- function migratePrFlow(config) {
17
- if (config.requiresPullRequest === true) {
18
- delete config.requiresPullRequest;
19
- config.prFlow = 'required';
20
- return 'required';
21
- }
22
- if (config.requiresPullRequest === false) {
23
- delete config.requiresPullRequest;
24
- config.prFlow = 'disabled';
25
- return 'disabled';
26
- }
27
- return null;
28
- }
29
- async function cmdUpdate() {
11
+ async function cmdSync() {
30
12
  console.log('');
31
- console.log(' ai update');
13
+ console.log(' ai sync');
32
14
  console.log(' ==================================');
33
15
  console.log('');
34
16
  // check config exists
@@ -61,7 +43,6 @@ async function cmdUpdate() {
61
43
  const sandboxAdded = !config.sandbox;
62
44
  const taskAdded = !config.task;
63
45
  const labelsAdded = !config.labels;
64
- const prFlowMigrated = migratePrFlow(config);
65
46
  if (platformAdded)
66
47
  config.platform = structuredClone(defaults.platform);
67
48
  if (sandboxAdded)
@@ -78,19 +59,13 @@ async function cmdUpdate() {
78
59
  platformType,
79
60
  language
80
61
  });
81
- info(`Updating seed files for: ${project}`);
62
+ info(`Syncing seed files for: ${project}`);
82
63
  console.log('');
83
64
  // update skill
84
65
  copySkillDir(path.join(templateDir, '.agents', 'skills', 'update-agent-infra'), path.join('.agents', 'skills', 'update-agent-infra'), replacements, language, platformType);
85
66
  ok('Updated .agents/skills/update-agent-infra/');
86
67
  renderFile(path.join(templateDir, '.agents', 'scripts', 'lib', 'agent-infra-package.js'), path.join('.agents', 'scripts', 'lib', 'agent-infra-package.js'), replacements);
87
68
  ok('Updated .agents/scripts/lib/agent-infra-package.js');
88
- try {
89
- fs.unlinkSync(path.join('.agents', 'skills', 'update-agent-infra', 'scripts', 'sync-templates.cjs'));
90
- }
91
- catch {
92
- // Ignore missing legacy script from pre-ESM installs.
93
- }
94
69
  const added = {
95
70
  managed: workflowPlan.projectAssets.registry.managed.filter((entry) => !currentRegistry.managed.includes(entry)),
96
71
  merged: workflowPlan.projectAssets.registry.merged.filter((entry) => !currentRegistry.merged.includes(entry))
@@ -110,7 +85,7 @@ async function cmdUpdate() {
110
85
  ok(` merged: ${entry}`);
111
86
  }
112
87
  }
113
- else if (platformAdded || sandboxAdded || taskAdded || labelsAdded || prFlowMigrated) {
88
+ else if (platformAdded || sandboxAdded || taskAdded || labelsAdded) {
114
89
  if (platformAdded) {
115
90
  info(`Default platform config added to ${CONFIG_PATH}.`);
116
91
  }
@@ -123,9 +98,6 @@ async function cmdUpdate() {
123
98
  if (labelsAdded) {
124
99
  info(`Default labels.in config added to ${CONFIG_PATH}.`);
125
100
  }
126
- if (prFlowMigrated) {
127
- info(`Migrated legacy requiresPullRequest to prFlow="${prFlowMigrated}" in ${CONFIG_PATH}.`);
128
- }
129
101
  }
130
102
  else {
131
103
  info(`File registry changed in ${CONFIG_PATH}.`);
@@ -142,14 +114,11 @@ async function cmdUpdate() {
142
114
  if (hasNewEntries && platformAdded) {
143
115
  info(`Default platform config added to ${CONFIG_PATH}.`);
144
116
  }
145
- if (hasNewEntries && prFlowMigrated) {
146
- info(`Migrated legacy requiresPullRequest to prFlow="${prFlowMigrated}" in ${CONFIG_PATH}.`);
147
- }
148
117
  ok(`Updated ${CONFIG_PATH}`);
149
118
  }
150
119
  // done
151
120
  console.log('');
152
- ok('Seed files updated successfully!');
121
+ ok('Seed files synced successfully!');
153
122
  console.log('');
154
123
  if (workflowPlan.nextSteps.length === 0) {
155
124
  console.log(' No Agent Client project integration enabled.');
@@ -165,5 +134,5 @@ async function cmdUpdate() {
165
134
  console.log('');
166
135
  }
167
136
  }
168
- export { cmdUpdate };
137
+ export { cmdSync };
169
138
  //# sourceMappingURL=update.js.map