@fitlab-ai/agent-infra 0.11.15 → 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 (253) 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-ledger.js +1 -12
  14. package/dist/lib/internal/task-operation-registry.js +2 -1
  15. package/dist/lib/internal/task-qualification.js +6 -15
  16. package/dist/lib/internal/task-review.js +3 -16
  17. package/dist/lib/internal/task-short-id.js +2 -1
  18. package/dist/lib/internal/task-verify.js +1 -11
  19. package/dist/lib/internal/task-warning.js +1 -12
  20. package/dist/lib/internal/task-workspace.js +43 -0
  21. package/dist/lib/merge.js +4 -3
  22. package/dist/lib/platform/pr-summary.js +6 -15
  23. package/dist/lib/platform/pull-requests.js +3 -15
  24. package/dist/lib/pr-review/evidence-grading.js +3 -2
  25. package/dist/lib/process-data/index.js +5 -4
  26. package/dist/lib/process-data/sources.js +6 -0
  27. package/dist/lib/run/index.js +3 -2
  28. package/dist/lib/sandbox/commands/create.js +5 -1
  29. package/dist/lib/sandbox/commands/enter.js +1 -1
  30. package/dist/lib/sandbox/commands/list-running.js +2 -1
  31. package/dist/lib/sandbox/control/client.js +8 -13
  32. package/dist/lib/sandbox/control/completed-reentry.js +2 -1
  33. package/dist/lib/sandbox/control/task-view.js +4 -2
  34. package/dist/lib/sandbox/mounts.js +4 -4
  35. package/dist/lib/sandbox/recovery.js +26 -16
  36. package/dist/lib/sandbox/removal.js +3 -10
  37. package/dist/lib/sandbox/task-resolver.js +9 -6
  38. package/dist/lib/sandbox/workspace-identity.js +6 -5
  39. package/dist/lib/sandbox/workspace-view.js +29 -22
  40. package/dist/lib/task/activity-intent.js +2 -12
  41. package/dist/lib/task/artifact-lifecycle.js +17 -11
  42. package/dist/lib/task/artifact-operations.js +41 -49
  43. package/dist/lib/task/capabilities.js +2 -2
  44. package/dist/lib/task/commands/ls.js +4 -3
  45. package/dist/lib/task/commands/status.js +1 -1
  46. package/dist/lib/task/commit-operation.js +6 -6
  47. package/dist/lib/task/control-authority.js +32 -32
  48. package/dist/lib/task/create-service.js +2 -4
  49. package/dist/lib/task/create.js +12 -65
  50. package/dist/lib/task/delegation-receipts.js +1 -1
  51. package/dist/lib/task/delivery.js +2 -8
  52. package/dist/lib/task/events.js +5 -31
  53. package/dist/lib/task/finalization.js +17 -22
  54. package/dist/lib/task/lifecycle.js +9 -48
  55. package/dist/lib/task/local-artifact-finalization.js +1 -2
  56. package/dist/lib/task/orchestration.js +24 -57
  57. package/dist/lib/task/platform-operation-recovery.js +1 -2
  58. package/dist/lib/task/resolve-ref.js +17 -12
  59. package/dist/lib/task/review-finalization.js +2 -35
  60. package/dist/lib/task/review-identity.js +3 -1
  61. package/dist/lib/task/short-id.js +10 -8
  62. package/dist/lib/task/verification-engine.js +19 -58
  63. package/dist/lib/task/workspace-root.js +59 -0
  64. package/dist/lib/task/workspace-snapshot.js +4 -3
  65. package/dist/lifecycle-build-manifest.json +3 -3
  66. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +6 -6
  67. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +8 -34
  68. package/lib/agent-clients/adapters/codex-orchestration.ts +1 -1
  69. package/lib/decide.ts +2 -7
  70. package/lib/internal/cli-route-inventory.ts +2 -0
  71. package/lib/internal/manual-validation.ts +8 -19
  72. package/lib/internal/platform-comment.ts +3 -2
  73. package/lib/internal/task-activity.ts +1 -10
  74. package/lib/internal/task-event.ts +1 -10
  75. package/lib/internal/task-ledger.ts +1 -10
  76. package/lib/internal/task-operation-registry.ts +2 -1
  77. package/lib/internal/task-qualification.ts +5 -11
  78. package/lib/internal/task-review.ts +3 -14
  79. package/lib/internal/task-short-id.ts +2 -1
  80. package/lib/internal/task-verify.ts +1 -9
  81. package/lib/internal/task-warning.ts +1 -10
  82. package/lib/internal/task-workspace.ts +39 -0
  83. package/lib/merge.ts +4 -3
  84. package/lib/platform/pr-summary.ts +7 -16
  85. package/lib/platform/pull-requests.ts +3 -24
  86. package/lib/pr-review/evidence-grading.ts +3 -2
  87. package/lib/process-data/index.ts +4 -3
  88. package/lib/process-data/sources.ts +6 -0
  89. package/lib/run/index.ts +3 -2
  90. package/lib/sandbox/commands/create.ts +5 -1
  91. package/lib/sandbox/commands/enter.ts +1 -1
  92. package/lib/sandbox/commands/list-running.ts +2 -1
  93. package/lib/sandbox/control/client.ts +7 -12
  94. package/lib/sandbox/control/completed-reentry.ts +2 -1
  95. package/lib/sandbox/control/task-view.ts +4 -2
  96. package/lib/sandbox/mounts.ts +4 -4
  97. package/lib/sandbox/recovery.ts +26 -18
  98. package/lib/sandbox/removal.ts +3 -26
  99. package/lib/sandbox/task-resolver.ts +9 -6
  100. package/lib/sandbox/workspace-identity.ts +6 -5
  101. package/lib/sandbox/workspace-view.ts +28 -20
  102. package/lib/task/activity-intent.ts +3 -17
  103. package/lib/task/artifact-lifecycle.ts +39 -11
  104. package/lib/task/artifact-operations.ts +28 -35
  105. package/lib/task/capabilities.ts +2 -2
  106. package/lib/task/commands/ls.ts +4 -3
  107. package/lib/task/commands/status.ts +1 -1
  108. package/lib/task/commit-operation.ts +5 -6
  109. package/lib/task/control-authority.ts +29 -35
  110. package/lib/task/create-service.ts +2 -4
  111. package/lib/task/create.ts +10 -51
  112. package/lib/task/delegation-receipts.ts +1 -1
  113. package/lib/task/delivery.ts +2 -10
  114. package/lib/task/events.ts +5 -32
  115. package/lib/task/finalization.ts +16 -24
  116. package/lib/task/lifecycle.ts +9 -50
  117. package/lib/task/local-artifact-finalization.ts +1 -4
  118. package/lib/task/orchestration.ts +31 -80
  119. package/lib/task/platform-operation-recovery.ts +1 -2
  120. package/lib/task/resolve-ref.ts +18 -12
  121. package/lib/task/review-finalization.ts +2 -52
  122. package/lib/task/review-identity.ts +3 -1
  123. package/lib/task/short-id.ts +10 -8
  124. package/lib/task/verification-engine.ts +20 -62
  125. package/lib/task/workspace-root.ts +78 -0
  126. package/lib/task/workspace-snapshot.ts +4 -3
  127. package/package.json +1 -1
  128. package/templates/.agents/QUICKSTART.en.md +19 -8
  129. package/templates/.agents/QUICKSTART.zh-CN.md +19 -8
  130. package/templates/.agents/README.en.md +10 -15
  131. package/templates/.agents/README.zh-CN.md +9 -13
  132. package/templates/.agents/rules/issue-sync.en.md +1 -1
  133. package/templates/.agents/rules/issue-sync.zh-CN.md +1 -1
  134. package/templates/.agents/rules/lifecycle-orchestration.en.md +1 -1
  135. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +1 -1
  136. package/templates/.agents/rules/next-step-output.en.md +2 -2
  137. package/templates/.agents/rules/next-step-output.zh-CN.md +2 -2
  138. package/templates/.agents/rules/pr-sync.en.md +1 -1
  139. package/templates/.agents/rules/pr-sync.zh-CN.md +1 -1
  140. package/templates/.agents/rules/release-commands.en.md +3 -0
  141. package/templates/.agents/rules/release-commands.zh-CN.md +3 -0
  142. package/templates/.agents/rules/task-management.en.md +0 -2
  143. package/templates/.agents/rules/task-management.zh-CN.md +0 -2
  144. package/templates/.agents/rules/task-short-id.en.md +2 -2
  145. package/templates/.agents/rules/task-short-id.zh-CN.md +2 -2
  146. package/templates/.agents/scripts/task-short-id.js +2 -8
  147. package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
  148. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
  149. package/templates/.agents/skills/analyze-task/config/verify.en.json +0 -1
  150. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +0 -1
  151. package/templates/.agents/skills/archive-tasks/SKILL.en.md +5 -5
  152. package/templates/.agents/skills/archive-tasks/SKILL.zh-CN.md +5 -5
  153. package/templates/.agents/skills/archive-tasks/scripts/archive-tasks.sh +10 -2
  154. package/templates/.agents/skills/block-task/SKILL.en.md +4 -4
  155. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +5 -5
  156. package/templates/.agents/skills/block-task/config/verify.json +0 -1
  157. package/templates/.agents/skills/cancel-task/SKILL.en.md +6 -6
  158. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +6 -6
  159. package/templates/.agents/skills/cancel-task/config/verify.json +0 -1
  160. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  161. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +3 -3
  162. package/templates/.agents/skills/code-task/config/verify.en.json +0 -1
  163. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +0 -1
  164. package/templates/.agents/skills/code-task/reference/output-template.en.md +1 -1
  165. package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +1 -1
  166. package/templates/.agents/skills/commit/SKILL.en.md +1 -1
  167. package/templates/.agents/skills/commit/SKILL.zh-CN.md +1 -1
  168. package/templates/.agents/skills/commit/config/verify.json +0 -1
  169. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +2 -2
  170. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +2 -2
  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/complete-manual-validation/SKILL.en.md +2 -2
  174. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +2 -2
  175. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +0 -1
  176. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +0 -1
  177. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
  178. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
  179. package/templates/.agents/skills/complete-task/SKILL.en.md +7 -7
  180. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +8 -8
  181. package/templates/.agents/skills/complete-task/config/verify.en.json +0 -1
  182. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +0 -1
  183. package/templates/.agents/skills/create-pr/SKILL.en.md +3 -3
  184. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +3 -3
  185. package/templates/.agents/skills/create-pr/config/verify.json +0 -1
  186. package/templates/.agents/skills/create-pr/reference/change-report.en.md +1 -1
  187. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +1 -1
  188. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +1 -1
  189. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +1 -1
  190. package/templates/.agents/skills/create-release-note/SKILL.en.md +2 -2
  191. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +2 -2
  192. package/templates/.agents/skills/create-task/SKILL.en.md +4 -4
  193. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +4 -4
  194. package/templates/.agents/skills/create-task/config/verify.json +0 -1
  195. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  196. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  197. package/templates/.agents/skills/import-codescan/config/verify.json +0 -1
  198. package/templates/.agents/skills/import-dependabot/SKILL.en.md +3 -3
  199. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +3 -3
  200. package/templates/.agents/skills/import-dependabot/config/verify.json +0 -1
  201. package/templates/.agents/skills/import-issue/SKILL.en.md +5 -5
  202. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -5
  203. package/templates/.agents/skills/import-issue/config/verify.json +0 -1
  204. package/templates/.agents/skills/plan-task/SKILL.en.md +5 -5
  205. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -5
  206. package/templates/.agents/skills/plan-task/config/verify.en.json +0 -1
  207. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +0 -1
  208. package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
  209. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +3 -3
  210. package/templates/.agents/skills/restore-task/config/verify.json +0 -1
  211. package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
  212. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
  213. package/templates/.agents/skills/review-analysis/config/verify.en.json +0 -1
  214. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +0 -1
  215. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +5 -5
  216. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +5 -5
  217. package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
  218. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
  219. package/templates/.agents/skills/review-code/reference/output-templates.en.md +4 -4
  220. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +4 -4
  221. package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
  222. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
  223. package/templates/.agents/skills/review-plan/config/verify.en.json +0 -1
  224. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +0 -1
  225. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +5 -5
  226. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +5 -5
  227. package/templates/.agents/skills/review-pr/SKILL.en.md +2 -2
  228. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +2 -2
  229. package/templates/.agents/skills/review-pr/config/verify.en.json +0 -1
  230. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +0 -1
  231. package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +1 -1
  232. package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +1 -1
  233. package/templates/.agents/skills/run-manual-validation/config/verify.en.json +0 -1
  234. package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +0 -1
  235. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  236. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  237. package/templates/.agents/skills/test/SKILL.en.md +0 -3
  238. package/templates/.agents/skills/test/SKILL.zh-CN.md +0 -3
  239. package/templates/.agents/skills/test-integration/SKILL.en.md +0 -3
  240. package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +0 -3
  241. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +13 -6
  242. package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +0 -3
  243. package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +0 -3
  244. package/templates/.agents/skills/watch-pr/SKILL.en.md +3 -3
  245. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +3 -3
  246. package/templates/.agents/skills/watch-pr/config/verify.json +0 -1
  247. package/templates/.agents/templates/handoff.en.md +1 -1
  248. package/templates/.agents/templates/handoff.zh-CN.md +1 -1
  249. package/templates/.agents/workflows/bug-fix.en.yaml +1 -1
  250. package/templates/.agents/workflows/feature-development.en.yaml +1 -1
  251. package/templates/.agents/workflows/refactoring.en.yaml +1 -1
  252. package/dist/lib/task/task-execution-lock.js +0 -250
  253. package/lib/task/task-execution-lock.ts +0 -320
