@fitlab-ai/agent-infra 0.11.14 → 0.12.0

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 (271) hide show
  1. package/bin/cli.ts +1 -1
  2. package/dist/bin/cli.js +1 -1
  3. package/dist/bin/internal-cli.js +6 -1
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +6 -6
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +8 -18
  6. package/dist/lib/agent-clients/adapters/codex-orchestration.js +1 -1
  7. package/dist/lib/decide.js +2 -7
  8. package/dist/lib/internal/cli-route-inventory.js +3 -0
  9. package/dist/lib/internal/manual-validation.js +8 -16
  10. package/dist/lib/internal/platform-comment.js +3 -2
  11. package/dist/lib/internal/task-activity.js +1 -12
  12. package/dist/lib/internal/task-event.js +1 -12
  13. package/dist/lib/internal/task-finalization.js +2 -2
  14. package/dist/lib/internal/task-ledger.js +1 -12
  15. package/dist/lib/internal/task-operation-registry.js +2 -1
  16. package/dist/lib/internal/task-qualification.js +6 -15
  17. package/dist/lib/internal/task-review.js +3 -16
  18. package/dist/lib/internal/task-short-id.js +2 -1
  19. package/dist/lib/internal/task-verify.js +1 -11
  20. package/dist/lib/internal/task-warning.js +1 -12
  21. package/dist/lib/internal/task-workspace.js +43 -0
  22. package/dist/lib/merge.js +4 -3
  23. package/dist/lib/platform/pr-change-report.js +10 -3
  24. package/dist/lib/platform/pr-summary.js +6 -15
  25. package/dist/lib/platform/pull-requests.js +3 -15
  26. package/dist/lib/pr-review/evidence-grading.js +3 -2
  27. package/dist/lib/process-data/index.js +18 -6
  28. package/dist/lib/process-data/sources.js +6 -0
  29. package/dist/lib/run/index.js +3 -2
  30. package/dist/lib/sandbox/commands/create.js +5 -1
  31. package/dist/lib/sandbox/commands/enter.js +1 -1
  32. package/dist/lib/sandbox/commands/list-running.js +2 -1
  33. package/dist/lib/sandbox/control/client.js +8 -13
  34. package/dist/lib/sandbox/control/completed-reentry.js +2 -1
  35. package/dist/lib/sandbox/control/lifecycle.js +2 -21
  36. package/dist/lib/sandbox/control/task-view.js +4 -2
  37. package/dist/lib/sandbox/mounts.js +4 -4
  38. package/dist/lib/sandbox/recovery.js +26 -16
  39. package/dist/lib/sandbox/removal.js +3 -10
  40. package/dist/lib/sandbox/task-resolver.js +9 -6
  41. package/dist/lib/sandbox/workspace-identity.js +36 -20
  42. package/dist/lib/sandbox/workspace-view.js +29 -22
  43. package/dist/lib/task/activity-intent.js +2 -12
  44. package/dist/lib/task/artifact-lifecycle.js +17 -11
  45. package/dist/lib/task/artifact-operations.js +41 -49
  46. package/dist/lib/task/capabilities.js +2 -2
  47. package/dist/lib/task/commands/ls.js +4 -3
  48. package/dist/lib/task/commands/status.js +1 -1
  49. package/dist/lib/task/commit-operation.js +6 -6
  50. package/dist/lib/task/control-authority.js +32 -32
  51. package/dist/lib/task/create-service.js +2 -4
  52. package/dist/lib/task/create.js +12 -65
  53. package/dist/lib/task/delegation-receipts.js +1 -1
  54. package/dist/lib/task/delivery.js +2 -8
  55. package/dist/lib/task/events.js +5 -31
  56. package/dist/lib/task/finalization.js +17 -22
  57. package/dist/lib/task/lifecycle.js +38 -49
  58. package/dist/lib/task/local-artifact-finalization.js +1 -2
  59. package/dist/lib/task/orchestration.js +24 -57
  60. package/dist/lib/task/platform-operation-recovery.js +1 -2
  61. package/dist/lib/task/resolve-ref.js +17 -12
  62. package/dist/lib/task/review-finalization.js +2 -35
  63. package/dist/lib/task/review-identity.js +3 -1
  64. package/dist/lib/task/short-id.js +12 -10
  65. package/dist/lib/task/verification-engine.js +19 -58
  66. package/dist/lib/task/workspace-root.js +59 -0
  67. package/dist/lib/task/workspace-snapshot.js +4 -3
  68. package/dist/lifecycle-build-manifest.json +3 -3
  69. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +6 -6
  70. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +8 -34
  71. package/lib/agent-clients/adapters/codex-orchestration.ts +1 -1
  72. package/lib/decide.ts +2 -7
  73. package/lib/internal/cli-route-inventory.ts +2 -0
  74. package/lib/internal/manual-validation.ts +8 -19
  75. package/lib/internal/platform-comment.ts +3 -2
  76. package/lib/internal/task-activity.ts +1 -10
  77. package/lib/internal/task-event.ts +1 -10
  78. package/lib/internal/task-finalization.ts +2 -2
  79. package/lib/internal/task-ledger.ts +1 -10
  80. package/lib/internal/task-operation-registry.ts +2 -1
  81. package/lib/internal/task-qualification.ts +5 -11
  82. package/lib/internal/task-review.ts +3 -14
  83. package/lib/internal/task-short-id.ts +2 -1
  84. package/lib/internal/task-verify.ts +1 -9
  85. package/lib/internal/task-warning.ts +1 -10
  86. package/lib/internal/task-workspace.ts +39 -0
  87. package/lib/merge.ts +4 -3
  88. package/lib/platform/pr-change-report.ts +9 -3
  89. package/lib/platform/pr-summary.ts +7 -16
  90. package/lib/platform/pull-requests.ts +3 -24
  91. package/lib/pr-review/evidence-grading.ts +3 -2
  92. package/lib/process-data/index.ts +17 -5
  93. package/lib/process-data/sources.ts +6 -0
  94. package/lib/run/index.ts +3 -2
  95. package/lib/sandbox/commands/create.ts +5 -1
  96. package/lib/sandbox/commands/enter.ts +1 -1
  97. package/lib/sandbox/commands/list-running.ts +2 -1
  98. package/lib/sandbox/control/client.ts +7 -12
  99. package/lib/sandbox/control/completed-reentry.ts +2 -1
  100. package/lib/sandbox/control/lifecycle.ts +4 -21
  101. package/lib/sandbox/control/task-view.ts +4 -2
  102. package/lib/sandbox/mounts.ts +4 -4
  103. package/lib/sandbox/recovery.ts +26 -18
  104. package/lib/sandbox/removal.ts +3 -26
  105. package/lib/sandbox/task-resolver.ts +9 -6
  106. package/lib/sandbox/workspace-identity.ts +42 -18
  107. package/lib/sandbox/workspace-view.ts +28 -20
  108. package/lib/task/activity-intent.ts +3 -17
  109. package/lib/task/artifact-lifecycle.ts +39 -11
  110. package/lib/task/artifact-operations.ts +28 -35
  111. package/lib/task/capabilities.ts +2 -2
  112. package/lib/task/commands/ls.ts +4 -3
  113. package/lib/task/commands/status.ts +1 -1
  114. package/lib/task/commit-operation.ts +5 -6
  115. package/lib/task/control-authority.ts +29 -35
  116. package/lib/task/create-service.ts +2 -4
  117. package/lib/task/create.ts +10 -51
  118. package/lib/task/delegation-receipts.ts +1 -1
  119. package/lib/task/delivery.ts +2 -10
  120. package/lib/task/events.ts +5 -32
  121. package/lib/task/finalization.ts +16 -24
  122. package/lib/task/lifecycle.ts +40 -51
  123. package/lib/task/local-artifact-finalization.ts +1 -4
  124. package/lib/task/orchestration.ts +31 -80
  125. package/lib/task/platform-operation-recovery.ts +1 -2
  126. package/lib/task/resolve-ref.ts +18 -12
  127. package/lib/task/review-finalization.ts +2 -52
  128. package/lib/task/review-identity.ts +3 -1
  129. package/lib/task/short-id.ts +15 -10
  130. package/lib/task/verification-engine.ts +20 -62
  131. package/lib/task/workspace-root.ts +78 -0
  132. package/lib/task/workspace-snapshot.ts +4 -3
  133. package/package.json +1 -1
  134. package/templates/.agents/QUICKSTART.en.md +19 -8
  135. package/templates/.agents/QUICKSTART.zh-CN.md +19 -8
  136. package/templates/.agents/README.en.md +10 -15
  137. package/templates/.agents/README.zh-CN.md +9 -13
  138. package/templates/.agents/rules/issue-sync.en.md +1 -1
  139. package/templates/.agents/rules/issue-sync.zh-CN.md +1 -1
  140. package/templates/.agents/rules/lifecycle-orchestration.en.md +1 -1
  141. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +1 -1
  142. package/templates/.agents/rules/next-step-output.en.md +2 -2
  143. package/templates/.agents/rules/next-step-output.zh-CN.md +2 -2
  144. package/templates/.agents/rules/pr-sync.en.md +1 -1
  145. package/templates/.agents/rules/pr-sync.zh-CN.md +1 -1
  146. package/templates/.agents/rules/release-commands.en.md +3 -0
  147. package/templates/.agents/rules/release-commands.zh-CN.md +3 -0
  148. package/templates/.agents/rules/task-management.en.md +0 -2
  149. package/templates/.agents/rules/task-management.zh-CN.md +0 -2
  150. package/templates/.agents/rules/task-short-id.en.md +2 -2
  151. package/templates/.agents/rules/task-short-id.zh-CN.md +2 -2
  152. package/templates/.agents/scripts/task-short-id.js +2 -8
  153. package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
  154. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
  155. package/templates/.agents/skills/analyze-task/config/verify.en.json +0 -1
  156. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +0 -1
  157. package/templates/.agents/skills/archive-tasks/SKILL.en.md +5 -5
  158. package/templates/.agents/skills/archive-tasks/SKILL.zh-CN.md +5 -5
  159. package/templates/.agents/skills/archive-tasks/scripts/archive-tasks.sh +10 -2
  160. package/templates/.agents/skills/block-task/SKILL.en.md +4 -4
  161. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +5 -5
  162. package/templates/.agents/skills/block-task/config/verify.json +0 -1
  163. package/templates/.agents/skills/cancel-task/SKILL.en.md +6 -6
  164. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +6 -6
  165. package/templates/.agents/skills/cancel-task/config/verify.json +0 -1
  166. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  167. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +3 -3
  168. package/templates/.agents/skills/code-task/config/verify.en.json +0 -1
  169. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +0 -1
  170. package/templates/.agents/skills/code-task/reference/output-template.en.md +1 -1
  171. package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +1 -1
  172. package/templates/.agents/skills/commit/SKILL.en.md +1 -1
  173. package/templates/.agents/skills/commit/SKILL.zh-CN.md +1 -1
  174. package/templates/.agents/skills/commit/config/verify.json +0 -1
  175. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +2 -2
  176. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +2 -2
  177. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  178. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  179. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +2 -2
  180. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +2 -2
  181. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +0 -1
  182. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +0 -1
  183. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
  184. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
  185. package/templates/.agents/skills/complete-task/SKILL.en.md +17 -9
  186. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +18 -10
  187. package/templates/.agents/skills/complete-task/config/verify.en.json +0 -1
  188. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +0 -1
  189. package/templates/.agents/skills/create-pr/SKILL.en.md +3 -3
  190. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +3 -3
  191. package/templates/.agents/skills/create-pr/config/verify.json +0 -1
  192. package/templates/.agents/skills/create-pr/reference/change-report.en.md +13 -3
  193. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +13 -3
  194. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +1 -1
  195. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +1 -1
  196. package/templates/.agents/skills/create-release-note/SKILL.en.md +2 -2
  197. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +2 -2
  198. package/templates/.agents/skills/create-task/SKILL.en.md +4 -4
  199. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +4 -4
  200. package/templates/.agents/skills/create-task/config/verify.json +0 -1
  201. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  202. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  203. package/templates/.agents/skills/import-codescan/config/verify.json +0 -1
  204. package/templates/.agents/skills/import-dependabot/SKILL.en.md +3 -3
  205. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +3 -3
  206. package/templates/.agents/skills/import-dependabot/config/verify.json +0 -1
  207. package/templates/.agents/skills/import-issue/SKILL.en.md +5 -5
  208. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -5
  209. package/templates/.agents/skills/import-issue/config/verify.json +0 -1
  210. package/templates/.agents/skills/plan-task/SKILL.en.md +5 -5
  211. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -5
  212. package/templates/.agents/skills/plan-task/config/verify.en.json +0 -1
  213. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +0 -1
  214. package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
  215. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +3 -3
  216. package/templates/.agents/skills/restore-task/config/verify.json +0 -1
  217. package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
  218. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
  219. package/templates/.agents/skills/review-analysis/config/verify.en.json +0 -1
  220. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +0 -1
  221. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +5 -5
  222. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +5 -5
  223. package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
  224. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
  225. package/templates/.agents/skills/review-code/reference/output-templates.en.md +4 -4
  226. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +4 -4
  227. package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
  228. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
  229. package/templates/.agents/skills/review-plan/config/verify.en.json +0 -1
  230. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +0 -1
  231. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +5 -5
  232. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +5 -5
  233. package/templates/.agents/skills/review-pr/SKILL.en.md +2 -2
  234. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +2 -2
  235. package/templates/.agents/skills/review-pr/config/verify.en.json +0 -1
  236. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +0 -1
  237. package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +1 -1
  238. package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +1 -1
  239. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -5
  240. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -5
  241. package/templates/.agents/skills/run-manual-validation/config/verify.en.json +0 -1
  242. package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +0 -1
  243. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +13 -2
  244. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +13 -2
  245. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
  246. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
  247. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  248. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  249. package/templates/.agents/skills/test/SKILL.en.md +0 -3
  250. package/templates/.agents/skills/test/SKILL.zh-CN.md +0 -3
  251. package/templates/.agents/skills/test-integration/SKILL.en.md +0 -3
  252. package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +0 -3
  253. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +13 -6
  254. package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +0 -3
  255. package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +0 -3
  256. package/templates/.agents/skills/watch-pr/SKILL.en.md +21 -4
  257. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +22 -5
  258. package/templates/.agents/skills/watch-pr/config/verify.json +0 -1
  259. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +14 -0
  260. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +15 -1
  261. package/templates/.agents/templates/handoff.en.md +1 -1
  262. package/templates/.agents/templates/handoff.zh-CN.md +1 -1
  263. package/templates/.agents/workflows/bug-fix.en.yaml +1 -1
  264. package/templates/.agents/workflows/feature-development.en.yaml +1 -1
  265. package/templates/.agents/workflows/refactoring.en.yaml +1 -1
  266. package/dist/lib/runtime/agent-runtime.js +0 -33
  267. package/dist/lib/task/runtime-paths.js +0 -10
  268. package/dist/lib/task/task-execution-lock.js +0 -250
  269. package/lib/runtime/agent-runtime.ts +0 -41
  270. package/lib/task/runtime-paths.ts +0 -12
  271. package/lib/task/task-execution-lock.ts +0 -320
