@fitlab-ai/agent-infra 0.9.13 → 0.9.14

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 (280) hide show
  1. package/bin/cli.ts +26 -9
  2. package/dist/bin/cli.js +173 -157
  3. package/dist/bin/internal-cli.js +42 -21
  4. package/dist/lib/agent-client.js +5 -0
  5. package/dist/lib/internal/agent-client.js +5 -2
  6. package/dist/lib/internal/cli-route-inventory.js +152 -0
  7. package/dist/lib/internal/codex-lifecycle.js +9 -6
  8. package/dist/lib/internal/codex-sandbox-controller.js +8 -2
  9. package/dist/lib/internal/git-workflow.js +10 -6
  10. package/dist/lib/internal/platform-checks.js +16 -4
  11. package/dist/lib/internal/platform-comment.js +16 -4
  12. package/dist/lib/internal/platform-context.js +4 -1
  13. package/dist/lib/internal/platform-issue.js +12 -4
  14. package/dist/lib/internal/platform-metadata.js +88 -0
  15. package/dist/lib/internal/platform-pr-review.js +16 -3
  16. package/dist/lib/internal/platform-pr.js +24 -10
  17. package/dist/lib/internal/platform-release-notes.js +6 -3
  18. package/dist/lib/internal/platform-security.js +93 -0
  19. package/dist/lib/internal/pr-review-grade.js +14 -3
  20. package/dist/lib/internal/release-workflow.js +19 -5
  21. package/dist/lib/internal/sandbox-control.js +22 -5
  22. package/dist/lib/internal/task-activity.js +13 -4
  23. package/dist/lib/internal/task-artifact.js +186 -27
  24. package/dist/lib/internal/task-context.js +4 -1
  25. package/dist/lib/internal/task-create.js +48 -15
  26. package/dist/lib/internal/task-delivery.js +4 -1
  27. package/dist/lib/internal/task-event.js +4 -1
  28. package/dist/lib/internal/task-finalization.js +4 -1
  29. package/dist/lib/internal/task-invalidation.js +4 -1
  30. package/dist/lib/internal/task-ledger.js +12 -1
  31. package/dist/lib/internal/task-lifecycle.js +4 -1
  32. package/dist/lib/internal/task-operation-registry.js +421 -0
  33. package/dist/lib/internal/task-orchestration.js +12 -0
  34. package/dist/lib/internal/task-override.js +10 -3
  35. package/dist/lib/internal/task-qualification.js +90 -0
  36. package/dist/lib/internal/task-review.js +4 -1
  37. package/dist/lib/internal/task-short-id.js +11 -1
  38. package/dist/lib/internal/task-snapshot.js +4 -1
  39. package/dist/lib/internal/task-validate.js +7 -2
  40. package/dist/lib/internal/task-verify.js +5 -1
  41. package/dist/lib/internal/task-warning.js +8 -1
  42. package/dist/lib/platform/comment-safety.js +226 -0
  43. package/dist/lib/platform/github-provider.js +233 -6
  44. package/dist/lib/platform/issue-comments.js +146 -34
  45. package/dist/lib/platform/pr-change-report.js +6 -12
  46. package/dist/lib/platform/pr-summary.js +28 -7
  47. package/dist/lib/platform/provider-contract.d.ts +67 -1
  48. package/dist/lib/platform/provider-contract.js +6 -2
  49. package/dist/lib/platform/provider-validation.js +76 -3
  50. package/dist/lib/platform/pull-requests.js +115 -170
  51. package/dist/lib/platform/repository-metadata.js +377 -0
  52. package/dist/lib/platform/security-alerts.js +155 -0
  53. package/dist/lib/platform/verification-sync.js +33 -26
  54. package/dist/lib/process-data/index.js +7 -0
  55. package/dist/lib/run/index.js +5 -0
  56. package/dist/lib/sandbox/commands/control-status.js +22 -0
  57. package/dist/lib/sandbox/commands/create.js +32 -6
  58. package/dist/lib/sandbox/commands/ls.js +5 -0
  59. package/dist/lib/sandbox/commands/rm.js +827 -120
  60. package/dist/lib/sandbox/commands/show.js +16 -0
  61. package/dist/lib/sandbox/control/client.js +83 -9
  62. package/dist/lib/sandbox/control/completed-reentry.js +85 -0
  63. package/dist/lib/sandbox/control/container-identity.js +28 -31
  64. package/dist/lib/sandbox/control/executor.js +149 -14
  65. package/dist/lib/sandbox/control/lifecycle.js +621 -127
  66. package/dist/lib/sandbox/control/native-file-lock.js +177 -0
  67. package/dist/lib/sandbox/control/server.js +305 -35
  68. package/dist/lib/sandbox/control/state.js +5 -3
  69. package/dist/lib/sandbox/control/task-view.js +183 -0
  70. package/dist/lib/sandbox/engines/authority.js +289 -0
  71. package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
  72. package/dist/lib/sandbox/index.js +5 -0
  73. package/dist/lib/sandbox/recovery.js +21 -1
  74. package/dist/lib/sandbox/workspace-view.js +2 -1
  75. package/dist/lib/sandbox/worktree-safety.js +4 -1
  76. package/dist/lib/server/index.js +8 -0
  77. package/dist/lib/task/artifact-lifecycle.js +42 -13
  78. package/dist/lib/task/artifact-operations.js +520 -0
  79. package/dist/lib/task/artifact-repair-intent.js +59 -0
  80. package/dist/lib/task/artifact-schema.js +135 -0
  81. package/dist/lib/task/capabilities.js +72 -1
  82. package/dist/lib/task/control-authority.js +4 -0
  83. package/dist/lib/task/create-service.js +103 -4
  84. package/dist/lib/task/create.js +25 -0
  85. package/dist/lib/task/events.js +186 -67
  86. package/dist/lib/task/index.js +11 -3
  87. package/dist/lib/task/local-artifact-finalization.js +75 -150
  88. package/dist/lib/task/orchestration.js +192 -52
  89. package/dist/lib/task/qualification-audit.js +490 -0
  90. package/dist/lib/task/qualification-confirmation.js +109 -0
  91. package/dist/lib/task/qualification-intents.js +148 -0
  92. package/dist/lib/task/review-artifacts.js +22 -23
  93. package/dist/lib/task/review-finalization.js +121 -4
  94. package/dist/lib/task/sections.js +6 -4
  95. package/dist/lib/task/verification-config.js +11 -0
  96. package/dist/lib/task/verification-engine.js +54 -4
  97. package/dist/lifecycle-build-manifest.json +3 -3
  98. package/lib/agent-client.ts +9 -0
  99. package/lib/internal/agent-client.ts +4 -2
  100. package/lib/internal/cli-route-inventory.ts +140 -0
  101. package/lib/internal/codex-lifecycle.ts +8 -6
  102. package/lib/internal/codex-sandbox-controller.ts +7 -2
  103. package/lib/internal/git-workflow.ts +9 -6
  104. package/lib/internal/platform-checks.ts +12 -4
  105. package/lib/internal/platform-comment.ts +12 -4
  106. package/lib/internal/platform-context.ts +3 -1
  107. package/lib/internal/platform-issue.ts +11 -4
  108. package/lib/internal/platform-metadata.ts +70 -0
  109. package/lib/internal/platform-pr-review.ts +14 -3
  110. package/lib/internal/platform-pr.ts +23 -10
  111. package/lib/internal/platform-release-notes.ts +5 -3
  112. package/lib/internal/platform-security.ts +83 -0
  113. package/lib/internal/pr-review-grade.ts +10 -3
  114. package/lib/internal/release-workflow.ts +17 -5
  115. package/lib/internal/sandbox-control.ts +19 -5
  116. package/lib/internal/task-activity.ts +12 -4
  117. package/lib/internal/task-artifact.ts +164 -25
  118. package/lib/internal/task-context.ts +3 -1
  119. package/lib/internal/task-create.ts +76 -15
  120. package/lib/internal/task-delivery.ts +3 -1
  121. package/lib/internal/task-event.ts +3 -1
  122. package/lib/internal/task-finalization.ts +3 -1
  123. package/lib/internal/task-invalidation.ts +3 -1
  124. package/lib/internal/task-ledger.ts +11 -1
  125. package/lib/internal/task-lifecycle.ts +3 -1
  126. package/lib/internal/task-operation-registry.ts +482 -0
  127. package/lib/internal/task-orchestration.ts +12 -0
  128. package/lib/internal/task-override.ts +9 -3
  129. package/lib/internal/task-qualification.ts +61 -0
  130. package/lib/internal/task-review.ts +3 -1
  131. package/lib/internal/task-short-id.ts +10 -1
  132. package/lib/internal/task-snapshot.ts +3 -1
  133. package/lib/internal/task-validate.ts +6 -2
  134. package/lib/internal/task-verify.ts +4 -1
  135. package/lib/internal/task-warning.ts +7 -1
  136. package/lib/platform/comment-safety.ts +250 -0
  137. package/lib/platform/github-provider.ts +229 -7
  138. package/lib/platform/issue-comments.ts +160 -35
  139. package/lib/platform/pr-change-report.ts +6 -12
  140. package/lib/platform/pr-summary.ts +27 -6
  141. package/lib/platform/provider-contract.ts +78 -3
  142. package/lib/platform/provider-validation.ts +85 -3
  143. package/lib/platform/pull-requests.ts +126 -178
  144. package/lib/platform/repository-metadata.ts +453 -0
  145. package/lib/platform/security-alerts.ts +191 -0
  146. package/lib/platform/verification-sync.ts +32 -28
  147. package/lib/process-data/index.ts +7 -0
  148. package/lib/run/index.ts +5 -0
  149. package/lib/sandbox/commands/control-status.ts +26 -0
  150. package/lib/sandbox/commands/create.ts +46 -6
  151. package/lib/sandbox/commands/ls.ts +7 -0
  152. package/lib/sandbox/commands/rm.ts +1000 -87
  153. package/lib/sandbox/commands/show.ts +16 -0
  154. package/lib/sandbox/control/client.ts +103 -9
  155. package/lib/sandbox/control/completed-reentry.ts +98 -0
  156. package/lib/sandbox/control/container-identity.ts +27 -43
  157. package/lib/sandbox/control/executor.ts +177 -12
  158. package/lib/sandbox/control/lifecycle.ts +629 -17
  159. package/lib/sandbox/control/native-file-lock.ts +209 -0
  160. package/lib/sandbox/control/protocol.ts +5 -2
  161. package/lib/sandbox/control/server.ts +319 -37
  162. package/lib/sandbox/control/state.ts +6 -3
  163. package/lib/sandbox/control/task-view.ts +240 -0
  164. package/lib/sandbox/engines/authority.ts +365 -0
  165. package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
  166. package/lib/sandbox/index.ts +7 -0
  167. package/lib/sandbox/recovery.ts +21 -1
  168. package/lib/sandbox/workspace-view.ts +9 -3
  169. package/lib/sandbox/worktree-safety.ts +4 -1
  170. package/lib/server/index.ts +10 -0
  171. package/lib/task/artifact-lifecycle.ts +38 -16
  172. package/lib/task/artifact-operations.ts +626 -0
  173. package/lib/task/artifact-repair-intent.ts +71 -0
  174. package/lib/task/artifact-schema.ts +192 -0
  175. package/lib/task/capabilities.ts +70 -1
  176. package/lib/task/control-authority.ts +11 -1
  177. package/lib/task/create-service.ts +131 -6
  178. package/lib/task/create.ts +27 -0
  179. package/lib/task/events.ts +161 -65
  180. package/lib/task/index.ts +16 -3
  181. package/lib/task/local-artifact-finalization.ts +87 -218
  182. package/lib/task/orchestration.ts +172 -49
  183. package/lib/task/qualification-audit.ts +599 -0
  184. package/lib/task/qualification-confirmation.ts +117 -0
  185. package/lib/task/qualification-intents.ts +150 -0
  186. package/lib/task/review-artifacts.ts +21 -22
  187. package/lib/task/review-finalization.ts +133 -4
  188. package/lib/task/sections.ts +6 -4
  189. package/lib/task/verification-config.ts +12 -0
  190. package/lib/task/verification-engine.ts +55 -4
  191. package/package.json +6 -1
  192. package/templates/.agents/rules/README.en.md +2 -0
  193. package/templates/.agents/rules/README.zh-CN.md +2 -0
  194. package/templates/.agents/rules/decision-qualification.en.md +22 -0
  195. package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
  196. package/templates/.agents/rules/evidence-reporting.en.md +28 -0
  197. package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
  198. package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
  199. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
  200. package/templates/.agents/rules/local-artifact-repair.en.md +5 -1
  201. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +5 -1
  202. package/templates/.agents/rules/security-alerts.en.md +8 -8
  203. package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
  204. package/templates/.agents/skills/analyze-task/SKILL.en.md +15 -2
  205. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +19 -2
  206. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
  207. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
  208. package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
  209. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
  210. package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
  211. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
  212. package/templates/.agents/skills/code-task/SKILL.en.md +14 -1
  213. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +14 -1
  214. package/templates/.agents/skills/code-task/config/verify.en.json +5 -14
  215. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +5 -14
  216. package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
  217. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
  218. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
  219. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
  220. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
  221. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
  222. package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
  223. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
  224. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -1
  225. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -1
  226. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
  227. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
  228. package/templates/.agents/skills/complete-task/SKILL.en.md +9 -22
  229. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +9 -22
  230. package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
  231. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
  232. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  233. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  234. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  235. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  236. package/templates/.agents/skills/init-labels/SKILL.en.md +8 -8
  237. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
  238. package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
  239. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
  240. package/templates/.agents/skills/plan-task/SKILL.en.md +14 -1
  241. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +16 -1
  242. package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
  243. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
  244. package/templates/.agents/skills/review-analysis/SKILL.en.md +14 -1
  245. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -1
  246. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
  247. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
  248. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
  249. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
  250. package/templates/.agents/skills/review-code/SKILL.en.md +14 -1
  251. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +14 -1
  252. package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
  253. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
  254. package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
  255. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
  256. package/templates/.agents/skills/review-plan/SKILL.en.md +14 -1
  257. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -1
  258. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
  259. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
  260. package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
  261. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
  262. package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
  263. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
  264. package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
  265. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
  266. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -1
  267. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -1
  268. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +3 -1
  269. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +3 -1
  270. package/templates/.agents/skills/test/SKILL.en.md +13 -2
  271. package/templates/.agents/skills/test/SKILL.zh-CN.md +13 -2
  272. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
  273. package/templates/.agents/templates/task.en.md +6 -0
  274. package/templates/.agents/templates/task.zh-CN.md +6 -0
  275. package/templates/.agents/scripts/security-alerts.github.sh +0 -151
  276. package/templates/.agents/scripts/security-alerts.sh +0 -31
  277. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
  278. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
  279. package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
  280. package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { randomUUID } from 'node:crypto';