@@ -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 };
@@ -31,7 +31,6 @@ import { findSectionHeading } from './sections.ts';
31
31
  import { validateLifecycleExecution } from './lifecycle-execution.ts';
32
32
  import { commitOrchestrationStageCompletion } from './orchestration.ts';
33
33
  import type { OrchestrationStageCompletion } from './orchestration.ts';
34
- import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
35
34
  import { captureTaskWriteMetadata, writeTask } from './write.ts';
36
35
  import type { TaskOperationSummary, TaskWriteErrorCode, TaskWriteOptions } from './write.ts';
37
36
  import { validateLocalArtifact } from './local-artifact-finalization.ts';
@@ -86,7 +85,6 @@ type TaskEventRequest = {
86
85
  type TaskEventError = { code: TaskEventErrorCode; message: string };
87
86
  type TaskEventOptions = TaskWriteOptions & {
88
87
  commitOrchestrationCompletion?: (plan: OrchestrationStageCompletion) => void;
89
- lockAlreadyHeld?: boolean;
90
88
  };
91
89
  type TaskEventResult = {
92
90
  status: 'planned' | 'applied' | 'no-op' | 'failed'; changed: boolean;
@@ -519,6 +517,7 @@ function reworkIntentMutationForCompletion(
519
517
  function buildCompletionReceipt(
520
518
  content: string,
521
519
  taskDir: string,
520
+ repositoryRoot: string | undefined,
522
521
  family: EventFamily,
523
522
  artifact: ArtifactIdentity,
524
523
  completedAt: string,
@@ -527,7 +526,7 @@ function buildCompletionReceipt(
527
526
  ): { ok: true; receipts: readonly ArtifactReceipt[] } | { ok: false; message: string } | null {
528
527
  if (!['analyze', 'plan', 'code'].includes(family) && !family.startsWith('review-')) return null;
529
528
  if (family === 'review-code') {
530
- const identity = inspectReviewIdentity(taskDir, fs.readFileSync(artifact.path, 'utf8'));
529
+ const identity = inspectReviewIdentity(taskDir, fs.readFileSync(artifact.path, 'utf8'), repositoryRoot);
531
530
  if (identity.status !== 'matched') return { ok: false, message: `review-code identity is ${identity.status}: ${identity.message}` };
532
531
  }
533
532
  const event = family === 'analyze' ? 'analysis.completed'
@@ -627,7 +626,7 @@ function replaceCompletionFact(facts: readonly CompletionFact[], next: Completio
627
626
  return [...facts.filter((fact) => !(fact.event === next.event && fact.output === next.output)), next];
628
627
  }
629
628
 
630
- function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOptions = {}): TaskEventResult {
629
+ function applyTaskEvent(request: TaskEventRequest, options: TaskEventOptions = {}): TaskEventResult {
631
630
  const invalid = validateTaskEventRequest(request);
632
631
  if (invalid) return failed(request, invalid);
633
632
  const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
@@ -757,7 +756,7 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
757
756
  }
758
757
  if (eventIdentity.phase === 'started') {
759
758
  const trigger = eventTrigger(normalized, eventIdentity.family);
760
- const facts = buildLifecycleFacts(resolved.taskDir, content, resolved.state);
759
+ const facts = buildLifecycleFacts(resolved.taskDir, content, resolved.state, false, resolved.repoRoot);
761
760
  if (!facts.ok) return failed(normalized, { code: 'EVENT_TRANSITION_INVALID', message: `${facts.code}: ${facts.message}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
762
761
  const capability = canStart(lifecycleAction(eventIdentity.family), facts.facts, trigger);
763
762
  const safetyFailure = capability.reasonCode === 'TASK_NOT_ACTIVE'
@@ -804,7 +803,7 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
804
803
  let currentFact: CompletionFact | null = null;
805
804
  let completionRework: ReturnType<typeof reworkIntentMutation> | null = null;
806
805
  if (eventIdentity.phase === 'completed' && completedArtifact) {
807
- const receipt = buildCompletionReceipt(content, resolved.taskDir, eventIdentity.family, completedArtifact, metadata.timestamp, frontmatter, normalized);
806
+ const receipt = buildCompletionReceipt(content, resolved.taskDir, resolved.repoRoot, eventIdentity.family, completedArtifact, metadata.timestamp, frontmatter, normalized);
808
807
  if (receipt && !receipt.ok) {
809
808
  return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: receipt.message }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
810
809
  }
@@ -978,32 +977,6 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
978
977
  };
979
978
  }
980
979
 
981
- function applyTaskEvent(request: TaskEventRequest, options: TaskEventOptions = {}): TaskEventResult {
982
- const invalid = validateTaskEventRequest(request);
983
- if (invalid) return failed(request, invalid);
984
- const parts = eventParts(request.event);
985
- if (options.lockAlreadyHeld) return applyTaskEventUnlocked(request, options);
986
- if (request.dryRun || !['started', 'completed'].includes(parts.phase)) {
987
- return applyTaskEventUnlocked(request, options);
988
- }
989
- const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
990
- if (!resolved.ok) return applyTaskEventUnlocked(request, options);
991
- try {
992
- return withTaskExecutionLock(
993
- resolved.repoRoot,
994
- resolved.taskId,
995
- `task-event.${request.event}`,
996
- () => applyTaskEventUnlocked(request, { ...options, lockAlreadyHeld: true })
997
- );
998
- } catch (error) {
999
- if (!(error instanceof TaskExecutionLockError)) throw error;
1000
- return failed(request, {
1001
- code: 'EVENT_TRANSITION_INVALID',
1002
- message: `${error.code}: ${error.message}`
1003
- }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
1004
- }
1005
- }
1006
-
1007
980
  function successNoOp(
1008
981
  request: TaskEventRequest, taskId: string, taskMdPath: string, currentStep: string,
1009
982
  eventIdentity: ReturnType<typeof identity>, timestamp: string,
@@ -14,7 +14,6 @@ import { parseTaskFrontmatter } from './frontmatter.ts';
14
14
  import { recoverPlatformOperations } from './platform-operation-recovery.ts';
15
15
  import {
16
16
  applyTaskLifecycle,
17
- applyTaskLifecycleWithinTaskLock,
18
17
  inspectTaskLifecycleProgress,
19
18
  type TaskLifecycleOptions,
20
19
  type TaskLifecycleRequest,
@@ -24,7 +23,6 @@ import {
24
23
  } from './lifecycle.ts';
25
24
  import { enumerateAllTaskDirs, resolveTaskRef } from './resolve-ref.ts';
26
25
  import { inspectShortIdRegistry } from './short-id.ts';
27
- import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
28
26
  import { verifyTaskEvent } from './verification.ts';
29
27
  import type { TaskVerificationResult } from './verification.ts';
30
28
  import { projectFinalizationWarning } from './workflow-warning-intents.ts';
@@ -586,7 +584,7 @@ function mutationPatch(current: TaskFinalizationReceipt, mutation: FinalizationM
586
584
  : { warningProjection: 'pending', lastError: mutation.error };
587
585
  }
588
586
 
589
- function applyFinalizationReceiptMutationUnderLock(
587
+ function mutateFinalizationReceipt(
590
588
  repoRoot: string,
591
589
  receipt: TaskFinalizationReceipt,
592
590
  capability: FinalizationCapability,
@@ -615,9 +613,7 @@ function applyFinalizationReceiptMutation(
615
613
  if (!hasFinalizationTaskDocument(repoRoot, receipt.taskId)) {
616
614
  throw capabilityError('FINALIZATION_SCOPE_INVALID', 'finalization capability requires an active task or a completed task');
617
615
  }
618
- return withTaskExecutionLock(repoRoot, receipt.taskId, 'task-finalization.receipt-mutation', () =>
619
- applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, mutation, new Set<string>())
620
- );
616
+ return mutateFinalizationReceipt(repoRoot, receipt, capability, mutation, new Set<string>());
621
617
  }
622
618
 
623
619
  function warningKey(warning: Pick<FinalizationWarning, 'step' | 'code' | 'target'>): string {
@@ -640,20 +636,20 @@ function reconcileWarningProjection(repoRoot: string, taskId: string, receipt: T
640
636
  retryable: true
641
637
  };
642
638
  const capability = issueCapability(receipt, 'warning-projection');
643
- return applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, { scope: 'warning-projection', operation: 'failed', error: detail }, consumed);
639
+ return mutateFinalizationReceipt(repoRoot, receipt, capability, { scope: 'warning-projection', operation: 'failed', error: detail }, consumed);
644
640
  }
645
641
  } catch (error) {
646
642
  const detail = errorOf(error, 'FINALIZATION_WARNING_PROJECTION_FAILED', true);
647
643
  try {
648
644
  const capability = issueCapability(receipt, 'warning-projection');
649
- return applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, { scope: 'warning-projection', operation: 'failed', error: detail }, consumed);
645
+ return mutateFinalizationReceipt(repoRoot, receipt, capability, { scope: 'warning-projection', operation: 'failed', error: detail }, consumed);
650
646
  }
651
647
  catch { return receipt; }
652
648
  }
653
649
  }
654
650
  try {
655
651
  const capability = issueCapability(receipt, 'warning-projection');
656
- return applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, { scope: 'warning-projection', operation: 'succeeded' }, consumed);
652
+ return mutateFinalizationReceipt(repoRoot, receipt, capability, { scope: 'warning-projection', operation: 'succeeded' }, consumed);
657
653
  }
658
654
  catch { return receipt; }
659
655
  }
@@ -692,7 +688,7 @@ async function syncPendingTaskComment(input: {
692
688
  if (result.status === 'applied' || result.status === 'no-op') {
693
689
  const skipped = result.error?.code === 'ISSUE_NOT_LINKED';
694
690
  const capability = issueCapability(receipt, 'task-comment');
695
- receipt = applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, {
691
+ receipt = mutateFinalizationReceipt(repoRoot, receipt, capability, {
696
692
  scope: 'task-comment', operation: 'succeeded', state: skipped ? 'skipped' : 'done'
697
693
  }, consumedCapabilities);
698
694
  receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
@@ -708,7 +704,7 @@ async function syncPendingTaskComment(input: {
708
704
  }
709
705
  const detail = step.error ?? { code: 'COMMENT_SYNC_FAILED', message: 'task comment synchronization failed', retryable: true };
710
706
  const capability = issueCapability(receipt, 'task-comment');
711
- receipt = applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, {
707
+ receipt = mutateFinalizationReceipt(repoRoot, receipt, capability, {
712
708
  scope: 'task-comment', operation: 'failed', error: detail
713
709
  }, consumedCapabilities);
714
710
  receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
@@ -717,7 +713,7 @@ async function syncPendingTaskComment(input: {
717
713
  const detail = errorOf(error, 'COMMENT_SYNC_FAILED', true);
718
714
  try {
719
715
  const capability = issueCapability(receipt, 'task-comment');
720
- receipt = applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, {
716
+ receipt = mutateFinalizationReceipt(repoRoot, receipt, capability, {
721
717
  scope: 'task-comment', operation: 'failed', error: detail
722
718
  }, consumedCapabilities);
723
719
  receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
@@ -863,14 +859,14 @@ function preparedResult(
863
859
  };
864
860
  }
865
861
 
866
- async function prepareUnderLock(
862
+ async function prepareTaskFinalizationCore(
867
863
  request: TaskFinalizationRequest,
868
864
  taskId: string,
869
865
  options: TaskFinalizationOptions
870
866
  ): Promise<TaskFinalizationResult> {
871
867
  const repoRoot = path.resolve(options.repoRoot);
872
868
  const lifecycle = options.lifecycle ?? ((lifecycleRequest, lifecycleOptions) => (
873
- applyTaskLifecycleWithinTaskLock(lifecycleRequest, lifecycleOptions ?? { repoRoot }, taskId)
869
+ applyTaskLifecycle(lifecycleRequest, lifecycleOptions ?? { repoRoot })
874
870
  ));
875
871
  const backfill = options.backfill ?? backfillCompletionComments;
876
872
  const commentSync = options.commentSync ?? syncPlatformComment;
@@ -1142,11 +1138,9 @@ async function prepareTaskFinalization(request: TaskFinalizationRequest, options
1142
1138
  const resolved = resolveTaskRef(request.taskRef, { repoRoot });
1143
1139
  if (!resolved.ok) return failed(resolved.taskId, { code: resolved.code, message: resolved.message, retryable: false });
1144
1140
  try {
1145
- return await withTaskExecutionLock(repoRoot, resolved.taskId, 'task-finalization.prepare', () => prepareUnderLock(request, resolved.taskId, options));
1141
+ return await prepareTaskFinalizationCore(request, resolved.taskId, options);
1146
1142
  } catch (error) {
1147
- const detail = error instanceof TaskExecutionLockError
1148
- ? { code: error.code, message: error.message, retryable: error.code === 'ORCHESTRATION_LOCK_BUSY' }
1149
- : errorOf(error, 'TASK_FINALIZATION_FAILED');
1143
+ const detail = errorOf(error, 'TASK_FINALIZATION_FAILED');
1150
1144
  return failed(resolved.taskId, detail);
1151
1145
  }
1152
1146
  }
@@ -1159,7 +1153,7 @@ async function commitPreparedTaskFinalization(request: TaskFinalizationRequest,
1159
1153
  const resolved = resolveTaskRef(request.taskRef, { repoRoot });
1160
1154
  if (!resolved.ok) return failed(resolved.taskId, { code: resolved.code, message: resolved.message, retryable: false });
1161
1155
  try {
1162
- return await withTaskExecutionLock(repoRoot, resolved.taskId, 'task-finalization.commit', async () => {
1156
+ return await (async () => {
1163
1157
  if (options.controlBinding && options.manifestPath) {
1164
1158
  try {
1165
1159
  const manifest = readSandboxControlManifest(options.manifestPath);
@@ -1223,7 +1217,7 @@ async function commitPreparedTaskFinalization(request: TaskFinalizationRequest,
1223
1217
  };
1224
1218
  const result = options.lifecycle
1225
1219
  ? options.lifecycle(lifecycleRequest, lifecycleOptions)
1226
- : applyTaskLifecycleWithinTaskLock(lifecycleRequest, lifecycleOptions, resolved.taskId);
1220
+ : applyTaskLifecycle(lifecycleRequest, lifecycleOptions);
1227
1221
  const step = lifecycleStep(result);
1228
1222
  if (result.status !== 'applied' && result.status !== 'no-op') {
1229
1223
  const updated = updateReceipt(repoRoot, receipt, { lifecycle: 'pending', lastError: step.error });
@@ -1305,11 +1299,9 @@ async function commitPreparedTaskFinalization(request: TaskFinalizationRequest,
1305
1299
  });
1306
1300
  }
1307
1301
  return terminalResult(resolved.taskId, updated, { lifecycle: step, verification }, result.changed, detail);
1308
- });
1302
+ })();
1309
1303
  } catch (error) {
1310
- const detail = error instanceof TaskExecutionLockError
1311
- ? { code: error.code, message: error.message, retryable: error.code === 'ORCHESTRATION_LOCK_BUSY' }
1312
- : errorOf(error, 'TASK_FINALIZATION_FAILED');
1304
+ const detail = errorOf(error, 'TASK_FINALIZATION_FAILED');
1313
1305
  return failed(resolved.taskId, detail);
1314
1306
  }
1315
1307
  }
@@ -18,7 +18,7 @@ import { validateCurrentTaskContract } from './current-contract.ts';
18
18
  import { isValidAgentInfraVersion } from '../version.ts';
19
19
  import { taskIssueIdentity } from '../platform/task-identities.ts';
20
20
  import { resourceIdentityEquals } from '../platform/resource-identity.ts';
21
- import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
21
+ import { resolveProjectTaskWorkspaceRoot } from './workspace-root.ts';
22
22
 
23
23
  const lifecycleIntentCatalog = [
24
24
  'block', 'activate', 'cancel', 'complete', 'close-codescan', 'close-dependabot', 'restore', 'recover-started'
@@ -138,7 +138,7 @@ const DEFAULT_IO: LifecycleFileSystem = {
138
138
 
139
139
  function isVirtiofsMountPoint(directory: string, mountInfo: string): boolean {
140
140
  const decodeMountInfoPath = (value: string): string => value.replace(/\\([0-7]{3})/gu, (_match, octal: string) => String.fromCharCode(Number.parseInt(octal, 8)));
141
- const expected = path.resolve(directory);
141
+ const expected = fs.realpathSync.native(directory);
142
142
  return mountInfo.split('\n').some((line) => {
143
143
  if (!line) return false;
144
144
  const fields = line.split(' ');
@@ -437,14 +437,14 @@ function validateRestoreStaging(request: Extract<TaskLifecycleRequest, { intent:
437
437
  let workspace: string;
438
438
  let stagingDir: string;
439
439
  try {
440
- workspace = fs.realpathSync.native(path.resolve(repoRoot, '.agents', 'workspace'));
440
+ workspace = fs.realpathSync.native(resolveProjectTaskWorkspaceRoot(repoRoot));
441
441
  stagingDir = fs.realpathSync.native(path.resolve(request.stagingDir));
442
442
  } catch (error) {
443
443
  return { code: 'LIFECYCLE_STAGING_INVALID', message: String(error) };
444
444
  }
445
445
  const relative = path.relative(workspace, stagingDir);
446
446
  if (!relative || relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative) || path.basename(stagingDir) === taskId) {
447
- return { code: 'LIFECYCLE_STAGING_INVALID', message: 'restore staging must be a non-final directory inside .agents/workspace' };
447
+ return { code: 'LIFECYCLE_STAGING_INVALID', message: 'restore staging must be a non-final directory inside the project task workspace' };
448
448
  }
449
449
  let entries: fs.Dirent[];
450
450
  try { entries = fs.readdirSync(stagingDir, { withFileTypes: true }); }
@@ -629,7 +629,7 @@ export function inspectTaskLifecycleProgress(
629
629
  }
630
630
  }
631
631
 
632
- function applyTaskLifecycleUnlocked(requestInput: TaskLifecycleRequest, options: TaskLifecycleOptions = {}): TaskLifecycleResult {
632
+ function applyTaskLifecycle(requestInput: TaskLifecycleRequest, options: TaskLifecycleOptions = {}): TaskLifecycleResult {
633
633
  if (requestInput.intent === 'recover-started') {
634
634
  return failed(requestInput, { code: 'LIFECYCLE_RECOVERY_REQUIRES_AUTHORITY', message: 'recover-started must be dispatched through task control authority' });
635
635
  }
@@ -671,7 +671,7 @@ function applyTaskLifecycleUnlocked(requestInput: TaskLifecycleRequest, options:
671
671
  } else {
672
672
  return failed(request, { code: 'LIFECYCLE_TASK_NOT_FOUND', message: `task ${taskId} has no lifecycle source` }, { taskId });
673
673
  }
674
- const targetPath = path.join(repoRoot, '.agents', 'workspace', spec.target, taskId);
674
+ const targetPath = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), spec.target, taskId);
675
675
  const requiredSteps = stepsForIntent(request.intent);
676
676
  const movingTaskDirectory = sourcePath !== targetPath;
677
677
  const currentTaskFile = path.join(sourcePath, 'task.md');
@@ -709,7 +709,7 @@ function applyTaskLifecycleUnlocked(requestInput: TaskLifecycleRequest, options:
709
709
  if (!contract.ok) return failed(request, { code: 'LIFECYCLE_DOCUMENT_INVALID', message: contract.message }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
710
710
  }
711
711
  const registryRequest = executeShortIdCommand({
712
- operation: 'list', activeDir: path.join(repoRoot, '.agents', 'workspace', 'active'),
712
+ operation: 'list', activeDir: path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active'),
713
713
  shortIdLength: configuredShortIdLength(repoRoot)
714
714
  });
715
715
  if (registryRequest.status === 'failed') {
@@ -880,7 +880,7 @@ function applyTaskLifecycleUnlocked(requestInput: TaskLifecycleRequest, options:
880
880
  }
881
881
  }
882
882
  }
883
- const activeTaskPath = path.join(repoRoot, '.agents', 'workspace', 'active', taskId);
883
+ const activeTaskPath = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active', taskId);
884
884
  if (request.intent === 'complete' && fs.existsSync(activeTaskPath)) {
885
885
  try {
886
886
  let sourceIsVirtiofsMount = false;
@@ -925,50 +925,9 @@ function applyTaskLifecycleUnlocked(requestInput: TaskLifecycleRequest, options:
925
925
  };
926
926
  }
927
927
 
928
- function applyTaskLifecycle(request: TaskLifecycleRequest, options: TaskLifecycleOptions = {}): TaskLifecycleResult {
929
- const normalized = normalizedRequest(request);
930
- if ('code' in normalized) return applyTaskLifecycleUnlocked(request, options);
931
- const repoRoot = path.resolve(options.repoRoot ?? process.cwd());
932
- const taskId = normalized.intent === 'restore'
933
- ? normalized.taskRef
934
- : (() => {
935
- const resolved = resolveTaskRef(normalized.taskRef, { repoRoot });
936
- return resolved.ok ? resolved.taskId : null;
937
- })();
938
- if (!taskId) return applyTaskLifecycleUnlocked(request, options);
939
- try {
940
- return withTaskExecutionLock(repoRoot, taskId, 'task-lifecycle.move', () => applyTaskLifecycleUnlocked(request, options));
941
- } catch (error) {
942
- if (error instanceof TaskExecutionLockError) {
943
- return failed(request, { code: error.code, message: error.message }, { taskId });
944
- }
945
- throw error;
946
- }
947
- }
948
-
949
- function applyTaskLifecycleWithinTaskLock(
950
- request: TaskLifecycleRequest,
951
- options: TaskLifecycleOptions,
952
- lockedTaskId: string
953
- ): TaskLifecycleResult {
954
- const normalized = normalizedRequest(request);
955
- if ('code' in normalized) return failed(request, normalized);
956
- const repoRoot = path.resolve(options.repoRoot ?? process.cwd());
957
- const resolved = normalized.intent === 'restore'
958
- ? { ok: true as const, taskId: normalized.taskRef }
959
- : resolveTaskRef(normalized.taskRef, { repoRoot });
960
- if (!resolved.ok || resolved.taskId !== lockedTaskId) {
961
- return failed(request, {
962
- code: 'LIFECYCLE_LOCK_SCOPE_MISMATCH',
963
- message: 'task lifecycle request does not match the held task lock'
964
- }, { taskId: resolved.ok ? resolved.taskId : resolved.taskId });
965
- }
966
- return applyTaskLifecycleUnlocked(request, options);
967
- }
968
-
969
928
  export {
970
929
  lifecycleIntentCatalog, lifecycleFailureCatalog, lifecycleProducerCatalog,
971
- applyTaskLifecycle, applyTaskLifecycleWithinTaskLock, previewTaskCompletion
930
+ applyTaskLifecycle, previewTaskCompletion
972
931
  };
973
932
  export type {
974
933
  TaskLifecycleIntent, TaskLifecycleRequest, TaskLifecycleResult, TaskLifecycleOptions,
@@ -52,7 +52,6 @@ type LocalArtifactFinalizationRequest = {
52
52
  artifact: string;
53
53
  repoRoot?: string;
54
54
  requiredSections?: readonly string[];
55
- lockAlreadyHeld?: boolean;
56
55
  };
57
56
 
58
57
  type LocalArtifactFinalizationResult = {
@@ -129,7 +128,6 @@ type LocalArtifactPreparation = Readonly<{
129
128
  result: LocalArtifactFinalizationResult;
130
129
  content: string;
131
130
  repoRoot?: string;
132
- lockAlreadyHeld?: boolean;
133
131
  }>;
134
132
 
135
133
  function prepareLocalArtifact(
@@ -180,8 +178,7 @@ function prepareLocalArtifact(
180
178
  return {
181
179
  result: validation.ok && !qualificationError ? { ...result, status: 'passed', error: null } : result,
182
180
  content,
183
- repoRoot: resolved.repoRoot,
184
- lockAlreadyHeld: request.lockAlreadyHeld
181
+ repoRoot: resolved.repoRoot
185
182
  };
186
183
 
187
184
  }