@fitlab-ai/agent-infra 0.12.1 → 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 (303) 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 +18 -10
  21. package/dist/lib/internal/task-create.js +1 -1
  22. package/dist/lib/internal/task-event.js +1 -8
  23. package/dist/lib/internal/task-finalization.js +0 -1
  24. package/dist/lib/internal/task-lifecycle.js +2 -2
  25. package/dist/lib/internal/task-operation-registry.js +4 -20
  26. package/dist/lib/internal/task-review.js +4 -4
  27. package/dist/lib/platform/comment-safety.js +1 -7
  28. package/dist/lib/platform/in-label-sync.js +1 -14
  29. package/dist/lib/platform/issue-comments.js +1 -16
  30. package/dist/lib/platform/issues.js +1 -1
  31. package/dist/lib/platform/operation-coordinator.js +4 -2
  32. package/dist/lib/platform/pr-change-report.js +1 -4
  33. package/dist/lib/platform/pr-checks.js +2 -8
  34. package/dist/lib/platform/provider-validation.js +0 -20
  35. package/dist/lib/platform/pull-requests.js +73 -84
  36. package/dist/lib/platform/releases.js +1 -43
  37. package/dist/lib/platform/verification-required.js +0 -2
  38. package/dist/lib/platform/verification-sync.js +1 -79
  39. package/dist/lib/process-data/normalize.js +1 -185
  40. package/dist/lib/process-data/privacy.js +1 -16
  41. package/dist/lib/process-data/sources.js +2 -279
  42. package/dist/lib/render.js +0 -12
  43. package/dist/lib/run/index.js +15 -10
  44. package/dist/lib/run/skills.js +0 -3
  45. package/dist/lib/sandbox/commands/create.js +40 -53
  46. package/dist/lib/sandbox/commands/list-running.js +0 -18
  47. package/dist/lib/sandbox/commands/start.js +7 -8
  48. package/dist/lib/sandbox/constants.js +0 -6
  49. package/dist/lib/sandbox/control/client.js +0 -1
  50. package/dist/lib/sandbox/control/container-identity.js +1 -16
  51. package/dist/lib/sandbox/control/executor.js +0 -4
  52. package/dist/lib/sandbox/control/identity-sentinel.js +0 -7
  53. package/dist/lib/sandbox/control/lifecycle.js +21 -37
  54. package/dist/lib/sandbox/control/native-file-lock.js +0 -3
  55. package/dist/lib/sandbox/control/server.js +18 -38
  56. package/dist/lib/sandbox/control/state.js +1 -9
  57. package/dist/lib/sandbox/dockerfile.js +0 -6
  58. package/dist/lib/sandbox/engine.js +0 -14
  59. package/dist/lib/sandbox/engines/docker-exact-discovery.js +1 -4
  60. package/dist/lib/sandbox/engines/wsl2-paths.js +0 -3
  61. package/dist/lib/sandbox/recovery.js +24 -26
  62. package/dist/lib/sandbox/removal.js +11 -519
  63. package/dist/lib/sandbox/task-resolver.js +0 -41
  64. package/dist/lib/sandbox/tools.js +1 -4
  65. package/dist/lib/sandbox/workspace-view.js +1 -2
  66. package/dist/lib/server/display.js +0 -6
  67. package/dist/lib/task/activity-intent.js +45 -40
  68. package/dist/lib/task/activity-log.js +9 -16
  69. package/dist/lib/task/artifact-command.js +1 -1
  70. package/dist/lib/task/artifact-lifecycle.js +61 -63
  71. package/dist/lib/task/artifact-name.js +0 -6
  72. package/dist/lib/task/artifact-operations.js +3 -3
  73. package/dist/lib/task/capabilities.js +4 -5
  74. package/dist/lib/task/commands/log.js +0 -6
  75. package/dist/lib/task/commands/status.js +17 -60
  76. package/dist/lib/task/commit-operation.js +28 -39
  77. package/dist/lib/task/completion-facts.js +56 -43
  78. package/dist/lib/task/control-authority.js +23 -256
  79. package/dist/lib/task/control-recovery.js +3 -5
  80. package/dist/lib/task/create.js +2 -4
  81. package/dist/lib/task/decision-intents.js +8 -9
  82. package/dist/lib/task/delivery-target.js +1 -40
  83. package/dist/lib/task/events.js +84 -107
  84. package/dist/lib/task/finalization.js +44 -8
  85. package/dist/lib/task/ledger-intents.js +1 -1
  86. package/dist/lib/task/lifecycle-execution.js +38 -33
  87. package/dist/lib/task/lifecycle.js +49 -41
  88. package/dist/lib/task/local-artifact-finalization.js +1 -4
  89. package/dist/lib/task/manual-validation-evidence.js +1 -37
  90. package/dist/lib/task/platform-operation-recovery.js +67 -24
  91. package/dist/lib/task/pr-delivery-fact.js +2 -5
  92. package/dist/lib/task/qualification-confirmation.js +5 -7
  93. package/dist/lib/task/resolve-ref.js +1 -25
  94. package/dist/lib/task/review-command.js +2 -2
  95. package/dist/lib/task/review-finalization.js +1 -21
  96. package/dist/lib/task/review-fingerprint.js +0 -4
  97. package/dist/lib/task/secure-fs.js +0 -56
  98. package/dist/lib/task/short-id.js +1 -39
  99. package/dist/lib/task/skill-scope.js +0 -1
  100. package/dist/lib/task/verification-engine.js +58 -282
  101. package/dist/lib/task/verification.js +6 -3
  102. package/dist/lib/task/write.js +11 -1
  103. package/lib/agent-clients/adapter.ts +0 -76
  104. package/lib/agent-clients/adapters/antigravity-cli.ts +0 -5
  105. package/lib/agent-clients/adapters/claude-code-credentials.ts +0 -19
  106. package/lib/agent-clients/adapters/claude-code.ts +0 -9
  107. package/lib/agent-clients/adapters/codex.ts +0 -56
  108. package/lib/agent-clients/adapters/opencode.ts +0 -5
  109. package/lib/agent-clients/adapters/traecli.ts +0 -5
  110. package/lib/agent-clients/config.ts +20 -61
  111. package/lib/agent-clients/registry.ts +1 -34
  112. package/lib/agent-clients/schema.ts +0 -18
  113. package/lib/agent-clients/tokens.ts +1 -2
  114. package/lib/agent-clients/types.ts +0 -15
  115. package/lib/decide.ts +1 -0
  116. package/lib/git/worktree-identity.ts +0 -10
  117. package/lib/internal/agent-client.ts +2 -70
  118. package/lib/internal/cli-route-inventory.ts +1 -4
  119. package/lib/internal/manual-validation.ts +16 -9
  120. package/lib/internal/task-create.ts +0 -1
  121. package/lib/internal/task-event.ts +1 -5
  122. package/lib/internal/task-finalization.ts +0 -1
  123. package/lib/internal/task-lifecycle.ts +3 -5
  124. package/lib/internal/task-operation-registry.ts +3 -19
  125. package/lib/internal/task-review.ts +4 -4
  126. package/lib/platform/comment-safety.ts +0 -10
  127. package/lib/platform/github-provider.ts +0 -2
  128. package/lib/platform/in-label-sync.ts +0 -16
  129. package/lib/platform/issue-comments.ts +0 -21
  130. package/lib/platform/issues.ts +0 -2
  131. package/lib/platform/operation-coordinator.ts +4 -2
  132. package/lib/platform/pr-change-report.ts +0 -5
  133. package/lib/platform/pr-checks.ts +1 -10
  134. package/lib/platform/pr-review.ts +1 -1
  135. package/lib/platform/provider-bridge.ts +0 -1
  136. package/lib/platform/provider-loader.ts +1 -5
  137. package/lib/platform/provider-validation.ts +1 -49
  138. package/lib/platform/pull-requests.ts +64 -99
  139. package/lib/platform/releases.ts +1 -40
  140. package/lib/platform/verification-required.ts +0 -2
  141. package/lib/platform/verification-sync.ts +0 -94
  142. package/lib/process-data/normalize.ts +1 -186
  143. package/lib/process-data/privacy.ts +2 -19
  144. package/lib/process-data/sources.ts +3 -283
  145. package/lib/render.ts +0 -15
  146. package/lib/run/index.ts +13 -7
  147. package/lib/run/skills.ts +0 -4
  148. package/lib/sandbox/commands/create.ts +37 -74
  149. package/lib/sandbox/commands/list-running.ts +0 -19
  150. package/lib/sandbox/commands/start.ts +7 -10
  151. package/lib/sandbox/constants.ts +0 -8
  152. package/lib/sandbox/control/client.ts +0 -1
  153. package/lib/sandbox/control/container-identity.ts +1 -16
  154. package/lib/sandbox/control/executor.ts +1 -5
  155. package/lib/sandbox/control/identity-sentinel.ts +0 -8
  156. package/lib/sandbox/control/lifecycle.ts +18 -51
  157. package/lib/sandbox/control/native-file-lock.ts +0 -8
  158. package/lib/sandbox/control/protocol.ts +0 -2
  159. package/lib/sandbox/control/server.ts +13 -39
  160. package/lib/sandbox/control/state.ts +1 -10
  161. package/lib/sandbox/dockerfile.ts +0 -11
  162. package/lib/sandbox/engine.ts +0 -17
  163. package/lib/sandbox/engines/docker-exact-discovery.ts +1 -5
  164. package/lib/sandbox/engines/wsl2-paths.ts +0 -4
  165. package/lib/sandbox/recovery.ts +24 -26
  166. package/lib/sandbox/removal.ts +11 -696
  167. package/lib/sandbox/shell.ts +1 -1
  168. package/lib/sandbox/task-resolver.ts +0 -48
  169. package/lib/sandbox/tools.ts +1 -5
  170. package/lib/sandbox/workspace-view.ts +2 -4
  171. package/lib/server/display.ts +0 -14
  172. package/lib/server/process-control.ts +1 -1
  173. package/lib/task/activity-intent.ts +36 -44
  174. package/lib/task/activity-log.ts +9 -22
  175. package/lib/task/artifact-command.ts +1 -1
  176. package/lib/task/artifact-lifecycle.ts +58 -67
  177. package/lib/task/artifact-name.ts +0 -7
  178. package/lib/task/artifact-operations.ts +3 -3
  179. package/lib/task/capabilities.ts +5 -6
  180. package/lib/task/commands/log.ts +0 -7
  181. package/lib/task/commands/status.ts +14 -79
  182. package/lib/task/commit-operation.ts +29 -51
  183. package/lib/task/completion-facts.ts +45 -42
  184. package/lib/task/control-authority.ts +27 -317
  185. package/lib/task/control-recovery.ts +2 -5
  186. package/lib/task/create.ts +2 -5
  187. package/lib/task/decision-intents.ts +5 -5
  188. package/lib/task/delivery-target.ts +0 -40
  189. package/lib/task/events.ts +89 -123
  190. package/lib/task/finalization.ts +41 -9
  191. package/lib/task/ledger-intents.ts +1 -1
  192. package/lib/task/lifecycle-execution.ts +37 -89
  193. package/lib/task/lifecycle.ts +48 -44
  194. package/lib/task/local-artifact-finalization.ts +0 -7
  195. package/lib/task/manual-validation-evidence.ts +1 -47
  196. package/lib/task/platform-operation-recovery.ts +50 -26
  197. package/lib/task/pr-delivery-fact.ts +2 -11
  198. package/lib/task/qualification-confirmation.ts +6 -6
  199. package/lib/task/resolve-ref.ts +0 -23
  200. package/lib/task/review-command.ts +2 -2
  201. package/lib/task/review-finalization.ts +1 -27
  202. package/lib/task/review-fingerprint.ts +0 -8
  203. package/lib/task/secure-fs.ts +0 -42
  204. package/lib/task/short-id.ts +0 -43
  205. package/lib/task/skill-scope.ts +0 -1
  206. package/lib/task/verification-engine.ts +56 -287
  207. package/lib/task/verification.ts +5 -5
  208. package/lib/task/write.ts +12 -1
  209. package/package.json +2 -2
  210. package/templates/.agents/README.en.md +1 -3
  211. package/templates/.agents/README.zh-CN.md +0 -2
  212. package/templates/.agents/rules/README.en.md +0 -1
  213. package/templates/.agents/rules/README.zh-CN.md +0 -1
  214. package/templates/.agents/rules/commit-and-pr.en.md +1 -1
  215. package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -1
  216. package/templates/.agents/rules/no-mid-flow-questions.en.md +0 -10
  217. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +0 -10
  218. package/templates/.agents/rules/pr-sync.en.md +1 -1
  219. package/templates/.agents/rules/pr-sync.zh-CN.md +1 -1
  220. package/templates/.agents/rules/task-management.en.md +3 -3
  221. package/templates/.agents/rules/task-management.zh-CN.md +3 -3
  222. package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -3
  223. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -3
  224. package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
  225. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
  226. package/templates/.agents/skills/code-task/SKILL.en.md +2 -2
  227. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +4 -4
  228. package/templates/.agents/skills/commit/SKILL.en.md +2 -2
  229. package/templates/.agents/skills/commit/SKILL.zh-CN.md +2 -2
  230. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +6 -6
  231. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +6 -6
  232. package/templates/.agents/skills/plan-task/SKILL.en.md +2 -2
  233. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -2
  234. package/templates/.agents/skills/review-analysis/SKILL.en.md +3 -3
  235. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -3
  236. package/templates/.agents/skills/review-code/SKILL.en.md +5 -5
  237. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -5
  238. package/templates/.agents/skills/review-plan/SKILL.en.md +3 -3
  239. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -3
  240. package/templates/.agents/skills/review-pr/SKILL.en.md +4 -4
  241. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +4 -4
  242. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
  243. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
  244. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +3 -75
  245. package/templates/.agents/templates/task.en.md +1 -0
  246. package/templates/.agents/templates/task.zh-CN.md +1 -0
  247. package/templates/.claude/settings.json +0 -24
  248. package/templates/.codex/hooks.json +0 -50
  249. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +0 -582
  250. package/dist/lib/agent-clients/adapters/codex-lifecycle/binding.js +0 -114
  251. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +0 -237
  252. package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +0 -443
  253. package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +0 -236
  254. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +0 -292
  255. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +0 -267
  256. package/dist/lib/agent-clients/adapters/codex-orchestration.js +0 -466
  257. package/dist/lib/internal/task-orchestration.js +0 -71
  258. package/dist/lib/platform/verification.js +0 -6
  259. package/dist/lib/run/prompt.js +0 -2
  260. package/dist/lib/sandbox/engines/authority.js +0 -289
  261. package/dist/lib/task/delegation-receipts.js +0 -410
  262. package/dist/lib/task/orchestration.js +0 -1233
  263. package/dist/lib/task/verification-checks.js +0 -9
  264. package/dist/lib/task/workspace-snapshot.js +0 -121
  265. package/dist/lifecycle-build-manifest.json +0 -6
  266. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +0 -688
  267. package/lib/agent-clients/adapters/codex-lifecycle/binding.ts +0 -147
  268. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +0 -349
  269. package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +0 -506
  270. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +0 -385
  271. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +0 -41
  272. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +0 -418
  273. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +0 -354
  274. package/lib/agent-clients/adapters/codex-orchestration.ts +0 -630
  275. package/lib/internal/task-orchestration.ts +0 -74
  276. package/lib/platform/verification.ts +0 -11
  277. package/lib/run/prompt.ts +0 -1
  278. package/lib/sandbox/engines/authority.ts +0 -365
  279. package/lib/task/delegation-receipts.ts +0 -547
  280. package/lib/task/orchestration.ts +0 -1651
  281. package/lib/task/verification-checks.ts +0 -10
  282. package/lib/task/workspace-snapshot.ts +0 -144
  283. package/templates/.agents/hooks/lifecycle-delegation.js +0 -140
  284. package/templates/.agents/rules/lifecycle-orchestration.en.md +0 -44
  285. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +0 -44
  286. package/templates/.agents/skills/run-task/SKILL.en.md +0 -29
  287. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +0 -29
  288. package/templates/.agents/skills/run-task/config/verify.en.json +0 -10
  289. package/templates/.agents/skills/run-task/config/verify.zh-CN.json +0 -10
  290. package/templates/.agents/skills/run-task/reference/host-validation.en.md +0 -24
  291. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +0 -24
  292. package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +0 -6
  293. package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +0 -6
  294. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +0 -6
  295. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +0 -6
  296. package/templates/.claude/commands/run-task.en.md +0 -8
  297. package/templates/.claude/commands/run-task.zh-CN.md +0 -8
  298. package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +0 -3
  299. package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +0 -3
  300. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +0 -3
  301. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +0 -3
  302. package/templates/.opencode/commands/run-task.en.md +0 -11
  303. package/templates/.opencode/commands/run-task.zh-CN.md +0 -11