3
+ import { createHash, randomUUID } from 'node:crypto';
4
4
 
5
5
  import { parseTypedTaskFrontmatter } from './frontmatter.ts';
6
6
  import { validateCurrentTaskContract } from './current-contract.ts';
@@ -154,6 +154,7 @@ type OrchestrationCompletionPlanResult = Readonly<{
154
154
  type OrchestrationOptions = {
155
155
  repoRoot?: string;
156
156
  gitWorktreeRoot?: string;
157
+ diagnosticLog?: OrchestrationDiagnosticLogger;
157
158
  id?: () => string;
158
159
  now?: () => string;
159
160
  maxSteps?: number;
@@ -178,6 +179,10 @@ type OrchestrationOptions = {
178
179
  hasActiveLifecycleEvidence?: (receipt: DelegationReceipt) => boolean;
179
180
  };
180
181
 
182
+ export type OrchestrationDiagnosticValue = string | number | boolean | null;
183
+ export type OrchestrationDiagnosticFields = Readonly<Record<string, OrchestrationDiagnosticValue>>;
184
+ export type OrchestrationDiagnosticLogger = (event: string, fields: OrchestrationDiagnosticFields) => void;
185
+
181
186
  function supportsLifecycleDelegation(client: AgentClientId): boolean {
182
187
  return getAgentClientCapability(client, 'subagents').level !== 'unsupported'
183
188
  && getAgentClientCapability(client, 'orchestration').level !== 'unsupported'
@@ -194,11 +199,13 @@ const ORCHESTRATION_STATE_INVALID_MESSAGE = 'orchestration.json does not match t
194
199
  class OrchestrationStateError extends Error {
195
200
  readonly code = 'ORCHESTRATION_STATE_INVALID';
196
201
  readonly taskId: string | null;
202
+ readonly reasonCodes: readonly string[];
197
203
 
198
- constructor(taskId: string | null = null) {
204
+ constructor(taskId: string | null = null, reasonCodes: readonly string[] = []) {
199
205
  super(ORCHESTRATION_STATE_INVALID_MESSAGE);
200
206
  this.name = 'OrchestrationStateError';
201
207
  this.taskId = taskId;
208
+ this.reasonCodes = Object.freeze([...reasonCodes]);
202
209
  }
203
210
  }
204
211
 
@@ -288,46 +295,128 @@ const ORCHESTRATION_RUN_KEYS = [
288
295
  'pause', 'commitAuthorization', 'completionEvidence', 'createdAt', 'updatedAt'
289
296
  ] as const;
290
297
 
291
- function parseOrchestrationRun(value: unknown, expectedTaskId?: string): OrchestrationRun {
292
- if (!hasExactKeys(value, ORCHESTRATION_RUN_KEYS)
293
- || !exactText(value.taskId)
294
- || (expectedTaskId !== undefined && value.taskId !== expectedTaskId)
295
- || !exactText(value.runId)
296
- || !['running', 'paused', 'completed'].includes(value.status as string)
297
- || !(value.nextStage === null || ['analysis', 'review-analysis', 'plan', 'review-plan', 'code', 'review-code', 'commit'].includes(value.nextStage as string))
298
- || !Number.isSafeInteger(value.stepCount) || (value.stepCount as number) < 0
299
- || !Number.isSafeInteger(value.maxSteps) || (value.maxSteps as number) < 1
300
- || !isModelPolicy(value.modelPolicy)
301
- || !isModelPolicySource(value.modelPolicySource)
302
- || !Array.isArray(value.recoveryHistory) || !value.recoveryHistory.every(isRecovery)
303
- || typeof value.baseline !== 'string'
304
- || !(value.pendingDelegation === null || isDelegationReceipt(value.pendingDelegation))
305
- || !Array.isArray(value.receipts) || !value.receipts.every(isDelegationReceipt)
306
- || !(value.pause === null || isPause(value.pause))
307
- || !hasExactKeys(value.commitAuthorization, ['issuedAt', 'consumedAt'])
308
- || !nullableText(value.commitAuthorization.issuedAt)
309
- || !nullableText(value.commitAuthorization.consumedAt)
310
- || !(value.completionEvidence === null || isCompletionEvidence(value.completionEvidence))
311
- || !exactText(value.createdAt)
312
- || !exactText(value.updatedAt)) {
313
- throw new OrchestrationStateError();
298
+ function orchestrationStateReasonCodes(value: unknown, expectedTaskId?: string): string[] {
299
+ if (!hasExactKeys(value, ORCHESTRATION_RUN_KEYS)) return ['top-level-keys'];
300
+ const record = value;
301
+ const reasons: string[] = [];
302
+ if (!exactText(record.taskId)) reasons.push('taskId');
303
+ else if (expectedTaskId !== undefined && record.taskId !== expectedTaskId) reasons.push('taskId-mismatch');
304
+ if (!exactText(record.runId)) reasons.push('runId');
305
+ if (!['running', 'paused', 'completed'].includes(record.status as string)) reasons.push('status');
306
+ if (!(record.nextStage === null || ['analysis', 'review-analysis', 'plan', 'review-plan', 'code', 'review-code', 'commit'].includes(record.nextStage as string))) {
307
+ reasons.push('nextStage');
308
+ }
309
+ if (!Number.isSafeInteger(record.stepCount) || (record.stepCount as number) < 0) reasons.push('stepCount');
310
+ if (!Number.isSafeInteger(record.maxSteps) || (record.maxSteps as number) < 1) reasons.push('maxSteps');
311
+ if (!isModelPolicy(record.modelPolicy)) reasons.push('modelPolicy');
312
+ if (!isModelPolicySource(record.modelPolicySource)) reasons.push('modelPolicySource');
313
+ if (!Array.isArray(record.recoveryHistory) || !record.recoveryHistory.every(isRecovery)) reasons.push('recoveryHistory');
314
+ if (typeof record.baseline !== 'string') reasons.push('baseline');
315
+ if (!(record.pendingDelegation === null || isDelegationReceipt(record.pendingDelegation))) reasons.push('pendingDelegation');
316
+ if (!Array.isArray(record.receipts) || !record.receipts.every(isDelegationReceipt)) reasons.push('receipts');
317
+ if (!(record.pause === null || isPause(record.pause))) reasons.push('pause');
318
+ if (!hasExactKeys(record.commitAuthorization, ['issuedAt', 'consumedAt'])) {
319
+ reasons.push('commitAuthorization');
320
+ } else {
321
+ if (!nullableText(record.commitAuthorization.issuedAt)) reasons.push('commitAuthorization.issuedAt');
322
+ if (!nullableText(record.commitAuthorization.consumedAt)) reasons.push('commitAuthorization.consumedAt');
314
323
  }
315
- const receipts = [...value.receipts, ...(value.pendingDelegation ? [value.pendingDelegation] : [])];
316
- if (receipts.some((receipt) => receipt.taskId !== value.taskId || receipt.runId !== value.runId)) {
317
- throw new OrchestrationStateError();
324
+ if (!(record.completionEvidence === null || isCompletionEvidence(record.completionEvidence))) reasons.push('completionEvidence');
325
+ if (!exactText(record.createdAt)) reasons.push('createdAt');
326
+ if (!exactText(record.updatedAt)) reasons.push('updatedAt');
327
+ if (Array.isArray(record.receipts) && record.receipts.every(isDelegationReceipt)
328
+ && (record.pendingDelegation === null || isDelegationReceipt(record.pendingDelegation))) {
329
+ const receipts = [...record.receipts, ...(record.pendingDelegation ? [record.pendingDelegation] : [])];
330
+ if (receipts.some((receipt) => receipt.taskId !== record.taskId || receipt.runId !== record.runId)) {
331
+ reasons.push('receipt-identity');
332
+ }
333
+ }
334
+ return reasons;
335
+ }
336
+
337
+ function emitDiagnostic(options: Pick<OrchestrationOptions, 'diagnosticLog'> | undefined, event: string, fields: OrchestrationDiagnosticFields): void {
338
+ try {
339
+ options?.diagnosticLog?.(event, fields);
340
+ } catch {
341
+ // Diagnostics must never change lifecycle behavior.
342
+ }
343
+ }
344
+
345
+ function fileObservation(file: string): OrchestrationDiagnosticFields {
346
+ try {
347
+ const stat = fs.lstatSync(file);
348
+ let realpath: string | null = null;
349
+ try { realpath = fs.realpathSync.native(file); } catch { /* retain the lstat observation */ }
350
+ return {
351
+ file,
352
+ exists: true,
353
+ fileType: stat.isFile() ? 'file' : stat.isDirectory() ? 'directory' : 'other',
354
+ symbolicLink: stat.isSymbolicLink(),
355
+ bytes: stat.isFile() ? stat.size : null,
356
+ mtimeMs: stat.mtimeMs,
357
+ device: stat.dev,
358
+ inode: stat.ino,
359
+ realpath
360
+ };
361
+ } catch (error) {
362
+ return {
363
+ file,
364
+ exists: false,
365
+ fileType: null,
366
+ symbolicLink: null,
367
+ bytes: null,
368
+ mtimeMs: null,
369
+ device: null,
370
+ inode: null,
371
+ realpath: null,
372
+ fsError: error instanceof Error && 'code' in error ? String(error.code) : null
373
+ };
318
374
  }
375
+ }
376
+
377
+ function parseOrchestrationRun(value: unknown, expectedTaskId?: string): OrchestrationRun {
378
+ const reasonCodes = orchestrationStateReasonCodes(value, expectedTaskId);
379
+ if (reasonCodes.length > 0) throw new OrchestrationStateError(expectedTaskId ?? null, reasonCodes);
319
380
  return value as OrchestrationRun;
320
381
  }
321
382
 
322
- function readRun(taskDir: string): OrchestrationRun | null {
383
+ function readRun(taskDir: string, options: Pick<OrchestrationOptions, 'diagnosticLog'> = {}): OrchestrationRun | null {
323
384
  const file = orchestrationPath(taskDir);
324
- if (!fs.existsSync(file)) return null;
325
385
  const taskId = path.basename(taskDir);
386
+ const observed = options.diagnosticLog ? fileObservation(file) : {};
387
+ if (!fs.existsSync(file)) {
388
+ emitDiagnostic(options, 'orchestration-state-missing', { taskId, taskDir, ...observed });
389
+ return null;
390
+ }
391
+ emitDiagnostic(options, 'orchestration-state-read-start', { taskId, taskDir, ...observed });
392
+ let rawBytes: number | null = null;
393
+ let rawSha256: string | null = null;
326
394
  try {
327
- return parseOrchestrationRun(JSON.parse(fs.readFileSync(file, 'utf8')), taskId);
395
+ const raw = fs.readFileSync(file, 'utf8');
396
+ if (options.diagnosticLog) {
397
+ rawBytes = Buffer.byteLength(raw, 'utf8');
398
+ rawSha256 = createHash('sha256').update(raw, 'utf8').digest('hex');
399
+ }
400
+ const run = parseOrchestrationRun(JSON.parse(raw), taskId);
401
+ emitDiagnostic(options, 'orchestration-state-read-ok', {
402
+ taskId, taskDir, file, rawBytes, rawSha256, status: run.status, runId: run.runId,
403
+ stepCount: run.stepCount, pendingDelegation: run.pendingDelegation !== null
404
+ });
405
+ return run;
328
406
  } catch (error) {
329
- if (error instanceof OrchestrationStateError) throw new OrchestrationStateError(taskId);
330
- throw new OrchestrationStateError(taskId);
407
+ const reasonCodes = error instanceof OrchestrationStateError && error.reasonCodes.length > 0
408
+ ? error.reasonCodes
409
+ : [error instanceof SyntaxError ? 'json-parse' : 'file-read'];
410
+ emitDiagnostic(options, 'orchestration-state-read-failed', {
411
+ taskId,
412
+ taskDir,
413
+ ...observed,
414
+ rawBytes,
415
+ rawSha256,
416
+ reasonCodes: reasonCodes.join(','),
417
+ errorType: error instanceof Error ? error.name : typeof error,
418
+ });
419
+ throw new OrchestrationStateError(taskId, reasonCodes);
331
420
  }
332
421
  }
333
422
 
@@ -417,7 +506,7 @@ function beginOrResumeOrchestration(taskRef: string, options: OrchestrationOptio
417
506
  }
418
507
  let existing: OrchestrationRun | null;
419
508
  try {
420
- existing = readRun(resolved.taskDir);
509
+ existing = readRun(resolved.taskDir, options);
421
510
  } catch (error) {
422
511
  return failed('ORCHESTRATION_STATE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
423
512
  }
@@ -616,7 +705,7 @@ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {})
616
705
  }
617
706
  let run: OrchestrationRun | null;
618
707
  try {
619
- run = readRun(resolved.taskDir);
708
+ run = readRun(resolved.taskDir, options);
620
709
  } catch (error) {
621
710
  return failed('ORCHESTRATION_STATE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
622
711
  }
@@ -729,7 +818,7 @@ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {})
729
818
  function statusOrchestration(taskRef: string, options: OrchestrationOptions = {}): OrchestrationResult {
730
819
  const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
731
820
  if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
732
- const run = readRun(resolved.taskDir);
821
+ const run = readRun(resolved.taskDir, options);
733
822
  if (!run) return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
734
823
  return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
735
824
  }
@@ -749,7 +838,15 @@ function prepareOrchestrationDelegationUnlocked(
749
838
  if (!(options.supportsLifecycleDelegation ?? supportsLifecycleDelegation)(input.client)) {
750
839
  return failed('ORCHESTRATION_CLIENT_UNSUPPORTED', `client '${input.client}' does not support lifecycle orchestration`, resolved.taskId);
751
840
  }
752
- const run = readRun(resolved.taskDir);
841
+ emitDiagnostic(options, 'orchestration-prepare-start', {
842
+ taskId: resolved.taskId,
843
+ taskDir: resolved.taskDir,
844
+ repoRoot: resolved.repoRoot,
845
+ client: input.client,
846
+ requestedModel: input.requestedModel ?? null,
847
+ requestedReasoningEffort: input.requestedReasoningEffort ?? null
848
+ });
849
+ const run = readRun(resolved.taskDir, options);
753
850
  if (!run || run.status !== 'running') return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'a running orchestration is required', resolved.taskId);
754
851
  if (run.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_BUSY', 'the run already has a pending delegation', resolved.taskId);
755
852
  const repositoryPending = matchingDelegations(() => true, options);
@@ -862,7 +959,7 @@ function dispatchOrchestrationDelegationUnlocked(
862
959
  ): OrchestrationResult {
863
960
  const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
864
961
  if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
865
- const run = readRun(resolved.taskDir);
962
+ const run = readRun(resolved.taskDir, options);
866
963
  if (!run?.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
867
964
  if (run.status !== 'running') {
868
965
  return failed('ORCHESTRATION_STATE_INVALID', 'spawn dispatch requires a running orchestration', resolved.taskId);
@@ -908,16 +1005,42 @@ function matchingDelegations(
908
1005
  const repoRoot = options.repoRoot ?? process.cwd();
909
1006
  const activeRoot = path.join(repoRoot, '.agents', 'workspace', 'active');
910
1007
  if (!fs.existsSync(activeRoot)) return [];
911
- return fs.readdirSync(activeRoot, { withFileTypes: true })
1008
+ emitDiagnostic(options, 'orchestration-active-scan-start', {
1009
+ activeRoot
1010
+ });
1011
+ const matches = fs.readdirSync(activeRoot, { withFileTypes: true })
912
1012
  .filter((entry) => entry.isDirectory())
913
1013
  .flatMap((entry) => {
914
- const run = readRun(path.join(activeRoot, entry.name));
1014
+ const candidateTaskId = entry.name;
1015
+ const candidateTaskDir = path.join(activeRoot, candidateTaskId);
1016
+ emitDiagnostic(options, 'orchestration-active-scan-candidate', {
1017
+ candidateTaskId,
1018
+ candidateTaskDir,
1019
+ orchestrationPath: orchestrationPath(candidateTaskDir)
1020
+ });
1021
+ let run: OrchestrationRun | null;
1022
+ try {
1023
+ run = readRun(candidateTaskDir, options);
1024
+ } catch (error) {
1025
+ emitDiagnostic(options, 'orchestration-active-scan-candidate-failed', {
1026
+ candidateTaskId,
1027
+ candidateTaskDir,
1028
+ errorType: error instanceof Error ? error.name : typeof error,
1029
+ reasonCodes: error instanceof OrchestrationStateError ? error.reasonCodes.join(',') : null
1030
+ });
1031
+ throw error;
1032
+ }
915
1033
  if (!run || run.status !== 'running' || !run.pendingDelegation) return [];
916
1034
  const receipt = run.pendingDelegation;
917
1035
  return predicate(receipt)
918
- ? [{ taskId: entry.name, run }]
1036
+ ? [{ taskId: candidateTaskId, run }]
919
1037
  : [];
920
1038
  });
1039
+ emitDiagnostic(options, 'orchestration-active-scan-finished', {
1040
+ activeRoot,
1041
+ matchingDelegations: matches.length
1042
+ });
1043
+ return matches;
921
1044
  }
922
1045
 
923
1046
  function uniqueMatchingDelegation(
@@ -1163,7 +1286,7 @@ function activateOrchestrationDelegation(
1163
1286
  ): OrchestrationResult {
1164
1287
  const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1165
1288
  if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
1166
- const run = readRun(resolved.taskDir);
1289
+ const run = readRun(resolved.taskDir, options);
1167
1290
  if (!run?.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
1168
1291
  if (run.status !== 'running') {
1169
1292
  return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
@@ -1191,7 +1314,7 @@ async function awaitOrchestrationDelegationActivation(
1191
1314
  while (true) {
1192
1315
  const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1193
1316
  if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
1194
- const run = readRun(resolved.taskDir);
1317
+ const run = readRun(resolved.taskDir, options);
1195
1318
  if (!run?.pendingDelegation) {
1196
1319
  return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
1197
1320
  }
@@ -1257,7 +1380,7 @@ function recoverPreparedOrchestrationDelegation(
1257
1380
  if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
1258
1381
  try {
1259
1382
  return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-orchestration.recover-prepared', () => {
1260
- const run = readRun(resolved.taskDir);
1383
+ const run = readRun(resolved.taskDir, options);
1261
1384
  const receipt = run?.pendingDelegation;
1262
1385
  if (!run || !receipt) {
1263
1386
  return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
@@ -1329,7 +1452,7 @@ function completeOrchestrationStage(
1329
1452
  ): OrchestrationResult {
1330
1453
  const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1331
1454
  if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
1332
- const run = readRun(resolved.taskDir);
1455
+ const run = readRun(resolved.taskDir, options);
1333
1456
  if (!run) return { status: 'running', changed: false, taskId: resolved.taskId, run: null, next: null, error: null };
1334
1457
  if (!run.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'active run has no pending delegation', resolved.taskId);
1335
1458
  const result = completeDelegationStage(run.pendingDelegation, event);
@@ -1348,7 +1471,7 @@ function inspectOrchestrationStage(
1348
1471
  if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
1349
1472
  let run: OrchestrationRun | null;
1350
1473
  try {
1351
- run = readRun(resolved.taskDir);
1474
+ run = readRun(resolved.taskDir, options);
1352
1475
  } catch (error) {
1353
1476
  return failed('ORCHESTRATION_STATE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
1354
1477
  }
@@ -1409,7 +1532,7 @@ function sealOrchestrationDelegation(
1409
1532
  ): OrchestrationResult {
1410
1533
  const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1411
1534
  if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
1412
- const run = readRun(resolved.taskDir);
1535
+ const run = readRun(resolved.taskDir, options);
1413
1536
  if (!run?.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
1414
1537
  const result = sealDelegation(run.pendingDelegation, event, { now: options.now });
1415
1538
  if (!result.ok) return pauseOrchestration(taskRef, result.code, result.message, true, options);
@@ -1421,7 +1544,7 @@ function sealOrchestrationDelegation(
1421
1544
  function advanceOrchestration(taskRef: string, options: OrchestrationOptions = {}): OrchestrationResult {
1422
1545
  const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1423
1546
  if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
1424
- const run = readRun(resolved.taskDir);
1547
+ const run = readRun(resolved.taskDir, options);
1425
1548
  if (!run?.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
1426
1549
  const result = consumeDelegation(run.pendingDelegation, { now: options.now });
1427
1550
  if (!result.ok) return pauseOrchestration(taskRef, result.code, result.message, true, options);
@@ -1448,7 +1571,7 @@ function pauseOrchestration(
1448
1571
  ): OrchestrationResult {
1449
1572
  const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1450
1573
  if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
1451
- const run = readRun(resolved.taskDir);
1574
+ const run = readRun(resolved.taskDir, options);
1452
1575
  if (!run) return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
1453
1576
  const updated = withUpdatedRun(run, { status: 'paused', pause: { code, message, recoverable } });
1454
1577
  saveRun(resolved.taskDir, updated);