@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
@@ -2,11 +2,12 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { randomUUID } from 'node:crypto';
4
4
 
5
- import { locateActivityLog, pairEntries } from './activity-log.ts';
5
+ import { locateActivityLog, pairEntries, startedBackedRows } from './activity-log.ts';
6
6
  import { parseArtifactName, validateCompletedArtifact } from './artifact-lifecycle.ts';
7
- import { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.ts';
7
+ import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.ts';
8
8
  import type { LedgerStageStatus, ReviewStage } from './ledger.ts';
9
9
  import { finalizeReviewSummaryContent } from './review-artifacts.ts';
10
+ import { inspectDecisionDetailDuplicates, repairDecisionDetailDuplicates } from './decision-details.ts';
10
11
  import { resolveTaskRef } from './resolve-ref.ts';
11
12
  import { validateLifecycleExecution } from './lifecycle-execution.ts';
12
13
  import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
@@ -25,6 +26,7 @@ type ReviewFinalizationErrorCode =
25
26
  | 'REVIEW_SUMMARY_NOT_FOUND'
26
27
  | 'REVIEW_SUMMARY_PLACEHOLDER_INVALID'
27
28
  | 'REVIEW_SUMMARY_COUNT_MISMATCH'
29
+ | 'REVIEW_DECISION_DETAIL_INVALID'
28
30
  | 'REVIEW_ARTIFACT_CONFLICT'
29
31
  | 'REVIEW_PROVENANCE_INVALID'
30
32
  | 'REVIEW_TEMP_WRITE_FAILED'
@@ -106,8 +108,8 @@ function openReviewRound(content: string, action: string, round: number): boolea
106
108
  const section = locateActivityLog(content);
107
109
  if (!section) return false;
108
110
  const expected = `${action} (Round ${round})`;
109
- return pairEntries(section.entries).filter((row) => (
110
- row.step === expected && Boolean(row.started) && !row.done
111
+ return startedBackedRows(pairEntries(section.entries)).filter((row) => (
112
+ row.step === expected && !row.done
111
113
  )).length === 1;
112
114
  }
113
115
 
@@ -194,7 +196,11 @@ function finalizeReviewSummaryUnlocked(
194
196
 
195
197
  let rows;
196
198
  try {
197
- rows = parseLedger(taskContent);
199
+ const ledger = parseLedgerDocument(taskContent);
200
+ if (!ledger.present) {
201
+ return failed(request, 'REVIEW_LEDGER_INVALID', `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`, resolved.taskId);
202
+ }
203
+ rows = ledger.rows;
198
204
  } catch (error) {
199
205
  return failed(request, 'REVIEW_LEDGER_INVALID', String(error), resolved.taskId);
200
206
  }
@@ -208,14 +214,34 @@ function finalizeReviewSummaryUnlocked(
208
214
  );
209
215
  }
210
216
  const stageStatus = summarizeLedgerStage(rows, stage);
217
+ const repaired = repairDecisionDetailDuplicates(artifactContent);
218
+ if (!repaired.ok) {
219
+ return failed(
220
+ request,
221
+ 'REVIEW_DECISION_DETAIL_INVALID',
222
+ `${repaired.code}: ${repaired.message}`,
223
+ resolved.taskId,
224
+ stageStatus
225
+ );
226
+ }
227
+ const detailInspection = inspectDecisionDetailDuplicates(repaired.content);
228
+ if (!detailInspection.ok) {
229
+ return failed(
230
+ request,
231
+ 'REVIEW_DECISION_DETAIL_INVALID',
232
+ `${detailInspection.code}: ${detailInspection.message}`,
233
+ resolved.taskId,
234
+ stageStatus
235
+ );
236
+ }
211
237
  const transformed = finalizeReviewSummaryContent(
212
- artifactContent,
238
+ repaired.content,
213
239
  stageStatus.unresolvedFindingCounts
214
240
  );
215
241
  if (!transformed.ok) {
216
242
  return failed(request, transformed.code, transformed.message, resolved.taskId, stageStatus);
217
243
  }
218
- if (!transformed.changed) {
244
+ if (!repaired.changed && !transformed.changed) {
219
245
  return {
220
246
  ...failed(request, 'REVIEW_ARTIFACT_CONFLICT', '', resolved.taskId, stageStatus),
221
247
  status: 'no-op',
@@ -22,9 +22,11 @@ import { resolveMaterializedReviewedHeadRelation } from "../platform/change-requ
22
22
  import { resolveReviewedHeadRelation } from "../platform/merged-pr-equivalence.ts";
23
23
  import { resolveLocalReviewedCommitRelation } from "../git/reviewed-commit-equivalence.ts";
24
24
  import { parseTypedTaskFrontmatter } from "./frontmatter.ts";
25
- import { parseLedger, summarizeLedgerStage, validateLedgerRows } from "./ledger.ts";
25
+ import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from "./ledger.ts";
26
26
  import type { LedgerRow } from "./ledger.ts";
27
+ import { isValidAgentInfraVersion } from "../version.ts";
27
28
  import { equalCounts, parseReviewSummary } from "./review-artifacts.ts";
29
+ import { inspectDecisionDetailDuplicates } from "./decision-details.ts";
28
30
  import { loadVerificationConfig } from "./verification-config.ts";
29
31
  import { snapshotReview } from "../git/review-snapshot.ts";
30
32
  import { OrchestrationStateError, readRun } from "./orchestration.ts";
@@ -48,7 +50,6 @@ const DEFAULT_REQUIRED_FIELDS = [
48
50
  ];
49
51
 
50
52
  const DATE_TIME_PATTERN = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:[+-]\d{2}:\d{2})?$/;
51
- const AGENT_INFRA_VERSION_PATTERN = /^v\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
52
53
  const ACTIVITY_LOG_PATTERN = /^- (\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:[+-]\d{2}:\d{2})?) — \*\*(.+?)\*\* by (.+?) — (.+)$/;
53
54
  // Start markers (action suffixed with ` [started]`) are excluded from the
54
55
  // "latest action" / freshness computation so a step's in-flight marker never
@@ -108,6 +109,8 @@ function runCheck(type: any, context: any, shared: any): any {
108
109
  return checkTaskMeta(context);
109
110
  case "artifact":
110
111
  return checkArtifact(context);
112
+ case "decision-details":
113
+ return checkDecisionDetails(context);
111
114
  case "implementation-input":
112
115
  return checkImplementationInput(context);
113
116
  case "activity-log":
@@ -341,9 +344,6 @@ function checkOrchestrationEvidence({ taskDir }: any): any {
341
344
  || (activatedCodex && (
342
345
  host?.kind !== 'codex-lifecycle-v2'
343
346
  || host.protocolVersion !== provenance.protocolVersion
344
- || host.packageVersion !== provenance.packageVersion
345
- || host.internalExecutableBuildHash !== provenance.internalExecutableBuildHash
346
- || host.lifecycleContractHash !== provenance.lifecycleContractHash
347
347
  || host.hookDefinitionHash !== provenance.hookDefinitionHash
348
348
  || host.hookSource !== provenance.hookSource
349
349
  || host.hookSourcePathDigest !== provenance.hookSourcePathDigest
@@ -395,23 +395,20 @@ function checkTaskMeta({ taskDir, config }: any): any {
395
395
  const metadata = task.metadata;
396
396
  const requiredFields = config.required_fields || DEFAULT_REQUIRED_FIELDS;
397
397
  const missingFields = requiredFields.filter((field: any) => isBlank(metadata[field]));
398
- const blockingMissingFields = missingFields.filter((field: any) => field !== "agent_infra_version");
398
+ const currentTask = metadata.status === 'active';
399
+ const blockingMissingFields = missingFields.filter((field: any) => field !== "agent_infra_version" || currentTask);
399
400
  const warnings = [];
400
- if (missingFields.includes("agent_infra_version")) {
401
+ if (missingFields.includes("agent_infra_version") && !currentTask) {
401
402
  warnings.push("field 'agent_infra_version' missing — historical task or skipped version stamp");
402
403
  }
403
404
  if (blockingMissingFields.length > 0) {
404
405
  return failResult("task-meta", `Missing required fields: ${blockingMissingFields.join(", ")}`);
405
406
  }
406
407
 
407
- if (
408
- !isBlank(metadata.agent_infra_version) &&
409
- metadata.agent_infra_version !== "unknown" &&
410
- !AGENT_INFRA_VERSION_PATTERN.test(metadata.agent_infra_version)
411
- ) {
408
+ if (currentTask && !isValidAgentInfraVersion(metadata.agent_infra_version)) {
412
409
  return failResult(
413
410
  "task-meta",
414
- `Invalid agent_infra_version: ${metadata.agent_infra_version}`
411
+ `Invalid agent_infra_version: ${String(metadata.agent_infra_version ?? 'missing')}`
415
412
  );
416
413
  }
417
414
 
@@ -625,6 +622,18 @@ function checkArtifact({ taskDir, config, artifactFile }: any): any {
625
622
  );
626
623
  }
627
624
 
625
+ function checkDecisionDetails({ taskDir, artifactFile }: any): any {
626
+ const resolvedArtifact = resolveArtifactPath(taskDir, "", artifactFile);
627
+ if (!resolvedArtifact.ok) return failResult("decision-details", resolvedArtifact.message);
628
+ const artifactPath = resolvedArtifact.path;
629
+ const stat = safeStat(artifactPath);
630
+ if (!stat?.isFile()) return failResult("decision-details", `Artifact not found: ${path.basename(artifactPath)}`);
631
+ if (stat.size === 0) return passResult("decision-details", `${path.basename(artifactPath)} contains no decision details`);
632
+ const inspection = inspectDecisionDetailDuplicates(fs.readFileSync(artifactPath, "utf8"));
633
+ if (!inspection.ok) return failResult("decision-details", `${path.basename(artifactPath)}: ${inspection.message}`);
634
+ return passResult("decision-details", `${path.basename(artifactPath)} has no duplicate decision detail ids`);
635
+ }
636
+
628
637
  function checkReviewSummary({ taskDir, config, artifactFile }: any): any {
629
638
  const stage = String(config.stage || "");
630
639
  if (!["analysis", "plan", "code"].includes(stage)) {
@@ -643,7 +652,9 @@ function checkReviewSummary({ taskDir, config, artifactFile }: any): any {
643
652
  if (!task.ok) return failResult("review-summary", task.message);
644
653
  let rows;
645
654
  try {
646
- rows = parseLedger(task.content);
655
+ const ledger = parseLedgerDocument(task.content);
656
+ if (!ledger.present) return failResult("review-summary", `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`);
657
+ rows = ledger.rows;
647
658
  } catch (error) {
648
659
  return failResult("review-summary", `Invalid disagreement ledger: ${String(error)}`);
649
660
  }
@@ -949,14 +960,11 @@ function checkReviewLedger({ taskDir, config }: any): any {
949
960
  return failResult("review-ledger", task.message);
950
961
  }
951
962
 
952
- const section = getSectionContent(task.content, LEDGER_SECTION_NAMES);
953
- if (!section.trim()) {
954
- return passResult("review-ledger", "No disagreement ledger section; treated as no open disagreements");
955
- }
956
-
957
963
  let rows;
958
964
  try {
959
- rows = parseLedger(task.content).map((row) => [
965
+ const ledger = parseLedgerDocument(task.content);
966
+ if (!ledger.present) return failResult("review-ledger", `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`);
967
+ rows = ledger.rows.map((row) => [
960
968
  row.id, row.stage, row.round, row.severity, row.status, row.evidence
961
969
  ]);
962
970
  } catch (error) {
@@ -1251,7 +1259,11 @@ function resolvePostReviewExemption(content: string):
1251
1259
  | { ok: true; exemptions: readonly LedgerRow[] }
1252
1260
  | { ok: false; code: "POST_REVIEW_EXEMPTION_INVALID"; message: string } {
1253
1261
  try {
1254
- const rows = parseLedger(content);
1262
+ const ledger = parseLedgerDocument(content);
1263
+ if (!ledger.present) {
1264
+ return { ok: false, code: "POST_REVIEW_EXEMPTION_INVALID", message: `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}` };
1265
+ }
1266
+ const rows = ledger.rows;
1255
1267
  const invalid = validateLedgerRows(rows);
1256
1268
  if (invalid) {
1257
1269
  return {
@@ -8,4 +8,4 @@ export {
8
8
  parseReviewVerdict,
9
9
  resolvePostReviewGlobs
10
10
  } from './review-fingerprint.ts';
11
- export { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.ts';
11
+ export { parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.ts';
@@ -53,13 +53,6 @@ function captureWorktreeTree(repoRoot: string, forcedPath: string | null): strin
53
53
  }
54
54
  }
55
55
 
56
- function captureLegacyWorkspaceSnapshot(repoRoot: string, taskId: string | null): string {
57
- const workspaceRelative = taskId
58
- ? `.agents/workspace/active/${taskId}`
59
- : '.agents/workspace/active';
60
- return captureWorktreeTree(repoRoot, workspaceRelative);
61
- }
62
-
63
56
  function taskFileFingerprints(stateRoot: string, taskId: string | null): TaskFileFingerprint[] {
64
57
  const activeRoot = path.join(stateRoot, '.agents', 'workspace', 'active');
65
58
  const roots = taskId ? [path.join(activeRoot, taskId)] : [activeRoot];
@@ -73,6 +66,7 @@ function taskFileFingerprints(stateRoot: string, taskId: string | null): TaskFil
73
66
  return;
74
67
  }
75
68
  if (!stat.isFile() && !stat.isSymbolicLink()) return;
69
+ if (relative.startsWith('.agents/workspace/active/') && path.posix.basename(relative) === 'orchestration.json') return;
76
70
  const content = stat.isSymbolicLink()
77
71
  ? Buffer.from(fs.readlinkSync(absolute))
78
72
  : fs.readFileSync(absolute);
@@ -108,14 +102,11 @@ function decodeWorkspaceFingerprint(value: string): OrchestrationWorkspaceFinger
108
102
  }
109
103
  }
110
104
 
111
- function captureWorkspaceSnapshot(context: WorkspaceSnapshotContext): string;
112
- function captureWorkspaceSnapshot(repoRoot: string, taskId: string | null): string;
113
- function captureWorkspaceSnapshot(contextOrRoot: WorkspaceSnapshotContext | string, taskId?: string | null): string {
114
- if (typeof contextOrRoot === 'string') return captureLegacyWorkspaceSnapshot(contextOrRoot, taskId ?? null);
105
+ function captureWorkspaceSnapshot(context: WorkspaceSnapshotContext): string {
115
106
  return encodeWorkspaceFingerprint({
116
107
  version: 2,
117
- gitTree: captureWorktreeTree(contextOrRoot.gitRoot, null),
118
- taskFiles: taskFileFingerprints(contextOrRoot.stateRoot, contextOrRoot.taskId)
108
+ gitTree: captureWorktreeTree(context.gitRoot, null),
109
+ taskFiles: taskFileFingerprints(context.stateRoot, context.taskId)
119
110
  });
120
111
  }
121
112
 
package/lib/task/write.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
 
4
- import { VERSION } from '../version.ts';
4
+ import { isValidAgentInfraVersion, VERSION } from '../version.ts';
5
5
  import {
6
6
  parseTypedTaskFrontmatter,
7
7
  updateTaskFrontmatter
@@ -15,6 +15,7 @@ import type {
15
15
  import { allowsManualOverride } from './guard-override.ts';
16
16
  import type { ManualOverrideCapability } from './guard-override.ts';
17
17
  import { mutateTableRow, upsertSection } from './sections.ts';
18
+ import { validateCurrentTaskContract } from './current-contract.ts';
18
19
  import type {
19
20
  TableRowDeleteMutation,
20
21
  TableRowUpsertMutation
@@ -84,6 +85,7 @@ type TaskWriteMetadata = {
84
85
  type TaskWriteErrorCode =
85
86
  | ResolveTaskRefErrorCode
86
87
  | 'TASK_STATE_MISMATCH'
88
+ | 'TASK_CURRENT_CONTRACT_INVALID'
87
89
  | 'TASK_READ_FAILED'
88
90
  | 'TASK_DOCUMENT_INVALID'
89
91
  | 'MUTATION_INVALID'
@@ -268,6 +270,13 @@ function writeTask(request: TaskWriteRequest, options: TaskWriteOptions = {}): T
268
270
  );
269
271
  }
270
272
 
273
+ if (resolved.state === 'active') {
274
+ const contract = validateCurrentTaskContract(original);
275
+ if (!contract.ok) {
276
+ return failure(request, identity, contract.code, contract.message);
277
+ }
278
+ }
279
+
271
280
  let candidate = original;
272
281
  const operations: TaskOperationSummary[] = [];
273
282
  try {
@@ -342,6 +351,16 @@ function writeTask(request: TaskWriteRequest, options: TaskWriteOptions = {}): T
342
351
  operations
343
352
  );
344
353
  }
354
+ if (resolved.state === 'active' && !isValidAgentInfraVersion(metadata.agentInfraVersion)) {
355
+ return failure(
356
+ request,
357
+ identity,
358
+ 'TASK_CURRENT_CONTRACT_INVALID',
359
+ `metadata agentInfraVersion must be a valid v-prefixed semver (received ${metadata.agentInfraVersion})`,
360
+ operations,
361
+ metadata
362
+ );
363
+ }
345
364
 
346
365
  if (candidate === original) {
347
366
  return {
package/lib/update.ts CHANGED
@@ -20,13 +20,11 @@ type UpdateConfig = {
20
20
  org: string;
21
21
  language: string;
22
22
  platform?: { type?: string };
23
- requiresPullRequest?: boolean; // legacy field; read-only, migrated to prFlow then removed
24
23
  prFlow?: 'required' | 'disabled';
25
24
  sandbox?: Record<string, unknown>;
26
25
  task?: { shortIdLength: number };
27
26
  labels?: Record<string, unknown>;
28
27
  files?: Partial<FileRegistry>;
29
- tuis?: unknown;
30
28
  agentClients?: unknown;
31
29
  customTUIs?: unknown;
32
30
  };
@@ -46,28 +44,9 @@ const defaults = JSON.parse(
46
44
  const CONFIG_DIR = '.agents';
47
45
  const CONFIG_PATH = path.join(CONFIG_DIR, '.airc.json');
48
46
 
49
- // One-time migration of the legacy project-level PR switch to the three-state
50
- // `prFlow` preference. `true` (the old default / "PR flow on") maps to the
51
- // strong constraint `required`; `false` maps to `disabled`. A missing or
52
- // already-migrated config is left untouched (idempotent). Returns the new
53
- // prFlow value when a migration happened, otherwise null.
54
- function migratePrFlow(config: UpdateConfig): 'required' | 'disabled' | null {
55
- if (config.requiresPullRequest === true) {
56
- delete config.requiresPullRequest;
57
- config.prFlow = 'required';
58
- return 'required';
59
- }
60
- if (config.requiresPullRequest === false) {
61
- delete config.requiresPullRequest;
62
- config.prFlow = 'disabled';
63
- return 'disabled';
64
- }
65
- return null;
66
- }
67
-
68
- async function cmdUpdate(): Promise<void> {
47
+ async function cmdSync(): Promise<void> {
69
48
  console.log('');
70
- console.log(' ai update');
49
+ console.log(' ai sync');
71
50
  console.log(' ==================================');
72
51
  console.log('');
73
52
 
@@ -104,7 +83,6 @@ async function cmdUpdate(): Promise<void> {
104
83
  const sandboxAdded = !config.sandbox;
105
84
  const taskAdded = !config.task;
106
85
  const labelsAdded = !config.labels;
107
- const prFlowMigrated = migratePrFlow(config);
108
86
  if (platformAdded) config.platform = structuredClone(defaults.platform);
109
87
  if (sandboxAdded) config.sandbox = structuredClone(defaults.sandbox);
110
88
  if (taskAdded) config.task = structuredClone(defaults.task);
@@ -119,7 +97,7 @@ async function cmdUpdate(): Promise<void> {
119
97
  language
120
98
  });
121
99
 
122
- info(`Updating seed files for: ${project}`);
100
+ info(`Syncing seed files for: ${project}`);
123
101
  console.log('');
124
102
 
125
103
  // update skill
@@ -137,12 +115,6 @@ async function cmdUpdate(): Promise<void> {
137
115
  replacements
138
116
  );
139
117
  ok('Updated .agents/scripts/lib/agent-infra-package.js');
140
- try {
141
- fs.unlinkSync(path.join('.agents', 'skills', 'update-agent-infra', 'scripts', 'sync-templates.cjs'));
142
- } catch {
143
- // Ignore missing legacy script from pre-ESM installs.
144
- }
145
-
146
118
  const added = {
147
119
  managed: workflowPlan.projectAssets.registry.managed.filter(
148
120
  (entry) => !currentRegistry.managed.includes(entry)
@@ -165,7 +137,7 @@ async function cmdUpdate(): Promise<void> {
165
137
  for (const entry of added.merged) {
166
138
  ok(` merged: ${entry}`);
167
139
  }
168
- } else if (platformAdded || sandboxAdded || taskAdded || labelsAdded || prFlowMigrated) {
140
+ } else if (platformAdded || sandboxAdded || taskAdded || labelsAdded) {
169
141
  if (platformAdded) {
170
142
  info(`Default platform config added to ${CONFIG_PATH}.`);
171
143
  }
@@ -178,9 +150,6 @@ async function cmdUpdate(): Promise<void> {
178
150
  if (labelsAdded) {
179
151
  info(`Default labels.in config added to ${CONFIG_PATH}.`);
180
152
  }
181
- if (prFlowMigrated) {
182
- info(`Migrated legacy requiresPullRequest to prFlow="${prFlowMigrated}" in ${CONFIG_PATH}.`);
183
- }
184
153
  } else {
185
154
  info(`File registry changed in ${CONFIG_PATH}.`);
186
155
  }
@@ -196,15 +165,12 @@ async function cmdUpdate(): Promise<void> {
196
165
  if (hasNewEntries && platformAdded) {
197
166
  info(`Default platform config added to ${CONFIG_PATH}.`);
198
167
  }
199
- if (hasNewEntries && prFlowMigrated) {
200
- info(`Migrated legacy requiresPullRequest to prFlow="${prFlowMigrated}" in ${CONFIG_PATH}.`);
201
- }
202
168
  ok(`Updated ${CONFIG_PATH}`);
203
169
  }
204
170
 
205
171
  // done
206
172
  console.log('');
207
- ok('Seed files updated successfully!');
173
+ ok('Seed files synced successfully!');
208
174
  console.log('');
209
175
  if (workflowPlan.nextSteps.length === 0) {
210
176
  console.log(' No Agent Client project integration enabled.');
@@ -220,4 +186,4 @@ async function cmdUpdate(): Promise<void> {
220
186
  }
221
187
  }
222
188
 
223
- export { cmdUpdate };
189
+ export { cmdSync };
package/lib/version.ts CHANGED
@@ -11,5 +11,10 @@ if (!packageJsonUrl) {
11
11
 
12
12
  const { version } = JSON.parse(readFileSync(packageJsonUrl, 'utf8'));
13
13
  const VERSION = `v${version}`;
14
+ const AGENT_INFRA_VERSION_PATTERN = /^v\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
14
15
 
15
- export { VERSION };
16
+ function isValidAgentInfraVersion(value: unknown): value is string {
17
+ return typeof value === 'string' && AGENT_INFRA_VERSION_PATTERN.test(value);
18
+ }
19
+
20
+ export { VERSION, isValidAgentInfraVersion };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fitlab-ai/agent-infra",
3
- "version": "0.9.9",
3
+ "version": "0.9.11",
4
4
  "description": "Bootstrap tool for AI multi-tool collaboration infrastructure — works with Claude Code, Codex, Antigravity CLI, and OpenCode",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -27,11 +27,10 @@ npm install
27
27
  npm run build
28
28
  ```
29
29
 
30
- Lifecycle hooks always invoke `agent-infra-internal` through `PATH`; they do not implicitly switch to checkout source or `dist`. In a Codex sandbox, the controller's `PATH`-first shim is authoritative and binds the controller's selected executable. Skills, rules, hooks, and task data continue to come from the current checkout. The commands above develop the CLI itself and do not change hook executable selection. If shell commands must also resolve to this checkout, link it and compare the active version with the package version:
30
+ Lifecycle hooks always invoke `agent-infra-internal` through `PATH`; they do not implicitly switch to checkout source or `dist`. In a Codex sandbox, the controller's `PATH`-first shim is authoritative and binds the controller's selected executable. Skills, rules, hooks, and task data continue to come from the current checkout. The commands above develop the CLI itself and do not change hook executable selection. To run the CLI from the current checkout, invoke the build output directly and compare its version with the package version. Do not run `npm link` on the host: it writes to npm's global prefix and can shadow a stable installation managed by Homebrew or npm.
31
31
 
32
32
  ```bash
33
- npm link
34
- ai version --raw
33
+ node ./dist/bin/cli.js version --raw
35
34
  node -p "'v' + require('./package.json').version"
36
35
  ```
37
36
 
@@ -27,11 +27,10 @@ npm install
27
27
  npm run build
28
28
  ```
29
29
 
30
- 生命周期 hook 始终通过 `PATH` 调用 `agent-infra-internal`,不会隐式切换到当前检出的 source 或 `dist`。在 Codex sandbox 中,controller 放在 `PATH` 首位的 shim 是权威入口,并绑定 controller 选定的 executable。skills、rules、hooks 和任务数据仍从当前检出读取。上面的命令只用于开发 CLI 本身,不会改变 hook 的 executable 选择。如果 shell 命令也必须解析到当前检出,请建立链接并比较当前版本与包版本:
30
+ 生命周期 hook 始终通过 `PATH` 调用 `agent-infra-internal`,不会隐式切换到当前检出的 source 或 `dist`。在 Codex sandbox 中,controller 放在 `PATH` 首位的 shim 是权威入口,并绑定 controller 选定的 executable。skills、rules、hooks 和任务数据仍从当前检出读取。上面的命令只用于开发 CLI 本身,不会改变 hook 的 executable 选择。如需运行当前检出的 CLI,请直接执行构建产物并比较当前版本与包版本。不要在宿主机上执行 `npm link`:它会写入 npm 全局前缀,并可能遮蔽由 Homebrew 或 npm 安装的稳定版。
31
31
 
32
32
  ```bash
33
- npm link
34
- ai version --raw
33
+ node ./dist/bin/cli.js version --raw
35
34
  node -p "'v' + require('./package.json').version"
36
35
  ```
37
36
 
@@ -124,7 +124,7 @@ To adapt agent-infra to a private code-hosting platform:
124
124
 
125
125
  1. Set `.agents/.airc.json` `platform.type` to a stable identifier such as `my-platform`.
126
126
  2. Copy the generated rule files in `.agents/rules/` and adapt them to your platform's CLI or API while keeping the runtime filenames unchanged.
127
- 3. Add the customized rule files to `.agents/.airc.json` `files.ejected` so future `agent-infra update` runs do not overwrite them.
127
+ 3. Add the customized rule files to `.agents/.airc.json` `files.ejected` so future `ai sync` runs do not overwrite them.
128
128
  4. If you maintain a fork of the template source, add matching `.{platform}.` template variants before adding that platform identifier to the sync logic.
129
129
  5. Validate the customized workflow on a test task before rolling it out broadly.
130
130
 
@@ -227,7 +227,7 @@ Ordinary `managed` files keep their overwrite semantics. A small set of platform
227
227
 
228
228
  ## Agent Client Configuration
229
229
 
230
- Use the top-level `.agents/.airc.json` `agentClients` array to configure all four built-in clients (`claude-code`, `codex`, `antigravity-cli`, `opencode`). Each canonical entry has three required fields and one optional orchestration policy:
230
+ Use the top-level `.agents/.airc.json` `agentClients` array to configure all five built-in clients (`claude-code`, `codex`, `antigravity-cli`, `opencode`, `traecli`). Each canonical entry has three required fields and one optional orchestration policy:
231
231
 
232
232
  | Field | Meaning |
233
233
  |-------|---------|
@@ -250,7 +250,9 @@ ai agent-client configure
250
250
 
251
251
  `enable` and `disable` only change `enabled`; `configure` edits both dimensions. `ai init` asks which clients to enable and preserves the displayed defaults when you press Enter. All clients default to `installInSandbox: true`.
252
252
 
253
- Legacy `tuis` and built-in client ids in `sandbox.tools` are migration inputs only. The next config-writing command converts them into the canonical `agentClients` array and removes the legacy fields. If canonical and legacy values conflict, the command stops without modifying files so the conflict can be resolved explicitly.
253
+ TraeCode CLI uses `.agents/skills/` as the canonical Skill source. agent-infra generates lightweight slash-command wrappers under `.traecli/commands/`; each wrapper reads the corresponding shared Skill and forwards `$ARGUMENTS` when that Skill declares arguments. It does not mirror Skill packages into `.trae/skills/`, which remains user-owned for intentional Trae-specific overrides.
254
+
255
+ `agentClients` is the sole source of truth for built-in client state. The top-level array must contain all five built-in clients in the fixed canonical order; `sandbox.tools` lists only non-client tools such as `agent-infra` and custom tools. A legacy `tuis` field or a built-in client id in `sandbox.tools` is rejected, and configuration is never rewritten automatically.
254
256
 
255
257
  ### Side effects of disabling an Agent Client
256
258
 
@@ -122,7 +122,7 @@
122
122
 
123
123
  1. 在 `.agents/.airc.json` 中把 `platform.type` 设为稳定标识,例如 `my-platform`。
124
124
  2. 以 `.agents/rules/` 下已生成的规则文件为起点,改写为你的平台 CLI 或 API 调用,同时保持运行时文件名不变。
125
- 3. 将这些自定义规则文件加入 `.agents/.airc.json` 的 `files.ejected`,避免后续执行 `agent-infra update` 时被覆盖。
125
+ 3. 将这些自定义规则文件加入 `.agents/.airc.json` 的 `files.ejected`,避免后续执行 `ai sync` 时被覆盖。
126
126
  4. 如果你维护的是模板源码分支或私有 fork,需要先补齐对应的 `.{platform}.` 模板变体,再把该平台标识加入模板同步逻辑。
127
127
  5. 在正式推广前,先用一个测试任务完整验证工作流和 gate 校验。
128
128
 
@@ -225,7 +225,7 @@ disable-model-invocation: true # 可选;由支持该能力的 TUI 适配器
225
225
 
226
226
  ## Agent Client 配置
227
227
 
228
- `.agents/.airc.json` 顶层 `agentClients` 数组用于配置全部四个内建客户端(`claude-code`、`codex`、`antigravity-cli`、`opencode`)。每个规范条目包含三个必填字段和一个可选编排策略:
228
+ `.agents/.airc.json` 顶层 `agentClients` 数组用于配置全部五个内建客户端(`claude-code`、`codex`、`antigravity-cli`、`opencode`、`traecli`)。每个规范条目包含三个必填字段和一个可选编排策略:
229
229
 
230
230
  | 字段 | 含义 |
231
231
  |------|------|
@@ -248,7 +248,9 @@ ai agent-client configure
248
248
 
249
249
  `enable` 和 `disable` 只修改 `enabled`;`configure` 同时编辑两个维度。`ai init` 会询问启用哪些客户端,直接回车会保留界面展示的默认值。所有客户端的 `installInSandbox` 默认均为 `true`。
250
250
 
251
- 旧 `tuis` 字段和 `sandbox.tools` 中的内建客户端 id 仅作为迁移输入。下一次写配置的命令会将其转换为规范 `agentClients` 数组并移除旧字段。若规范配置与旧配置冲突,命令会在修改文件前停止,要求显式解决冲突。
251
+ TraeCode CLI 以 `.agents/skills/` 作为 Skill 的唯一权威源。agent-infra 仅在 `.traecli/commands/` 下生成轻量 slash-command 包装文件;每个包装文件读取对应的共享 Skill,并在该 Skill 声明参数时转发 `$ARGUMENTS`。它不会把 Skill 包镜像到 `.trae/skills/`;该目录继续由用户所有,仅用于有意添加的 Trae 专属覆盖。
252
+
253
+ `agentClients` 是内建客户端状态的唯一配置来源。顶层数组必须按固定规范顺序包含全部五个内建客户端;`sandbox.tools` 只列出 `agent-infra` 和自定义工具等非客户端工具。旧 `tuis` 字段或 `sandbox.tools` 中的内建客户端 id 会被拒绝,配置不会被自动改写。
252
254
 
253
255
  ### 取消 Agent Client 的副作用
254
256
 
@@ -12,6 +12,7 @@ so you can quickly find "which ones to read" without opening each file.
12
12
  - [`next-step-output.md`](next-step-output.md) — "Next step" output rules: task short-id rendering and the `Completed at` trailer.
13
13
  - [`version-stamp.md`](version-stamp.md) — How and when to stamp `agent_infra_version`.
14
14
  - [`debugging-guide.md`](debugging-guide.md) — Structured debugging flow: gather evidence → form hypothesis → verify hypothesis → fix the root cause; no blind patch-and-retry.
15
+ - [`compatibility-policy.md`](compatibility-policy.md) — Compatibility by exception: admission evidence, implementation boundaries, exit conditions, and lifecycle review requirements.
15
16
 
16
17
  ## Issue / PR
17
18
 
@@ -11,6 +11,7 @@
11
11
  - [`next-step-output.md`](next-step-output.md) — 「下一步」输出规则:任务短号渲染与 `Completed at` 收尾行。
12
12
  - [`version-stamp.md`](version-stamp.md) — `agent_infra_version` 版本戳的取值命令与写入时机。
13
13
  - [`debugging-guide.md`](debugging-guide.md) — 结构化调试流程:收集证据→形成假设→验证假设→修复根因,禁止盲目改代码重试。
14
+ - [`compatibility-policy.md`](compatibility-policy.md) — 兼容性默认关闭:准入证据、实现边界、退出条件与生命周期审查要求。
14
15
 
15
16
  ## Issue / PR
16
17
 
@@ -0,0 +1,40 @@
1
+ # Compatibility Policy
2
+
3
+ This rule governs compatibility decisions during analysis, planning, implementation, and review. Support the current contract by default. Compatibility is an exception budget that requires evidence and an exit condition; it is not a synonym for safety.
4
+
5
+ ## What Counts as Compatibility
6
+
7
+ Compatibility logic includes accepting or writing multiple generations of schemas, fields, states, or commands; retaining legacy aliases, adapters, wrappers, or shims; migrating data in the normal runtime path; falling back to different semantics based on an old format; or maintaining parallel facts and tests for old callers.
8
+
9
+ Current-operation idempotency and crash recovery, explicit platform or permission capability branches, fail-closed handling of unknown state, and user-required public multi-version contracts are not inherently legacy compatibility. Do not use those labels to expand support for an old protocol.
10
+
11
+ ## Admission Gate
12
+
13
+ Add or extend compatibility only when the task or an existing public contract explicitly requires it and the artifact records all four items:
14
+
15
+ 1. **Subject**: concrete consumers, released versions, or stored data still using the old contract.
16
+ 2. **Necessity**: why an atomic cutover, one-time conversion, or actionable failure is insufficient.
17
+ 3. **Window**: a version, date, or verifiable endpoint for the compatibility period.
18
+ 4. **Exit**: removal conditions and the code, tests, and documentation to delete.
19
+
20
+ If any item is missing, compatibility is unauthorized scope expansion. Implement the current contract, fail closed with an actionable repair message for old input, or perform an explicit one-time conversion outside the main path.
21
+
22
+ ## Implementation Boundaries
23
+
24
+ - Keep one canonical write format and one source of truth. Do not dual-write, maintain shadow state, or try the new path before silently falling back to the old path.
25
+ - Concentrate an approved conversion at one boundary and immediately enter the current model; business logic must not understand several historical versions.
26
+ - Mark temporarily retained compatibility at its single boundary with `TODO(compat): Remove ... once ...`, naming the removal target and a verifiable removal condition. The TODO stays co-located with the code; tasks and issues are optional scheduling aids, not compatibility facts or workflow gates.
27
+ - Use warnings only when the primary action remains correct and the user can retry an ancillary operation. Never hide uncertainty about identity, authorization, data interpretation, or side-effect correctness.
28
+ - When removing an old capability, remove its adapters, branches, fixtures, tests, and migration documentation together. Do not add negative tests that permanently remember deleted behavior.
29
+ - Never guess the meaning of unknown state. Preserve it, stop writes, and provide rebuild or manual-repair guidance.
30
+
31
+ ## Lifecycle Requirements
32
+
33
+ - **Analysis**: separate explicit compatibility requirements from model speculation and identify real consumers or stored data. State current-only when evidence is absent.
34
+ - **Plan**: for approved compatibility, identify the boundary, single conversion point, window, and deletion plan. Do not present a compatibility layer as the default low-risk option.
35
+ - **Implementation**: implement only the approved compatibility budget, and add or move its `TODO(compat)` at the single compatibility boundary. Do not preserve an unapproved old branch “just in case.”
36
+ - **Review**: verify the four admission items, single source of truth, exit condition, and co-located `TODO(compat)`. Flag unauthorized compatibility, indefinite shims, dual writes, and runtime-path migrations as scope and maintainability problems.
37
+
38
+ ## Existing Compatibility Logic
39
+
40
+ During an ordinary task, when out-of-scope compatibility is found, only ensure that its single boundary has one `TODO(compat)` and record the location and risk. Do not delete the logic or duplicate the marker. A dedicated simplification task should first search those TODOs and inventory consumers and stored data, then process items in this order: remove now, warn then remove, retain temporarily.