@fitlab-ai/agent-infra 0.9.7 → 0.9.9

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 (195) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/bin/internal-cli.js +11 -1
  4. package/dist/lib/agent-clients/adapters/claude-code.js +3 -3
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +60 -22
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +8 -1
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +175 -11
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +147 -0
  9. package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +14 -0
  10. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +86 -69
  11. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +2 -1
  12. package/dist/lib/decide.js +68 -14
  13. package/dist/lib/git/workflow.js +10 -1
  14. package/dist/lib/init.js +1 -1
  15. package/dist/lib/internal/codex-lifecycle.js +19 -5
  16. package/dist/lib/internal/codex-sandbox-controller.js +6 -13
  17. package/dist/lib/internal/git-workflow.js +5 -7
  18. package/dist/lib/internal/platform-pr.js +20 -7
  19. package/dist/lib/internal/release-workflow.js +3 -4
  20. package/dist/lib/internal/sandbox-control.js +79 -2
  21. package/dist/lib/internal/task-activity.js +57 -7
  22. package/dist/lib/internal/task-event.js +57 -4
  23. package/dist/lib/internal/task-finalization.js +54 -0
  24. package/dist/lib/internal/task-ledger.js +59 -5
  25. package/dist/lib/internal/task-lifecycle.js +59 -8
  26. package/dist/lib/internal/task-orchestration.js +161 -288
  27. package/dist/lib/internal/task-override.js +131 -0
  28. package/dist/lib/internal/task-review.js +64 -6
  29. package/dist/lib/internal/task-verify.js +65 -3
  30. package/dist/lib/internal/task-warning.js +57 -4
  31. package/dist/lib/platform/pr-summary.js +53 -13
  32. package/dist/lib/platform/pull-requests.js +205 -41
  33. package/dist/lib/runtime/agent-runtime.js +56 -0
  34. package/dist/lib/sandbox/commands/create.js +389 -311
  35. package/dist/lib/sandbox/commands/rm.js +316 -55
  36. package/dist/lib/sandbox/control/client.js +190 -6
  37. package/dist/lib/sandbox/control/container-identity.js +1 -1
  38. package/dist/lib/sandbox/control/controller-registration.js +273 -0
  39. package/dist/lib/sandbox/control/executor.js +145 -5
  40. package/dist/lib/sandbox/control/lifecycle.js +395 -73
  41. package/dist/lib/sandbox/control/protocol.js +67 -6
  42. package/dist/lib/sandbox/control/state.js +27 -11
  43. package/dist/lib/sandbox/engine.js +14 -7
  44. package/dist/lib/sandbox/index.js +5 -3
  45. package/dist/lib/sandbox/managed-fs.js +5 -0
  46. package/dist/lib/sandbox/mounts.js +20 -5
  47. package/dist/lib/sandbox/recovery.js +78 -10
  48. package/dist/lib/sandbox/task-resolver.js +50 -6
  49. package/dist/lib/sandbox/workspace-identity.js +27 -0
  50. package/dist/lib/sandbox/workspace-view.js +62 -40
  51. package/dist/lib/sandbox/worktree-safety.js +215 -2
  52. package/dist/lib/server/process-state.js +51 -1
  53. package/dist/lib/task/activity-intent.js +4 -1
  54. package/dist/lib/task/codex-orchestration.js +158 -33
  55. package/dist/lib/task/commands/status.js +59 -1
  56. package/dist/lib/task/commit-operation.js +335 -0
  57. package/dist/lib/task/commit-policy.js +18 -0
  58. package/dist/lib/task/decision-intents.js +4 -2
  59. package/dist/lib/task/delegation-receipts.js +279 -17
  60. package/dist/lib/task/events.js +9 -5
  61. package/dist/lib/task/finalization.js +567 -0
  62. package/dist/lib/task/guard-override.js +31 -0
  63. package/dist/lib/task/human-override.js +1240 -0
  64. package/dist/lib/task/ledger-intents.js +6 -3
  65. package/dist/lib/task/lifecycle.js +69 -14
  66. package/dist/lib/task/operation-outcome.js +33 -0
  67. package/dist/lib/task/orchestration.js +199 -719
  68. package/dist/lib/task/review-finalization.js +3 -2
  69. package/dist/lib/task/short-id.js +59 -1
  70. package/dist/lib/task/task-execution-lock.js +6 -1
  71. package/dist/lib/task/verification-engine.js +138 -59
  72. package/dist/lib/task/verification.js +15 -2
  73. package/dist/lib/task/workflow-warning-intents.js +54 -3
  74. package/dist/lib/task/write.js +2 -1
  75. package/dist/lifecycle-build-manifest.json +3 -3
  76. package/lib/agent-clients/adapters/claude-code.ts +3 -3
  77. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +63 -20
  78. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +12 -0
  79. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +278 -13
  80. package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +180 -0
  81. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +19 -0
  82. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +9 -1
  83. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +112 -93
  84. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +2 -1
  85. package/lib/decide.ts +59 -14
  86. package/lib/git/workflow.ts +11 -2
  87. package/lib/init.ts +1 -1
  88. package/lib/internal/codex-lifecycle.ts +18 -5
  89. package/lib/internal/codex-sandbox-controller.ts +6 -12
  90. package/lib/internal/git-workflow.ts +5 -6
  91. package/lib/internal/platform-pr.ts +14 -7
  92. package/lib/internal/release-workflow.ts +3 -4
  93. package/lib/internal/sandbox-control.ts +92 -2
  94. package/lib/internal/task-activity.ts +46 -6
  95. package/lib/internal/task-event.ts +53 -4
  96. package/lib/internal/task-finalization.ts +68 -0
  97. package/lib/internal/task-ledger.ts +48 -4
  98. package/lib/internal/task-lifecycle.ts +56 -12
  99. package/lib/internal/task-orchestration.ts +32 -107
  100. package/lib/internal/task-override.ts +138 -0
  101. package/lib/internal/task-review.ts +51 -6
  102. package/lib/internal/task-verify.ts +52 -3
  103. package/lib/internal/task-warning.ts +44 -3
  104. package/lib/platform/pr-summary.ts +65 -14
  105. package/lib/platform/pull-requests.ts +211 -41
  106. package/lib/runtime/agent-runtime.ts +76 -0
  107. package/lib/sandbox/commands/create.ts +88 -4
  108. package/lib/sandbox/commands/rm.ts +413 -61
  109. package/lib/sandbox/config.ts +2 -1
  110. package/lib/sandbox/control/client.ts +255 -6
  111. package/lib/sandbox/control/container-identity.ts +3 -3
  112. package/lib/sandbox/control/controller-registration.ts +347 -0
  113. package/lib/sandbox/control/executor.ts +172 -6
  114. package/lib/sandbox/control/lifecycle.ts +465 -77
  115. package/lib/sandbox/control/protocol.ts +85 -9
  116. package/lib/sandbox/control/state.ts +35 -18
  117. package/lib/sandbox/engine.ts +20 -9
  118. package/lib/sandbox/index.ts +5 -3
  119. package/lib/sandbox/managed-fs.ts +5 -0
  120. package/lib/sandbox/mounts.ts +23 -5
  121. package/lib/sandbox/recovery.ts +90 -9
  122. package/lib/sandbox/task-resolver.ts +58 -7
  123. package/lib/sandbox/workspace-identity.ts +50 -2
  124. package/lib/sandbox/workspace-view.ts +73 -43
  125. package/lib/sandbox/worktree-safety.ts +236 -2
  126. package/lib/server/process-state.ts +48 -1
  127. package/lib/task/activity-intent.ts +4 -2
  128. package/lib/task/codex-orchestration.ts +167 -33
  129. package/lib/task/commands/status.ts +78 -1
  130. package/lib/task/commit-operation.ts +430 -0
  131. package/lib/task/commit-policy.ts +30 -0
  132. package/lib/task/decision-intents.ts +4 -2
  133. package/lib/task/delegation-receipts.ts +304 -19
  134. package/lib/task/events.ts +10 -5
  135. package/lib/task/finalization.ts +719 -0
  136. package/lib/task/guard-override.ts +50 -0
  137. package/lib/task/human-override.ts +1503 -0
  138. package/lib/task/ledger-intents.ts +6 -3
  139. package/lib/task/lifecycle.ts +71 -14
  140. package/lib/task/operation-outcome.ts +48 -0
  141. package/lib/task/orchestration.ts +239 -922
  142. package/lib/task/review-finalization.ts +6 -2
  143. package/lib/task/short-id.ts +65 -1
  144. package/lib/task/task-execution-lock.ts +10 -1
  145. package/lib/task/verification-engine.ts +145 -64
  146. package/lib/task/verification.ts +18 -3
  147. package/lib/task/workflow-warning-intents.ts +61 -4
  148. package/lib/task/write.ts +4 -1
  149. package/package.json +2 -2
  150. package/templates/.agents/QUICKSTART.en.md +2 -2
  151. package/templates/.agents/QUICKSTART.zh-CN.md +2 -2
  152. package/templates/.agents/hooks/lifecycle-delegation.js +9 -24
  153. package/templates/.agents/rules/lifecycle-orchestration.en.md +4 -3
  154. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +4 -3
  155. package/templates/.agents/rules/task-management.en.md +2 -2
  156. package/templates/.agents/rules/task-management.zh-CN.md +2 -2
  157. package/templates/.agents/skills/block-task/SKILL.en.md +2 -4
  158. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -4
  159. package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
  160. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
  161. package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -2
  162. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -2
  163. package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -2
  164. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -2
  165. package/templates/.agents/skills/commit/SKILL.en.md +41 -125
  166. package/templates/.agents/skills/commit/SKILL.zh-CN.md +52 -123
  167. package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
  168. package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
  169. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +14 -43
  170. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +14 -49
  171. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  172. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  173. package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -35
  174. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -35
  175. package/templates/.agents/skills/complete-task/SKILL.en.md +31 -38
  176. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +31 -38
  177. package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
  178. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
  179. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
  180. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
  181. package/templates/.agents/skills/create-pr/SKILL.en.md +6 -4
  182. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +6 -4
  183. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +2 -2
  184. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +2 -2
  185. package/templates/.agents/skills/run-task/SKILL.en.md +5 -5
  186. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -5
  187. package/templates/.agents/skills/run-task/reference/host-validation.en.md +3 -1
  188. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +3 -1
  189. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +1 -1
  190. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +1 -1
  191. package/templates/.codex/hooks.json +4 -4
  192. package/dist/lib/task/commit-finalization.js +0 -305
  193. package/dist/lib/task/commit-intent.js +0 -165
  194. package/lib/task/commit-finalization.ts +0 -419
  195. package/lib/task/commit-intent.ts +0 -230