@@ -10,8 +10,6 @@ import {
10
10
  } from './artifact-lifecycle.ts';
11
11
  import { parseArtifactName } from './artifact-name.ts';
12
12
  import type { ArtifactIdentity } from './artifact-lifecycle.ts';
13
- import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
14
- import type { TaskExecutionLockOptions } from './task-execution-lock.ts';
15
13
  import { captureTaskWriteMetadata, writeTask } from './write.ts';
16
14
  import type { TaskMutation, TaskOperationSummary, TaskWriteOptions } from './write.ts';
17
15
 
@@ -95,7 +93,7 @@ export type PrReviewInspectionResult = {
95
93
  error: { code: string; message: string } | null;
96
94
  };
97
95
 
98
- type ActivityIntentOptions = TaskWriteOptions & { lockOptions?: TaskExecutionLockOptions; lockAlreadyHeld?: boolean };
96
+ type ActivityIntentOptions = TaskWriteOptions;
99
97
  type ArtifactRecord = { identity: ArtifactIdentity; head: string; status: PrReviewArtifactStatus };
100
98
  type RoundState = ArtifactRecord & {
101
99
  startedAgent: string | null;
@@ -299,7 +297,7 @@ function terminalNote(intent: PrReviewCompleteIntent | PrReviewTerminateIntent):
299
297
  return `Outcome: ${outcomeLabel(intent.outcome)}, reason: ${intent.reason} → ${intent.artifact}`;
300
298
  }
