@fitlab-ai/agent-infra 0.12.0 → 0.12.2

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 (331) hide show
  1. package/README.md +0 -2
  2. package/README.zh-CN.md +0 -2
  3. package/dist/bin/internal-cli.js +2 -17
  4. package/dist/lib/agent-clients/adapter.js +0 -13
  5. package/dist/lib/agent-clients/adapters/antigravity-cli.js +0 -5
  6. package/dist/lib/agent-clients/adapters/claude-code-credentials.js +0 -17
  7. package/dist/lib/agent-clients/adapters/claude-code.js +0 -9
  8. package/dist/lib/agent-clients/adapters/codex.js +0 -52
  9. package/dist/lib/agent-clients/adapters/opencode.js +0 -5
  10. package/dist/lib/agent-clients/adapters/traecli.js +0 -5
  11. package/dist/lib/agent-clients/config.js +19 -54
  12. package/dist/lib/agent-clients/registry.js +1 -12
  13. package/dist/lib/agent-clients/schema.js +0 -18
  14. package/dist/lib/agent-clients/tokens.js +1 -2
  15. package/dist/lib/agent-clients/types.js +0 -1
  16. package/dist/lib/decide.js +2 -0
  17. package/dist/lib/git/worktree-identity.js +1 -9
  18. package/dist/lib/internal/agent-client.js +2 -62
  19. package/dist/lib/internal/cli-route-inventory.js +1 -5
  20. package/dist/lib/internal/manual-validation.js +29 -27
  21. package/dist/lib/internal/platform-comment.js +56 -7
  22. package/dist/lib/internal/task-create.js +1 -1
  23. package/dist/lib/internal/task-event.js +1 -8
  24. package/dist/lib/internal/task-finalization.js +0 -1
  25. package/dist/lib/internal/task-lifecycle.js +2 -2
  26. package/dist/lib/internal/task-operation-registry.js +4 -20
  27. package/dist/lib/internal/task-review.js +4 -4
  28. package/dist/lib/platform/comment-safety.js +1 -7
  29. package/dist/lib/platform/in-label-sync.js +1 -14
  30. package/dist/lib/platform/issue-comments.js +373 -282
  31. package/dist/lib/platform/issue-summary-content.js +112 -0
  32. package/dist/lib/platform/issues.js +1 -1
  33. package/dist/lib/platform/operation-coordinator.js +4 -2
  34. package/dist/lib/platform/pr-change-report.js +176 -30
  35. package/dist/lib/platform/pr-checks.js +2 -8
  36. package/dist/lib/platform/pr-summary-content.js +54 -0
  37. package/dist/lib/platform/pr-summary.js +318 -284
  38. package/dist/lib/platform/provider-validation.js +0 -20
  39. package/dist/lib/platform/pull-requests.js +73 -84
  40. package/dist/lib/platform/releases.js +1 -43
  41. package/dist/lib/platform/summary-content-pipeline.js +18 -0
  42. package/dist/lib/platform/verification-required.js +0 -2
  43. package/dist/lib/platform/verification-sync.js +1 -79
  44. package/dist/lib/process-data/normalize.js +1 -185
  45. package/dist/lib/process-data/privacy.js +1 -16
  46. package/dist/lib/process-data/sources.js +2 -279
  47. package/dist/lib/render.js +0 -12
  48. package/dist/lib/run/index.js +15 -10
  49. package/dist/lib/run/skills.js +0 -3
  50. package/dist/lib/sandbox/commands/create.js +40 -53
  51. package/dist/lib/sandbox/commands/list-running.js +0 -18
  52. package/dist/lib/sandbox/commands/start.js +7 -8
  53. package/dist/lib/sandbox/constants.js +0 -6
  54. package/dist/lib/sandbox/control/client.js +0 -1
  55. package/dist/lib/sandbox/control/container-identity.js +1 -16
  56. package/dist/lib/sandbox/control/executor.js +0 -4
  57. package/dist/lib/sandbox/control/identity-sentinel.js +0 -7
  58. package/dist/lib/sandbox/control/lifecycle.js +21 -37
  59. package/dist/lib/sandbox/control/native-file-lock.js +0 -3
  60. package/dist/lib/sandbox/control/server.js +18 -38
  61. package/dist/lib/sandbox/control/state.js +1 -9
  62. package/dist/lib/sandbox/dockerfile.js +0 -6
  63. package/dist/lib/sandbox/engine.js +0 -14
  64. package/dist/lib/sandbox/engines/docker-exact-discovery.js +1 -4
  65. package/dist/lib/sandbox/engines/wsl2-paths.js +0 -3
  66. package/dist/lib/sandbox/recovery.js +24 -26
  67. package/dist/lib/sandbox/removal.js +67 -518
  68. package/dist/lib/sandbox/task-resolver.js +0 -41
  69. package/dist/lib/sandbox/tools.js +1 -4
  70. package/dist/lib/sandbox/workspace-view.js +1 -2
  71. package/dist/lib/server/display.js +0 -6
  72. package/dist/lib/task/activity-intent.js +45 -40
  73. package/dist/lib/task/activity-log.js +9 -16
  74. package/dist/lib/task/artifact-command.js +1 -1
  75. package/dist/lib/task/artifact-lifecycle.js +61 -63
  76. package/dist/lib/task/artifact-name.js +0 -6
  77. package/dist/lib/task/artifact-operations.js +3 -3
  78. package/dist/lib/task/capabilities.js +4 -5
  79. package/dist/lib/task/commands/log.js +0 -6
  80. package/dist/lib/task/commands/status.js +17 -60
  81. package/dist/lib/task/commit-operation.js +28 -39
  82. package/dist/lib/task/completion-facts.js +56 -43
  83. package/dist/lib/task/control-authority.js +23 -256
  84. package/dist/lib/task/control-recovery.js +3 -5
  85. package/dist/lib/task/create.js +2 -4
  86. package/dist/lib/task/decision-intents.js +8 -9
  87. package/dist/lib/task/delivery-target.js +1 -40
  88. package/dist/lib/task/events.js +84 -107
  89. package/dist/lib/task/finalization.js +60 -15
  90. package/dist/lib/task/ledger-intents.js +1 -1
  91. package/dist/lib/task/lifecycle-execution.js +38 -33
  92. package/dist/lib/task/lifecycle.js +49 -41
  93. package/dist/lib/task/local-artifact-finalization.js +1 -4
  94. package/dist/lib/task/manual-validation-evidence.js +1 -37
  95. package/dist/lib/task/platform-operation-recovery.js +151 -108
  96. package/dist/lib/task/pr-delivery-fact.js +2 -5
  97. package/dist/lib/task/qualification-confirmation.js +5 -7
  98. package/dist/lib/task/resolve-ref.js +1 -25
  99. package/dist/lib/task/review-command.js +2 -2
  100. package/dist/lib/task/review-finalization.js +1 -21
  101. package/dist/lib/task/review-fingerprint.js +0 -4
  102. package/dist/lib/task/secure-fs.js +0 -56
  103. package/dist/lib/task/short-id.js +1 -39
  104. package/dist/lib/task/skill-scope.js +0 -1
  105. package/dist/lib/task/verification-engine.js +58 -282
  106. package/dist/lib/task/verification.js +6 -3
  107. package/dist/lib/task/write.js +11 -1
  108. package/lib/agent-clients/adapter.ts +0 -76
  109. package/lib/agent-clients/adapters/antigravity-cli.ts +0 -5
  110. package/lib/agent-clients/adapters/claude-code-credentials.ts +0 -19
  111. package/lib/agent-clients/adapters/claude-code.ts +0 -9
  112. package/lib/agent-clients/adapters/codex.ts +0 -56
  113. package/lib/agent-clients/adapters/opencode.ts +0 -5
  114. package/lib/agent-clients/adapters/traecli.ts +0 -5
  115. package/lib/agent-clients/config.ts +20 -61
  116. package/lib/agent-clients/registry.ts +1 -34
  117. package/lib/agent-clients/schema.ts +0 -18
  118. package/lib/agent-clients/tokens.ts +1 -2
  119. package/lib/agent-clients/types.ts +0 -15
  120. package/lib/decide.ts +1 -0
  121. package/lib/git/worktree-identity.ts +0 -10
  122. package/lib/internal/agent-client.ts +2 -70
  123. package/lib/internal/cli-route-inventory.ts +1 -4
  124. package/lib/internal/manual-validation.ts +28 -27
  125. package/lib/internal/platform-comment.ts +46 -4
  126. package/lib/internal/task-create.ts +0 -1
  127. package/lib/internal/task-event.ts +1 -5
  128. package/lib/internal/task-finalization.ts +0 -1
  129. package/lib/internal/task-lifecycle.ts +3 -5
  130. package/lib/internal/task-operation-registry.ts +3 -19
  131. package/lib/internal/task-review.ts +4 -4
  132. package/lib/platform/comment-safety.ts +0 -10
  133. package/lib/platform/github-provider.ts +0 -2
  134. package/lib/platform/in-label-sync.ts +0 -16
  135. package/lib/platform/issue-comments.ts +478 -301
  136. package/lib/platform/issue-summary-content.ts +132 -0
  137. package/lib/platform/issues.ts +0 -2
  138. package/lib/platform/operation-coordinator.ts +4 -2
  139. package/lib/platform/pr-change-report.ts +165 -25
  140. package/lib/platform/pr-checks.ts +1 -10
  141. package/lib/platform/pr-review.ts +1 -1
  142. package/lib/platform/pr-summary-content.ts +60 -0
  143. package/lib/platform/pr-summary.ts +364 -257
  144. package/lib/platform/provider-bridge.ts +0 -1
  145. package/lib/platform/provider-loader.ts +1 -5
  146. package/lib/platform/provider-validation.ts +1 -49
  147. package/lib/platform/pull-requests.ts +64 -99
  148. package/lib/platform/releases.ts +1 -40
  149. package/lib/platform/summary-content-pipeline.ts +23 -0
  150. package/lib/platform/verification-required.ts +0 -2
  151. package/lib/platform/verification-sync.ts +0 -94
  152. package/lib/process-data/normalize.ts +1 -186
  153. package/lib/process-data/privacy.ts +2 -19
  154. package/lib/process-data/sources.ts +3 -283
  155. package/lib/render.ts +0 -15
  156. package/lib/run/index.ts +13 -7
  157. package/lib/run/skills.ts +0 -4
  158. package/lib/sandbox/commands/create.ts +37 -74
  159. package/lib/sandbox/commands/list-running.ts +0 -19
  160. package/lib/sandbox/commands/start.ts +7 -10
  161. package/lib/sandbox/constants.ts +0 -8
  162. package/lib/sandbox/control/client.ts +0 -1
  163. package/lib/sandbox/control/container-identity.ts +1 -16
  164. package/lib/sandbox/control/executor.ts +1 -5
  165. package/lib/sandbox/control/identity-sentinel.ts +0 -8
  166. package/lib/sandbox/control/lifecycle.ts +18 -51
  167. package/lib/sandbox/control/native-file-lock.ts +0 -8
  168. package/lib/sandbox/control/protocol.ts +0 -2
  169. package/lib/sandbox/control/server.ts +13 -39
  170. package/lib/sandbox/control/state.ts +1 -10
  171. package/lib/sandbox/dockerfile.ts +0 -11
  172. package/lib/sandbox/engine.ts +0 -17
  173. package/lib/sandbox/engines/docker-exact-discovery.ts +1 -5
  174. package/lib/sandbox/engines/wsl2-paths.ts +0 -4
  175. package/lib/sandbox/recovery.ts +24 -26
  176. package/lib/sandbox/removal.ts +58 -687
  177. package/lib/sandbox/shell.ts +1 -1
  178. package/lib/sandbox/task-resolver.ts +0 -48
  179. package/lib/sandbox/tools.ts +1 -5
  180. package/lib/sandbox/workspace-view.ts +2 -4
  181. package/lib/server/display.ts +0 -14
  182. package/lib/server/process-control.ts +1 -1
  183. package/lib/task/activity-intent.ts +36 -44
  184. package/lib/task/activity-log.ts +9 -22
  185. package/lib/task/artifact-command.ts +1 -1
  186. package/lib/task/artifact-lifecycle.ts +58 -67
  187. package/lib/task/artifact-name.ts +0 -7
  188. package/lib/task/artifact-operations.ts +3 -3
  189. package/lib/task/capabilities.ts +5 -6
  190. package/lib/task/commands/log.ts +0 -7
  191. package/lib/task/commands/status.ts +14 -79
  192. package/lib/task/commit-operation.ts +29 -51
  193. package/lib/task/completion-facts.ts +45 -42
  194. package/lib/task/control-authority.ts +27 -317
  195. package/lib/task/control-recovery.ts +2 -5
  196. package/lib/task/create.ts +2 -5
  197. package/lib/task/decision-intents.ts +5 -5
  198. package/lib/task/delivery-target.ts +0 -40
  199. package/lib/task/events.ts +89 -123
  200. package/lib/task/finalization.ts +52 -15
  201. package/lib/task/ledger-intents.ts +1 -1
  202. package/lib/task/lifecycle-execution.ts +37 -89
  203. package/lib/task/lifecycle.ts +48 -44
  204. package/lib/task/local-artifact-finalization.ts +0 -7
  205. package/lib/task/manual-validation-evidence.ts +1 -47
  206. package/lib/task/platform-operation-recovery.ts +117 -104
  207. package/lib/task/pr-delivery-fact.ts +2 -11
  208. package/lib/task/qualification-confirmation.ts +6 -6
  209. package/lib/task/resolve-ref.ts +0 -23
  210. package/lib/task/review-command.ts +2 -2
  211. package/lib/task/review-finalization.ts +1 -27
  212. package/lib/task/review-fingerprint.ts +0 -8
  213. package/lib/task/secure-fs.ts +0 -42
  214. package/lib/task/short-id.ts +0 -43
  215. package/lib/task/skill-scope.ts +0 -1
  216. package/lib/task/verification-engine.ts +56 -287
  217. package/lib/task/verification.ts +5 -5
  218. package/lib/task/write.ts +12 -1
  219. package/package.json +2 -2
  220. package/templates/.agents/README.en.md +1 -3
  221. package/templates/.agents/README.zh-CN.md +0 -2
  222. package/templates/.agents/rules/README.en.md +0 -1
  223. package/templates/.agents/rules/README.zh-CN.md +0 -1
  224. package/templates/.agents/rules/commit-and-pr.en.md +1 -1
  225. package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -1
  226. package/templates/.agents/rules/no-mid-flow-questions.en.md +0 -10
  227. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +0 -10
  228. package/templates/.agents/rules/pr-sync.en.md +4 -2
  229. package/templates/.agents/rules/pr-sync.zh-CN.md +6 -9
  230. package/templates/.agents/rules/task-management.en.md +3 -3
  231. package/templates/.agents/rules/task-management.zh-CN.md +3 -3
  232. package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -3
  233. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -3
  234. package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
  235. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
  236. package/templates/.agents/skills/code-task/SKILL.en.md +2 -2
  237. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +4 -4
  238. package/templates/.agents/skills/commit/SKILL.en.md +2 -2
  239. package/templates/.agents/skills/commit/SKILL.zh-CN.md +2 -2
  240. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +6 -6
  241. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +6 -6
  242. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  243. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  244. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +2 -2
  245. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +2 -2
  246. package/templates/.agents/skills/complete-task/SKILL.en.md +10 -25
  247. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +11 -26
  248. package/templates/.agents/skills/create-pr/SKILL.en.md +1 -1
  249. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +1 -1
  250. package/templates/.agents/skills/create-pr/reference/change-report.en.md +9 -2
  251. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +9 -2
  252. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +2 -2
  253. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +2 -2
  254. package/templates/.agents/skills/create-task/SKILL.en.md +0 -18
  255. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +0 -18
  256. package/templates/.agents/skills/plan-task/SKILL.en.md +2 -2
  257. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -2
  258. package/templates/.agents/skills/review-analysis/SKILL.en.md +3 -3
  259. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -3
  260. package/templates/.agents/skills/review-code/SKILL.en.md +5 -5
  261. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -5
  262. package/templates/.agents/skills/review-plan/SKILL.en.md +3 -3
  263. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -3
  264. package/templates/.agents/skills/review-pr/SKILL.en.md +4 -4
  265. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +4 -4
  266. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
  267. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
  268. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +3 -75
  269. package/templates/.agents/skills/watch-pr/SKILL.en.md +1 -1
  270. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +1 -1
  271. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +3 -3
  272. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +3 -3
  273. package/templates/.agents/templates/task.en.md +1 -0
  274. package/templates/.agents/templates/task.zh-CN.md +1 -0
  275. package/templates/.claude/settings.json +0 -24
  276. package/templates/.codex/hooks.json +0 -50
  277. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +0 -582
  278. package/dist/lib/agent-clients/adapters/codex-lifecycle/binding.js +0 -114
  279. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +0 -237
  280. package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +0 -443
  281. package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +0 -236
  282. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +0 -292
  283. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +0 -267
  284. package/dist/lib/agent-clients/adapters/codex-orchestration.js +0 -466
  285. package/dist/lib/internal/task-orchestration.js +0 -71
  286. package/dist/lib/platform/verification.js +0 -6
  287. package/dist/lib/run/prompt.js +0 -2
  288. package/dist/lib/sandbox/engines/authority.js +0 -289
  289. package/dist/lib/task/delegation-receipts.js +0 -410
  290. package/dist/lib/task/orchestration.js +0 -1233
  291. package/dist/lib/task/verification-checks.js +0 -9
  292. package/dist/lib/task/workspace-snapshot.js +0 -121
  293. package/dist/lifecycle-build-manifest.json +0 -6
  294. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +0 -688
  295. package/lib/agent-clients/adapters/codex-lifecycle/binding.ts +0 -147
  296. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +0 -349
  297. package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +0 -506
  298. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +0 -385
  299. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +0 -41
  300. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +0 -418
  301. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +0 -354
  302. package/lib/agent-clients/adapters/codex-orchestration.ts +0 -630
  303. package/lib/internal/task-orchestration.ts +0 -74
  304. package/lib/platform/verification.ts +0 -11
  305. package/lib/run/prompt.ts +0 -1
  306. package/lib/sandbox/engines/authority.ts +0 -365
  307. package/lib/task/delegation-receipts.ts +0 -547
  308. package/lib/task/orchestration.ts +0 -1651
  309. package/lib/task/verification-checks.ts +0 -10
  310. package/lib/task/workspace-snapshot.ts +0 -144
  311. package/templates/.agents/hooks/lifecycle-delegation.js +0 -140
  312. package/templates/.agents/rules/lifecycle-orchestration.en.md +0 -44
  313. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +0 -44
  314. package/templates/.agents/skills/run-task/SKILL.en.md +0 -29
  315. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +0 -29
  316. package/templates/.agents/skills/run-task/config/verify.en.json +0 -10
  317. package/templates/.agents/skills/run-task/config/verify.zh-CN.json +0 -10
  318. package/templates/.agents/skills/run-task/reference/host-validation.en.md +0 -24
  319. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +0 -24
  320. package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +0 -6
  321. package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +0 -6
  322. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +0 -6
  323. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +0 -6
  324. package/templates/.claude/commands/run-task.en.md +0 -8
  325. package/templates/.claude/commands/run-task.zh-CN.md +0 -8
  326. package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +0 -3
  327. package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +0 -3
  328. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +0 -3
  329. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +0 -3
  330. package/templates/.opencode/commands/run-task.en.md +0 -11
  331. package/templates/.opencode/commands/run-task.zh-CN.md +0 -11