@@ -8,6 +8,7 @@ import { finalizeReviewSummaryContent } from './review-artifacts.js';
8
8
  import { resolveTaskRef } from './resolve-ref.js';
9
9
  import { validateLifecycleExecution } from './lifecycle-execution.js';
10
10
  import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
11
+ import { allowsManualOverride } from './guard-override.js';
11
12
  const STAGES = {
12
13
  analysis: { family: 'review-analysis', action: 'Review Analysis' },
13
14
  plan: { family: 'review-plan', action: 'Review Plan' },
@@ -60,7 +61,7 @@ function finalizeReviewSummaryUnlocked(request, options = {}) {
60
61
  const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
61
62
  if (!resolved.ok)
62
63
  return failed(request, resolved.code, resolved.message, resolved.taskId);
63
- if (resolved.state !== 'active') {
64
+ if (resolved.state !== 'active' && !allowsManualOverride(options.manualOverride, 'review-finalization', 'TASK_STATE_MISMATCH')) {
64
65
  return failed(request, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
65
66
  }
66
67
  const parsedArtifact = parseArtifactName(request.artifact);
@@ -168,7 +169,7 @@ function finalizeReviewSummaryUnlocked(request, options = {}) {
168
169
  };
169
170
  }
170
171
  function finalizeReviewSummary(request, options = {}) {
171
- if (request.dryRun)
172
+ if (request.dryRun || options.lockAlreadyHeld)
172
173
  return finalizeReviewSummaryUnlocked(request, options);
173
174
  const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
174
175
  if (!resolved.ok)
@@ -343,6 +343,64 @@ function loadShortIdByTaskId(repoRoot) {
343
343
  }
344
344
  return map;
345
345
  }
346
+ function inspectShortIdRegistry(repoRoot) {
347
+ const registryPath = path.join(repoRoot, '.agents', 'workspace', 'active', REGISTRY_NAME);
348
+ if (!fs.existsSync(registryPath)) {
349
+ return {
350
+ status: 'missing',
351
+ error: {
352
+ code: 'SHORT_ID_REGISTRY_NOT_FOUND',
353
+ message: 'canonical short-id registry is missing'
354
+ }
355
+ };
356
+ }
357
+ let raw;
358
+ try {
359
+ raw = fs.readFileSync(registryPath, 'utf8');
360
+ }
361
+ catch {
362
+ return {
363
+ status: 'invalid',
364
+ error: {
365
+ code: 'SHORT_ID_REGISTRY_READ_FAILED',
366
+ message: 'canonical short-id registry is unreadable'
367
+ }
368
+ };
369
+ }
370
+ let parsed;
371
+ try {
372
+ parsed = JSON.parse(raw);
373
+ }
374
+ catch {
375
+ return {
376
+ status: 'invalid',
377
+ error: {
378
+ code: 'SHORT_ID_REGISTRY_INVALID_JSON',
379
+ message: 'canonical short-id registry contains invalid JSON'
380
+ }
381
+ };
382
+ }
383
+ try {
384
+ const registry = validateRegistry(parsed, registryPath, configuredShortIdLength(repoRoot));
385
+ const shortIds = new Map();
386
+ for (const [key, taskId] of Object.entries(registry.ids))
387
+ shortIds.set(taskId, key);
388
+ return { status: 'valid', shortIds };
389
+ }
390
+ catch (error) {
391
+ return {
392
+ status: 'invalid',
393
+ error: {
394
+ code: typeof error.code === 'string'
395
+ ? error.code
396
+ : 'SHORT_ID_REGISTRY_INVALID_SCHEMA',
397
+ message: error.code === 'SHORT_ID_REGISTRY_DUPLICATE_TASK'
398
+ ? 'canonical short-id registry contains duplicate task entries'
399
+ : 'canonical short-id registry has invalid schema'
400
+ }
401
+ };
402
+ }
403
+ }
346
404
  /**
347
405
  * Resolve a branch to its active-task short id (`NN`), or `null` when no
348
406
  * active task is bound to that branch.
@@ -371,5 +429,5 @@ function lookupShortIdByBranch(branch, repoRoot, _opts) {
371
429
  }
372
430
  return matches[0];
373
431
  }
374
- export { normalizeShortIdInput, isRemovedHashShortIdInput, resolveShortIdReadOnly, lookupShortIdByBranch, loadShortIdByTaskId, configuredShortIdLength, mutateShortIdRegistry, executeShortIdCommand };
432
+ export { normalizeShortIdInput, isRemovedHashShortIdInput, resolveShortIdReadOnly, lookupShortIdByBranch, loadShortIdByTaskId, inspectShortIdRegistry, configuredShortIdLength, mutateShortIdRegistry, executeShortIdCommand };
375
433
  //# sourceMappingURL=short-id.js.map
@@ -207,6 +207,8 @@ function withTaskExecutionLock(repoRoot, taskId, ownerName, callback, options =
207
207
  catch (error) {
208
208
  if (error instanceof TaskExecutionLockError)
209
209
  throw error;
210
+ if (error instanceof Error && error.name === 'OrchestrationStateError')
211
+ throw error;
210
212
  throw new TaskExecutionLockError('ORCHESTRATION_LOCK_FAILED', 'lifecycle task lock operation failed', { operation: ownsFixed ? 'callback' : 'acquire', errno: errno(error), key });
211
213
  }
212
214
  finally {
@@ -228,5 +230,8 @@ function withTaskExecutionLock(repoRoot, taskId, ownerName, callback, options =
228
230
  }
229
231
  }
230
232
  }
231
- export { TaskExecutionLockError, lockKey, mapLinkError, withTaskExecutionLock };
233
+ function withRepositoryMutationLock(repoRoot, callback, options = {}) {
234
+ return withTaskExecutionLock(repoRoot, '__repository_mutation__', 'repository-mutation', callback, options);
235
+ }
236
+ export { TaskExecutionLockError, lockKey, mapLinkError, withRepositoryMutationLock, withTaskExecutionLock };
232
237
  //# sourceMappingURL=task-execution-lock.js.map
@@ -16,6 +16,7 @@ import { parseLedger, summarizeLedgerStage, validateLedgerRows } from "./ledger.
16
16
  import { equalCounts, parseReviewSummary } from "./review-artifacts.js";
17
17
  import { loadVerificationConfig } from "./verification-config.js";
18
18
  import { snapshotReview } from "../git/review-snapshot.js";
19
+ import { OrchestrationStateError, readRun } from "./orchestration.js";
19
20
  const TASK_ENUMS = {
20
21
  type: ["feature", "bugfix", "refactor", "docs", "chore"],
21
22
  workflow: ["feature-development", "bug-fix", "refactoring"],
@@ -104,6 +105,8 @@ function runCheck(type, context, shared) {
104
105
  return checkReviewFact(context);
105
106
  case "post-review-commit":
106
107
  return checkPostReviewCommit(context);
108
+ case "required-pr-delivery":
109
+ return checkRequiredPrDelivery(context);
107
110
  case "orchestration-state":
108
111
  return checkOrchestrationState(context);
109
112
  case "orchestration-evidence":
@@ -120,22 +123,6 @@ function runCheck(type, context, shared) {
120
123
  }
121
124
  }
122
125
  }
123
- function isFailClosedLegacyPause(run) {
124
- if (run.schemaVersion !== 1
125
- || run.status !== 'paused'
126
- || run.pause?.recoverable !== false
127
- || !exactText(run.pause?.message)
128
- || !Array.isArray(run.receipts)) {
129
- return false;
130
- }
131
- if (run.pause.code === 'ORCHESTRATION_HISTORICAL_EFFORT_UNVERIFIED') {
132
- return run.receipts.length > 0 && run.pendingDelegation === null;
133
- }
134
- if (run.pause.code === 'ORCHESTRATION_DELEGATION_BUSY') {
135
- return run.pendingDelegation != null && run.pendingDelegation.status !== 'sealed';
136
- }
137
- return false;
138
- }
139
126
  function checkOrchestrationState({ taskDir }) {
140
127
  const file = path.join(taskDir, 'orchestration.json');
141
128
  const stat = safeStat(file);
@@ -143,16 +130,14 @@ function checkOrchestrationState({ taskDir }) {
143
130
  return failResult('orchestration-state', 'orchestration.json is missing');
144
131
  let run;
145
132
  try {
146
- run = JSON.parse(fs.readFileSync(file, 'utf8'));
133
+ run = readRun(taskDir);
147
134
  }
148
135
  catch (error) {
149
- return failResult('orchestration-state', `Invalid orchestration.json: ${String(error)}`);
150
- }
151
- if (isFailClosedLegacyPause(run)) {
152
- return passResult('orchestration-state', 'Legacy orchestration run is safely paused');
136
+ const message = error instanceof OrchestrationStateError ? error.message : String(error);
137
+ return failResult('orchestration-state', `Invalid orchestration.json: ${message}`);
153
138
  }
154
- if (![2, 3].includes(run.schemaVersion) || !['paused', 'completed'].includes(run.status)) {
155
- return failResult('orchestration-state', `Expected paused or completed run, received '${run.status}'`);
139
+ if (!run || !['paused', 'completed'].includes(run.status)) {
140
+ return failResult('orchestration-state', `Expected paused or completed run, received '${run?.status ?? 'missing'}'`);
156
141
  }
157
142
  if (run.status === 'paused' && (!run.pause?.code || !run.pause?.message)) {
158
143
  return failResult('orchestration-state', 'Paused run requires a stable pause code and message');
@@ -215,13 +200,11 @@ function checkOrchestrationEvidence({ taskDir }) {
215
200
  return failResult('orchestration-evidence', 'orchestration.json is missing');
216
201
  let run;
217
202
  try {
218
- run = JSON.parse(fs.readFileSync(file, 'utf8'));
203
+ run = readRun(taskDir);
219
204
  }
220
205
  catch (error) {
221
- return failResult('orchestration-evidence', `Invalid orchestration.json: ${String(error)}`);
222
- }
223
- if (isFailClosedLegacyPause(run)) {
224
- return passResult('orchestration-evidence', 'Legacy orchestration evidence is preserved in a fail-closed pause');
206
+ const message = error instanceof OrchestrationStateError ? error.message : String(error);
207
+ return failResult('orchestration-evidence', `Invalid orchestration.json: ${message}`);
225
208
  }
226
209
  const policy = run.modelPolicy;
227
210
  if (!policy
@@ -239,18 +222,11 @@ function checkOrchestrationEvidence({ taskDir }) {
239
222
  return failResult('orchestration-evidence', 'Run model policy source or recovery history is invalid');
240
223
  }
241
224
  for (const recovery of run.recoveryHistory) {
242
- const validModelRecovery = recovery.code === 'MODEL_POLICY_SUPPLEMENTED'
243
- && recovery.previousSchemaVersion === 1
244
- && recovery.receiptCount === 0
245
- && recovery.pendingDelegation === false
246
- && exactText(recovery.recoveredAt)
247
- && recovery.policySource;
248
225
  const guards = recovery.guards;
249
- const validCapabilityRecovery = recovery.code === 'CLIENT_CAPABILITY_ENABLED'
250
- && recovery.previousSchemaVersion === 2
226
+ const validClaudeCodeRecovery = recovery.code === 'CLIENT_CAPABILITY_ENABLED'
251
227
  && recovery.previousStatus === 'paused'
252
228
  && recovery.previousPause?.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED'
253
- && recovery.client === 'codex'
229
+ && recovery.client === 'claude-code'
254
230
  && recovery.resultingStatus === 'running'
255
231
  && exactText(recovery.recoveredAt)
256
232
  && guards?.stepCount === 0
@@ -259,9 +235,8 @@ function checkOrchestrationEvidence({ taskDir }) {
259
235
  && guards?.receiptCount === 0
260
236
  && guards?.pendingDelegation === false
261
237
  && guards?.commitAuthorizationUnused === true
262
- && guards?.completionEvidenceAbsent === true
263
- && guards?.commitIntentAbsent === true;
264
- if (!validModelRecovery && !validCapabilityRecovery) {
238
+ && guards?.completionEvidenceAbsent === true;
239
+ if (!validClaudeCodeRecovery) {
265
240
  return failResult('orchestration-evidence', 'Run recovery history contains invalid provenance');
266
241
  }
267
242
  }
@@ -292,35 +267,69 @@ function checkOrchestrationEvidence({ taskDir }) {
292
267
  }
293
268
  const activated = ['activated', 'stage-completed', 'sealed', 'consumed'].includes(receipt.status);
294
269
  if (activated) {
295
- if (!exactText(receipt.actualModel)) {
296
- return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has no host-observed actual model`);
270
+ const isClaudeCode = receipt.client === 'claude-code';
271
+ if (!isClaudeCode) {
272
+ if (!exactText(receipt.actualModel)) {
273
+ return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has no host-observed actual model`);
274
+ }
275
+ if (!exactText(receipt.actualReasoningEffort)) {
276
+ return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has no host-observed actual reasoning effort`);
277
+ }
297
278
  }
298
- if (!exactText(receipt.actualReasoningEffort)) {
299
- return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has no host-observed actual reasoning effort`);
279
+ // parentId/childId 校验对所有 client 保持统一(不新增分支);spawnMode 检查按 client 判断
280
+ if (!exactText(receipt.parentId) || !exactText(receipt.childId) || receipt.parentId === receipt.childId) {
281
+ return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has invalid delegation identity`);
300
282
  }
301
- if (!exactText(receipt.parentId) || !exactText(receipt.childId) || receipt.parentId === receipt.childId || receipt.spawnMode !== 'fresh') {
283
+ if (!isClaudeCode && receipt.spawnMode !== 'fresh') {
302
284
  return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has invalid fresh delegation identity`);
303
285
  }
304
- if (receipt.actualModel !== receipt.requestedModel && !exactText(receipt.modelFallbackReason)) {
305
- return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' model fallback is not justified`);
286
+ if (receipt.actualModel != null && receipt.actualModel !== receipt.requestedModel) {
287
+ if (!isClaudeCode && !exactText(receipt.modelFallbackReason)) {
288
+ return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' model fallback is not justified`);
289
+ }
306
290
  }
307
- if (receipt.actualModel === receipt.requestedModel && receipt.modelFallbackReason !== null) {
291
+ if ((receipt.actualModel == null || receipt.actualModel === receipt.requestedModel)
292
+ && receipt.modelFallbackReason !== null) {
308
293
  return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has an unrelated model fallback reason`);
309
294
  }
310
- if (receipt.actualReasoningEffort !== receipt.requestedReasoningEffort
311
- && !exactText(receipt.reasoningEffortFallbackReason)) {
312
- return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' reasoning-effort fallback is not justified`);
295
+ if (receipt.actualReasoningEffort != null && receipt.actualReasoningEffort !== receipt.requestedReasoningEffort) {
296
+ if (!isClaudeCode && !exactText(receipt.reasoningEffortFallbackReason)) {
297
+ return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' reasoning-effort fallback is not justified`);
298
+ }
313
299
  }
314
- if (receipt.actualReasoningEffort === receipt.requestedReasoningEffort
300
+ if ((receipt.actualReasoningEffort == null || receipt.actualReasoningEffort === receipt.requestedReasoningEffort)
315
301
  && receipt.reasoningEffortFallbackReason !== null) {
316
302
  return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has an unrelated reasoning-effort fallback reason`);
317
303
  }
318
304
  if (receipt.client === 'codex') {
305
+ const activatedCodex = ['activated', 'stage-completed', 'sealed', 'consumed'].includes(receipt.status);
319
306
  const host = receipt.hostEvidence;
320
- if (host?.kind !== 'codex-lifecycle-v1'
321
- || !exactText(host.hookDefinitionHash)
322
- || !Number.isSafeInteger(host.startRevision)
323
- || host.startRevision < 1) {
307
+ const provenance = receipt.lifecycleProvenance;
308
+ if (!provenance
309
+ || (activatedCodex && (host?.kind !== 'codex-lifecycle-v2'
310
+ || host.protocolVersion !== provenance.protocolVersion
311
+ || host.packageVersion !== provenance.packageVersion
312
+ || host.internalExecutableBuildHash !== provenance.internalExecutableBuildHash
313
+ || host.lifecycleContractHash !== provenance.lifecycleContractHash
314
+ || host.hookDefinitionHash !== provenance.hookDefinitionHash
315
+ || host.hookSource !== provenance.hookSource
316
+ || host.hookSourcePathDigest !== provenance.hookSourcePathDigest
317
+ || host.hookSourceHash !== provenance.hookSourceHash
318
+ || host.capabilitySessionId !== provenance.capabilitySessionId
319
+ || receipt.parentId !== provenance.capabilitySessionId
320
+ || host.capabilityTurnId !== provenance.capabilityTurnId
321
+ || host.controllerInstanceDigest !== provenance.controllerInstanceDigest
322
+ || host.controlGeneration !== provenance.controlGeneration
323
+ || host.spawnToolUseId === provenance.capabilityToolUseId
324
+ || !exactText(host.spawnToolUseId)
325
+ || !Number.isFinite(Date.parse(host.spawnObservedAt ?? ''))
326
+ || !Number.isFinite(Date.parse(receipt.spawnDispatchedAt ?? ''))
327
+ || !Number.isFinite(Date.parse(receipt.activationDeadlineAt ?? ''))
328
+ || Date.parse(host.spawnObservedAt ?? '') < Date.parse(receipt.spawnDispatchedAt ?? '')
329
+ || Date.parse(host.spawnObservedAt ?? '') > Date.parse(receipt.activationDeadlineAt ?? '')
330
+ || !exactText(host.hookDefinitionHash)
331
+ || !Number.isSafeInteger(host.startRevision)
332
+ || host.startRevision < 1))) {
324
333
  return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has invalid Codex start evidence`);
325
334
  }
326
335
  if (['sealed', 'consumed'].includes(receipt.status) && (!Number.isSafeInteger(host.stopRevision)
@@ -412,6 +421,60 @@ function checkTaskMeta({ taskDir, config }) {
412
421
  const warningSuffix = warnings.length > 0 ? `; warnings: ${warnings.join("; ")}` : "";
413
422
  return passResult("task-meta", `Task metadata valid (${requiredFields.length} required fields checked${warningSuffix})`, warnings);
414
423
  }
424
+ function checkRequiredPrDelivery({ taskDir, repositoryRoot, mode }) {
425
+ const task = loadTask(taskDir);
426
+ if (!task.ok)
427
+ return failResult('required-pr-delivery', task.message);
428
+ let projectConfig = {};
429
+ try {
430
+ const file = path.join(repositoryRoot, '.agents', '.airc.json');
431
+ if (safeStat(file)?.isFile())
432
+ projectConfig = JSON.parse(fs.readFileSync(file, 'utf8'));
433
+ }
434
+ catch {
435
+ return failResult('required-pr-delivery', 'Project configuration is invalid');
436
+ }
437
+ const flow = projectConfig.prFlow;
438
+ const status = String(task.metadata.pr_status || 'pending');
439
+ const prNumber = parsePrNumber(task.metadata.pr_number);
440
+ if (flow === 'disabled')
441
+ return passResult('required-pr-delivery', 'Pull request delivery is disabled by project policy');
442
+ if (flow === 'required' && (status !== 'created' || prNumber === null)) {
443
+ return failResult('required-pr-delivery', 'Project policy requires a bound pull request before completion');
444
+ }
445
+ if (flow !== 'required' && status === 'pending') {
446
+ return failResult('required-pr-delivery', 'Pull request delivery is pending; create a PR or explicitly skip it');
447
+ }
448
+ if (status === 'created' && prNumber === null) {
449
+ return failResult('required-pr-delivery', 'Task claims a created pull request without a valid pr_number');
450
+ }
451
+ if (flow === 'required') {
452
+ if (mode === 'gate') {
453
+ return passResult('required-pr-delivery', 'Bound pull request shape is valid; merged-state inspection is reserved for hard preflight');
454
+ }
455
+ const inspected = inspectPlatformPullRequest(task.metadata.id, { cwd: repositoryRoot });
456
+ if (inspected.status === 'blocked') {
457
+ return blockedResult('required-pr-delivery', inspected.error?.message || 'Unable to inspect the required pull request', inspected.error?.code || 'PR_INSPECTION_BLOCKED');
458
+ }
459
+ const pullRequest = inspected.pullRequest;
460
+ const repository = inspected.platform.repository?.toLowerCase() || '';
461
+ const taskBranch = String(task.metadata.branch || '').trim();
462
+ if (inspected.status !== 'no-op' || !pullRequest || pullRequest.number !== prNumber) {
463
+ return failResult('required-pr-delivery', 'Bound pull request identity could not be verified');
464
+ }
465
+ if (!repository
466
+ || pullRequest.repository.toLowerCase() !== repository
467
+ || pullRequest.base.repository.toLowerCase() !== repository
468
+ || (taskBranch && pullRequest.head.ref !== taskBranch)) {
469
+ return failResult('required-pr-delivery', 'Bound pull request identity does not match the task');
470
+ }
471
+ if (pullRequest.state !== 'closed' || !pullRequest.mergedAt || !pullRequest.mergeCommitSha) {
472
+ return failResult('required-pr-delivery', 'Project policy requires the bound pull request to be merged');
473
+ }
474
+ return passResult('required-pr-delivery', `Pull request delivery policy satisfied (merged PR #${prNumber})`);
475
+ }
476
+ return passResult('required-pr-delivery', `Pull request delivery policy satisfied (${status})`);
477
+ }
415
478
  function validateTaskBranch(metadata) {
416
479
  if (isBlank(metadata.branch)) {
417
480
  return null;
@@ -774,7 +837,7 @@ function checkReviewLedger({ taskDir, config }) {
774
837
  return passResult("review-ledger", "Disagreement ledger has no entries");
775
838
  }
776
839
  const stageScope = Array.isArray(config.stage_scope) ? config.stage_scope : null;
777
- const maxRounds = Number(resolveReviewSetting(config, "max_handshake_rounds", DEFAULT_MAX_HANDSHAKE_ROUNDS));
840
+ const maxRounds = Number(resolveReviewSetting(config, "maxHandshakeRounds", DEFAULT_MAX_HANDSHAKE_ROUNDS));
778
841
  const problems = [];
779
842
  let inScopeCount = 0;
780
843
  for (const cells of rows) {
@@ -1311,7 +1374,14 @@ function verifyInProcess({ mode, skillName, taskDir, artifactFile, checks: reque
1311
1374
  for (const [type, checkConfig] of Object.entries(verifyConfig.checks || {})) {
1312
1375
  if (checkConfig === null)
1313
1376
  continue;
1314
- const result = runCheck(type, { skillName, taskDir: path.resolve(taskDir), artifactFile, config: checkConfig }, shared);
1377
+ const result = runCheck(type, {
1378
+ mode,
1379
+ skillName,
1380
+ taskDir: path.resolve(taskDir),
1381
+ artifactFile,
1382
+ repositoryRoot: repoRoot,
1383
+ config: checkConfig
1384
+ }, shared);
1315
1385
  checks.push(result);
1316
1386
  if (result.status === "blocked")
1317
1387
  break;
@@ -1325,7 +1395,16 @@ function verifyInProcess({ mode, skillName, taskDir, artifactFile, checks: reque
1325
1395
  return { skill: skillName, ...failResult(type, `Unknown check type '${type}' for skill '${skillName}'.`) };
1326
1396
  if (config === null)
1327
1397
  return { skill: skillName, ...passResult(type, `Check '${type}' is disabled for skill '${skillName}'.`) };
1328
- return { skill: skillName, ...runCheck(type, { skillName, taskDir: path.resolve(taskDir), artifactFile, config }, shared) };
1398
+ return {
1399
+ skill: skillName,
1400
+ ...runCheck(type, {
1401
+ skillName,
1402
+ taskDir: path.resolve(taskDir),
1403
+ artifactFile,
1404
+ repositoryRoot: repoRoot,
1405
+ config
1406
+ }, shared)
1407
+ };
1329
1408
  }
1330
1409
  export { verifyInProcess };
1331
1410
  //# sourceMappingURL=verification-engine.js.map
@@ -4,6 +4,7 @@ import path from 'node:path';
4
4
  import { verifyInProcess } from './verification-engine.js';
5
5
  import { parseArtifactName } from './artifact-lifecycle.js';
6
6
  import { resolveTaskRef } from './resolve-ref.js';
7
+ import { allowsManualOverride } from './guard-override.js';
7
8
  const gate = (skill, expectedState, artifactFamily) => ({
8
9
  skill, expectedState, mode: 'gate', ...(artifactFamily ? { artifactFamily } : {})
9
10
  });
@@ -20,7 +21,8 @@ const VERIFICATION_CATALOG = {
20
21
  'block-task.completed': gate('block-task', 'blocked'),
21
22
  'cancel-task.completed': gate('cancel-task', 'completed'),
22
23
  'commit.completed': gate('commit', 'active'),
23
- 'complete-task.preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['review-ledger', 'manual-validation', 'post-review-commit', 'platform-sync-preflight'] },
24
+ 'complete-task.preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['required-pr-delivery'] },
25
+ 'complete-task.hard-preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['required-pr-delivery'] },
24
26
  'complete-task.completed': gate('complete-task', 'completed'),
25
27
  'create-pr.completed': gate('create-pr', 'active'),
26
28
  'create-task.completed': gate('create-task', 'active'),
@@ -47,7 +49,7 @@ function verifyTaskEvent(request, options = {}) {
47
49
  if (!resolved.ok)
48
50
  return failure(request, resolved.code, resolved.message, { taskId: resolved.taskId, skill: spec.skill, mode: spec.mode });
49
51
  const identity = { taskId: resolved.taskId, taskDir: resolved.taskDir, taskState: resolved.state, skill: spec.skill, mode: spec.mode };
50
- if (resolved.state !== spec.expectedState) {
52
+ if (resolved.state !== spec.expectedState && !allowsManualOverride(options.manualOverride, 'task-verify', 'VERIFY_TASK_STATE_MISMATCH')) {
51
53
  return failure(request, 'VERIFY_TASK_STATE_MISMATCH', `event '${request.event}' requires workspace '${spec.expectedState}', received '${resolved.state}'`, identity);
52
54
  }
53
55
  if (spec.artifactFamily) {
@@ -66,12 +68,23 @@ function verifyTaskEvent(request, options = {}) {
66
68
  if (spec.mode === 'gate') {
67
69
  const payload = engine({ mode: 'gate', skillName: spec.skill, taskDir: resolved.taskDir, artifactFile: request.artifact, checks: [], repositoryRoot: resolved.repoRoot });
68
70
  const status = payload.gate;
71
+ if (status !== 'pass' && allowsManualOverride(options.manualOverride, 'verification-engine', status === 'blocked' ? 'CHECK_BLOCKED' : 'CHECK_FAILED')) {
72
+ invocations.push({ status: 'pass', exitCode: 0, payload: { ...payload, gate: 'pass', humanOverride: 'human-approved', originalGate: status } });
73
+ return {
74
+ status: 'pass', changed: false, event: request.event, requestRef: request.taskRef,
75
+ ...identity, artifact: request.artifact ?? null, invocations, error: null
76
+ };
77
+ }
69
78
  invocations.push({ status, exitCode: { pass: 0, fail: 1, blocked: 2 }[status], payload });
70
79
  }
71
80
  else {
72
81
  for (const check of spec.checks ?? []) {
73
82
  const payload = engine({ mode: 'checks', skillName: spec.skill, taskDir: resolved.taskDir, artifactFile: request.artifact, checks: [check], repositoryRoot: resolved.repoRoot });
74
83
  const status = payload.status;
84
+ if (status !== 'pass' && allowsManualOverride(options.manualOverride, 'verification-engine', status === 'blocked' ? 'CHECK_BLOCKED' : 'CHECK_FAILED')) {
85
+ invocations.push({ status: 'pass', exitCode: 0, payload: { ...payload, status: 'pass', humanOverride: 'human-approved', originalStatus: status } });
86
+ break;
87
+ }
75
88
  invocations.push({ status, exitCode: { pass: 0, fail: 1, blocked: 2 }[status], payload });
76
89
  if (status !== 'pass')
77
90
  break;
@@ -2,6 +2,8 @@ import fs from 'node:fs';
2
2
  import { resolveTaskRef } from './resolve-ref.js';
3
3
  import { WORKFLOW_WARNING_COLUMNS, WORKFLOW_WARNING_HEADINGS, WORKFLOW_WARNING_SEVERITIES, WORKFLOW_WARNING_STATUSES, parseWorkflowWarnings } from './workflow-warnings.js';
4
4
  import { captureTaskWriteMetadata, writeTask } from './write.js';
5
+ import { allowsManualOverride } from './guard-override.js';
6
+ import { retryHintForWarning } from './operation-outcome.js';
5
7
  function failed(intent, code, message, taskId = null, entityId = null) {
6
8
  return { status: 'failed', changed: false, intent: intent.kind, taskId, entityId, before: null, after: null, warnings: [], operations: [], error: { code, message } };
7
9
  }
@@ -54,7 +56,8 @@ function applyWorkflowWarningIntent(intent, options = {}) {
54
56
  const resolved = resolveTaskRef(intent.taskRef, { repoRoot: options.repoRoot });
55
57
  if (!resolved.ok)
56
58
  return failed(intent, resolved.code, resolved.message, resolved.taskId);
57
- if (resolved.state !== 'active')
59
+ const stateOverride = allowsManualOverride(options.manualOverride, 'workflow-warning', 'TASK_STATE_MISMATCH');
60
+ if (resolved.state !== 'active' && !stateOverride)
58
61
  return failed(intent, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
59
62
  let content;
60
63
  let rows;
@@ -114,7 +117,7 @@ function applyWorkflowWarningIntent(intent, options = {}) {
114
117
  after = { ...before, status: intent.status, resolvedAt: metadata.timestamp, resolution };
115
118
  }
116
119
  const writeResult = writeTask({
117
- taskRef: intent.taskRef, expectedState: 'active', mutations: [...sectionMutation(content), rowMutation(after)], dryRun: intent.dryRun
120
+ taskRef: intent.taskRef, expectedState: stateOverride ? resolved.state : 'active', mutations: [...sectionMutation(content), rowMutation(after)], dryRun: intent.dryRun
118
121
  }, {
119
122
  ...options,
120
123
  taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state },
@@ -127,5 +130,53 @@ function applyWorkflowWarningIntent(intent, options = {}) {
127
130
  entityId: after.id, before, after, warnings: [], operations: writeResult.operations, error: null
128
131
  };
129
132
  }
130
- export { applyWorkflowWarningIntent };
133
+ function projectFinalizationWarning(taskRef, warning, options = {}) {
134
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
135
+ if (!resolved.ok)
136
+ return { status: 'failed', changed: false, error: { code: resolved.code, message: resolved.message } };
137
+ if (resolved.state !== 'completed')
138
+ return { status: 'failed', changed: false, error: { code: 'TASK_STATE_MISMATCH', message: `task ${resolved.taskId} is ${resolved.state}, expected completed` } };
139
+ let content;
140
+ let rows;
141
+ try {
142
+ content = fs.readFileSync(resolved.taskMdPath, 'utf8');
143
+ rows = parseWorkflowWarnings(content);
144
+ }
145
+ catch (error) {
146
+ return { status: 'failed', changed: false, error: { code: 'WARNING_DOCUMENT_INVALID', message: error instanceof Error ? error.message : String(error) } };
147
+ }
148
+ const metadata = (options.metadataProvider ?? captureTaskWriteMetadata)();
149
+ const existing = rows.find((row) => row.step === warning.step && row.code === warning.code && row.target === warning.target);
150
+ const after = existing
151
+ ? {
152
+ ...existing,
153
+ severity: warning.severity,
154
+ status: warning.status,
155
+ message: warning.message,
156
+ action: retryHintForWarning(warning),
157
+ resolvedAt: warning.resolvedAt || (warning.status === 'resolved' ? metadata.timestamp : ''),
158
+ resolution: warning.status === 'resolved' ? 'Resolved by a successful finalization retry' : ''
159
+ }
160
+ : {
161
+ id: nextWarningId(rows), time: metadata.timestamp, step: warning.step,
162
+ severity: warning.severity, code: warning.code, status: warning.status,
163
+ target: warning.target, message: warning.message, action: retryHintForWarning(warning),
164
+ resolvedAt: warning.resolvedAt || '', resolution: warning.status === 'resolved' ? 'Resolved by a successful finalization retry' : ''
165
+ };
166
+ if (existing && JSON.stringify(existing) === JSON.stringify(after))
167
+ return { status: 'no-op', changed: false, error: null };
168
+ const written = writeTask({
169
+ taskRef: resolved.taskId,
170
+ expectedState: 'completed',
171
+ mutations: [...sectionMutation(content), rowMutation(after)]
172
+ }, {
173
+ ...options,
174
+ taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: 'completed' },
175
+ metadataProvider: () => metadata
176
+ });
177
+ if (written.status === 'failed')
178
+ return { status: 'failed', changed: false, error: { code: written.error.code, message: written.error.message } };
179
+ return { status: written.status === 'applied' ? 'applied' : 'no-op', changed: written.changed, error: null };
180
+ }
181
+ export { applyWorkflowWarningIntent, projectFinalizationWarning };
131
182
  //# sourceMappingURL=workflow-warning-intents.js.map
@@ -3,6 +3,7 @@ import path from 'node:path';
3
3
  import { VERSION } from '../version.js';
4
4
  import { parseTypedTaskFrontmatter, updateTaskFrontmatter } from './frontmatter.js';
5
5
  import { resolveTaskRef } from './resolve-ref.js';
6
+ import { allowsManualOverride } from './guard-override.js';
6
7
  import { mutateTableRow, upsertSection } from './sections.js';
7
8
  const DEFAULT_FILE_SYSTEM = {
8
9
  readFileSync: (file) => fs.readFileSync(file, 'utf8'),
@@ -70,7 +71,7 @@ function writeTask(request, options = {}) {
70
71
  taskMdPath: resolved.taskMdPath,
71
72
  actualState: resolved.state
72
73
  };
73
- if (resolved.state !== request.expectedState) {
74
+ if (resolved.state !== request.expectedState && !allowsManualOverride(options.manualOverride, 'task.write', 'TASK_STATE_MISMATCH')) {
74
75
  return failure(request, identity, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected ${request.expectedState}`);
75
76
  }
76
77
  const io = { ...DEFAULT_FILE_SYSTEM, ...options.fileSystem };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "protocolVersion": 3,
3
- "packageVersion": "0.9.7",
4
- "sourceInputHash": "ee12d1ec7a586eeaba04deb4253ccd7f5d16552915ca141e493248c0728459a9",
5
- "internalExecutableBuildHash": "0eceb914e6c1cb9937b86d34bfbf933d1010f499b9976813556146d2e6fa0132"
3
+ "packageVersion": "0.9.9",
4
+ "sourceInputHash": "2f2db695db557531b3139eccd1bd530fbeb7b99409e2d49338f3b4646aac9ef6",
5
+ "internalExecutableBuildHash": "1391ebe2614f3071e7a60920325046a47f1473e7fb23c821327072f947f639d3"
6
6
  }
@@ -16,7 +16,7 @@ const claudeCodeAdapter = defineAgentClientAdapter({
16
16
  commands: { level: 'integrated' },
17
17
  hooks: { level: 'integrated' },
18
18
  subagents: { level: 'experimental' },
19
- orchestration: { level: 'unsupported' },
19
+ orchestration: { level: 'experimental' },
20
20
  sandbox: { level: 'integrated' },
21
21
  verification: { level: 'compatible' }
22
22
  },
@@ -26,8 +26,8 @@ const claudeCodeAdapter = defineAgentClientAdapter({
26
26
  guidance: 'Use the host model picker for the complete model and reasoning-effort catalog.'
27
27
  },
28
28
  delegationEvidence: {
29
- actualModel: 'unavailable',
30
- actualReasoningEffort: 'unavailable'
29
+ actualModel: 'host-event',
30
+ actualReasoningEffort: 'spawn-ack'
31
31
  },
32
32
  project: {
33
33
  ownedPathPrefixes: ['.claude/'],