301
299
 
302
- function applyLocked(intent: PrReviewActivityIntent, normalizedAgent: string, options: ActivityIntentOptions): PrReviewActivityResult {
300
+ function applyActivityIntent(intent: PrReviewActivityIntent, normalizedAgent: string, options: ActivityIntentOptions): PrReviewActivityResult {
303
301
  const state = readSnapshot(intent.taskRef, options.repoRoot);
304
302
  if (!state.ok) return failed(intent, state.code, state.message, state.taskId);
305
303
  const parsed = parseArtifactName(intent.artifact)!;
@@ -401,19 +399,7 @@ export function applyPrReviewActivityIntent(intent: PrReviewActivityIntent, opti
401
399
  if (resolved.state !== 'active') {
402
400
  return failed(intent, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
403
401
  }
404
- if (options.lockAlreadyHeld) return applyLocked(intent, validated.agent, { ...options, repoRoot: resolved.repoRoot });
405
- try {
406
- return withTaskExecutionLock(
407
- resolved.repoRoot,
408
- resolved.taskId,
409
- `task-activity:${intent.kind}`,
410
- () => applyLocked(intent, validated.agent, { ...options, repoRoot: resolved.repoRoot }),
411
- options.lockOptions
412
- );
413
- } catch (error) {
414
- if (error instanceof TaskExecutionLockError) return failed(intent, error.code, error.message, resolved.taskId);
415
- return failed(intent, 'ACTIVITY_LOCK_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
416
- }
402
+ return applyActivityIntent(intent, validated.agent, { ...options, repoRoot: resolved.repoRoot });
417
403
  }
418
404
 
419
405
  export type { ActivityIntentOptions };
@@ -134,6 +134,8 @@ type InspectOptions = {
134
134
  sourceSha256?: string;
135
135
  };
136
136
 
137
+ type ResolvedTaskRef = Extract<ReturnType<typeof resolveTaskRef>, { ok: true }>;
138
+
137
139
  const BLOCKING_DIAGNOSTICS = new Set<ArtifactDiagnosticCode>([
138
140
  'NONCANONICAL_NAME', 'ROUND_OUT_OF_RANGE', 'MISSING_BASE', 'ROUND_GAP',
139
141
  'DUPLICATE_LOGICAL_ROUND', 'NON_REGULAR_FILE', 'SYMBOLIC_LINK',
@@ -160,10 +162,15 @@ function diagnostic(code: ArtifactDiagnosticCode, family: ArtifactFamily, name:
160
162
  return { code, family, name, message };
161
163
  }
162
164
 
163
- function inspectTaskArtifacts(taskRef: string, family: string, options: InspectOptions = {}): ArtifactInventoryResult {
165
+ function inspectTaskArtifacts(
166
+ taskRef: string,
167
+ family: string,
168
+ options: InspectOptions = {},
169
+ resolvedTask?: ResolvedTaskRef
170
+ ): ArtifactInventoryResult {
164
171
  const spec = familySpec(family);
165
172
  if (!spec) return failure(taskRef, family, { code: 'ARTIFACT_FAMILY_UNKNOWN', message: `unknown artifact family '${family}'` });
166
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
173
+ const resolved = resolvedTask ?? resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
167
174
  if (!resolved.ok) return failure(taskRef, family, { code: resolved.code, message: resolved.message }, { taskId: resolved.taskId });
168
175
  return inspectArtifactDirectory(resolved.taskDir, spec.family, {
169
176
  requestRef: taskRef, taskId: resolved.taskId, taskState: resolved.state
@@ -452,8 +459,13 @@ function attachArtifactSelection(
452
459
  }
453
460
  }
454
461
 
455
- function resolveArtifactContext(taskRef: string, family: string, options: InspectOptions = {}): ArtifactContextResult {
456
- const inventory = inspectTaskArtifacts(taskRef, family, options);
462
+ function resolveArtifactContextFromTask(
463
+ taskRef: string,
464
+ family: string,
465
+ options: InspectOptions,
466
+ resolvedTask?: ResolvedTaskRef
467
+ ): ArtifactContextResult {
468
+ const inventory = inspectTaskArtifacts(taskRef, family, options, resolvedTask);
457
469
  if (inventory.status === 'failed') return { ...inventory, inputs: [], codeMode: null };
458
470
  const writableError = assertWritableInventory(inventory);
459
471
  if (writableError) return { ...inventory, status: 'failed', inputs: [], codeMode: null, error: writableError };
@@ -462,7 +474,7 @@ function resolveArtifactContext(taskRef: string, family: string, options: Inspec
462
474
  return contextFailure(inventory, 'LIFECYCLE_PATH_INVALID', pathState.message);
463
475
  }
464
476
  if (inventory.family === 'code') {
465
- const code = resolveCodeContext(inventory, options);
477
+ const code = resolveCodeContext(inventory, options, resolvedTask);
466
478
  const selected = attachArtifactSelection(code, pathState, options);
467
479
  if (selected.status !== 'ready' || !selected.selection) return selected;
468
480
  if (selected.selection.disposition === 'reuse') return selected;
@@ -496,7 +508,7 @@ function resolveArtifactContext(taskRef: string, family: string, options: Inspec
496
508
  const required = REQUIRED_INPUT[inventory.family as ArtifactFamily];
497
509
  const inputs: ArtifactIdentity[] = [];
498
510
  if (required) {
499
- const input = inspectTaskArtifacts(taskRef, required, options);
511
+ const input = inspectTaskArtifacts(taskRef, required, options, resolvedTask);
500
512
  if (input.status === 'failed' || !input.latest) {
501
513
  return { ...inventory, status: 'failed', inputs, codeMode: null, error: { code: 'ARTIFACT_INPUT_MISSING', message: `latest ${required} artifact is required` } };
502
514
  }
@@ -505,7 +517,7 @@ function resolveArtifactContext(taskRef: string, family: string, options: Inspec
505
517
  const optional = OPTIONAL_CONTEXT[inventory.family as ArtifactFamily];
506
518
  if (optional) {
507
519
  for (const family of optional) {
508
- const context = inspectTaskArtifacts(taskRef, family, options);
520
+ const context = inspectTaskArtifacts(taskRef, family, options, resolvedTask);
509
521
  if (context.status === 'ready' && context.latest) inputs.push(context.latest);
510
522
  }
511
523
  }
@@ -515,14 +527,30 @@ function resolveArtifactContext(taskRef: string, family: string, options: Inspec
515
527
  return attachArtifactSelection({ ...inventory, inputs, codeMode: null }, pathState, options);
516
528
  }
517
529
 
518
- function resolveCodeContext(inventory: ArtifactInventoryResult, options: InspectOptions): ArtifactContextResult {
530
+ function resolveArtifactContext(taskRef: string, family: string, options: InspectOptions = {}): ArtifactContextResult {
531
+ return resolveArtifactContextFromTask(taskRef, family, options);
532
+ }
533
+
534
+ export function resolveArtifactContextForResolvedTask(
535
+ resolvedTask: ResolvedTaskRef,
536
+ family: string,
537
+ options: InspectOptions = {}
538
+ ): ArtifactContextResult {
539
+ return resolveArtifactContextFromTask(resolvedTask.taskId, family, options, resolvedTask);
540
+ }
541
+
542
+ function resolveCodeContext(
543
+ inventory: ArtifactInventoryResult,
544
+ options: InspectOptions,
545
+ resolvedTask?: ResolvedTaskRef
546
+ ): ArtifactContextResult {
519
547
  const taskRef = inventory.requestRef;
520
548
  const pathState = lifecyclePathState(inventory.taskDir!);
521
549
  if (pathState.status !== 'valid') return contextFailure(inventory, 'LIFECYCLE_PATH_INVALID', pathState.message);
522
550
  const inputFamily = pathState.decision.path === 'streamlined' ? 'analysis' : 'plan';
523
- const source = inspectTaskArtifacts(taskRef, inputFamily, options);
551
+ const source = inspectTaskArtifacts(taskRef, inputFamily, options, resolvedTask);
524
552
  if (source.status === 'failed' || !source.latest) return contextFailure(inventory, 'ARTIFACT_INPUT_MISSING', `latest ${inputFamily} artifact is required`);
525
- const reviewCode = inspectTaskArtifacts(taskRef, 'review-code', options);
553
+ const reviewCode = inspectTaskArtifacts(taskRef, 'review-code', options, resolvedTask);
526
554
  try {
527
555
  const taskContent = fs.readFileSync(path.join(inventory.taskDir!, 'task.md'), 'utf8');
528
556
  const hasPendingImplementation = parseImplementationInputs(taskContent).rows.some((input) => input.needsImplementation && input.status === 'pending');
@@ -538,7 +566,7 @@ function resolveCodeContext(inventory: ArtifactInventoryResult, options: Inspect
538
566
  const reviewMax = reviewCode.latest?.round ?? 0;
539
567
  const inputs = [source.latest];
540
568
  const sourceReviewFamily = inputFamily === 'analysis' ? 'review-analysis' : 'review-plan';
541
- const sourceReview = inspectTaskArtifacts(taskRef, sourceReviewFamily, options);
569
+ const sourceReview = inspectTaskArtifacts(taskRef, sourceReviewFamily, options, resolvedTask);
542
570
  if (sourceReview.status === 'ready' && sourceReview.latest && sourceReview.reviewedInput?.name === source.latest.name) inputs.push(sourceReview.latest);
543
571
  if (inventory.openRounds.length > 0) {
544
572
  try {
@@ -9,7 +9,6 @@ import {
9
9
  getArtifactSchema,
10
10
  renderArtifactSkeleton
11
11
  } from './artifact-schema.ts';
12
- import { withTaskExecutionLock } from './task-execution-lock.ts';
13
12
  import type {
14
13
  ArtifactSchema,
15
14
  ArtifactSchemaFamily,
@@ -352,42 +351,36 @@ function initializeArtifactSkeleton(request: ArtifactInitRequest): ArtifactFileR
352
351
  const target = path.join(request.taskDir, request.artifact);
353
352
  if (path.dirname(path.resolve(target)) !== path.resolve(request.taskDir)) return resultFailure('ARTIFACT_INIT_TARGET_INVALID', 'artifact must be inside the current task directory');
354
353
  try {
355
- return withTaskExecutionLock(request.repoRoot, request.taskId, 'task-artifact.init', () => {
356
- try {
357
- const existing = fs.lstatSync(target);
358
- if (existing.isSymbolicLink() || !existing.isFile()) return resultFailure('ARTIFACT_INIT_TARGET_INVALID', 'artifact target is not a regular file');
359
- return resultNoOp(readStableFileSync(target, { maxBytes: 1024 * 1024 }).bytes.toString('utf8'));
360
- } catch (error) {
361
- if ((error as NodeJS.ErrnoException).code !== 'ENOENT') return resultFailure('ARTIFACT_INIT_TARGET_INVALID', String(error));
362
- }
363
- const skeleton = renderArtifactSkeleton({
364
- taskId: request.taskId,
365
- family: request.family,
366
- artifact: request.artifact,
367
- ...(request.locale ? { locale: request.locale } : {})
368
- });
369
- const content = skeleton.trimEnd();
370
- const tempPath = path.join(request.taskDir, `.${request.artifact}.init-${process.pid}-${Date.now()}.tmp`);
371
- try {
372
- fs.writeFileSync(tempPath, content, { encoding: 'utf8', mode: 0o600, flag: 'wx' });
373
- try { fs.lstatSync(target); fs.unlinkSync(tempPath); return resultFailure('ARTIFACT_INIT_TARGET_EXISTS', 'artifact was created concurrently'); }
374
- catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; }
375
- fs.renameSync(tempPath, target);
376
- } catch (error) {
377
- try { fs.unlinkSync(tempPath); } catch { /* preserve primary error */ }
378
- return resultFailure('ARTIFACT_INIT_WRITE_FAILED', String(error));
379
- }
380
- return {
381
- status: 'applied',
382
- changed: true,
383
- artifactSha256: sha256Content(content),
384
- semanticDigest: canonicalSemanticDigest(content),
385
- error: null
386
- };
387
- });
354
+ const existing = fs.lstatSync(target);
355
+ if (existing.isSymbolicLink() || !existing.isFile()) return resultFailure('ARTIFACT_INIT_TARGET_INVALID', 'artifact target is not a regular file');
356
+ return resultNoOp(readStableFileSync(target, { maxBytes: 1024 * 1024 }).bytes.toString('utf8'));
357
+ } catch (error) {
358
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') return resultFailure('ARTIFACT_INIT_TARGET_INVALID', String(error));
359
+ }
360
+ const skeleton = renderArtifactSkeleton({
361
+ taskId: request.taskId,
362
+ family: request.family,
363
+ artifact: request.artifact,
364
+ ...(request.locale ? { locale: request.locale } : {})
365
+ });
366
+ const content = skeleton.trimEnd();
367
+ const tempPath = path.join(request.taskDir, `.${request.artifact}.init-${process.pid}-${Date.now()}.tmp`);
368
+ try {
369
+ fs.writeFileSync(tempPath, content, { encoding: 'utf8', mode: 0o600, flag: 'wx' });
370
+ try { fs.lstatSync(target); fs.unlinkSync(tempPath); return resultFailure('ARTIFACT_INIT_TARGET_EXISTS', 'artifact was created concurrently'); }
371
+ catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; }
372
+ fs.renameSync(tempPath, target);
388
373
  } catch (error) {
389
- return resultFailure('ARTIFACT_INIT_LOCK_FAILED', String(error));
374
+ try { fs.unlinkSync(tempPath); } catch { /* preserve primary error */ }
375
+ return resultFailure('ARTIFACT_INIT_WRITE_FAILED', String(error));
390
376
  }
377
+ return {
378
+ status: 'applied',
379
+ changed: true,
380
+ artifactSha256: sha256Content(content),
381
+ semanticDigest: canonicalSemanticDigest(content),
382
+ error: null
383
+ };
391
384
  }
392
385
 
393
386
  export {
@@ -302,7 +302,7 @@ function recommendNext(facts: LifecycleFacts): LifecycleRecommendation {
302
302
  return { action: null, reasonCode: 'LIFECYCLE_REVIEWED', evidence: ['latest code review is approved'] };
303
303
  }
304
304
 
305
- function buildLifecycleFacts(taskDir: string, content: string, taskState = 'active', executionBusy = false): LifecycleFactsResult {
305
+ function buildLifecycleFacts(taskDir: string, content: string, taskState = 'active', executionBusy = false, repositoryRoot?: string): LifecycleFactsResult {
306
306
  try {
307
307
  const metadata = parseTypedTaskFrontmatter(content);
308
308
  const rework = parseReworkIntentDocument(content);
@@ -393,7 +393,7 @@ function buildLifecycleFacts(taskDir: string, content: string, taskState = 'acti
393
393
  && artifactHashes[input] === receipt.inputSha256
394
394
  ) reviewedInputs[family] = input;
395
395
  if (family === 'review-code') {
396
- const identity = inspectReviewIdentity(taskDir, reviewContent);
396
+ const identity = inspectReviewIdentity(taskDir, reviewContent, repositoryRoot);
397
397
  reviewCodeIdentityValid = identity.status === 'matched'
398
398
  && hasArtifactCompletionFact(content, path.join(taskDir, latest), 'review-code.completed')
399
399
  && hasArtifactCompletionLog(content, latest, 'review-code.completed');
@@ -4,10 +4,11 @@ import { execFileSync } from 'node:child_process';
4
4
  import { formatTable } from '../../table.ts';
5
5
  import { parseTaskFrontmatter, extractTitle } from '../frontmatter.ts';
6
6
  import { loadShortIdByTaskId } from '../short-id.ts';
7
+ import { resolveProjectTaskWorkspaceRoot } from '../workspace-root.ts';
7
8
 
8
9
  const USAGE = `Usage: ai task ls [--all | --blocked | --completed]
9
10
 
10
- Lists tasks under .agents/workspace/. Defaults to active tasks only.
11
+ Lists tasks under the project task workspace. Defaults to active tasks only.
11
12
  --all Include active + blocked + completed (excludes archive)
12
13
  --blocked Only blocked tasks
13
14
  --completed Only completed tasks
@@ -73,7 +74,7 @@ type TaskRow = {
73
74
  };
74
75
 
75
76
  function collectTasks(repoRoot: string, state: 'active' | 'blocked' | 'completed'): TaskRow[] {
76
- const dir = path.join(repoRoot, '.agents', 'workspace', state);
77
+ const dir = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), state);
77
78
  if (!fs.existsSync(dir)) return [];
78
79
  const shortIdByTaskId = state === 'active' || state === 'completed'
79
80
  ? loadShortIdByTaskId(repoRoot)
@@ -117,7 +118,7 @@ function ls(args: string[] = []): void {
117
118
  rows.push(...collectTasks(repoRoot, state));
118
119
  }
119
120
  if (rows.length === 0) {
120
- process.stdout.write(`No tasks under .agents/workspace/${selection.join('|')}\n`);
121
+ process.stdout.write(`No tasks under project task workspace state ${selection.join('|')}\n`);
121
122
  return;
122
123
  }
123
124
  const tableRows = rows.map((r, i) => [
@@ -521,7 +521,7 @@ function buildFromResolved(input: BuildStatusModelInput): StatusModel {
521
521
  const run = input.run ?? makeRunner(input.repoRoot);
522
522
  const artifacts = enumerateArtifacts(input.taskDir);
523
523
  const workflow = collectWorkflow(content, input.now);
524
- const facts = buildLifecycleFacts(input.taskDir, content, fm.status ?? 'active');
524
+ const facts = buildLifecycleFacts(input.taskDir, content, fm.status ?? 'active', false, input.repoRoot);
525
525
  const next = facts.ok ? recommendNext(facts.facts) : null;
526
526
  const recommendation = next?.action ?? DASH;
527
527
 
@@ -1,4 +1,5 @@
1
1
  import fs from 'node:fs';
2
+ import path from 'node:path';
2
3
  import { execFileSync } from 'node:child_process';
3
4
 
4
5
  import { commitExplicitPaths, inspectGitWorkflow, pushGitRefs } from '../git/workflow.ts';
@@ -7,7 +8,6 @@ import { checkpointCommitMatches } from './commit-identity.ts';
7
8
  import { resolveTaskContext, resolveTaskRef } from './resolve-ref.ts';
8
9
  import { commitPushDecision } from './commit-policy.ts';
9
10
  import { mergeOperationWarnings, type OperationWarning } from './operation-outcome.ts';
10
- import { TaskExecutionLockError, withRepositoryMutationLock, withTaskExecutionLock } from './task-execution-lock.ts';
11
11
  import { appendActivityEntry, locateActivityLog } from './activity-log.ts';
12
12
  import { captureTaskWriteMetadata, writeTask } from './write.ts';
13
13
  import type { TaskMutation } from './write.ts';
@@ -135,6 +135,7 @@ function resolveBoundTask(input: CommitOperationInput):
135
135
  };
136
136
  return boundTask(input, resolved.taskId, resolved.taskDir, resolved.taskMdPath, resolved.repoRoot, resolved.state);
137
137
  }
138
+ if (!fs.existsSync(path.join(input.cwd, '.agents', '.airc.json'))) return { kind: 'none' };
138
139
  const context = resolveTaskContext(undefined, { repoRoot: input.cwd });
139
140
  if (context.ok) return boundTask(input, context.taskId, context.taskDir, context.taskMdPath, context.repoRoot, context.state);
140
141
  if (context.code === 'TASK_CONTEXT_NOT_FOUND') return { kind: 'none' };
@@ -280,7 +281,7 @@ function checkpointTimestamp(): string {
280
281
  return new Date().toISOString();
281
282
  }
282
283
 
283
- function executeUnlocked(input: CommitOperationInput, task: BoundTask | null, mode: CommitExecutionMode): CommitOperationResult {
284
+ function executeCommitOperationCore(input: CommitOperationInput, task: BoundTask | null, mode: CommitExecutionMode): CommitOperationResult {
284
285
  const inspected = inspectGitWorkflow(input.cwd);
285
286
  if (!inspected.snapshot) return failure(input, mode, task?.taskId ?? null, 'GIT_INSPECT_FAILED', 'Unable to inspect Git repository');
286
287
  if (mode === 'orchestrated') return failure(
@@ -516,11 +517,9 @@ function executeCommitOperation(input: CommitOperationInput): CommitOperationRes
516
517
  const task = resolved.kind === 'bound' ? resolved.task : null;
517
518
  if (mode === 'orchestrated' && !task) return failure(normalizedInput, mode, null, 'ORCHESTRATION_TASK_REQUIRED', 'orchestrated commit requires a task context', 'blocked');
518
519
  try {
519
- return task
520
- ? withRepositoryMutationLock(normalizedInput.cwd, () => withTaskExecutionLock(normalizedInput.cwd, task.taskId, 'commit-operation', () => executeUnlocked(normalizedInput, task, mode)))
521
- : withRepositoryMutationLock(normalizedInput.cwd, () => executeUnlocked(normalizedInput, null, mode));
520
+ return executeCommitOperationCore(normalizedInput, task, mode);
522
521
  } catch (error) {
523
- const code = error instanceof TaskExecutionLockError ? error.code : 'COMMIT_OPERATION_FAILED';
522
+ const code = 'COMMIT_OPERATION_FAILED';
524
523
  return failure(normalizedInput, mode, task?.taskId ?? null, code, error instanceof Error ? error.message : String(error), 'blocked');
525
524
  }
526
525
  }
@@ -1,3 +1,4 @@
1
+ import fs from 'node:fs';
1
2
  import path from 'node:path';
2
3
 
3
4
  import { normalizeAgentToken } from '../agent-clients/tokens.ts';
@@ -29,8 +30,6 @@ import {
29
30
  } from './finalization.ts';
30
31
  import {
31
32
  applyTaskLifecycle,
32
- applyTaskLifecycleWithinTaskLock,
33
- taskLifecycleFailure,
34
33
  type TaskLifecycleRequest,
35
34
  type TaskLifecycleResult
36
35
  } from './lifecycle.ts';
@@ -39,8 +38,8 @@ import type {
39
38
  AgentClientLifecycleRecoveryResult as LifecycleRecoveryResult
40
39
  } from '../agent-clients/adapter.ts';
41
40
  import { resolveTaskContext, resolveTaskRef, TASK_ID_RE } from './resolve-ref.ts';
42
- import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
43
41
  import { verifyTaskEvent } from './verification.ts';
42
+ import { releaseStaleShortIdRegistry } from './short-id.ts';
44
43
 
45
44
  export type TaskControlExecutionContext =
46
45
  | Readonly<{
@@ -215,7 +214,7 @@ async function applyLifecycleWithAuthority(
215
214
  ): Promise<TaskLifecycleResult | LifecycleRecoveryResult> {
216
215
  const taskId = lifecycleTaskId(request, context.repoRoot);
217
216
  const controlRoot = taskId ? controlRootForTask(context, taskId) : undefined;
218
- const execute = async (underTaskLock = false): Promise<TaskLifecycleResult | LifecycleRecoveryResult> => {
217
+ const execute = async (): Promise<TaskLifecycleResult | LifecycleRecoveryResult> => {
219
218
  if (request.intent === 'recover-started') {
220
219
  const adapter = isAgentClientId(request.agent)
221
220
  ? getAgentClientAdapter(request.agent).orchestrationAdapter
@@ -229,38 +228,13 @@ async function applyLifecycleWithAuthority(
229
228
  }
230
229
  return adapter.recoverStarted(request as LifecycleRecoveryRequest, { repoRoot: context.repoRoot });
231
230
  }
232
- return underTaskLock
233
- ? applyTaskLifecycleWithinTaskLock(request, {
234
- repoRoot: context.repoRoot,
235
- ...(controlRoot ? { controlRoot } : {}),
236
- ...(context.source === 'direct-host' && context.project ? { controlProject: context.project } : {})
237
- }, taskId!)
238
- : applyTaskLifecycle(request, {
239
- repoRoot: context.repoRoot,
240
- ...(controlRoot ? { controlRoot } : {}),
241
- ...(context.source === 'direct-host' && context.project ? { controlProject: context.project } : {})
242
- });
231
+ return applyTaskLifecycle(request, {
232
+ repoRoot: context.repoRoot,
233
+ ...(controlRoot ? { controlRoot } : {}),
234
+ ...(context.source === 'direct-host' && context.project ? { controlProject: context.project } : {})
235
+ });
243
236
  };
244
- if (!taskId) return await execute();
245
- try {
246
- return await withTaskExecutionLock(
247
- context.repoRoot,
248
- taskId,
249
- `task-lifecycle.${request.intent}`,
250
- () => execute(true)
251
- );
252
- } catch (error) {
253
- if (!(error instanceof TaskExecutionLockError)) throw error;
254
- if (request.intent === 'recover-started') {
255
- return {
256
- status: 'owner-unknown', changed: false, targetState: 'active',
257
- requestRef: request.taskRef, intent: 'recover-started', taskId,
258
- receiptId: null, childId: null,
259
- error: { code: error.code, message: error.message }
260
- };
261
- }
262
- return taskLifecycleFailure(request, { code: error.code, message: error.message }, taskId);
263
- }
237
+ return await execute();
264
238
  }
265
239
 
266
240
  function controlRootForTask(context: TaskControlExecutionContext, taskId: string): string | undefined {
@@ -393,6 +367,26 @@ export function dispatchTaskControlOperation(
393
367
  preflight: (request, options) => verifyTaskEvent(
394
368
  { ...request, event: 'complete-task.hard-preflight' }, options
395
369
  )
370
+ }).then((result) => {
371
+ if (context.source !== 'direct-host' || !controlRoot || result.status !== 'completed' || !result.taskId) return result;
372
+ try {
373
+ if (fs.lstatSync(controlRoot, { throwIfNoEntry: false }) === undefined) {
374
+ const released = releaseStaleShortIdRegistry(context.repoRoot, result.taskId);
375
+ return { ...result, changed: result.changed || released.changed };
376
+ }
377
+ return result;
378
+ } catch (error) {
379
+ return {
380
+ ...result,
381
+ status: 'blocked',
382
+ result: 'blocked',
383
+ error: {
384
+ code: 'TASK_FINALIZATION_SHORT_ID_RELEASE_FAILED',
385
+ message: error instanceof Error ? error.message : String(error),
386
+ retryable: true
387
+ }
388
+ };
389
+ }
396
390
  });
397
391
  }
398
392
  try {
@@ -4,7 +4,6 @@ import type { PlatformResult } from '../platform/types.ts';
4
4
  import { VERSION } from '../version.ts';
5
5
  import { applyWorkflowWarningIntent } from './workflow-warning-intents.ts';
6
6
  import { createLocalTask, type TaskCreateCandidateV1 } from './create.ts';
7
- import { TaskExecutionLockError } from './task-execution-lock.ts';
8
7
  import { verifyTaskEvent } from './verification.ts';
9
8
 
10
9
  type TaskCreateStatus = 'applied' | 'no-op' | 'degraded' | 'failed' | 'blocked';
@@ -201,9 +200,8 @@ async function createTask(value: unknown, options: CreateTaskOptions): Promise<T
201
200
  });
202
201
  } catch (error) {
203
202
  const message = error instanceof Error ? error.message : String(error);
204
- const lockBusy = error instanceof TaskExecutionLockError && error.code === 'ORCHESTRATION_LOCK_BUSY';
205
- const code = lockBusy ? error.code : /^([A-Z][A-Z0-9_]+)/.exec(message)?.[1] ?? 'TASK_CREATE_FAILED';
206
- return taskCreateFailure({ code, message, retryable: lockBusy || code === 'TASK_CREATE_LOCK_TIMEOUT' });
203
+ const code = /^([A-Z][A-Z0-9_]+)/.exec(message)?.[1] ?? 'TASK_CREATE_FAILED';
204
+ return taskCreateFailure({ code, message, retryable: false });
207
205
  }
208
206
 
209
207
  const operations: TaskCreateOperation[] = [{
@@ -5,13 +5,13 @@ import path from 'node:path';
5
5
 
6
6
  import { isValidAgentInfraVersion, VERSION } from '../version.ts';
7
7
  import { loadShortIdByTaskId, mutateShortIdRegistry } from './short-id.ts';
8
- import { TaskExecutionLockError, withRepositoryMutationLock, withTaskExecutionLock } from './task-execution-lock.ts';
9
8
  import { readDeliveryDefaults, validateBaseRef, validateRemote } from './delivery-target.ts';
10
9
  import { buildUnboundFact, encodePrDeliveryFact } from './pr-delivery-fact.ts';
11
10
  import { CANDIDATE_COLUMNS, CONSTRAINT_COLUMNS, parseTaskQualification } from './qualification-audit.ts';
12
11
  import { enumerateAllTaskDirs } from './resolve-ref.ts';
13
12
  import { parseTaskFrontmatter } from './frontmatter.ts';
14
13
  import { AGENT_TOKEN_VALUES, type AgentToken } from '../agent-clients/tokens.ts';
14
+ import { resolveProjectTaskWorkspaceRoot } from './workspace-root.ts';
15
15
 
16
16
  const TYPES = ['feature', 'bugfix', 'refactor', 'docs', 'chore'] as const;
17
17
  const PRIORITIES = ['Urgent', 'High', 'Medium', 'Low'] as const;
@@ -348,42 +348,6 @@ function ensureRealDirectory(directory: string, code: string): void {
348
348
  fs.mkdirSync(directory, { mode: 0o700 });
349
349
  }
350
350
 
351
- function createLockRoot(project: string): string {
352
- const root = path.join(os.homedir(), '.agent-infra');
353
- const directories = [root, path.join(root, 'run'), path.join(root, 'run', project), path.join(root, 'run', project, 'task-create-locks')];
354
- for (const directory of directories) {
355
- if (fs.existsSync(directory)) {
356
- assertRealDirectory(directory, 'TASK_CREATE_LOCK_FAILED');
357
- continue;
358
- }
359
- try {
360
- fs.mkdirSync(directory, { mode: 0o700 });
361
- } catch (error) {
362
- if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
363
- assertRealDirectory(directory, 'TASK_CREATE_LOCK_FAILED');
364
- }
365
- }
366
- return directories.at(-1)!;
367
- }
368
-
369
- function withCreateLock<T>(repoRoot: string, project: string, operation: () => T): T {
370
- const lockRoot = createLockRoot(project);
371
- let callbackError: unknown;
372
- try {
373
- return withTaskExecutionLock(repoRoot, 'task-create', 'task-create', () => {
374
- try { return operation(); }
375
- catch (error) { callbackError = error; throw error; }
376
- }, { lockRoot, identityScope: `task-create\0${project}` });
377
- } catch (error) {
378
- if (callbackError !== undefined) throw callbackError;
379
- if (error instanceof TaskExecutionLockError && error.code === 'ORCHESTRATION_LOCK_BUSY') {
380
- throw new Error('TASK_CREATE_LOCK_TIMEOUT');
381
- }
382
- const message = error instanceof Error ? error.message : String(error);
383
- throw new Error(`TASK_CREATE_LOCK_FAILED: ${message}`);
384
- }
385
- }
386
-
387
351
  function recoverPublishedTask(repoRoot: string, keyDigest: string, candidateDigest: string): { taskId: string; shortId: string | null; state: 'active' | 'blocked' | 'completed' | 'archive' } | null {
388
352
  for (const task of enumerateAllTaskDirs(repoRoot)) {
389
353
  const taskMd = path.join(task.taskDir, 'task.md');
@@ -410,16 +374,22 @@ function createLocalTask(value: unknown, options: LocalTaskCreateOptions): Local
410
374
  throw new Error('TASK_CREATE_VERSION_INVALID: agentInfraVersion must be a valid v-prefixed semver');
411
375
  }
412
376
  const repoRoot = fs.realpathSync.native(options.repoRoot);
413
- const workspaceRoot = path.join(repoRoot, '.agents', 'workspace');
377
+ const workspaceRoot = resolveProjectTaskWorkspaceRoot(repoRoot);
414
378
  const activeRoot = path.join(workspaceRoot, 'active');
415
379
  ensureRealDirectory(path.join(repoRoot, '.agents'), 'TASK_CREATE_WORKSPACE_INVALID');
380
+ const home = path.resolve(process.env.HOME || os.homedir());
381
+ const managedRoots = [path.join(home, '.agent-infra'), path.join(home, '.agent-infra', 'task-workspaces')];
382
+ for (const root of managedRoots) {
383
+ if (fs.existsSync(root)) assertRealDirectory(root, 'TASK_CREATE_WORKSPACE_INVALID');
384
+ else fs.mkdirSync(root, { mode: 0o700 });
385
+ }
416
386
  ensureRealDirectory(workspaceRoot, 'TASK_CREATE_WORKSPACE_INVALID');
417
387
  ensureRealDirectory(activeRoot, 'TASK_CREATE_WORKSPACE_INVALID');
418
388
  const projectConfig = readProject(repoRoot);
419
389
  const canonical = canonicalTaskCreateCandidate(candidate);
420
390
  const candidateDigest = sha256(canonical);
421
391
  const keyDigest = sha256(candidate.idempotencyKey);
422
- function createUnderRepositoryLock(): LocalTaskCreateResult {
392
+ function createOnce(): LocalTaskCreateResult {
423
393
  const recovered = recoverPublishedTask(repoRoot, keyDigest, candidateDigest);
424
394
  if (recovered) {
425
395
  return { status: 'no-op', changed: false, task: { id: recovered.taskId, shortId: recovered.shortId, state: recovered.state } };
@@ -451,18 +421,7 @@ function createLocalTask(value: unknown, options: LocalTaskCreateOptions): Local
451
421
  return { status: 'applied', changed: true, task: { id: taskId, shortId: allocated.shortId, state: 'active' } };
452
422
  }
453
423
 
454
- return withCreateLock(repoRoot, projectConfig.project, () => {
455
- let callbackError: unknown;
456
- try {
457
- return withRepositoryMutationLock(repoRoot, () => {
458
- try { return createUnderRepositoryLock(); }
459
- catch (error) { callbackError = error; throw error; }
460
- });
461
- } catch (error) {
462
- if (callbackError !== undefined) throw callbackError;
463
- throw error;
464
- }
465
- });
424
+ return createOnce();
466
425
 
467
426
  }
468
427
 
@@ -263,7 +263,7 @@ function validateSealRequest(
263
263
 
264
264
  function validateReviewerSealPaths(receipt: DelegationReceipt, changedPaths: readonly string[]): ReceiptFailure | null {
265
265
  if (receipt.role !== 'reviewer') return null;
266
- const taskRoot = `.agents/workspace/active/${receipt.taskId}/`;
266
+ const taskRoot = `task-workspace/active/${receipt.taskId}/`;
267
267
  const allowed = new Set([
268
268
  `${taskRoot}${receipt.artifact}`,
269
269
  `${taskRoot}task.md`,
@@ -6,7 +6,6 @@ import { parseTaskFrontmatter } from './frontmatter.ts';
6
6
  import { resolveTaskRef } from './resolve-ref.ts';
7
7
  import { resolveDeliveryTarget, validateBaseRef, validateRemote } from './delivery-target.ts';
8
8
  import { captureTaskWriteMetadata, writeTask } from './write.ts';
9
- import { withRepositoryMutationLock, withTaskExecutionLock } from './task-execution-lock.ts';
10
9
 
11
10
  export type DeliveryState = 'absent' | 'same' | 'known-old' | 'unknown-drift';
12
11
 
@@ -50,7 +49,7 @@ function failure(taskId: string | null, code: string, message: string, retryable
50
49
  return { status, changed: false, taskId, remoteHead: null, localHead: null, state: null, error: { code, message, retryable } };
51
50
  }
52
51
 
53
- function deliverUnlocked(
52
+ function deliverTaskBranchCore(
54
53
  taskId: string,
55
54
  taskDir: string,
56
55
  repoRoot: string,
@@ -108,14 +107,7 @@ function deliverUnlocked(
108
107
  function deliverTaskBranch(taskRef: string, options: { repoRoot?: string; agent: string; remote?: string; baseRef?: string; dryRun?: boolean }): TaskBranchDeliveryResult {
109
108
  const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
110
109
  if (!resolved.ok) return failure(resolved.taskId, resolved.code, resolved.message);
111
- try {
112
- return withRepositoryMutationLock(resolved.repoRoot, () => withTaskExecutionLock(
113
- resolved.repoRoot, resolved.taskId, 'task-delivery',
114
- () => deliverUnlocked(resolved.taskId, resolved.taskDir, resolved.repoRoot, options)
115
- ));
116
- } catch (error) {
117
- return failure(resolved.taskId, 'DELIVERY_LOCK_FAILED', error instanceof Error ? error.message : String(error), true, 'blocked');
118
- }
110
+ return deliverTaskBranchCore(resolved.taskId, resolved.taskDir, resolved.repoRoot, options);
119
111
  }
120
112
 
121
113
  export { classifyDeliveryState, deliverTaskBranch };