@@ -3,13 +3,6 @@ import path from "node:path";
3
3
 
4
4
  import { inspectArtifactDirectory } from "./artifact-lifecycle.ts";
5
5
 
6
- type ReviewedGitTree = string & { readonly __reviewedGitTree: unique symbol };
7
- const REVIEWED_TREE_RE = /^[a-f0-9]{40,64}$/;
8
-
9
- export function parseReviewedGitTree(value: string): ReviewedGitTree | null {
10
- return REVIEWED_TREE_RE.test(value) ? value as ReviewedGitTree : null;
11
- }
12
-
13
6
  // Paths excluded from the post-review coverage gate. Fail-closed: the gate
14
7
  // covers ALL tracked changes by default. This default denylist is EMPTY —
15
8
  // projects opt into exclusions explicitly via review.post_review_exclude_globs.
@@ -87,7 +80,6 @@ export function extractReviewedSnapshotTree(content: string): string {
87
80
  return match ? match[1]!.trim().replace(/`/g, "") : "";
88
81
  }
89
82
 
90
- export type { ReviewedGitTree };
91
83
 
92
84
  export function parseReviewVerdict(content: string): string {
93
85
  const match = String(content).match(/^[-*]?\s*\*\*(?:总体结论|Overall Verdict)\*\*[::]\s*(.*?)\s*$/m);
@@ -3,7 +3,6 @@ import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
 
5
5
  const NO_FOLLOW = fs.constants.O_NOFOLLOW ?? 0;
6
- const DIRECTORY = fs.constants.O_DIRECTORY ?? 0;
7
6
 
8
7
  export class SecureFileError extends Error {
9
8
  readonly code: string;
@@ -40,10 +39,6 @@ function assertNoSymlinkAncestors(candidate: string): void {
40
39
  }
41
40
  }
42
41
 
43
- export async function readStableFile(candidate: string, options: Readonly<{ maxBytes: number; expectedSha256?: string }>): Promise<StableFile> {
44
- return readStableFileSync(candidate, options);
45
- }
46
-
47
42
  export function readStableFileSync(candidate: string, options: Readonly<{ maxBytes: number; expectedSha256?: string }>): StableFile {
48
43
  if (!path.isAbsolute(candidate)) conflict('candidate path must be absolute and terminal');
49
44
  assertNoSymlinkAncestors(candidate);
@@ -74,40 +69,3 @@ export function readStableFileSync(candidate: string, options: Readonly<{ maxByt
74
69
  if (descriptor !== undefined) fs.closeSync(descriptor);
75
70
  }
76
71
  }
77
-
78
- export async function writeAtomicFile(target: string, bytes: Buffer, mode = 0o600, expectedSha256?: string): Promise<void> {
79
- const parent = path.dirname(target);
80
- let current = path.resolve(parent);
81
- while (true) {
82
- let stat: fs.Stats;
83
- try { stat = await fs.promises.lstat(current); }
84
- catch { throw new SecureFileError('TASK_ARTIFACT_WRITE_CONFLICT', 'authoritative parent directory is missing'); }
85
- if (!stat.isDirectory() || stat.isSymbolicLink()) throw new SecureFileError('TASK_ARTIFACT_WRITE_CONFLICT', 'authoritative parent directory is not a real directory');
86
- const next = path.dirname(current);
87
- if (next === current) break;
88
- current = next;
89
- }
90
- try {
91
- const existing = await fs.promises.lstat(target);
92
- if (existing.isSymbolicLink() || !existing.isFile()) throw new SecureFileError('TASK_ARTIFACT_WRITE_CONFLICT', 'authoritative target is not a regular file');
93
- } catch (error) {
94
- if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
95
- }
96
- const temporary = path.join(parent, `.${path.basename(target)}.${process.pid}.${Date.now()}.tmp`);
97
- let handle: fs.promises.FileHandle | undefined;
98
- try {
99
- handle = await fs.promises.open(temporary, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL, mode);
100
- await handle.writeFile(bytes);
101
- await handle.sync();
102
- await handle.chmod(mode);
103
- await handle.close();
104
- handle = undefined;
105
- if (expectedSha256 !== undefined) readStableFileSync(target, { maxBytes: 1024 * 1024, expectedSha256 });
106
- fs.renameSync(temporary, target);
107
- const directory = await fs.promises.open(parent, fs.constants.O_RDONLY | DIRECTORY);
108
- try { await directory.sync(); } finally { await directory.close(); }
109
- } finally {
110
- await handle?.close().catch(() => undefined);
111
- await fs.promises.rm(temporary, { force: true }).catch(() => undefined);
112
- }
113
- }
@@ -447,15 +447,6 @@ function readRegistry(repoRoot: string): RegistrySchema | null {
447
447
  }
448
448
  }
449
449
 
450
- function readBranchFromTaskMd(repoRoot: string, taskId: string): string | null {
451
- const taskMdPath = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active', taskId, 'task.md');
452
- if (!fs.existsSync(taskMdPath)) return null;
453
- const content = fs.readFileSync(taskMdPath, 'utf8');
454
- const m = content.match(/^branch:\s*(.+)$/m);
455
- if (!m || !m[1]) return null;
456
- return m[1].trim().replace(/^(["'])(.*)\1$/, '$2');
457
- }
458
-
459
450
  function loadShortIdByTaskId(repoRoot: string): Map<string, string> {
460
451
  const registry = readRegistry(repoRoot);
461
452
  const map = new Map<string, string>();
@@ -527,44 +518,10 @@ function inspectShortIdRegistry(
527
518
  }
528
519
  }
529
520
 
530
- /**
531
- * Resolve a branch to its active-task short id (`NN`), or `null` when no
532
- * active task is bound to that branch.
533
- *
534
- * Two-state semantics: this only consults the active registry
535
- * (`active/.short-ids.json`) plus each `active/{taskId}/task.md`. Tasks moved
536
- * to completed/blocked/cancelled/archive have already released their short id,
537
- * so their branches return `null` — in `ai sandbox ls` that surfaces as `-`,
538
- * meaning the sandbox is free to remove.
539
- */
540
- function lookupShortIdByBranch(
541
- branch: string,
542
- repoRoot: string,
543
- _opts?: { shortIdLength?: number }
544
- ): string | null {
545
- const registry = readRegistry(repoRoot);
546
- if (!registry) return null;
547
- const matches: string[] = [];
548
- for (const [key, taskId] of Object.entries(registry.ids)) {
549
- const taskBranch = readBranchFromTaskMd(repoRoot, taskId);
550
- if (taskBranch && taskBranch === branch) {
551
- matches.push(key);
552
- }
553
- }
554
- if (matches.length === 0) return null;
555
- if (matches.length > 1) {
556
- process.stderr.write(
557
- `Warning: branch '${branch}' is bound to multiple active tasks: ${matches.join(', ')}; using ${matches[0]}\n`
558
- );
559
- }
560
- return matches[0]!;
561
- }
562
-
563
521
  export {
564
522
  normalizeShortIdInput,
565
523
  isRemovedHashShortIdInput,
566
524
  resolveShortIdReadOnly,
567
- lookupShortIdByBranch,
568
525
  loadShortIdByTaskId,
569
526
  inspectShortIdRegistry,
570
527
  configuredShortIdLength,
@@ -13,7 +13,6 @@ const TASK_SCOPE_SKILLS: ReadonlySet<string> = new Set([
13
13
  'review-code',
14
14
  'review-plan',
15
15
  'run-manual-validation',
16
- 'run-task',
17
16
  'watch-pr'
18
17
  ]);
19
18
 
@@ -1,7 +1,6 @@
1
1
  // frontmatter, ledger, Git snapshot and platform domains are imported below.
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
- import process from "node:process";
5
4
  import { execFileSync } from "node:child_process";
6
5
  import { fileURLToPath } from "node:url";
7
6
 
@@ -29,10 +28,8 @@ import { inspectDecisionDetailDuplicates } from "./decision-details.ts";
29
28
  import { parseImplementationInputs, type ImplementationInput } from "./implementation-inputs.ts";
30
29
  import { scanVisibleMarkdown } from "./markdown.ts";
31
30
  import { loadVerificationConfig } from "./verification-config.ts";
32
- import { inspectActivityLog, pairEntries } from "./activity-log.ts";
31
+ import { inspectActivityLog } from "./activity-log.ts";
33
32
  import { parseWorkflowWarnings } from "./workflow-warnings.ts";
34
- import { OrchestrationStateError, readRun } from "./orchestration.ts";
35
- import type { OrchestrationRun } from "./orchestration.ts";
36
33
  import { readPrDeliveryFact } from "./pr-delivery-fact.ts";
37
34
  import { validateLocalArtifact } from "./local-artifact-finalization.ts";
38
35
  import { getArtifactSchema } from "./artifact-schema.ts";
@@ -41,13 +38,12 @@ import type { VerificationShared } from "./verification-types.ts";
41
38
  import { normalizeVerificationRecord } from './gate-policy.ts';
42
39
  import { manualValidationFinalSummaryProjectionMatches } from "./manual-validation-receipt.ts";
43
40
  import { readManualValidationCompletion } from "./manual-validation-completion.ts";
44
- import { sha256File } from "./artifact-receipts.ts";
41
+ import { parseArtifactReceipts, receiptForOutput, sha256File } from "./artifact-receipts.ts";
45
42
  import { inspectArtifactDirectory } from "./artifact-lifecycle.ts";
46
- import { getAgentClientAdapter } from "../agent-clients/registry.ts";
47
43
  import { parseArtifactName } from "./artifact-name.ts";
48
44
  import { summaryCommentState } from "../platform/pr-summary.ts";
49
45
  import { taskIssueIdentity } from "../platform/task-identities.ts";
50
- import { hasArtifactCompletionFact, hasArtifactCompletionLog, parseCompletionFacts } from './completion-facts.ts';
46
+ import { hasValidArtifactCompletion } from './completion-facts.ts';
51
47
  import { inspectReviewIdentity } from './review-identity.ts';
52
48
 
53
49
  const TASK_ENUMS = {
@@ -70,11 +66,9 @@ const DEFAULT_REQUIRED_FIELDS = [
70
66
 
71
67
  const DATE_TIME_PATTERN = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:[+-]\d{2}:\d{2})?$/;
72
68
  // Started rows do not represent completed lifecycle actions.
73
- const ACTIVITY_LOG_STARTED_RE = /\s*\[started\]\s*$/;
74
69
  const BRANCH_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
75
70
 
76
71
  // Review disagreement ledger (see .agents/rules/review-handshake.md).
77
- const LEDGER_SECTION_NAMES = ["审查分歧账本", "Review Disagreement Ledger"];
78
72
  const POST_REVIEW_COMMIT_STAGE = "post-review-commit";
79
73
  const SHA_PATTERN = /^[0-9a-f]{7,40}$/i;
80
74
 
@@ -126,10 +120,6 @@ async function runCheck(type: any, context: any, shared: any): Promise<any> {
126
120
  return checkPostReviewCommit(context);
127
121
  case "required-pr-delivery":
128
122
  return checkRequiredPrDelivery(context);
129
- case "orchestration-state":
130
- return checkOrchestrationState(context);
131
- case "orchestration-evidence":
132
- return checkOrchestrationEvidence(context);
133
123
  default: {
134
124
  const adapter = PLATFORM_ADAPTERS[type];
135
125
  if (!adapter) {
@@ -145,162 +135,6 @@ async function runCheck(type: any, context: any, shared: any): Promise<any> {
145
135
  }
146
136
  }
147
137
 
148
- function checkOrchestrationState({ taskDir }: any): any {
149
- const file = path.join(taskDir, '.runtime', 'orchestration.json');
150
- const stat = safeStat(file);
151
- if (!stat?.isFile()) return failResult('orchestration-state', 'orchestration.json is missing');
152
- let run: OrchestrationRun | null;
153
- try {
154
- run = readRun(taskDir);
155
- } catch (error) {
156
- const message = error instanceof OrchestrationStateError ? error.message : String(error);
157
- return failResult('orchestration-state', `Invalid orchestration.json: ${message}`);
158
- }
159
- if (!run || !['paused', 'completed'].includes(run.status)) {
160
- return failResult('orchestration-state', `Expected paused or completed run, received '${run?.status ?? 'missing'}'`);
161
- }
162
- const adapterEvidenceError = validateAdapterReceiptEvidence(run);
163
- if (adapterEvidenceError) return failResult('orchestration-state', adapterEvidenceError);
164
- if (run.status === 'paused' && (!run.pause?.code || !run.pause?.message)) {
165
- return failResult('orchestration-state', 'Paused run requires a stable pause code and message');
166
- }
167
- if (run.status === 'completed') {
168
- const evidenceError = validateCleanCompletionEvidence(run);
169
- if (evidenceError) return failResult('orchestration-state', evidenceError);
170
- } else if (run.completionEvidence != null) {
171
- return failResult('orchestration-state', 'Clean completion evidence requires a completed run');
172
- } else if (run.pendingDelegation?.status === 'sealed') {
173
- return failResult('orchestration-state', 'Paused run has a sealed delegation that could still advance');
174
- }
175
- return passResult('orchestration-state', `Orchestration run is safely ${run.status}`);
176
- }
177
-
178
- function exactText(value: unknown): value is string {
179
- return typeof value === 'string' && value.length > 0 && value.trim() === value;
180
- }
181
-
182
- function validateCleanCompletionEvidence(run: OrchestrationRun): string | null {
183
- const evidence = run.completionEvidence;
184
- const shaPattern = /^[0-9a-f]{40}$/i;
185
- if (
186
- !evidence
187
- || Number.isNaN(Date.parse(evidence.observedAt))
188
- || !shaPattern.test(evidence.head)
189
- || !shaPattern.test(evidence.headTree)
190
- || !shaPattern.test(evidence.worktreeTree)
191
- || !shaPattern.test(evidence.lastReviewedCommit)
192
- || !(evidence.prHead === null || shaPattern.test(evidence.prHead))
193
- ) {
194
- return 'Clean completion evidence has an invalid structure';
195
- }
196
- if (
197
- evidence.head !== evidence.lastReviewedCommit
198
- || (evidence.prHead !== null && evidence.head !== evidence.prHead)
199
- || evidence.headTree !== evidence.worktreeTree
200
- ) {
201
- return 'Clean completion evidence does not bind matching commit and tree identities';
202
- }
203
- if (
204
- run.status !== 'completed'
205
- || run.pendingDelegation !== null
206
- || run.receipts.some((receipt) => receipt.stage === 'commit')
207
- ) {
208
- return 'Clean completion evidence conflicts with pending delegation or commit receipt state';
209
- }
210
- return null;
211
- }
212
-
213
- function validateAdapterReceiptEvidence(run: OrchestrationRun): string | null {
214
- const receipts = [...run.receipts, ...(run.pendingDelegation ? [run.pendingDelegation] : [])];
215
- for (const receipt of receipts) {
216
- const validate = getAgentClientAdapter(receipt.client).orchestrationAdapter?.validateReceiptEvidence;
217
- if (validate && !validate(receipt)) {
218
- return `Receipt '${receipt.id}' has invalid evidence from its agent adapter`;
219
- }
220
- }
221
- return null;
222
- }
223
-
224
- function checkOrchestrationEvidence({ taskDir }: any): any {
225
- const file = path.join(taskDir, '.runtime', 'orchestration.json');
226
- const stat = safeStat(file);
227
- if (!stat?.isFile()) return failResult('orchestration-evidence', 'orchestration.json is missing');
228
- let run: OrchestrationRun | null;
229
- try {
230
- run = readRun(taskDir);
231
- } catch (error) {
232
- const message = error instanceof OrchestrationStateError ? error.message : String(error);
233
- return failResult('orchestration-evidence', `Invalid orchestration.json: ${message}`);
234
- }
235
- if (!run) return failResult('orchestration-evidence', 'orchestration.json is missing');
236
- const adapterEvidenceError = validateAdapterReceiptEvidence(run);
237
- if (adapterEvidenceError) return failResult('orchestration-evidence', adapterEvidenceError);
238
- // readRun validates the persisted schema, receipt identities and client provenance.
239
- const policy = run.modelPolicy;
240
- if (run.completionEvidence != null) {
241
- const evidenceError = validateCleanCompletionEvidence(run);
242
- if (evidenceError) return failResult('orchestration-evidence', evidenceError);
243
- } else if (run.status === 'completed') {
244
- return failResult('orchestration-evidence', 'Completed run requires reviewed-head-clean completion evidence');
245
- }
246
- if (run.pendingDelegation && !['prepared', 'activated', 'stage-completed'].includes(run.pendingDelegation.status)) {
247
- return failResult('orchestration-evidence', `Pending receipt has unsafe status '${run.pendingDelegation.status}'`);
248
- }
249
- const receipts = [...run.receipts, ...(run.pendingDelegation ? [run.pendingDelegation] : [])];
250
- for (const receipt of receipts) {
251
- const expectedPolicy = policy[receipt.role];
252
- if (!expectedPolicy || receipt.requestedModel !== expectedPolicy.model) {
253
- return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' requested model does not match its role policy`);
254
- }
255
- if (receipt.requestedReasoningEffort !== expectedPolicy.reasoningEffort) {
256
- return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' requested reasoning effort does not match its role policy`);
257
- }
258
- const activated = ['activated', 'stage-completed', 'sealed', 'consumed'].includes(receipt.status);
259
- if (activated) {
260
- const evidencePolicy = getAgentClientAdapter(receipt.client).delegationEvidence;
261
- if (evidencePolicy.actualModelRequired !== false) {
262
- if (!exactText(receipt.actualModel)) {
263
- return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has no host-observed actual model`);
264
- }
265
- }
266
- if (evidencePolicy.actualReasoningEffortRequired !== false) {
267
- if (!exactText(receipt.actualReasoningEffort)) {
268
- return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has no host-observed actual reasoning effort`);
269
- }
270
- }
271
- if (evidencePolicy.spawnModeRequired !== false && receipt.spawnMode !== 'fresh') {
272
- return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has invalid fresh delegation identity`);
273
- }
274
- if (receipt.actualModel != null && receipt.actualModel !== receipt.requestedModel) {
275
- if (evidencePolicy.fallbackReasonRequired !== false && !exactText(receipt.modelFallbackReason)) {
276
- return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' model fallback is not justified`);
277
- }
278
- }
279
- if (
280
- (receipt.actualModel == null || receipt.actualModel === receipt.requestedModel)
281
- && receipt.modelFallbackReason !== null
282
- ) {
283
- return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has an unrelated model fallback reason`);
284
- }
285
- if (receipt.actualReasoningEffort != null && receipt.actualReasoningEffort !== receipt.requestedReasoningEffort) {
286
- if (evidencePolicy.fallbackReasonRequired !== false && !exactText(receipt.reasoningEffortFallbackReason)) {
287
- return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' reasoning-effort fallback is not justified`);
288
- }
289
- }
290
- if (
291
- (receipt.actualReasoningEffort == null || receipt.actualReasoningEffort === receipt.requestedReasoningEffort)
292
- && receipt.reasoningEffortFallbackReason !== null
293
- ) {
294
- return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has an unrelated reasoning-effort fallback reason`);
295
- }
296
- }
297
- }
298
- if (run.receipts.some((receipt) => receipt.status !== 'consumed')) {
299
- return failResult('orchestration-evidence', 'Historical receipts must be consumed');
300
- }
301
- return passResult('orchestration-evidence', 'Persisted orchestration model and delegation evidence is internally consistent');
302
- }
303
-
304
138
  // === Check Functions ===
305
139
 
306
140
  function checkTaskMeta({ taskDir, config, repositoryRoot, skillName, artifactFile }: any): any {
@@ -356,12 +190,9 @@ function checkTaskMeta({ taskDir, config, repositoryRoot, skillName, artifactFil
356
190
  if (expectedStep && metadata.current_step !== expectedStep) {
357
191
  let validLifecycleArtifact = false;
358
192
  if (currentTask) {
359
- const { section, invalidEntries } = inspectActivityLog(task.content);
360
- if (section && invalidEntries.length === 0) {
361
- validLifecycleArtifact = checkArtifactLifecycleActivityLog(
362
- taskDir, artifactFile, skillName, task, section.entries
363
- )?.status === "pass";
364
- }
193
+ validLifecycleArtifact = checkArtifactLifecycleActivityLog(
194
+ taskDir, artifactFile, skillName, task
195
+ )?.status === "pass";
365
196
  }
366
197
  if (!validLifecycleArtifact) {
367
198
  return failResult(
@@ -678,12 +509,6 @@ function checkImplementationInput({ taskDir, artifactFile }: any): any {
678
509
  return failResult("implementation-input", `Invalid Implementation Inputs: ${error instanceof Error ? error.message : String(error)}`);
679
510
  }
680
511
 
681
- const doneActions = (inspectActivityLog(task.content).section?.entries ?? [])
682
- .filter((entry) => !ACTIVITY_LOG_STARTED_RE.test(entry.step))
683
- .map((entry) => entry.step)
684
- .filter((step) => /^(?:Code Task|Code) \(Round \d+/.test(step));
685
- const latestAction = doneActions.at(-1) || "";
686
- const actionDecision = /(?:Code Task|Code) \(Round \d+, decision (II-[1-9]\d*)\)/.exec(latestAction)?.[1] || null;
687
512
  const report = fs.readFileSync(artifactPath, "utf8");
688
513
  const reportSection = getSectionContent(report, ["实现输入", "Implementation Input"]);
689
514
  if (!reportSection) return failResult("implementation-input", "Implementation Input report section not found");
@@ -696,37 +521,31 @@ function checkImplementationInput({ taskDir, artifactFile }: any): any {
696
521
  const reportLedger = field("账本 ID", "Ledger ID");
697
522
  const reportEvidence = field("裁决证据", "Decision Evidence");
698
523
 
699
- if (!actionDecision) {
700
- if (reportInput && reportInput !== "N/A") {
701
- return failResult("implementation-input", "Non-decision code action must report Decision Input as N/A");
702
- }
524
+ if (!reportInput || reportInput === "N/A") {
703
525
  return passResult("implementation-input", "Non-decision implementation input identity is consistent");
704
526
  }
705
- if (reportInput !== actionDecision) {
706
- return failResult("implementation-input", `Report decision input '${reportInput}' does not match Activity Log ${actionDecision}`);
707
- }
708
- const matches = rows.filter((row) => row.id === actionDecision);
709
- if (matches.length !== 1) return failResult("implementation-input", `${actionDecision} is missing or duplicated in task table`);
527
+ const matches = rows.filter((row) => row.id === reportInput);
528
+ if (matches.length !== 1) return failResult("implementation-input", `${reportInput} is missing or duplicated in task table`);
710
529
  const row = matches[0]!;
711
530
  if (row.stage !== "code" || !row.needsImplementation || row.status !== "consumed" || row.consumedBy !== artifactFile || !row.decisionEvidence) {
712
- return failResult("implementation-input", `${actionDecision} is not a consumed input for ${artifactFile}`);
531
+ return failResult("implementation-input", `${reportInput} is not a consumed input for ${artifactFile}`);
713
532
  }
714
533
  if (reportLedger !== row.ledgerId || reportEvidence !== row.decisionEvidence) {
715
- return failResult("implementation-input", `${actionDecision} report identity does not match task table evidence`);
534
+ return failResult("implementation-input", `${reportInput} report identity does not match task table evidence`);
716
535
  }
717
- return passResult("implementation-input", `${actionDecision} matches Activity Log, report, and task table`);
536
+ return passResult("implementation-input", `${reportInput} matches report and task table`);
718
537
  }
719
538
 
720
- const ARTIFACT_ACTIVITY_STAGES: Readonly<Record<string, { family: string; event: string; action: string }>> = {
721
- "analyze-task": { family: "analysis", event: "analyze.completed", action: "Analyze Task" },
722
- "plan-task": { family: "plan", event: "plan.completed", action: "Plan Task" },
723
- "review-analysis": { family: "review-analysis", event: "review-analysis.completed", action: "Review Analysis" },
724
- "review-plan": { family: "review-plan", event: "review-plan.completed", action: "Review Plan" },
725
- "code-task": { family: "code", event: "code.completed", action: "Code Task" },
726
- "review-code": { family: "review-code", event: "review-code.completed", action: "Review Code" }
539
+ const ARTIFACT_ACTIVITY_STAGES: Readonly<Record<string, { family: string; event: string }>> = {
540
+ "analyze-task": { family: "analysis", event: "analyze.completed" },
541
+ "plan-task": { family: "plan", event: "plan.completed" },
542
+ "review-analysis": { family: "review-analysis", event: "review-analysis.completed" },
543
+ "review-plan": { family: "review-plan", event: "review-plan.completed" },
544
+ "code-task": { family: "code", event: "code.completed" },
545
+ "review-code": { family: "review-code", event: "review-code.completed" }
727
546
  };
728
547
 
729
- function checkArtifactLifecycleActivityLog(taskDir: string, artifactFile: string | undefined, skillName: string, task: any, entries: any[]): any {
548
+ function checkArtifactLifecycleActivityLog(taskDir: string, artifactFile: string | undefined, skillName: string, task: any): any {
730
549
  const stage = ARTIFACT_ACTIVITY_STAGES[skillName];
731
550
  if (!stage) return null;
732
551
  const inventory = inspectArtifactDirectory(taskDir, stage.family as any);
@@ -741,38 +560,21 @@ function checkArtifactLifecycleActivityLog(taskDir: string, artifactFile: string
741
560
  if (!identity || identity.family !== stage.family) {
742
561
  return failResult("activity-log", `Artifact '${output}' does not belong to ${stage.family}`);
743
562
  }
744
- if (!hasArtifactCompletionFact(task.content, path.join(taskDir, output), stage.event)) {
563
+ if (!hasValidArtifactCompletion(task.content, taskDir, output, stage.event)) {
745
564
  return failResult("activity-log", `Completion fact for '${output}' is missing or stale; rerun ${skillName} to record a current completion`);
746
565
  }
747
566
 
748
- const frontmatter = parseTypedTaskFrontmatter(task.content);
749
- const fact = parseCompletionFacts(frontmatter.completion_facts).find((item) => item.event === stage.event && item.output === output);
750
- if (!fact) return failResult("activity-log", `Completion fact for '${output}' is missing; rerun ${skillName}`);
751
- if (!Array.isArray(fact.lifecycleInputs)) {
752
- return failResult("activity-log", `Completion fact for '${output}' has no input snapshot; rerun ${skillName}`);
753
- }
754
- for (const input of fact.lifecycleInputs) {
755
- if (!input || typeof input.name !== "string" || !/^[a-f0-9]{64}$/u.test(input.sha256)) {
756
- return failResult("activity-log", `Completion fact for '${output}' has an invalid input snapshot; rerun ${skillName}`);
757
- }
758
- const inputIdentity = parseArtifactName(input.name);
759
- if (!inputIdentity) return failResult("activity-log", `Completion fact for '${output}' has invalid input '${input.name}'; rerun ${skillName}`);
567
+ const receipts = parseArtifactReceipts(task.content).rows.filter((receipt) => receipt.output === output);
568
+ for (const receipt of receipts) {
569
+ const inputIdentity = parseArtifactName(receipt.input);
570
+ if (!inputIdentity) return failResult("activity-log", `Completion receipt for '${output}' has invalid input '${receipt.input}'; rerun ${skillName}`);
760
571
  const inputInventory = inspectArtifactDirectory(taskDir, inputIdentity.family as any);
761
- if (inputInventory.status !== "ready" || inputInventory.latest?.name !== input.name
762
- || sha256File(path.join(taskDir, input.name)) !== input.sha256) {
763
- return failResult("activity-log", `Input '${input.name}' changed or is no longer current; rerun ${skillName} to refresh '${output}'`);
572
+ if (inputInventory.status !== "ready" || inputInventory.latest?.name !== receipt.input
573
+ || sha256File(path.join(taskDir, receipt.input)) !== receipt.inputSha256) {
574
+ return failResult("activity-log", `Input '${receipt.input}' changed or is no longer current; rerun ${skillName} to refresh '${output}'`);
764
575
  }
765
576
  }
766
577
 
767
- const roundActionPattern = new RegExp(`^${stage.action} \\(Round ${identity.round}(?:, [^)]+)?\\)$`);
768
- const roundAction = entries.find((entry) => {
769
- return !ACTIVITY_LOG_STARTED_RE.test(entry.step)
770
- && roundActionPattern.test(entry.step)
771
- && entry.note.includes(`→ ${output}`);
772
- });
773
- if (!roundAction) {
774
- return failResult("activity-log", `No completed Activity Log row records '${output}' for ${skillName} Round ${identity.round}`);
775
- }
776
578
  return passResult("activity-log", `Current ${stage.family} completion '${output}' is valid for Round ${identity.round}`);
777
579
  }
778
580
 
@@ -782,22 +584,19 @@ function checkActivityLog({ taskDir, skillName, artifactFile }: any): any {
782
584
  return failResult("activity-log", task.message);
783
585
  }
784
586
 
785
- const { section, invalidEntries } = inspectActivityLog(task.content);
786
- if (!section) return failResult("activity-log", "Activity Log section not found or ambiguous");
787
- if (invalidEntries.length) return failResult("activity-log", `Invalid Activity Log entry format: ${invalidEntries[0]}`);
788
- const entries = section.entries;
789
- if (entries.length === 0) return failResult("activity-log", "Activity Log has no entries");
587
+ const artifactLifecycleResult = checkArtifactLifecycleActivityLog(taskDir, artifactFile, skillName, task);
588
+ if (artifactLifecycleResult && artifactLifecycleResult.status !== "pass") return artifactLifecycleResult;
790
589
 
791
- for (const entry of entries) {
792
- if (!entry.note.trim()) return failResult("activity-log", "Activity Log entry note is required");
590
+ const { section, invalidEntries } = inspectActivityLog(task.content);
591
+ const warnings: string[] = [];
592
+ if (!section) warnings.push('Activity Log section is missing or ambiguous');
593
+ else {
594
+ if (invalidEntries.length) warnings.push('Activity Log contains malformed entries');
595
+ if (!section.entries.length) warnings.push('Activity Log has no entries');
596
+ if (section.entries.some((entry) => !entry.note.trim())) warnings.push('Activity Log entry note is empty');
793
597
  }
598
+ return passResult("activity-log", artifactLifecycleResult?.message ?? "Business checks are valid", warnings);
794
599
 
795
- const artifactLifecycleResult = checkArtifactLifecycleActivityLog(
796
- taskDir, artifactFile, skillName, task, entries
797
- );
798
- if (artifactLifecycleResult) return artifactLifecycleResult;
799
-
800
- return passResult("activity-log", "Activity Log is valid");
801
600
  }
802
601
 
803
602
  function checkCompletionChecklist({ taskDir, config }: any): any {
@@ -837,18 +636,7 @@ function checkCompletionChecklist({ taskDir, config }: any): any {
837
636
  return passResult("completion-checklist", `Completion Checklist valid (${items.length} items checked)`);
838
637
  }
839
638
 
840
- function resolveReviewSetting(config: any, key: any, fallback: any, repoRoot: string): any {
841
- if (config && config[key] !== undefined && config[key] !== null) {
842
- return config[key];
843
- }
844
- const reviewConfig = loadPostReviewConfig(repoRoot);
845
- if (reviewConfig[key] !== undefined && reviewConfig[key] !== null) {
846
- return reviewConfig[key];
847
- }
848
- return fallback;
849
- }
850
-
851
- function checkReviewLedger({ taskDir, config, repositoryRoot }: any): any {
639
+ function checkReviewLedger({ taskDir, config }: any): any {
852
640
  const task = loadTask(taskDir);
853
641
  if (!task.ok) {
854
642
  return failResult("review-ledger", task.message);
@@ -931,8 +719,7 @@ async function checkManualValidation({ taskDir, repositoryRoot }: any): Promise<
931
719
  `${parsed.summary.manualValidation} manual validation item(s) pending: run /complete-manual-validation`
932
720
  );
933
721
  }
934
- // 5. The Activity Log must record the matching completion entry; the file name
935
- // is derived with path.basename (resolveArtifactPath does not return fileName).
722
+ // 5. Validate the committed receipt, transaction, and artifact.
936
723
  const artifactName = path.basename(resolved.path);
937
724
  const task = loadTask(taskDir);
938
725
  if (!task.ok) return failResult("manual-validation", task.message);
@@ -954,29 +741,21 @@ async function checkManualValidation({ taskDir, repositoryRoot }: any): Promise<
954
741
  if (sha256File(resolved.path) !== receipt.artifactSha256) {
955
742
  return failResult("manual-validation", `Manual validation artifact digest does not match the receipt for ${artifactName}`);
956
743
  }
957
- // 6. Timing correlation (PL-3 fix, PL-4 corrected): the completion entry must
958
- // sit AFTER the latest review-code round's completion entry in the
959
- // append-only Activity Log. indexOf is a literal search, so the artifactName
960
- // must NOT be passed through escapeRegExp here (that escapes '.' to '\.',
961
- // which indexOf never matches). Not reusing completed.exec().index: exec
962
- // anchors to the first '**Complete Manual Validation**' heading, which can
963
- // belong to an earlier round's entry and misorder the standard path.
964
- const inspectedLog = inspectActivityLog(task.content);
965
- if (!inspectedLog.section) return failResult("manual-validation", "Activity Log is missing or invalid");
966
- const reviewRow = pairEntries(inspectedLog.section.entries).find((row) =>
967
- row.step === `Review Code (Round ${review.round})` && row.started !== '' && row.done !== ''
968
- );
969
- const reviewDoneIndex = reviewRow
970
- ? inspectedLog.section.entries.findIndex((entry) => entry.step === reviewRow.step && entry.time === reviewRow.done)
971
- : -1;
972
- const completedIndex = inspectedLog.section.entries.findIndex((entry) => entry.step === "Complete Manual Validation"
973
- && entry.note.includes(`Manual validation passed → ${artifactName};`)
974
- && entry.note.includes(`transaction=${receipt.transactionId};`)
975
- && entry.note.includes(`receipt=${receipt.receiptDigest};`)
976
- && entry.note.includes(`head=${receipt.prHeadSha}`));
977
- if (reviewDoneIndex === -1) return failResult("manual-validation", `Latest review-code (round ${review.round}) completion entry is missing from the Activity Log`);
978
- if (completedIndex === -1) return failResult("manual-validation", `Committed manual validation completion is not recorded for ${artifactName}`);
979
- if (completedIndex < reviewDoneIndex) return failResult("manual-validation", `Latest review-code (round ${review.round}) came after the manual validation completion recorded for ${artifactName}`);
744
+ // 6. Compare the review completion and committed manual-validation times.
745
+ let reviewReceipt;
746
+ try {
747
+ reviewReceipt = receiptForOutput(task.content, review.fileName!);
748
+ } catch (error) {
749
+ return failResult("manual-validation", `Review-code receipt is invalid: ${error instanceof Error ? error.message : String(error)}`);
750
+ }
751
+ if (!reviewReceipt || reviewReceipt.event !== "review-code.completed") {
752
+ return failResult("manual-validation", `Review-code completion time is unavailable for ${review.fileName}`);
753
+ }
754
+ const reviewTime = Date.parse(reviewReceipt.completedAt.replace(' ', 'T'));
755
+ const validationTime = Date.parse(receipt.committedAt);
756
+ if (!Number.isFinite(reviewTime) || !Number.isFinite(validationTime) || validationTime <= reviewTime) {
757
+ return failResult("manual-validation", `Manual validation must be completed after review-code ${review.fileName}`);
758
+ }
980
759
 
981
760
  const fact = readPrDeliveryFact(task.metadata);
982
761
  if (fact.status === "invalid") return failResult("manual-validation", fact.error.message);
@@ -1212,12 +991,9 @@ function checkReviewFact({ taskDir, artifactFile, repositoryRoot }: any): any {
1212
991
  }
1213
992
 
1214
993
  const content = fs.readFileSync(resolvedArtifact.path, "utf8");
1215
- if (!hasArtifactCompletionFact(task.content, resolvedArtifact.path, 'review-code.completed')) {
994
+ if (!hasValidArtifactCompletion(task.content, taskDir, path.basename(resolvedArtifact.path), 'review-code.completed')) {
1216
995
  return failResult("review-fact", `Review Code completion fact does not match ${path.basename(resolvedArtifact.path)}`);
1217
996
  }
1218
- if (!hasArtifactCompletionLog(task.content, path.basename(resolvedArtifact.path), 'review-code.completed')) {
1219
- return failResult("review-fact", `Review Code completion entry does not reference ${path.basename(resolvedArtifact.path)}`);
1220
- }
1221
997
  const verdict = parseReviewVerdict(content);
1222
998
  const reviewTargetHead = extractReviewTargetHead(content);
1223
999
  const reviewReviewedHead = extractReviewedHead(content);
@@ -1368,13 +1144,6 @@ function normalizeContent(text: any): any {
1368
1144
  .trim();
1369
1145
  }
1370
1146
 
1371
- function interpolate(template: any, taskDir: any, artifactFile: any): any {
1372
- const artifactStem = artifactFile ? path.basename(artifactFile, path.extname(artifactFile)) : "";
1373
- return template
1374
- .replace(/\{task-id\}/g, path.basename(taskDir))
1375
- .replace(/\{artifact-stem\}/g, artifactStem);
1376
- }
1377
-
1378
1147
  function summarizeGate(checks: any): any {
1379
1148
  if (checks.some((check: any) => check.effectiveStatus === "blocked")) {
1380
1149
  return "blocked";