@@ -1,12 +1,12 @@
1
1
  import fs from 'node:fs';
2
2
  import { normalizeAgentToken } from '../agent-clients/tokens.js';
3
3
  import { resolveTaskRef } from './resolve-ref.js';
4
- import { locateActivityLog, appendActivityEntry } from './activity-log.js';
4
+ import { locateActivityLog, appendActivityEntry, inspectActivityLog } from './activity-log.js';
5
+ import { lifecycleExecutions, prReviewExecution, serializeLifecycleExecution } from './lifecycle-execution.js';
5
6
  import { assertWritableInventory, buildArtifactLinkSection, inspectTaskArtifacts } from './artifact-lifecycle.js';
6
7
  import { parseArtifactName } from './artifact-name.js';
7
8
  import { captureTaskWriteMetadata, writeTask } from './write.js';
8
9
  const HEAD_RE = /^[0-9a-f]{40}$/;
9
- const REVIEW_ACTION_RE = /^Review PR \(Round ([1-9]\d*)\)( \[started\])?$/;
10
10
  const VERDICTS = new Set(['approved', 'changes-requested', 'commented']);
11
11
  const OUTCOMES = new Set(['aborted', 'superseded']);
12
12
  const SUCCESS_STATUSES = new Set(['applied', 'no-op']);
