@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
@@ -9,6 +9,7 @@ import { parseReviewSummary } from './review-artifacts.ts';
9
9
  import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.ts';
10
10
  import type { LedgerDocument } from './ledger.ts';
11
11
  import { resolveTaskRef, TASK_ID_RE } from './resolve-ref.ts';
12
+ import { resolveProjectTaskWorkspaceRoot } from './workspace-root.ts';
12
13
  import {
13
14
  activateDelegation,
14
15
  abortActivatedDelegation,
@@ -48,14 +49,13 @@ import {
48
49
  import type { RepositorySnapshot } from './workspace-snapshot.ts';
49
50
  import type { WorkspaceSnapshotContext } from './workspace-snapshot.ts';
50
51
  import { assertGitRepositoryBinding } from '../git/worktree-identity.ts';
51
- import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
52
52
  import { extractReviewBaseline, extractReviewDiffBase, extractReviewTargetHead, extractReviewedHead } from './review-fingerprint.ts';
53
53
  import { resolveDeliveryTarget, resolveDiffBase, resolveTargetHead } from './delivery-target.ts';
54
54
  import { buildLifecycleFacts, canStart, recommendNext } from './capabilities.ts';
55
55
  import { hasOpenLifecycleExecution } from './activity-log.ts';
56
56
  import type { LifecycleAction, LifecycleFacts } from './capabilities.ts';
57
57
  import { normalizeAgentToken } from '../agent-clients/tokens.ts';
58
- import { resolveArtifactContext } from './artifact-lifecycle.ts';
58
+ import { resolveArtifactContextForResolvedTask } from './artifact-lifecycle.ts';
59
59
 
60
60
  type OrchestrationStatus = 'running' | 'paused' | 'completed';
61
61
  type ModelPolicySource = Readonly<{
@@ -441,19 +441,7 @@ function mutateDelegationAdapterEvidence(
441
441
  ): unknown {
442
442
  const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
443
443
  if (!resolved.ok) throw new Error(`${resolved.code}: ${resolved.message}`);
444
- return mutateDelegationAdapterEvidenceAtResolved(resolved, receiptId, adapterId, mutate, options, false);
445
- }
446
-
447
- function mutateDelegationAdapterEvidenceWithinTaskLock(
448
- taskRef: string,
449
- receiptId: string,
450
- adapterId: AgentClientId,
451
- mutate: (current: unknown) => unknown,
452
- options: OrchestrationOptions = {}
453
- ): unknown {
454
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
455
- if (!resolved.ok) throw new Error(`${resolved.code}: ${resolved.message}`);
456
- return mutateDelegationAdapterEvidenceAtResolved(resolved, receiptId, adapterId, mutate, options, true);
444
+ return mutateDelegationAdapterEvidenceAtResolved(resolved, receiptId, adapterId, mutate, options);
457
445
  }
458
446
 
459
447
  function mutateDelegationAdapterEvidenceAtResolved(
@@ -461,8 +449,7 @@ function mutateDelegationAdapterEvidenceAtResolved(
461
449
  receiptId: string,
462
450
  adapterId: AgentClientId,
463
451
  mutate: (current: unknown) => unknown,
464
- options: OrchestrationOptions,
465
- taskLockHeld: boolean
452
+ options: OrchestrationOptions
466
453
  ): unknown {
467
454
  const update = () => {
468
455
  try {
@@ -494,9 +481,7 @@ function mutateDelegationAdapterEvidenceAtResolved(
494
481
  }
495
482
  };
496
483
  let mutationError: unknown;
497
- const result = taskLockHeld
498
- ? update()
499
- : withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `agent-adapter.${adapterId}.evidence`, update);
484
+ const result = update();
500
485
  if (mutationError) throw mutationError;
501
486
  return result;
502
487
  }
@@ -733,9 +718,13 @@ function routeFromFacts(facts: LifecycleFacts): Omit<OrchestrationNext, 'request
733
718
  return { action: command, role, stage: action };
734
719
  }
735
720
 
736
- function routeOrchestration(taskRef: string, options: OrchestrationOptions = {}): OrchestrationResult {
737
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
738
- if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
721
+ type ResolvedTaskRef = Extract<ReturnType<typeof resolveTaskRef>, { ok: true }>;
722
+
723
+ function routeResolvedTask(
724
+ taskRef: string,
725
+ resolved: ResolvedTaskRef,
726
+ options: OrchestrationOptions
727
+ ): OrchestrationResult {
739
728
  let gitRoot: string;
740
729
  try {
741
730
  gitRoot = gitRootFor(resolved.repoRoot, options);
@@ -772,7 +761,8 @@ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {})
772
761
  resolved.taskDir,
773
762
  content,
774
763
  resolved.state,
775
- hasOpenLifecycleExecution(content) || Boolean(run?.pendingDelegation)
764
+ hasOpenLifecycleExecution(content) || Boolean(run?.pendingDelegation),
765
+ resolved.repoRoot
776
766
  );
777
767
  if (!facts.ok) return failed('ORCHESTRATION_CAPABILITY_FACTS_INVALID', facts.message, resolved.taskId);
778
768
  const routed = routeFromFacts(facts.facts);
@@ -864,7 +854,7 @@ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {})
864
854
  }
865
855
  const action = routed.stage as LifecycleAction;
866
856
  const family = action === 'analysis' ? 'analysis' : action;
867
- const artifactContext = resolveArtifactContext(taskRef, family, {
857
+ const artifactContext = resolveArtifactContextForResolvedTask(resolved, family, {
868
858
  repoRoot: resolved.repoRoot
869
859
  });
870
860
  if (artifactContext.status !== 'ready' || !artifactContext.selection) {
@@ -898,6 +888,12 @@ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {})
898
888
  return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
899
889
  }
900
890
 
891
+ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {}): OrchestrationResult {
892
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
893
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
894
+ return routeResolvedTask(taskRef, resolved, options);
895
+ }
896
+
901
897
  function statusOrchestration(taskRef: string, options: OrchestrationOptions = {}): OrchestrationResult {
902
898
  const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
903
899
  if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
@@ -952,7 +948,7 @@ function validateDelegationRun(
952
948
  return null;
953
949
  }
954
950
 
955
- function prepareOrchestrationDelegationUnlocked(
951
+ function prepareOrchestrationDelegation(
956
952
  taskRef: string,
957
953
  input: Readonly<{
958
954
  client: AgentClientId;
@@ -978,7 +974,7 @@ function prepareOrchestrationDelegationUnlocked(
978
974
  const runFailure = validateDelegationRun(taskRef, resolved.taskId, run, options);
979
975
  if (runFailure) return runFailure;
980
976
  const activeRun = run!;
981
- const routed = routeOrchestration(taskRef, options);
977
+ const routed = routeResolvedTask(taskRef, resolved, options);
982
978
  if (!routed.next) return routed;
983
979
  const next = routed.next;
984
980
  const policyFailure = validatePreparedModelPolicy(resolved.taskId, activeRun, next, input);
@@ -1010,31 +1006,7 @@ function prepareOrchestrationDelegationUnlocked(
1010
1006
  return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next, error: null };
1011
1007
  }
1012
1008
 
1013
- function prepareOrchestrationDelegation(
1014
- taskRef: string,
1015
- input: Readonly<{
1016
- client: AgentClientId;
1017
- requestedModel?: string;
1018
- requestedReasoningEffort?: string;
1019
- }>,
1020
- options: OrchestrationOptions = {}
1021
- ): OrchestrationResult {
1022
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1023
- if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
1024
- try {
1025
- return withTaskExecutionLock(
1026
- resolved.repoRoot,
1027
- resolved.taskId,
1028
- 'task-orchestration.prepare.task',
1029
- () => prepareOrchestrationDelegationUnlocked(taskRef, input, options)
1030
- );
1031
- } catch (error) {
1032
- if (error instanceof TaskExecutionLockError) return failed(error.code, error.message, resolved.taskId);
1033
- throw error;
1034
- }
1035
- }
1036
-
1037
- function dispatchOrchestrationDelegationUnlocked(
1009
+ function dispatchOrchestrationDelegation(
1038
1010
  taskRef: string,
1039
1011
  options: OrchestrationOptions = {}
1040
1012
  ): OrchestrationResult {
@@ -1055,25 +1027,6 @@ function dispatchOrchestrationDelegationUnlocked(
1055
1027
  return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
1056
1028
  }
1057
1029
 
1058
- function dispatchOrchestrationDelegation(
1059
- taskRef: string,
1060
- options: OrchestrationOptions = {}
1061
- ): OrchestrationResult {
1062
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1063
- if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
1064
- try {
1065
- return withTaskExecutionLock(
1066
- resolved.repoRoot,
1067
- resolved.taskId,
1068
- 'task-orchestration.dispatch.task',
1069
- () => dispatchOrchestrationDelegationUnlocked(taskRef, options)
1070
- );
1071
- } catch (error) {
1072
- if (error instanceof TaskExecutionLockError) return failed(error.code, error.message, resolved.taskId);
1073
- throw error;
1074
- }
1075
- }
1076
-
1077
1030
  type MatchingDelegationsResult =
1078
1031
  | Readonly<{ ok: true; matches: readonly { taskId: string; run: OrchestrationRun }[] }>
1079
1032
  | Readonly<{ ok: false; error: OrchestrationResult }>;
@@ -1090,7 +1043,7 @@ function matchingDelegations(
1090
1043
  return { ok: false, error: failed('ORCHESTRATION_TASK_ID_INVALID', 'taskId must be a canonical TASK-id') };
1091
1044
  }
1092
1045
  const repoRoot = options.repoRoot ?? process.cwd();
1093
- const taskDir = path.join(repoRoot, '.agents', 'workspace', 'active', taskId);
1046
+ const taskDir = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active', taskId);
1094
1047
  const run = readRun(taskDir, options);
1095
1048
  const matches = run && run.status === 'running' && run.pendingDelegation && predicate(run.pendingDelegation)
1096
1049
  ? [{ taskId, run }]
@@ -1385,7 +1338,7 @@ function recoverPreparedOrchestrationDelegation(
1385
1338
  const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1386
1339
  if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
1387
1340
  try {
1388
- return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-orchestration.recover-prepared', () => {
1341
+ return (() => {
1389
1342
  const run = readRun(resolved.taskDir, options);
1390
1343
  const receipt = run?.pendingDelegation;
1391
1344
  if (!run || !receipt) {
@@ -1430,9 +1383,8 @@ function recoverPreparedOrchestrationDelegation(
1430
1383
  }, options.now);
1431
1384
  saveRun(resolved.taskDir, updated);
1432
1385
  return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
1433
- });
1386
+ })();
1434
1387
  } catch (error) {
1435
- if (error instanceof TaskExecutionLockError) return failed(error.code, error.message, resolved.taskId);
1436
1388
  return failed(
1437
1389
  'ORCHESTRATION_PREPARED_RECOVERY_FAILED',
1438
1390
  error instanceof Error ? error.message : String(error),
@@ -1462,7 +1414,7 @@ function recoveryReceiptMatches(
1462
1414
  && normalizeAgentToken(event.startedAgent) === normalizeAgentToken(receipt.client);
1463
1415
  }
1464
1416
 
1465
- function recoverActivatedOrchestrationDelegationUnderLock(
1417
+ function recoverActivatedOrchestrationDelegation(
1466
1418
  taskRef: string,
1467
1419
  event: ActivatedRecoveryEvent,
1468
1420
  options: OrchestrationOptions = {}
@@ -1598,7 +1550,7 @@ function sealOrchestrationDelegation(
1598
1550
  const orchestrationOptions = options;
1599
1551
  const resolved = resolveTaskRef(taskRef, { repoRoot: orchestrationOptions.repoRoot });
1600
1552
  if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
1601
- return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-orchestration.seal', () => {
1553
+ return (() => {
1602
1554
  const run = readRun(resolved.taskDir, orchestrationOptions);
1603
1555
  if (!run?.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
1604
1556
  if (expected && (run.runId !== expected.runId || run.pendingDelegation.id !== expected.receiptId)) {
@@ -1616,7 +1568,7 @@ function sealOrchestrationDelegation(
1616
1568
  const updated = withUpdatedRun(current, { pendingDelegation: result.receipt });
1617
1569
  saveRun(resolved.taskDir, updated);
1618
1570
  return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
1619
- });
1571
+ })();
1620
1572
  }
1621
1573
 
1622
1574
  function advanceOrchestration(taskRef: string, options: OrchestrationOptions = {}): OrchestrationResult {
@@ -1672,7 +1624,6 @@ export {
1672
1624
  orchestrationPath,
1673
1625
  readDelegationAdapterEvidence,
1674
1626
  mutateDelegationAdapterEvidence,
1675
- mutateDelegationAdapterEvidenceWithinTaskLock,
1676
1627
  pauseMatchingOrchestrationDelegation,
1677
1628
  pauseOrchestration,
1678
1629
  planOrchestrationStageCompletion,
@@ -1680,7 +1631,7 @@ export {
1680
1631
  readRun,
1681
1632
  reconcileMatchingOrchestrationDelegation,
1682
1633
  recoverPreparedOrchestrationDelegation,
1683
- recoverActivatedOrchestrationDelegationUnderLock,
1634
+ recoverActivatedOrchestrationDelegation,
1684
1635
  routeOrchestration,
1685
1636
  sealMatchingOrchestrationDelegation,
1686
1637
  sealOrchestrationDelegation,
@@ -222,8 +222,7 @@ async function replayPullRequestSummary(taskId: string, operation: ReturnType<ty
222
222
  ...(summary.manualValidation ? { manualValidation: summary.manualValidation } : {}),
223
223
  primaryResult: 'no_op',
224
224
  strict: true,
225
- skipQueue: true,
226
- lockAlreadyHeld: true
225
+ skipQueue: true
227
226
  });
228
227
  if ((replayed.status === 'applied' || replayed.status === 'no-op') && !replayed.error && replayed.warnings.length === 0) {
229
228
  return platformResult(replayed.status, { changed: replayed.changed });
@@ -3,12 +3,15 @@ import path from 'node:path';
3
3
  import { execFileSync } from 'node:child_process';
4
4
  import { normalizeShortIdInput, resolveShortIdReadOnly } from './short-id.ts';
5
5
  import { parseTaskFrontmatter } from './frontmatter.ts';
6
+ import { resolveProjectTaskWorkspaceRoot } from './workspace-root.ts';
6
7
 
7
8
  const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
8
9
  // Flat-structured workspace dirs that hold tasks under `{dir}/{taskId}/task.md`.
9
10
  // Note: `archive` uses a three-level YYYY/MM/DD layout and is handled separately.
10
11
  const FLAT_WORKSPACE_DIRS = ['active', 'blocked', 'completed'] as const;
11
12
 
13
+ function taskRoot(repoRoot: string): string { return resolveProjectTaskWorkspaceRoot(repoRoot); }
14
+
12
15
  type ResolveRefResult =
13
16
  | {
14
17
  ok: true;
@@ -80,12 +83,12 @@ function listSortedNumeric(dir: string, width: number): string[] {
80
83
  .reverse();
81
84
  }
82
85
 
83
- function findInArchive(repoRoot: string, taskId: string): string | null {
84
- // archive-tasks stores local material at .agents/workspace/archive/YYYY/MM/DD/{taskId}/local/task.md
86
+ function findInArchive(workspaceRoot: string, taskId: string): string | null {
87
+ // archive-tasks stores local material at the project task workspace archive/YYYY/MM/DD/{taskId}/local/task.md
85
88
  // where YYYY/MM/DD comes from completed_at (or updated_at fallback) — NOT from
86
89
  // the task id's creation date. So we cannot derive the path from taskId alone;
87
90
  // walk the bounded YYYY/MM/DD tree instead. Newest-first to favor recent archives.
88
- const archiveDir = path.join(repoRoot, '.agents', 'workspace', 'archive');
91
+ const archiveDir = path.join(workspaceRoot, 'archive');
89
92
  for (const year of listSortedNumeric(archiveDir, 4)) {
90
93
  const yearDir = path.join(archiveDir, year);
91
94
  for (const month of listSortedNumeric(yearDir, 2)) {
@@ -100,14 +103,14 @@ function findInArchive(repoRoot: string, taskId: string): string | null {
100
103
  }
101
104
 
102
105
  function findTaskMd(
103
- repoRoot: string,
106
+ workspaceRoot: string,
104
107
  taskId: string
105
108
  ): { taskMdPath: string; state: TaskWorkspaceState } | null {
106
109
  for (const sub of FLAT_WORKSPACE_DIRS) {
107
- const candidate = path.join(repoRoot, '.agents', 'workspace', sub, taskId, 'task.md');
110
+ const candidate = path.join(workspaceRoot, sub, taskId, 'task.md');
108
111
  if (fs.existsSync(candidate)) return { taskMdPath: candidate, state: sub };
109
112
  }
110
- const archived = findInArchive(repoRoot, taskId);
113
+ const archived = findInArchive(workspaceRoot, taskId);
111
114
  return archived ? { taskMdPath: archived, state: 'archive' } : null;
112
115
  }
113
116
 
@@ -119,8 +122,9 @@ function findTaskMd(
119
122
  */
120
123
  function enumerateTaskDirs(repoRoot: string): { taskId: string; taskDir: string }[] {
121
124
  const out: { taskId: string; taskDir: string }[] = [];
125
+ const workspaceRoot = taskRoot(repoRoot);
122
126
  for (const sub of FLAT_WORKSPACE_DIRS) {
123
- const base = path.join(repoRoot, '.agents', 'workspace', sub);
127
+ const base = path.join(workspaceRoot, sub);
124
128
  if (!fs.existsSync(base)) continue;
125
129
  for (const entry of fs.readdirSync(base).sort()) {
126
130
  if (!TASK_ID_RE.test(entry)) continue;
@@ -139,6 +143,7 @@ function enumerateTaskDirs(repoRoot: string): { taskId: string; taskDir: string
139
143
  */
140
144
  function enumerateAllTaskDirs(repoRoot: string): { taskId: string; taskDir: string; state: TaskWorkspaceState }[] {
141
145
  const out: { taskId: string; taskDir: string; state: TaskWorkspaceState }[] = [];
146
+ const workspaceRoot = taskRoot(repoRoot);
142
147
  const seen = new Map<string, string>();
143
148
  const add = (taskId: string, taskDir: string, state: TaskWorkspaceState) => {
144
149
  if (!TASK_ID_RE.test(taskId)) return;
@@ -155,12 +160,12 @@ function enumerateAllTaskDirs(repoRoot: string): { taskId: string; taskDir: stri
155
160
  };
156
161
 
157
162
  for (const state of FLAT_WORKSPACE_DIRS) {
158
- const base = path.join(repoRoot, '.agents', 'workspace', state);
163
+ const base = path.join(workspaceRoot, state);
159
164
  if (!fs.existsSync(base)) continue;
160
165
  for (const taskId of fs.readdirSync(base).sort()) add(taskId, path.join(base, taskId), state);
161
166
  }
162
167
 
163
- const archive = path.join(repoRoot, '.agents', 'workspace', 'archive');
168
+ const archive = path.join(workspaceRoot, 'archive');
164
169
  for (const year of listSortedNumeric(archive, 4).reverse()) {
165
170
  const yearDir = path.join(archive, year);
166
171
  for (const month of listSortedNumeric(yearDir, 2).reverse()) {
@@ -178,8 +183,9 @@ function locateHotTaskDirs(
178
183
  repoRoot: string,
179
184
  taskId: string
180
185
  ): { taskId: string; taskDir: string; taskMdPath: string; state: Exclude<TaskWorkspaceState, 'archive'> }[] {
186
+ const workspaceRoot = taskRoot(repoRoot);
181
187
  return FLAT_WORKSPACE_DIRS.flatMap((state) => {
182
- const taskDir = path.join(repoRoot, '.agents', 'workspace', state, taskId);
188
+ const taskDir = path.join(workspaceRoot, state, taskId);
183
189
  const taskMdPath = path.join(taskDir, 'task.md');
184
190
  return fs.existsSync(taskMdPath) ? [{ taskId, taskDir, taskMdPath, state }] : [];
185
191
  });
@@ -244,7 +250,7 @@ function resolveTaskRef(arg: string, options: ResolveTaskRefOptions = {}): Resol
244
250
  }
245
251
  taskId = shortResult.taskId;
246
252
  }
247
- const located = findTaskMd(repoRoot, taskId);
253
+ const located = findTaskMd(taskRoot(repoRoot), taskId);
248
254
  if (!located) {
249
255
  return {
250
256
  ok: false,
@@ -300,7 +306,7 @@ function resolveTaskContext(
300
306
  };
301
307
  }
302
308
 
303
- const activeDir = path.join(repoRoot, '.agents', 'workspace', 'active');
309
+ const activeDir = path.join(taskRoot(repoRoot), 'active');
304
310
  const matches: { taskId: string; taskDir: string; taskMdPath: string }[] = [];
305
311
  if (fs.existsSync(activeDir)) {
306
312
  for (const entry of fs.readdirSync(activeDir).sort()) {
@@ -10,7 +10,6 @@ import { finalizeReviewSummaryContent } from './review-artifacts.ts';
10
10
  import { inspectDecisionDetailDuplicates } from './decision-details.ts';
11
11
  import { resolveTaskRef } from './resolve-ref.ts';
12
12
  import { validateLifecycleExecution } from './lifecycle-execution.ts';
13
- import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
14
13
  import type { ResolveTaskRefErrorCode } from './resolve-ref.ts';
15
14
  import { getArtifactSchema } from './artifact-schema.ts';
16
15
  import { validateQualificationAudit } from './qualification-audit.ts';
@@ -69,7 +68,6 @@ type ReviewFileSystem = {
69
68
  type ReviewFinalizationOptions = {
70
69
  repoRoot?: string;
71
70
  fileSystem?: Partial<ReviewFileSystem>;
72
- lockAlreadyHeld?: boolean;
73
71
  };
74
72
 
75
73
  const STAGES: Record<ReviewStage, { family: 'review-analysis' | 'review-plan' | 'review-code' }> = {
@@ -123,7 +121,7 @@ function preflightFailed(
123
121
  }
124
122
 
125
123
  /** Validate the current review draft before any finding-ledger write. */
126
- function preflightReviewSummaryUnlocked(
124
+ function preflightReviewSummary(
127
125
  request: ReviewFinalizationRequest,
128
126
  options: ReviewFinalizationOptions = {}
129
127
  ): ReviewPreflightResult {
@@ -178,30 +176,9 @@ function preflightReviewSummaryUnlocked(
178
176
  };
179
177
  }
180
178
 
181
- function preflightReviewSummary(
182
- request: ReviewFinalizationRequest,
183
- options: ReviewFinalizationOptions = {}
184
- ): ReviewPreflightResult {
185
- if (request.dryRun || options.lockAlreadyHeld) return preflightReviewSummaryUnlocked(request, options);
186
- const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
187
- if (!resolved.ok) return preflightReviewSummaryUnlocked(request, options);
188
- try {
189
- return withTaskExecutionLock(
190
- resolved.repoRoot,
191
- resolved.taskId,
192
- 'task-review.preflight',
193
- () => preflightReviewSummaryUnlocked(request, { ...options, lockAlreadyHeld: true })
194
- );
195
- } catch (error) {
196
- if (!(error instanceof TaskExecutionLockError)) throw error;
197
- return preflightFailed(request, 'REVIEW_PROVENANCE_INVALID', `${error.code}: ${error.message}`, resolved.taskId);
198
- }
199
- }
200
-
201
179
  type ReviewSummaryCandidatePreparation = Readonly<{
202
180
  result: ReviewFinalizationResult;
203
181
  content: string;
204
- lockAlreadyHeld?: boolean;
205
182
  }>;
206
183
 
207
184
  /** Prepare domain validation, summary bytes and provenance before any publication. */
@@ -279,7 +256,6 @@ function prepareReviewSummaryCandidate(
279
256
  operations: transformed.changed ? [{ kind: 'artifact', artifact: request.artifact, operation: 'update' }] : [],
280
257
  error: null
281
258
  },
282
- lockAlreadyHeld: options.lockAlreadyHeld
283
259
  };
284
260
  }
285
261
 
@@ -303,7 +279,7 @@ function replaceCurrentArtifactAtomically(file: string, content: string): void {
303
279
  }
304
280
  }
305
281
 
306
- function finalizeReviewSummaryUnlocked(
282
+ function finalizeReviewSummary(
307
283
  request: ReviewFinalizationRequest,
308
284
  options: ReviewFinalizationOptions = {}
309
285
  ): ReviewFinalizationResult {
@@ -340,32 +316,6 @@ function finalizeReviewSummaryUnlocked(
340
316
  }
341
317
  }
342
318
 
343
- function finalizeReviewSummary(
344
- request: ReviewFinalizationRequest,
345
- options: ReviewFinalizationOptions = {}
346
- ): ReviewFinalizationResult {
347
- const effectiveOptions = options;
348
- if (request.dryRun || options.lockAlreadyHeld) return finalizeReviewSummaryUnlocked(request, effectiveOptions);
349
- const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
350
- if (!resolved.ok) return finalizeReviewSummaryUnlocked(request, effectiveOptions);
351
- try {
352
- return withTaskExecutionLock(
353
- resolved.repoRoot,
354
- resolved.taskId,
355
- 'task-review.finalize-summary',
356
- () => finalizeReviewSummaryUnlocked(request, { ...effectiveOptions, lockAlreadyHeld: true })
357
- );
358
- } catch (error) {
359
- if (!(error instanceof TaskExecutionLockError)) throw error;
360
- return failed(
361
- request,
362
- 'REVIEW_PROVENANCE_INVALID',
363
- `${error.code}: ${error.message}`,
364
- resolved.taskId
365
- );
366
- }
367
- }
368
-
369
319
  export { finalizeReviewSummary, preflightReviewSummary, prepareReviewSummaryCandidate, commitReviewSummaryProvenance };
370
320
  export type {
371
321
  ReviewFinalizationError,
@@ -45,7 +45,9 @@ export function inspectReviewIdentity(taskDir: string, reportContent: string, re
45
45
  }
46
46
  try {
47
47
  const metadata = parseTypedTaskFrontmatter(fs.readFileSync(path.join(taskDir, 'task.md'), 'utf8'));
48
- const taskRepositoryRoot = git(taskDir, ['rev-parse', '--show-toplevel']);
48
+ const taskRepositoryRoot = repositoryRoot
49
+ ? fs.realpathSync.native(repositoryRoot)
50
+ : git(taskDir, ['rev-parse', '--show-toplevel']);
49
51
  const branch = String(metadata.branch ?? '').trim();
50
52
  const gitRoot = branch ? resolveBranchWorktree(taskRepositoryRoot, branch) : taskRepositoryRoot;
51
53
  if (!gitRoot) return result('indeterminate', `Task branch '${branch}' is not checked out in a registered worktree`, reviewedHead);
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { parseTypedTaskFrontmatter } from './frontmatter.ts';
4
+ import { resolveProjectTaskWorkspaceRoot } from './workspace-root.ts';
4
5
 
5
6
  const REGISTRY_NAME = '.short-ids.json';
6
7
  const LOCK_NAME = '.short-ids.json.lock';
@@ -85,7 +86,7 @@ function resolveShortIdReadOnly(
85
86
  }
86
87
 
87
88
  const key = normalized.value;
88
- const registryPath = path.join(repoRoot, '.agents', 'workspace', 'active', REGISTRY_NAME);
89
+ const registryPath = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active', REGISTRY_NAME);
89
90
  if (!fs.existsSync(registryPath)) {
90
91
  return shortIdFailure(
91
92
  'SHORT_ID_REGISTRY_NOT_FOUND',
@@ -164,8 +165,9 @@ function resolveShortIdReadOnly(
164
165
  : `short id '${key}' not found in task short-id registry.`;
165
166
  return shortIdFailure('SHORT_ID_NOT_FOUND', message);
166
167
  }
167
- const activeTask = path.join(repoRoot, '.agents', 'workspace', 'active', taskId, 'task.md');
168
- const completedTask = path.join(repoRoot, '.agents', 'workspace', 'completed', taskId, 'task.md');
168
+ const workspaceRoot = resolveProjectTaskWorkspaceRoot(repoRoot);
169
+ const activeTask = path.join(workspaceRoot, 'active', taskId, 'task.md');
170
+ const completedTask = path.join(workspaceRoot, 'completed', taskId, 'task.md');
169
171
  let isValidCompletedTask = false;
170
172
  try {
171
173
  const task = parseTypedTaskFrontmatter(fs.readFileSync(completedTask, 'utf8'));
@@ -300,7 +302,7 @@ function mutateShortIdRegistry(
300
302
  taskId: string,
301
303
  requested: ShortIdMutationEffect
302
304
  ): ShortIdMutationResult {
303
- const activeDir = path.join(repoRoot, '.agents', 'workspace', 'active');
305
+ const activeDir = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active');
304
306
  return mutateShortIdRegistryAt(activeDir, configuredShortIdLength(repoRoot), taskId, requested);
305
307
  }
306
308
 
@@ -369,7 +371,7 @@ function mutateShortIdRegistryAt(
369
371
  }
370
372
 
371
373
  function releaseStaleShortIdRegistry(repoRoot: string, taskId: string): ShortIdMutationResult {
372
- const activeDir = path.join(repoRoot, '.agents', 'workspace', 'active');
374
+ const activeDir = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active');
373
375
  return mutateShortIdRegistryAt(
374
376
  activeDir,
375
377
  configuredShortIdLength(repoRoot),
@@ -433,7 +435,7 @@ function executeShortIdCommand(request: ShortIdCommandRequest): ShortIdCommandRe
433
435
  }
434
436
 
435
437
  function readRegistry(repoRoot: string): RegistrySchema | null {
436
- const registryPath = path.join(repoRoot, '.agents', 'workspace', 'active', REGISTRY_NAME);
438
+ const registryPath = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active', REGISTRY_NAME);
437
439
  if (!fs.existsSync(registryPath)) return null;
438
440
  try {
439
441
  const raw = fs.readFileSync(registryPath, 'utf8');
@@ -446,7 +448,7 @@ function readRegistry(repoRoot: string): RegistrySchema | null {
446
448
  }
447
449
 
448
450
  function readBranchFromTaskMd(repoRoot: string, taskId: string): string | null {
449
- const taskMdPath = path.join(repoRoot, '.agents', 'workspace', 'active', taskId, 'task.md');
451
+ const taskMdPath = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active', taskId, 'task.md');
450
452
  if (!fs.existsSync(taskMdPath)) return null;
451
453
  const content = fs.readFileSync(taskMdPath, 'utf8');
452
454
  const m = content.match(/^branch:\s*(.+)$/m);
@@ -464,8 +466,11 @@ function loadShortIdByTaskId(repoRoot: string): Map<string, string> {
464
466
  return map;
465
467
  }
466
468
 
467
- function inspectShortIdRegistry(repoRoot: string): ShortIdRegistryInspection {
468
- const registryPath = path.join(repoRoot, '.agents', 'workspace', 'active', REGISTRY_NAME);
469
+ function inspectShortIdRegistry(
470
+ repoRoot: string,
471
+ shortIdLength = configuredShortIdLength(repoRoot)
472
+ ): ShortIdRegistryInspection {
473
+ const registryPath = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active', REGISTRY_NAME);
469
474
  if (!fs.existsSync(registryPath)) {
470
475
  return {
471
476
  status: 'missing',
@@ -503,7 +508,7 @@ function inspectShortIdRegistry(repoRoot: string): ShortIdRegistryInspection {
503
508
  }
504
509
 
505
510
  try {
506
- const registry = validateRegistry(parsed, registryPath, configuredShortIdLength(repoRoot));
511
+ const registry = validateRegistry(parsed, registryPath, shortIdLength);
507
512
  const shortIds = new Map<string, string>();
508
513
  for (const [key, taskId] of Object.entries(registry.ids)) shortIds.set(taskId, key);
509
514
  return { status: 'valid', shortIds };