@@ -76,47 +76,37 @@ function readSnapshot(taskRef, repoRoot) {
76
76
  catch (error) {
77
77
  return { ok: false, code: 'TASK_READ_FAILED', message: error instanceof Error ? error.message : String(error), taskId: resolved.taskId };
78
78
  }
79
- const activity = locateActivityLog(content);
80
- if (!activity)
81
- return { ok: false, code: 'ACTIVITY_SECTION_MISSING', message: 'task has no unique Activity Log section', taskId: resolved.taskId };
82
- const knownRounds = new Set(inventory.artifacts.map((artifact) => artifact.round));
83
- for (const entry of activity.entries) {
84
- const match = REVIEW_ACTION_RE.exec(entry.step);
85
- if (match && !knownRounds.has(Number(match[1]))) {
86
- return { ok: false, code: 'ACTIVITY_STATE_CONFLICT', message: `Activity Log round ${match[1]} has no canonical pr-review artifact`, taskId: resolved.taskId };
87
- }
79
+ let pr;
80
+ try {
81
+ pr = prReviewExecution(content);
82
+ }
83
+ catch (error) {
84
+ return { ok: false, code: 'ACTIVITY_STATE_CONFLICT', message: String(error), taskId: resolved.taskId };
88
85
  }
89
86
  const states = [];
90
87
  for (const identity of inventory.artifacts) {
91
88
  const parsed = parseArtifactRecord(identity);
92
89
  if ('code' in parsed)
93
90
  return { ok: false, ...parsed, taskId: resolved.taskId };
94
- const base = `Review PR (Round ${identity.round})`;
95
- const started = activity.entries.filter((entry) => entry.step === `${base} [started]`);
96
- const terminal = activity.entries.filter((entry) => entry.step === base);
97
- if (started.length > 1 || terminal.length > 1) {
98
- return { ok: false, code: 'ACTIVITY_STATE_CONFLICT', message: `${base} has duplicate started or terminal rows`, taskId: resolved.taskId };
99
- }
100
- if (terminal.length === 1 && started.length === 0 && !terminal[0].note.startsWith('receipt ')) {
101
- return { ok: false, code: 'ACTIVITY_STATE_CONFLICT', message: `${base} has a terminal row without a matching started row`, taskId: resolved.taskId };
91
+ const started = pr.open?.artifact === identity.name ? pr.open : null;
92
+ const terminals = pr.terminals.filter((entry) => entry.artifact === identity.name);
93
+ if (terminals.length > 1)
94
+ return { ok: false, code: 'ACTIVITY_STATE_CONFLICT', message: `duplicate PR review terminal for ${identity.name}`, taskId: resolved.taskId };
95
+ const terminal = terminals[0];
96
+ if (terminal) {
97
+ const matches = (terminal.note.startsWith('Verdict: ') && LOCAL_COMPLETION_STATUSES.has(parsed.status))
98
+ || (terminal.note.startsWith('Outcome: Aborted, ') && parsed.status === 'aborted')
99
+ || (terminal.note.startsWith('Outcome: Superseded, ') && parsed.status === 'superseded')
100
+ || (terminal.note.startsWith('receipt ') && SUCCESS_STATUSES.has(parsed.status));
101
+ if (!matches)
102
+ return { ok: false, code: 'ACTIVITY_STATE_CONFLICT', message: `terminal note conflicts with artifact status ${parsed.status}`, taskId: resolved.taskId };
102
103
  }
103
- if (terminal.length === 1) {
104
- const note = terminal[0].note;
105
- const terminalMatchesStatus = (note.startsWith('Verdict: ') && LOCAL_COMPLETION_STATUSES.has(parsed.status))
106
- || (note.startsWith('Outcome: Aborted, ') && parsed.status === 'aborted')
107
- || (note.startsWith('Outcome: Superseded, ') && parsed.status === 'superseded')
108
- || (note.startsWith('receipt ') && SUCCESS_STATUSES.has(parsed.status));
109
- if (!terminalMatchesStatus) {
110
- return { ok: false, code: 'ACTIVITY_STATE_CONFLICT', message: `${base} terminal note conflicts with artifact status ${parsed.status}`, taskId: resolved.taskId };
111
- }
112
- }
113
- states.push({
114
- ...parsed,
115
- startedAgent: started[0]?.agent ?? null,
116
- terminalAgent: terminal[0]?.agent ?? null,
117
- terminalNote: terminal[0]?.note ?? null
118
- });
104
+ if ((started && started.head !== parsed.head) || (terminal && terminal.head !== parsed.head))
105
+ return { ok: false, code: 'ACTIVITY_IDENTITY_MISMATCH', message: `head does not match ${identity.name}`, taskId: resolved.taskId };
106
+ states.push({ ...parsed, startedAgent: started?.agent ?? null, terminalAgent: terminal?.agent ?? null, terminalNote: terminal?.note ?? null });
119
107
  }
108
+ if ((pr.open && !states.some((state) => state.identity.name === pr.open.artifact)) || pr.terminals.some((entry) => !states.some((state) => state.identity.name === entry.artifact)))
109
+ return { ok: false, code: 'ACTIVITY_STATE_CONFLICT', message: 'PR review execution references a missing artifact', taskId: resolved.taskId };
120
110
  const openStates = states.filter((state) => state.startedAgent !== null && state.terminalNote === null);
121
111
  const preparedStates = states.filter((state) => state.status === 'pending' && state.startedAgent === null && state.terminalNote === null);
122
112
  if (openStates.length > 1 || preparedStates.length > 1) {
@@ -250,10 +240,16 @@ function applyActivityIntent(intent, normalizedAgent, options) {
250
240
  return failed(intent, 'METADATA_CAPTURE_FAILED', error instanceof Error ? error.message : String(error), state.taskId);
251
241
  }
252
242
  const section = locateActivityLog(state.content);
253
- const mutations = [{
254
- kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: section.heading,
255
- body: appendActivityEntry(section, { time: metadata.timestamp, step, agent: normalizedAgent, note })
256
- }];
243
+ const pr = prReviewExecution(state.content);
244
+ if (intent.kind === 'pr-review-start')
245
+ pr.open = { artifact: intent.artifact, head: intent.head, agent: normalizedAgent, startedAt: metadata.timestamp };
246
+ else {
247
+ pr.open = null;
248
+ pr.terminals.push({ artifact: intent.artifact, head: intent.head, agent: normalizedAgent, note });
249
+ }
250
+ const mutations = [{ kind: 'frontmatter', lifecycleExecutionDomain: 'pr-review', set: { lifecycle_execution: serializeLifecycleExecution(lifecycleExecutions(state.content), state.content, pr) } }];
251
+ if (section)
252
+ mutations.push({ kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: section.heading, body: appendActivityEntry(section, { time: metadata.timestamp, step, agent: normalizedAgent, note }) });
257
253
  if (linkArtifact) {
258
254
  const link = buildArtifactLinkSection(state.content, roundState.identity);
259
255
  mutations.push({ kind: 'section', aliases: link.aliases, heading: link.heading, body: link.body });
@@ -296,6 +292,15 @@ export function applyPrReviewActivityIntent(intent, options = {}) {
296
292
  if (resolved.state !== 'active') {
297
293
  return failed(intent, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
298
294
  }
299
- return applyActivityIntent(intent, validated.agent, { ...options, repoRoot: resolved.repoRoot });
295
+ let warnings = [];
296
+ try {
297
+ const inspected = inspectActivityLog(fs.readFileSync(resolved.taskMdPath, 'utf8'));
298
+ warnings = !inspected.section ? [{ code: 'ACTIVITY_LOG_UNAVAILABLE', message: 'Activity Log is missing or ambiguous' }] : inspected.invalidEntries.length ? [{ code: 'ACTIVITY_LOG_INVALID_ENTRIES', message: 'Activity Log contains malformed entries' }] : [];
299
+ }
300
+ catch { /* The operation reports read errors. */ }
301
+ const result = applyActivityIntent(intent, validated.agent, { ...options, repoRoot: resolved.repoRoot });
302
+ if (result.status === 'failed')
303
+ return result;
304
+ return warnings.length ? { ...result, warnings } : result;
300
305
  }
301
306
  //# sourceMappingURL=activity-intent.js.map
@@ -3,8 +3,6 @@ const ENTRY_RE = /^- (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}) — \*
3
3
  const STARTED_SUFFIX_RE = /\s*\[started\]\s*$/;
4
4
  const ABORTED_SUFFIX_RE = /\s*\[aborted\]\s*$/;
5
5
  const ATTEMPT_RE = /(?:^|;\s*)attempt=([A-Za-z0-9-]{8,64})(?:;|$)/;
6
- const COMMIT_STARTED_RE = /^started; attempt=([A-Za-z0-9-]{8,64}); baseline=([a-f0-9]{40,64}); agent=([a-z-]+)$/;
7
- const LIFECYCLE_STEP_RE = /^(?:Complete Manual Validation|(?:Analyze Task|Review Analysis|Plan Task|Review Plan|Code Task|Review Code|Run Manual Validation) \(Round [1-9]\d*(?:(?:, fix for review-code(?:-r[2-9]|-r[1-9]\d+)?\.md)|(?:, decision II-[1-9]\d*))?\))$/;
8
6
  function parseActivityLog(content) {
9
7
  const section = locateActivityLog(content);
10
8
  return section ? { sectionFound: true, entries: section.entries } : { sectionFound: false, entries: [] };
@@ -33,6 +31,14 @@ function inspectActivityLog(content) {
33
31
  const body = content.slice(heading.end + 1, end).replace(/\r\n/g, '\n').replace(/^\n+|\n+$/g, '');
34
32
  return { section: { heading: heading.text, body, entries }, invalidEntries };
35
33
  }
34
+ function activityWarnings(content) {
35
+ const inspected = inspectActivityLog(content);
36
+ if (!inspected.section)
37
+ return [{ code: 'ACTIVITY_LOG_UNAVAILABLE', message: 'Activity Log is missing or ambiguous; repair the section to resume logging' }];
38
+ if (inspected.invalidEntries.length)
39
+ return [{ code: 'ACTIVITY_LOG_INVALID_ENTRIES', message: 'Activity Log contains malformed entries; repair the entries' }];
40
+ return [];
41
+ }
36
42
  function locateActivityLog(content) {
37
43
  const { section } = inspectActivityLog(content);
38
44
  if (!section)
@@ -47,13 +53,6 @@ function appendActivityEntry(section, entry) {
47
53
  function activityAttempt(note) {
48
54
  return ATTEMPT_RE.exec(note)?.[1] ?? null;
49
55
  }
50
- function parseCommitAttemptStarted(note) {
51
- const match = COMMIT_STARTED_RE.exec(note);
52
- return match ? { attempt: match[1], baseline: match[2], agent: match[3] } : null;
53
- }
54
- function commitAttemptStartedNote(attempt) {
55
- return `started; attempt=${attempt.attempt}; baseline=${attempt.baseline}; agent=${attempt.agent}`;
56
- }
57
56
  function pairEntries(entries) {
58
57
  const rows = [];
59
58
  const open = new Map();
@@ -94,11 +93,5 @@ function pairEntries(entries) {
94
93
  function startedBackedRows(rows) {
95
94
  return rows.filter((row) => row.started !== '');
96
95
  }
97
- function hasOpenLifecycleExecution(content) {
98
- const section = locateActivityLog(content);
99
- if (!section)
100
- return false;
101
- return pairEntries(section.entries).some((row) => row.done === '' && LIFECYCLE_STEP_RE.test(row.step));
102
- }
103
- export { activityAttempt, appendActivityEntry, hasOpenLifecycleExecution, commitAttemptStartedNote, inspectActivityLog, locateActivityLog, pairEntries, parseActivityLog, parseCommitAttemptStarted, startedBackedRows };
96
+ export { activityAttempt, activityWarnings, appendActivityEntry, inspectActivityLog, locateActivityLog, pairEntries, parseActivityLog, startedBackedRows };
104
97
  //# sourceMappingURL=activity-log.js.map
@@ -88,7 +88,7 @@ export function executeArtifactCommand(command, options = {}) {
88
88
  }
89
89
  catch { /* an absent candidate is created only for the selected identity */ }
90
90
  if ((context.status !== 'ready' && !matchingInitializedSkeleton) || (context.next?.name !== artifact
91
- && !hasOpenArtifactRound(fs.readFileSync(resolved.taskMdPath, 'utf8'), family, parsed.round)
91
+ && !hasOpenArtifactRound(taskDir, fs.readFileSync(resolved.taskMdPath, 'utf8'), family, parsed.round)
92
92
  && !matchingInitializedSkeleton)) {
93
93
  return fail('ARTIFACT_INIT_CONTEXT_INVALID', context.error?.message ?? `artifact '${artifact}' is not the next ${family} artifact`);
94
94
  }
@@ -2,46 +2,42 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { ARTIFACT_FAMILY_CATALOG, artifactName, parseArtifactName } from './artifact-name.js';
4
4
  import { resolveTaskRef } from './resolve-ref.js';
5
- import { locateActivityLog, pairEntries, startedBackedRows } from './activity-log.js';
5
+ import { lifecycleExecutions } from './lifecycle-execution.js';
6
6
  import { parseImplementationInputs, selectPendingImplementationInput } from './implementation-inputs.js';
7
7
  import { parseTypedTaskFrontmatter } from './frontmatter.js';
8
8
  import { parseVerdict } from './review-artifacts.js';
9
9
  import { extractSection, findSectionHeading } from './sections.js';
10
10
  import { receiptForOutput, receiptsForOutput, sha256File } from './artifact-receipts.js';
11
- import { artifactCompletionOrder, hasArtifactCompletionFact, hasArtifactCompletionLog, parseCompletionFacts } from './completion-facts.js';
11
+ import { hasArtifactCompletionRecord, hasValidArtifactCompletion } from './completion-facts.js';
12
12
  import { inspectReviewIdentity } from './review-identity.js';
13
13
  import { parseLifecyclePathDecision } from './lifecycle-path.js';
14
14
  import { parseReworkIntentDocument } from './rework-intent.js';
15
15
  import { selectArtifactDisposition } from './artifact-selection.js';
16
16
  import { executionInputsMatchLatest } from './execution-inputs.js';
17
17
  const artifactFamilyCatalog = ARTIFACT_FAMILY_CATALOG;
18
- const ARTIFACT_STEPS = {
19
- analysis: 'Analyze Task', 'review-analysis': 'Review Analysis',
20
- plan: 'Plan Task', 'review-plan': 'Review Plan',
21
- code: 'Code Task', 'review-code': 'Review Code'
22
- };
23
18
  /** Init, repair and finalization share the same authoritative round identity. */
24
- export function hasOpenArtifactRound(content, family, round) {
25
- return openArtifactRounds(content, family).includes(round);
19
+ export function hasOpenArtifactRound(taskDir, content, family, round) {
20
+ return openArtifactRounds(taskDir, content, family).includes(round);
26
21
  }
27
- function openArtifactRounds(content, family) {
28
- const activity = locateActivityLog(content);
29
- const step = ARTIFACT_STEPS[family];
30
- if (!activity || !step)
31
- return [];
32
- const qualifier = family === 'code'
33
- ? '(?:, (?:fix for review-code(?:-r(?:[2-9]|[1-9]\\d+))?\\.md|decision II-[1-9]\\d*))?'
34
- : '';
35
- const expected = new RegExp(`^${step} \\(Round ([1-9]\\d*)${qualifier}\\)$`);
36
- return [...new Set(startedBackedRows(pairEntries(activity.entries))
37
- .filter((row) => expected.test(row.step) && !row.done)
38
- .map((row) => Number(expected.exec(row.step)?.[1])))].sort((left, right) => left - right);
22
+ function openArtifactRounds(taskDir, content, family) {
23
+ const executionFamily = family === 'analysis' ? 'analyze' : family;
24
+ const event = completionEvent(family);
25
+ return [...new Set(lifecycleExecutions(content)
26
+ .filter((item) => item.family === executionFamily && item.round !== undefined)
27
+ .flatMap((item) => {
28
+ const round = item.round;
29
+ const name = artifactName(family, round);
30
+ if (!event || !hasArtifactCompletionRecord(content, name, event))
31
+ return [round];
32
+ if (!hasValidArtifactCompletion(content, taskDir, name, event))
33
+ throw new Error(`completion evidence for started ${family} Round ${round} is invalid`);
34
+ return [];
35
+ }))].sort((left, right) => left - right);
39
36
  }
40
- function hasCompletedArtifactRound(content, family, round, name) {
37
+ function hasCompletedArtifactRound(taskDir, content, family, round, name) {
41
38
  const event = completionEvent(family);
42
39
  return Boolean(event && parseArtifactName(name)?.round === round
43
- && artifactCompletionOrder(content, name, event) !== null
44
- && hasArtifactCompletionFact(content, name, event));
40
+ && hasValidArtifactCompletion(content, taskDir, name, event));
45
41
  }
46
42
  function completionEvent(family) {
47
43
  if (family === 'analysis')
@@ -153,7 +149,16 @@ function inspectArtifactDirectory(taskDir, family, identity = {}) {
153
149
  }
154
150
  artifacts.sort((left, right) => left.round - right.round);
155
151
  knownArtifacts.sort((left, right) => left.round - right.round);
156
- const openRounds = openArtifactRounds(taskContent, family);
152
+ let openRounds;
153
+ try {
154
+ openRounds = openArtifactRounds(taskDir, taskContent, family);
155
+ }
156
+ catch (error) {
157
+ return failure(requestRef, family, {
158
+ code: 'ARTIFACT_SELECTION_INDETERMINATE',
159
+ message: error instanceof Error ? error.message : String(error)
160
+ }, { taskId: identity.taskId ?? null, taskDir, taskState: identity.taskState ?? null });
161
+ }
157
162
  const knownRounds = new Set(knownArtifacts.map((item) => item.round));
158
163
  for (const round of openRounds)
159
164
  knownRounds.add(round);
@@ -166,11 +171,8 @@ function inspectArtifactDirectory(taskDir, family, identity = {}) {
166
171
  }
167
172
  const event = completionEvent(family);
168
173
  const completed = event
169
- ? artifacts.flatMap((artifact) => {
170
- const order = artifactCompletionOrder(taskContent, artifact.name, event);
171
- return order !== null && hasArtifactCompletionFact(taskContent, artifact.path, event)
172
- ? [{ artifact, order }] : [];
173
- }).sort((left, right) => left.order - right.order).map(({ artifact }) => artifact)
174
+ ? artifacts.filter((artifact) => hasValidArtifactCompletion(taskContent, taskDir, artifact.name, event))
175
+ .sort((left, right) => left.round - right.round)
174
176
  : [];
175
177
  if (openRounds.length > 1)
176
178
  diagnostics.push(diagnostic('MULTIPLE_OPEN_ROUNDS', family, null, `multiple incomplete ${family} rounds are open: ${openRounds.join(', ')}`));
@@ -321,8 +323,8 @@ function hasSelectionEvidence(content, family, options, context, pathState) {
321
323
  const reportContent = fs.readFileSync(context.latest.path, 'utf8');
322
324
  const identity = inspectReviewIdentity(context.taskDir, reportContent, options.repoRoot);
323
325
  return identity.status !== 'matched'
324
- || !hasArtifactCompletionFact(content, context.latest.path, 'review-code.completed')
325
- || !hasArtifactCompletionLog(content, context.latest.name, 'review-code.completed');
326
+ || !context.taskDir
327
+ || !hasValidArtifactCompletion(content, context.taskDir, context.latest.name, 'review-code.completed');
326
328
  }
327
329
  catch {
328
330
  return true;
@@ -366,14 +368,11 @@ function attachArtifactSelection(context, pathState, options) {
366
368
  });
367
369
  if (!context.next)
368
370
  return fail('ARTIFACT_SELECTION_INDETERMINATE', 'artifact inventory did not provide a writable identity');
369
- let taskContent;
370
- try {
371
- taskContent = fs.readFileSync(path.join(context.taskDir, 'task.md'), 'utf8');
372
- }
373
- catch (error) {
374
- return fail('ARTIFACT_SELECTION_INDETERMINATE', error instanceof Error ? error.message : String(error));
375
- }
376
- const openRounds = openArtifactRounds(taskContent, family);
371
+ const state = readArtifactSelectionState(context.taskDir, family);
372
+ if (!state.ok)
373
+ return fail('ARTIFACT_SELECTION_INDETERMINATE', state.message);
374
+ const taskContent = state.content;
375
+ const openRounds = state.openRounds;
377
376
  if (openRounds.length > 1)
378
377
  return fail('ARTIFACT_TOPOLOGY_CONFLICT', `multiple incomplete ${family} rounds are open: ${openRounds.join(', ')}`);
379
378
  const openRound = openRounds[0] ?? null;
@@ -399,6 +398,15 @@ function attachArtifactSelection(context, pathState, options) {
399
398
  return fail('ARTIFACT_SELECTION_INDETERMINATE', error instanceof Error ? error.message : String(error));
400
399
  }
401
400
  }
401
+ function readArtifactSelectionState(taskDir, family) {
402
+ try {
403
+ const content = fs.readFileSync(path.join(taskDir, 'task.md'), 'utf8');
404
+ return { ok: true, content, openRounds: openArtifactRounds(taskDir, content, family) };
405
+ }
406
+ catch (error) {
407
+ return { ok: false, message: error instanceof Error ? error.message : String(error) };
408
+ }
409
+ }
402
410
  function resolveArtifactContextFromTask(taskRef, family, options, resolvedTask) {
403
411
  const inventory = inspectTaskArtifacts(taskRef, family, options, resolvedTask);
404
412
  if (inventory.status === 'failed')
@@ -469,9 +477,6 @@ function resolveArtifactContextFromTask(taskRef, family, options, resolvedTask)
469
477
  function resolveArtifactContext(taskRef, family, options = {}) {
470
478
  return resolveArtifactContextFromTask(taskRef, family, options);
471
479
  }
472
- export function resolveArtifactContextForResolvedTask(resolvedTask, family, options = {}) {
473
- return resolveArtifactContextFromTask(resolvedTask.taskId, family, options, resolvedTask);
474
- }
475
480
  function resolveCodeContext(inventory, options, resolvedTask) {
476
481
  const taskRef = inventory.requestRef;
477
482
  const pathState = lifecyclePathState(inventory.taskDir);
@@ -486,8 +491,7 @@ function resolveCodeContext(inventory, options, resolvedTask) {
486
491
  const taskContent = fs.readFileSync(path.join(inventory.taskDir, 'task.md'), 'utf8');
487
492
  const hasPendingImplementation = parseImplementationInputs(taskContent).rows.some((input) => input.needsImplementation && input.status === 'pending');
488
493
  if (hasPendingImplementation && !reviewCode.latest) {
489
- const candidate = reviewCode.artifacts.at(-1)?.name ?? 'review-code.md';
490
- return contextFailure(inventory, 'ARTIFACT_REFERENCE_INVALID', `cannot find completed Activity Log identity for ${candidate}`);
494
+ return contextFailure(inventory, 'ARTIFACT_REFERENCE_INVALID', 'no valid review-code completion fact is available for pending implementation inputs');
491
495
  }
492
496
  }
493
497
  catch (error) {
@@ -508,12 +512,10 @@ function resolveCodeContext(inventory, options, resolvedTask) {
508
512
  const review = reviewCode.latest;
509
513
  const openInputs = review ? [...inputs, review] : inputs;
510
514
  const openVerdict = review ? parseVerdict(review.path) : null;
511
- const openRow = locateActivityLog(taskContent)
512
- ? startedBackedRows(pairEntries(locateActivityLog(taskContent).entries))
513
- .find((row) => row.step.startsWith(`Code Task (Round ${openRound}`) && !row.done)
514
- : null;
515
- const decisionInput = /, decision (II-[1-9]\d*)\)$/u.exec(openRow?.step ?? '')?.[1] ?? null;
516
- const fixInput = /, fix for (review-code(?:-r\d+)?\.md)\)$/u.exec(openRow?.step ?? '')?.[1] ?? null;
515
+ const openRow = lifecycleExecutions(taskContent)
516
+ .find((row) => row.family === 'code' && row.round === openRound);
517
+ const decisionInput = openRow?.implementationInput ?? null;
518
+ const fixInput = openRow?.fixFor ?? null;
517
519
  const mode = decisionInput ? 'decision'
518
520
  : fixInput || (openVerdict?.ok && openVerdict.verdict === 'Changes Requested') ? 'fix'
519
521
  : 'init';
@@ -544,7 +546,7 @@ function resolveCodeContext(inventory, options, resolvedTask) {
544
546
  const verdict = parseVerdict(review.path);
545
547
  if (!verdict.ok)
546
548
  return contextFailure(inventory, 'ARTIFACT_VERDICT_INVALID', `${verdict.code}: ${verdict.message}`, review.name);
547
- const decision = resolveDecisionImplementationInput(inventory, review);
549
+ const decision = resolveDecisionImplementationInput(reviewCode, review);
548
550
  if ('error' in decision)
549
551
  return contextFailure(inventory, 'ARTIFACT_REFERENCE_INVALID', decision.error, review.name);
550
552
  if (decision.input) {
@@ -598,12 +600,10 @@ function resolveDecisionImplementationInput(inventory, review) {
598
600
  const pending = rows.filter((row) => row.needsImplementation && row.status === 'pending');
599
601
  if (pending.length === 0)
600
602
  return { input: null };
601
- const activity = locateActivityLog(content);
602
- if (!activity)
603
- return { error: 'task has no unique Activity Log section' };
604
- const completed = activity.entries.some((entry) => /^Review Code \(Round \d+\)$/.test(entry.step) && entry.note.includes(`→ ${review.name}`));
605
- if (!completed)
606
- return { error: `cannot find completed Activity Log identity for ${review.name}` };
603
+ if (inventory.latest?.name !== review.name
604
+ || !inventory.completed.some((artifact) => artifact.name === review.name)) {
605
+ return { error: `Review Code completion facts do not validate ${review.name}` };
606
+ }
607
607
  try {
608
608
  return { input: selectPendingImplementationInput(rows) };
609
609
  }
@@ -653,14 +653,12 @@ export function validateArtifactPublication(taskDir, family, name) {
653
653
  if (existing) {
654
654
  try {
655
655
  const task = fs.readFileSync(path.join(taskDir, 'task.md'), 'utf8');
656
- if (hasCompletedArtifactRound(task, family, existing.round, name)) {
656
+ if (hasCompletedArtifactRound(taskDir, task, family, existing.round, name)) {
657
657
  return { code: 'ARTIFACT_IDENTITY_INVALID', message: `completed artifact '${name}' is immutable` };
658
658
  }
659
659
  const event = completionEvent(family);
660
- const frontmatter = parseTypedTaskFrontmatter(task);
661
- const completionFactExists = event !== null && parseCompletionFacts(frontmatter.completion_facts)
662
- .some((fact) => fact.event === event && fact.output === name);
663
- if (event && completionFactExists && artifactCompletionOrder(task, name, event) !== null) {
660
+ const completionFactExists = event !== null && hasArtifactCompletionRecord(task, name, event);
661
+ if (event && completionFactExists) {
664
662
  return { code: 'ARTIFACT_IDENTITY_INVALID', message: `completion evidence for '${name}' does not match its current content` };
665
663
  }
666
664
  }
@@ -26,11 +26,5 @@ export function parseArtifactName(name) {
26
26
  return null;
27
27
  return { family: match[1], round, name };
28
28
  }
29
- export function maxArtifactRound(names, family) {
30
- return names.reduce((max, name) => {
31
- const identity = parseArtifactName(name);
32
- return identity?.family === family ? Math.max(max, identity.round) : max;
33
- }, 0);
34
- }
35
29
  export { ARTIFACT_FAMILY_CATALOG };
36
30
  //# sourceMappingURL=artifact-name.js.map
@@ -59,7 +59,7 @@ function stripHtmlComments(content) {
59
59
  function diagnostic(code, message, sectionId, line) {
60
60
  return { code, message, sectionId, line };
61
61
  }
62
- function sectionHeadings(schema, section) {
62
+ function sectionHeadings(section) {
63
63
  return [section.headings.zh, section.headings.en];
64
64
  }
65
65
  function canonicalContent(content) {
@@ -82,7 +82,7 @@ function inspectArtifactStructure(content, schema) {
82
82
  diagnostics.push(diagnostic('ARTIFACT_UNCLOSED_FENCE', 'artifact contains an unclosed Markdown fence', null, null));
83
83
  const headingsBySection = new Map();
84
84
  for (const section of schema.sections) {
85
- const aliases = sectionHeadings(schema, section);
85
+ const aliases = sectionHeadings(section);
86
86
  const exact = scanned.headings.filter((heading) => heading.level === 2 && aliases.includes(heading.text));
87
87
  const punctuated = scanned.headings.filter((heading) => heading.level === 2 && aliases.some((alias) => heading.text === `${alias}:` || heading.text === `${alias}:`));
88
88
  headingsBySection.set(section.id, exact.length > 0 ? exact : punctuated);
@@ -182,7 +182,7 @@ function inspectArtifactStateCheck(content, schema, scopeToStateCheckSection) {
182
182
  };
183
183
  }
184
184
  const scanned = scanVisibleMarkdown(content);
185
- const headings = scanned.headings.filter((heading) => (heading.level === 2 && sectionHeadings(schema, stateCheck).includes(heading.text)));
185
+ const headings = scanned.headings.filter((heading) => (heading.level === 2 && sectionHeadings(stateCheck).includes(heading.text)));
186
186
  if (headings.length !== 1) {
187
187
  return { ok: true, family: schema.family, semanticDigest: canonicalSemanticDigest(content), diagnostics: [] };
188
188
  }
@@ -7,11 +7,11 @@ import { parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from '.
7
7
  import { parseReviewSummary, resolveCanonicalVerdict } from './review-artifacts.js';
8
8
  import { parseReworkIntentDocument } from './rework-intent.js';
9
9
  import { sha256File } from './artifact-receipts.js';
10
- import { hasOpenLifecycleExecution } from './activity-log.js';
10
+ import { lifecycleExecutions } from './lifecycle-execution.js';
11
11
  import { parseQualificationAudit, parseTaskQualification } from './qualification-audit.js';
12
12
  import { parseLifecyclePathDecision, pathIncludes } from './lifecycle-path.js';
13
13
  import { parseImplementationInputs } from './implementation-inputs.js';
14
- import { hasArtifactCompletionFact, hasArtifactCompletionLog } from './completion-facts.js';
14
+ import { hasValidArtifactCompletion } from './completion-facts.js';
15
15
  import { inspectReviewIdentity } from './review-identity.js';
16
16
  import { inspectArtifactDirectory } from './artifact-lifecycle.js';
17
17
  import { executionInputsMatchLatest as executionReceiptsMatchLatest } from './execution-inputs.js';
@@ -389,8 +389,7 @@ function buildLifecycleFacts(taskDir, content, taskState = 'active', executionBu
389
389
  if (family === 'review-code') {
390
390
  const identity = inspectReviewIdentity(taskDir, reviewContent, repositoryRoot);
391
391
  reviewCodeIdentityValid = identity.status === 'matched'
392
- && hasArtifactCompletionFact(content, path.join(taskDir, latest), 'review-code.completed')
393
- && hasArtifactCompletionLog(content, latest, 'review-code.completed');
392
+ && hasValidArtifactCompletion(content, taskDir, latest, 'review-code.completed');
394
393
  }
395
394
  const parsed = parseReviewSummary(reviewContent);
396
395
  if (!parsed.ok)
@@ -455,7 +454,7 @@ function buildLifecycleFacts(taskDir, content, taskState = 'active', executionBu
455
454
  reviewedInputs, executionInputs, artifactHashes,
456
455
  reworkIntents: rework.intents,
457
456
  unresolvedLedger,
458
- executionBusy: executionBusy || hasOpenLifecycleExecution(content),
457
+ executionBusy: lifecycleExecutions(content).some((item) => item.family !== 'other') || executionBusy,
459
458
  recommendedAction: null,
460
459
  qualificationStale: qualificationStaleArtifacts.length > 0,
461
460
  qualificationStaleArtifacts,
@@ -84,12 +84,6 @@ function humanValidationCount(note) {
84
84
  function isHumanCountField(field) {
85
85
  return fieldNumber(field, 'manual-validation') !== undefined || fieldNumber(field, 'human-decision') !== undefined;
86
86
  }
87
- // A step is human-executed when its agent token is not a known AI token.
88
- // Known long names classify as AI; empty or unknown tokens classify as non-AI
89
- // (human).
90
- export function isHumanAgent(agent) {
91
- return classifyAgent(agent).status !== 'ai';
92
- }
93
87
  // Fold the two human counts into a review row's verdict NOTE: comma-joined, right
94
88
  // after the blockers/major/minor list and before the ` → artifact` link, mirroring
95
89
  // the review count line. Review done notes already carry `Manual-validation` as a
@@ -9,7 +9,8 @@ import { parseTaskFrontmatter, extractTitle } from '../frontmatter.js';
9
9
  import { loadShortIdByTaskId } from '../short-id.js';
10
10
  import { getOpenWorkflowWarnings, formatWorkflowWarningSummary } from '../workflow-warnings.js';
11
11
  import { parseActivityLog, pairEntries, startedBackedRows } from '../activity-log.js';
12
- import { readRun } from '../orchestration.js';
12
+ import { lifecycleExecutions } from '../lifecycle-execution.js';
13
+ import { parseTypedTaskFrontmatter } from '../frontmatter.js';
13
14
  import { statusCard } from '../../server/display.js';
14
15
  import { buildLifecycleFacts, recommendNext } from '../capabilities.js';
15
16
  const USAGE = `Usage: ai task status [--task <ref> | -t <ref>]
@@ -151,6 +152,18 @@ function parseActivityTime(value) {
151
152
  const epoch = Date.parse(value.replace(' ', 'T'));
152
153
  return Number.isFinite(epoch) ? epoch : Number.NaN;
153
154
  }
155
+ function collectStructuredWorkflow(content, now) {
156
+ try {
157
+ const current = lifecycleExecutions(content).filter((row) => row.family !== 'other').at(-1);
158
+ if (!current)
159
+ return { state: 'idle', step: DASH, agent: DASH, startedAt: DASH, doneAt: DASH, stale: DASH };
160
+ const started = parseActivityTime(current.startedAt);
161
+ return { state: 'in-progress', step: current.family, agent: current.agent, startedAt: current.startedAt, doneAt: DASH, stale: Number.isFinite(started) ? (now.getTime() - started > STALE_MS ? 'yes' : 'no') : 'unknown' };
162
+ }
163
+ catch {
164
+ return { state: 'unknown', step: DASH, agent: DASH, startedAt: DASH, doneAt: DASH, stale: DASH };
165
+ }
166
+ }
154
167
  function collectWorkflow(content, now = new Date()) {
155
168
  const parsed = parseActivityLog(content);
156
169
  if (!parsed.sectionFound || parsed.entries.length === 0) {
@@ -264,38 +277,6 @@ function collectRuntime(taskDir, workflow, run) {
264
277
  log: record.log_file
265
278
  };
266
279
  }
267
- function pendingDelegationInfo(receipt) {
268
- if (!receipt)
269
- return null;
270
- return {
271
- id: receipt.id,
272
- role: receipt.role,
273
- stage: receipt.stage,
274
- round: receipt.round,
275
- artifact: receipt.artifact,
276
- client: receipt.client,
277
- status: receipt.status
278
- };
279
- }
280
- function collectOrchestration(taskDir) {
281
- const run = readRun(taskDir);
282
- if (!run) {
283
- return {
284
- status: 'absent',
285
- runId: DASH,
286
- nextStage: DASH,
287
- pause: null,
288
- pendingDelegation: null
289
- };
290
- }
291
- return {
292
- status: run.status,
293
- runId: run.runId,
294
- nextStage: run.nextStage ?? DASH,
295
- pause: run.pause,
296
- pendingDelegation: pendingDelegationInfo(run.pendingDelegation)
297
- };
298
- }
299
280
  // Indent each label/value pair by two spaces and pad labels to a common width so
300
281
  // every section reads as an aligned "key value" block.
301
282
  function renderPairs(rows) {
@@ -323,21 +304,6 @@ function renderStatus(model) {
323
304
  ['action', model.recommendation ?? DASH],
324
305
  ['reason', model.recommendationReason ?? DASH]
325
306
  ]));
326
- lines.push('', 'Orchestration', ...renderPairs([
327
- ['status', model.orchestration.status],
328
- ['run_id', model.orchestration.runId],
329
- ['next_stage', model.orchestration.nextStage],
330
- ['pause_code', model.orchestration.pause?.code ?? DASH],
331
- ['pause_message', model.orchestration.pause?.message ?? DASH],
332
- ['recoverable', model.orchestration.pause ? String(model.orchestration.pause.recoverable) : DASH],
333
- ['pending_id', model.orchestration.pendingDelegation?.id ?? DASH],
334
- ['pending_role', model.orchestration.pendingDelegation?.role ?? DASH],
335
- ['pending_stage', model.orchestration.pendingDelegation?.stage ?? DASH],
336
- ['pending_round', model.orchestration.pendingDelegation ? String(model.orchestration.pendingDelegation.round) : DASH],
337
- ['pending_artifact', model.orchestration.pendingDelegation?.artifact ?? DASH],
338
- ['pending_client', model.orchestration.pendingDelegation?.client ?? DASH],
339
- ['pending_status', model.orchestration.pendingDelegation?.status ?? DASH]
340
- ]));
341
307
  lines.push('', 'Workflow', ...renderPairs([
342
308
  ['state', model.workflow.state],
343
309
  ['step', model.workflow.step],
@@ -367,12 +333,6 @@ function renderStatus(model) {
367
333
  return lines;
368
334
  }
369
335
  function modelTone(model) {
370
- if (model.orchestration.status === 'paused')
371
- return 'warning';
372
- if (model.orchestration.status === 'running')
373
- return 'running';
374
- if (model.orchestration.status === 'completed')
375
- return 'success';
376
336
  if (model.workflow.state === 'in-progress')
377
337
  return model.workflow.stale === 'yes' ? 'warning' : 'running';
378
338
  if (model.metadata.some(([key, value]) => key === 'status' && value === 'completed'))
@@ -387,7 +347,8 @@ function buildFromResolved(input) {
387
347
  const fm = parseTaskFrontmatter(content);
388
348
  const run = input.run ?? makeRunner(input.repoRoot);
389
349
  const artifacts = enumerateArtifacts(input.taskDir);
390
- const workflow = collectWorkflow(content, input.now);
350
+ const workflow = parseTypedTaskFrontmatter(content).lifecycle_execution !== undefined
351
+ ? collectStructuredWorkflow(content, input.now ?? new Date()) : collectWorkflow(content, input.now);
391
352
  const facts = buildLifecycleFacts(input.taskDir, content, fm.status ?? 'active', false, input.repoRoot);
392
353
  const next = facts.ok ? recommendNext(facts.facts) : null;
393
354
  const recommendation = next?.action ?? DASH;
@@ -400,7 +361,6 @@ function buildFromResolved(input) {
400
361
  artifacts: { count: artifacts.length, groups: groupArtifacts(artifacts) },
401
362
  workflow,
402
363
  runtime: collectRuntime(input.taskDir, workflow, run),
403
- orchestration: collectOrchestration(input.taskDir),
404
364
  recommendation,
405
365
  recommendationReason: next?.reasonCode ?? 'TASK_CAPABILITY_FACTS_INVALID',
406
366
  lifecyclePath: facts.ok && facts.facts.pathState?.status === 'valid' ? facts.facts.pathState.decision.path : facts.ok ? facts.facts.pathState?.status ?? DASH : DASH,
@@ -426,9 +386,6 @@ function buildStatusModel(refOrInput, options = {}) {
426
386
  }
427
387
  function statusModelToDisplay(model) {
428
388
  return statusCard(`Task ${model.taskId} (${model.shortId})`, modelTone(model), [
429
- ['orchestration', model.orchestration.status],
430
- ['run', model.orchestration.runId],
431
- ['next', model.orchestration.nextStage],
432
389
  ['workflow', model.workflow.state],
433
390
  ['step', model.workflow.step],
434
391
  ['runtime', model.runtime.status],
@@ -463,5 +420,5 @@ function status(args = []) {
463
420
  process.stdout.write(`${line}\n`);
464
421
  }
465
422
  }
466
- export { status, makeRunner, buildStatusModel, collectMetadata, groupArtifacts, collectGit, collectWorkflow, collectRuntime, collectOrchestration, renderStatus, statusModelToDisplay, METADATA_KEYS };
423
+ export { status, makeRunner, buildStatusModel, collectMetadata, groupArtifacts, collectGit, collectWorkflow, collectRuntime, renderStatus, statusModelToDisplay, METADATA_KEYS };
467
424
  //# sourceMappingURL=status.js.map