@zq-silk/yui 0.14.1 → 0.14.2

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 (177) hide show
  1. package/ARCHITECTURE.md +64 -35
  2. package/README.md +254 -124
  3. package/dist/agent/managedRuntimeEnvironment.js +5 -8
  4. package/dist/cli/commandCatalog.js +74 -109
  5. package/dist/cli/helpRenderer.js +1 -2
  6. package/dist/cli/interactionCandidates.js +10 -0
  7. package/dist/cli/interactionPolicy.js +76 -37
  8. package/dist/cli/roleWizard.js +5 -6
  9. package/dist/cli/updateOrchestrator.js +10 -8
  10. package/dist/cli/updatePorts.js +17 -13
  11. package/dist/cli/upgradeCommand.js +2 -5
  12. package/dist/cli.js +268 -231
  13. package/dist/commands/agentCommands.js +13 -0
  14. package/dist/commands/configCommands.js +7 -19
  15. package/dist/commands/configOverview.js +3 -2
  16. package/dist/commands/durableJobCommands.js +1 -1
  17. package/dist/commands/executionAuditCommands.js +4 -9
  18. package/dist/commands/globalRoleCommands.js +17 -0
  19. package/dist/commands/profileCommands.js +200 -30
  20. package/dist/commands/sessionCommands.js +1 -1
  21. package/dist/commands/taskActor.js +29 -127
  22. package/dist/commands/taskChangeSetCommands.js +1 -3
  23. package/dist/commands/taskCommands.js +1771 -2107
  24. package/dist/commands/taskCompletionGate.js +27 -103
  25. package/dist/commands/taskContextCommand.js +47 -80
  26. package/dist/commands/taskExecutionCommands.js +9 -27
  27. package/dist/commands/taskInputCommands.js +10 -10
  28. package/dist/commands/taskIntegrationCommands.js +69 -61
  29. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  30. package/dist/commands/taskNextActionCommand.js +8 -29
  31. package/dist/commands/taskOverviewCommand.js +25 -3
  32. package/dist/commands/taskPublicationCommands.js +151 -85
  33. package/dist/commands/taskPublicationVerifyCommand.js +181 -0
  34. package/dist/commands/taskRemoteDeliveryCommand.js +134 -0
  35. package/dist/commands/taskRoleRuntimeStatus.js +5 -6
  36. package/dist/commands/taskUpstreamCommands.js +79 -95
  37. package/dist/config/configCatalog.js +1 -1
  38. package/dist/context/contextSnapshot.js +17 -2
  39. package/dist/context/sourceTurnContext.js +30 -0
  40. package/dist/context/turnContextPack.js +107 -48
  41. package/dist/context/turnInputContract.js +11 -4
  42. package/dist/context/wakeNotification.js +84 -22
  43. package/dist/controller/agentRuntimeObserver.js +9 -12
  44. package/dist/controller/clientRuntime.js +13 -13
  45. package/dist/controller/controller.js +11 -11
  46. package/dist/controller/fileSchedulerStoreAdapter.js +262 -219
  47. package/dist/controller/jobControl.js +11 -10
  48. package/dist/controller/resourceInventory.js +6 -6
  49. package/dist/controller/resourceInventoryLinux.js +2 -2
  50. package/dist/controller/runtime.js +18 -61
  51. package/dist/controller/runtimeEventInbox.js +57 -34
  52. package/dist/controller/runtimeEventProcessor.js +36 -96
  53. package/dist/controller/runtimeHookTurnFence.js +33 -57
  54. package/dist/controller/runtimeLaunchCoordinator.js +44 -40
  55. package/dist/controller/runtimeObservationHook.js +18 -12
  56. package/dist/controller/sessionNotify.js +43 -28
  57. package/dist/controller/sessionOwnerReconciliation.js +21 -21
  58. package/dist/controller/structuredProviderObservation.js +13 -16
  59. package/dist/coordination/workMailboxQueue.js +132 -0
  60. package/dist/core/controllerServer.js +1 -0
  61. package/dist/domain/agentResultTransport.js +60 -0
  62. package/dist/execution/executionHealth.js +111 -118
  63. package/dist/execution/reviewMainTurn.js +161 -0
  64. package/dist/execution/workItemExecution.js +454 -0
  65. package/dist/execution/workItemExecutionProjection.js +309 -0
  66. package/dist/execution/workItemMainTurn.js +164 -0
  67. package/dist/executor/agentExecutor.js +19 -19
  68. package/dist/executor/executorRegistry.js +7 -28
  69. package/dist/executor/fileRoleLaunchPlanner.js +16 -82
  70. package/dist/external/githubPublicationVerifier.js +114 -0
  71. package/dist/external/gitlabPublicationVerifier.js +158 -0
  72. package/dist/external/pinnedCommandRunner.js +73 -0
  73. package/dist/integration/changeSetManifest.js +0 -3
  74. package/dist/integration/deliveryObligation.js +43 -6
  75. package/dist/integration/gitIntegrationService.js +284 -101
  76. package/dist/integration/integrationAttempt.js +64 -14
  77. package/dist/integration/integrationQueueEntry.js +5 -35
  78. package/dist/integration/integrationQueueService.js +65 -194
  79. package/dist/lifecycle/canonicalLifecycleEvent.js +16 -15
  80. package/dist/lifecycle/exactTurnTerminalization.js +100 -97
  81. package/dist/message/message.js +16 -0
  82. package/dist/observability/executionAudit.js +4 -25
  83. package/dist/observability/faultClassification.js +58 -70
  84. package/dist/observability/orchestrationMetrics.js +8 -50
  85. package/dist/profile/agentProfile.js +60 -15
  86. package/dist/profile/agentProfileRuntime.js +100 -0
  87. package/dist/release/releaseWorkflowPorts.js +5 -68
  88. package/dist/repository/executionLaneGitSnapshot.js +80 -0
  89. package/dist/repository/gitWorkspace.js +9 -67
  90. package/dist/repository/taskBaseFreshness.js +3 -1
  91. package/dist/repository/taskWorkspaceCoordinator.js +32 -4
  92. package/dist/repository/taskWorkspacePreparer.js +252 -411
  93. package/dist/resources/liveReferences.js +6 -55
  94. package/dist/resources/resourceDiscovery.js +1 -30
  95. package/dist/resources/resourceRegistrar.js +0 -23
  96. package/dist/review/deltaRecheck.js +8 -65
  97. package/dist/review/reviewAcceptance.js +22 -19
  98. package/dist/review/reviewConfig.js +0 -10
  99. package/dist/review/reviewDecision.js +2 -2
  100. package/dist/review/reviewRound.js +147 -273
  101. package/dist/review/reviewerAvailability.js +13 -23
  102. package/dist/role/role.js +3 -0
  103. package/dist/runtime/agentDriverObservation.js +1 -1
  104. package/dist/runtime/agentHost.js +51 -51
  105. package/dist/runtime/builtinAgentDrivers.js +9 -2
  106. package/dist/runtime/codexAppServerRuntime.js +23 -5
  107. package/dist/runtime/exactControlPlane.js +2 -267
  108. package/dist/runtime/firstProgressAdvisory.js +1 -1
  109. package/dist/runtime/index.js +1 -1
  110. package/dist/runtime/launchBroker.js +11 -11
  111. package/dist/runtime/lifecycleReservation.js +2 -2
  112. package/dist/runtime/managedCaller.js +111 -0
  113. package/dist/runtime/ports.js +16 -3
  114. package/dist/runtime/preallocatedNativeSession.js +2 -2
  115. package/dist/runtime/processExitObservation.js +3 -2
  116. package/dist/runtime/providerContinuationReconciliationService.js +1 -1
  117. package/dist/runtime/runtimeBinding.js +1 -1
  118. package/dist/runtime/runtimeObservation.js +33 -16
  119. package/dist/runtime/runtimeSessionCandidate.js +2 -2
  120. package/dist/runtime/runtimeStopReceipt.js +11 -11
  121. package/dist/runtime/sessionLaunchRequest.js +2 -2
  122. package/dist/runtime/sessionOwnerIdentity.js +9 -9
  123. package/dist/runtime/sessionOwnerRegistry.js +13 -13
  124. package/dist/runtime/sessionReconciliation.js +4 -4
  125. package/dist/runtime/sessionTerminationGuard.js +9 -9
  126. package/dist/runtime/sessionTokenMetrics.js +5 -7
  127. package/dist/runtime/structuredProviderHost.js +5 -5
  128. package/dist/runtime/taskRuntimeIsolation.js +24 -24
  129. package/dist/runtime/tmuxAdapters.js +32 -30
  130. package/dist/scheduler/activeRoleTurnDelivery.js +37 -15
  131. package/dist/scheduler/leaderWakeupProcessor.js +78 -55
  132. package/dist/scheduler/roleTurnLiveness.js +6 -6
  133. package/dist/scheduler/roleTurnStall.js +8 -9
  134. package/dist/scheduler/taskExecutionProjection.js +19 -41
  135. package/dist/scheduler/taskObservabilityProjection.js +75 -122
  136. package/dist/storage/sqliteSchema.js +1 -21
  137. package/dist/storage/sqliteStore.js +62 -47
  138. package/dist/storage/storageSchema.js +1 -8
  139. package/dist/storage/storageVersions.js +7 -4
  140. package/dist/storage/taskStore.js +8 -8
  141. package/dist/storage/upgrade/upgradeOrchestrator.js +112 -83
  142. package/dist/task/completionReadiness.js +17 -98
  143. package/dist/task/deliveryGuard.js +9 -11
  144. package/dist/task/draftPlan.js +142 -0
  145. package/dist/task/nextAction.js +179 -477
  146. package/dist/task/publicationVerification.js +1 -0
  147. package/dist/task/remoteDelivery.js +233 -0
  148. package/dist/task/task.js +108 -4
  149. package/dist/task/taskRecordReference.js +0 -1
  150. package/dist/turn/turn.js +109 -16
  151. package/dist/verification/verificationGateService.js +0 -26
  152. package/dist/web/assets/client/components.js +99 -121
  153. package/dist/web/assets/client/i18n.js +136 -58
  154. package/dist/web/assets/client/view.js +44 -0
  155. package/dist/web/assets/styles/cards.js +3 -15
  156. package/dist/web/assets/styles/widgets.js +4 -0
  157. package/dist/web/webSnapshot.js +29 -14
  158. package/dist/workItem/dependencyGate.js +34 -0
  159. package/dist/workItem/workItem.js +99 -266
  160. package/dist/workspace/workItemChangeSetManager.js +15 -59
  161. package/docs/task-local-identity.md +1 -1
  162. package/i18n/README.zh-CN.md +164 -58
  163. package/package.json +1 -1
  164. package/skills/yui-leader/SKILL.md +272 -782
  165. package/skills/yui-operator/SKILL.md +190 -365
  166. package/skills/yui-reviewer/SKILL.md +40 -33
  167. package/skills/yui-runtime/SKILL.md +22 -0
  168. package/skills/yui-worker/SKILL.md +19 -6
  169. package/dist/execution/candidateConvergence.js +0 -623
  170. package/dist/execution/executionGroup.js +0 -854
  171. package/dist/execution/resourceBroker.js +0 -426
  172. package/dist/executor/launchPlan.js +0 -16
  173. package/dist/review/reviewFinding.js +0 -240
  174. package/dist/review/reviewFindingLedger.js +0 -550
  175. package/dist/review/reviewOutcomeClassifier.js +0 -255
  176. package/dist/scheduler/resourceQueueProjection.js +0 -71
  177. package/dist/task/repairWave.js +0 -150
package/dist/cli.js CHANGED
@@ -4,6 +4,7 @@ import { readFileSync } from "node:fs";
4
4
  import { join, resolve } from "node:path";
5
5
  import { createInterface } from "node:readline/promises";
6
6
  import { fileURLToPath } from "node:url";
7
+ import { isDeepStrictEqual } from "node:util";
7
8
  import { renderCommandHelp } from "./cli/helpRenderer.js";
8
9
  import { describeCommandTree, findCommandNode } from "./cli/commandCatalog.js";
9
10
  import { routeInvocation } from "./cli/invocationRouter.js";
@@ -35,8 +36,12 @@ import { runTelemetryCommand } from "./commands/telemetryCommands.js";
35
36
  import { runResourcesCommand } from "./commands/resourcesCommands.js";
36
37
  import { applyOperatorSessionControl, runOperatorCommand } from "./commands/operatorCommands.js";
37
38
  import { runProjectCommand } from "./commands/projectCommands.js";
38
- import { runProfileCommand } from "./commands/profileCommands.js";
39
- import { dispatchPreparedReviewRound, failPendingReviewRound, preserveReviewRoundWorkspace, parseTaskCompletionRequest, preflightTaskCompletion, runTaskCommand, normalizedExecutionLanePlan, resolvedExecutionStageRetryGroup, validateTaskArchiveRequest } from "./commands/taskCommands.js";
39
+ import { previewProfileAgentConfigurationMutation, runProfileCommand } from "./commands/profileCommands.js";
40
+ import { assertWorkItemDependenciesCompletedForCommand, dispatchPreparedReviewRound, failPendingReviewRound, preserveReviewRoundWorkspace, parseTaskCompletionRequest, previewTaskRoleAgentConfigurationMutation, preflightTaskCompletion, runTaskCommand, planReplicatedWorkItemLanes, validateTaskArchiveRequest } from "./commands/taskCommands.js";
41
+ import { assertTaskRemoteDeliveryIntegrated, createTaskRemoteDeliveryProof } from "./commands/taskRemoteDeliveryCommand.js";
42
+ import { runTaskPublicationVerifyCommand } from "./commands/taskPublicationVerifyCommand.js";
43
+ import { createGitHubCliPublicationVerifier } from "./external/githubPublicationVerifier.js";
44
+ import { createGitLabCliPublicationVerifier } from "./external/gitlabPublicationVerifier.js";
40
45
  import { taskActor } from "./commands/taskActor.js";
41
46
  import { parseTaskExecutionStartRequest, parseTaskExecutionStopRequest, finalizeStoppedTaskExecution, startTaskExecutionCommand, stopTaskExecutionCommand } from "./commands/taskExecutionCommands.js";
42
47
  import { runTaskIntegrationCommand } from "./commands/taskIntegrationCommands.js";
@@ -78,21 +83,23 @@ import { runSetupCommand, validateSetupInvocation } from "./setup/setupCommand.j
78
83
  import { NodeCommandExecutor } from "./tmux/commandExecutor.js";
79
84
  import { TmuxManager } from "./tmux/tmuxManager.js";
80
85
  import { WorkItemChangeSetManager } from "./workspace/workItemChangeSetManager.js";
86
+ import { workspaceProjectEntry } from "./worktree/managedWorkspace.js";
81
87
  import { parseWebCommandOptions, startYuiWebServer } from "./web/webServer.js";
82
- import { AgentConfigurationCatalogService } from "./executor/agentConfigurationCatalog.js";
88
+ import { AgentConfigurationCatalogService, validateAgentLaunchConfiguration } from "./executor/agentConfigurationCatalog.js";
89
+ import { resolveAgentProfileView } from "./profile/agentProfileRuntime.js";
83
90
  import { TmuxWebTerminalService } from "./web/tmuxWebTerminal.js";
84
91
  import { listOperatorSessions, operatorSessionRef } from "./operator/operatorSessionHistory.js";
85
92
  import { YUI_VERSION, yuiVersionIdentity } from "./version.js";
86
93
  import { SqliteSchemaMigrationError } from "./storage/sqliteSchema.js";
87
- import { YUI_CONTROL_PLANE_DESCRIPTOR, YUI_TASK_RUNTIME_DESCRIPTOR, assertCompatibleControlPlanePreflight, assertExactControlPlanePreflight, assertExactTaskRuntimeEnvironment, assertExactTaskRuntimeState, exactControlPlaneDigest, extractExactControlArgument, createExactControlPlaneDescriptor, parseExactControlPlaneDescriptor } from "./runtime/exactControlPlane.js";
94
+ import { YUI_CONTROL_PLANE_DESCRIPTOR, assertCompatibleControlPlanePreflight, assertExactControlPlanePreflight, exactControlPlaneDigest, extractExactControlArgument, createExactControlPlaneDescriptor, parseExactControlPlaneDescriptor } from "./runtime/exactControlPlane.js";
95
+ import { requireManagedTaskCaller } from "./runtime/managedCaller.js";
88
96
  import { readSessionBootstrapManifest, refreshManagedSessionCliWrappers } from "./context/sessionBootstrapManifest.js";
89
- import { builtinAgentDriverRegistry } from "./runtime/builtinAgentDrivers.js";
90
97
  import { createTaskFinalReviewContract, extractTaskFinalReviewRequest } from "./review/taskFinalReviewContract.js";
91
98
  import { resolveRecordedTaskFinalReviewContract } from "./review/taskFinalReviewContractResolution.js";
92
99
  import { assessDeltaRecheck } from "./review/deltaRecheck.js";
93
- import { isAcceptedTaskReviewBaseline } from "./review/reviewAcceptance.js";
100
+ import { isCompletedTaskReviewEvidence } from "./review/reviewAcceptance.js";
94
101
  import { NodeGitWorkspace } from "./repository/gitWorkspace.js";
95
- import { currentWorkItemExecutionGroup, workItemExecutionGroupById } from "./workItem/workItem.js";
102
+ import { currentWorkItemExecutionGroup } from "./workItem/workItem.js";
96
103
  const VERSION = YUI_VERSION;
97
104
  const exactControlInvocation = extractExactControlArgument(process.argv.slice(2));
98
105
  const taskFinalReviewInvocation = extractTaskFinalReviewRequest(exactControlInvocation.args);
@@ -134,8 +141,7 @@ export async function main() {
134
141
  const routedForFence = args.length === 0 ? undefined : routeInvocation(args);
135
142
  const managedInvocation = process.env.YUI_SESSION_SCOPE === "task"
136
143
  || process.env.YUI_SESSION_SCOPE === "global"
137
- || process.env[YUI_CONTROL_PLANE_DESCRIPTOR] !== undefined
138
- || process.env[YUI_TASK_RUNTIME_DESCRIPTOR] !== undefined;
144
+ || process.env[YUI_CONTROL_PLANE_DESCRIPTOR] !== undefined;
139
145
  const homeFreeInvocation = args.length === 0
140
146
  || (args[0] === "version" && args.length === 1)
141
147
  || routedForFence?.kind === "help"
@@ -298,7 +304,6 @@ export async function main() {
298
304
  return;
299
305
  }
300
306
  if (args[0] === "upgrade") {
301
- // Admission diagnostic only: never quiesces, repairs, or switches storage.
302
307
  const result = await runUpgradeCommand(args.slice(1), home);
303
308
  process.exitCode = result.exitCode;
304
309
  emit(result.output, false, result.data);
@@ -318,7 +323,7 @@ export async function main() {
318
323
  if (args[1] === "agent-host" && args.length === 4) {
319
324
  process.exitCode = await runAgentHost({
320
325
  home,
321
- launchId: args[2],
326
+ runtimeGenerationId: args[2],
322
327
  ticket: args[3]
323
328
  });
324
329
  return;
@@ -553,7 +558,7 @@ export async function main() {
553
558
  emit("Cancelled.");
554
559
  return;
555
560
  }
556
- await preflightAgentConfigurationMutation(resolved, store, catalogs);
561
+ const validateAgentConfiguration = await preflightAgentConfigurationMutation(resolved, store, catalogs);
557
562
  const executor = new NodeCommandExecutor();
558
563
  const tmux = new TmuxManager(resolveTmuxBin(store.getConfig().tmuxBin), executor, {
559
564
  yuiHome: home,
@@ -682,7 +687,7 @@ export async function main() {
682
687
  return;
683
688
  }
684
689
  if (domain === "profile") {
685
- const result = runProfileCommand(resolved.slice(2), store);
690
+ const result = runProfileCommand(resolved.slice(2), store, () => new Date(), validateAgentConfiguration === undefined ? {} : { validateAgentConfiguration });
686
691
  emit(result.output, false, result.data);
687
692
  return;
688
693
  }
@@ -820,6 +825,27 @@ export async function main() {
820
825
  emit(result.output, false, result.data);
821
826
  return;
822
827
  }
828
+ if (resolved[1] === "publication" && resolved[2] === "verify") {
829
+ const result = await runTaskPublicationVerifyCommand(resolved.slice(3), store, {
830
+ verifiers: {
831
+ github: createGitHubCliPublicationVerifier({
832
+ environmentPath: process.env.PATH
833
+ }),
834
+ gitlab: createGitLabCliPublicationVerifier({
835
+ environmentPath: process.env.PATH
836
+ })
837
+ },
838
+ candidateForTask: async (taskId) => {
839
+ const status = store.getTask(taskId)?.status;
840
+ return status === "active" || status === "retired"
841
+ ? snapshotActualTaskReviewCandidate(taskId, store, workspacePreparer)
842
+ : null;
843
+ },
844
+ environment: process.env
845
+ });
846
+ emit(result.output, false, result.data);
847
+ return;
848
+ }
823
849
  if (resolved[1] === "overlap") {
824
850
  const result = await runTaskOverlapCommand(resolved.slice(2), store);
825
851
  emit(result.output, false, result.data);
@@ -877,6 +903,7 @@ export async function main() {
877
903
  throw usageError("Task work capture usage: yui task work capture <task>/<work>.");
878
904
  }
879
905
  const reference = cliWorkItemReference(workItemId, process.env);
906
+ taskActor(process.env, reference.taskId);
880
907
  const changeSets = await new WorkItemChangeSetManager(store).capture(reference.taskId, reference.localId, taskFinalReviewContract === undefined
881
908
  ? {}
882
909
  : { taskFinalReviewContract });
@@ -968,12 +995,23 @@ export async function main() {
968
995
  });
969
996
  return;
970
997
  }
998
+ let archiveRemoteDeliveryProof;
999
+ let archiveTaskReviewCandidate;
971
1000
  if (resolved[1] === "archive") {
972
- const { taskId, disposition } = validateTaskArchiveRequest(resolved.slice(2), store, { runtime, environment: process.env, yuiHome: home });
1001
+ const { taskId, disposition, forceUnverified } = validateTaskArchiveRequest(resolved.slice(2), store, {
1002
+ runtime,
1003
+ environment: process.env,
1004
+ yuiHome: home
1005
+ });
973
1006
  const task = store.getTask(taskId);
974
1007
  if (task === null)
975
1008
  throw new Error(`Task disappeared after archive validation: ${taskId}.`);
976
1009
  if (task.status !== "archived") {
1010
+ if (disposition === "integrated") {
1011
+ archiveTaskReviewCandidate = await actualTaskReviewCandidateForTaskCommand(resolved, store, workspacePreparer, process.env);
1012
+ archiveRemoteDeliveryProof = createTaskRemoteDeliveryProof(store, task, archiveTaskReviewCandidate ?? null);
1013
+ assertTaskRemoteDeliveryIntegrated(archiveRemoteDeliveryProof.delivery, { forceUnverified });
1014
+ }
977
1015
  const workItemIds = store.listManagedWorkspaces(task.id)
978
1016
  .flatMap(({ owner }) => owner.type === "work-item" ? [owner.workItemId] : []);
979
1017
  for (const workItemId of workItemIds) {
@@ -1012,6 +1050,7 @@ export async function main() {
1012
1050
  }
1013
1051
  }
1014
1052
  }
1053
+ assertWorkItemExecutionDependenciesForCommand(resolved, store, process.env);
1015
1054
  if (resolved[1] === "work" && resolved[2] === "dispatch") {
1016
1055
  const workItemId = resolved[3];
1017
1056
  const reference = workItemId === undefined
@@ -1021,11 +1060,19 @@ export async function main() {
1021
1060
  ? null
1022
1061
  : store.getWorkItem(reference.taskId, reference.localId);
1023
1062
  const task = item === null ? null : store.getTask(item.taskId);
1063
+ if (item !== null && task !== null) {
1064
+ // Authority and pure Lane-shape checks precede every physical or
1065
+ // durable workspace preparation performed for dispatch.
1066
+ taskActor(process.env, task.id);
1067
+ if (item.assignee !== undefined) {
1068
+ workItemDispatchLanePlan(resolved, store, item);
1069
+ }
1070
+ }
1024
1071
  // A rejected Candidate starts a new execution iteration. Release every
1025
1072
  // terminal Lane Role runtime before preparing the new Lane workspaces;
1026
1073
  // durable Turns, Groups, Candidates, and workspace owners remain intact.
1027
1074
  if (item?.status === "failed"
1028
- && currentWorkItemExecutionGroup(item)?.resolution !== undefined) {
1075
+ && currentWorkItemExecutionGroup(item)?.lanes.every(({ disposition }) => disposition !== "open")) {
1029
1076
  await workspaceCoordinator.cleanupWorkItemRuntime(item.taskId, item.id);
1030
1077
  }
1031
1078
  // Every Task needs an authoritative runtime owner before dispatch. A
@@ -1078,7 +1125,9 @@ export async function main() {
1078
1125
  return;
1079
1126
  }
1080
1127
  if (resolved[1] === "upstream") {
1081
- const result = await runTaskUpstreamCommand(resolved.slice(2), store);
1128
+ const result = await runTaskUpstreamCommand(resolved.slice(2), store, home, {
1129
+ environment: process.env
1130
+ });
1082
1131
  emit(result.output, false, result.data);
1083
1132
  return;
1084
1133
  }
@@ -1116,19 +1165,24 @@ export async function main() {
1116
1165
  // Keep completion offline by default. An explicit refresh is the only
1117
1166
  // path that may fetch and reconcile a moved remote baseline.
1118
1167
  if (refreshRemote) {
1119
- await reconcileTaskRemoteBaselines(resolved[2], store, home, { environment: process.env, jobPort: createControllerIntegrationJobPort(home, { environment: process.env }) });
1168
+ const reconciled = await reconcileTaskRemoteBaselines(resolved[2], store, home, { environment: process.env, jobPort: createControllerIntegrationJobPort(home, { environment: process.env }) });
1169
+ if (reconciled.length > 0) {
1170
+ const updates = reconciled.map((entry) => (`${entry.projectId}: ${entry.fromCommit} -> ${entry.toCommit} `
1171
+ + `(Integration ${entry.integrationId})`)).join("; ");
1172
+ throw usageError(`Remote baseline reconciliation advanced Task ${resolved[2]} (${updates}). `
1173
+ + "The Task remains active so the Leader can inspect the new authoritative head, "
1174
+ + "decide how prior Review evidence applies, and retry task complete.");
1175
+ }
1120
1176
  }
1121
1177
  }
1122
1178
  }
1123
1179
  let releaseReviewHandoverLock;
1124
1180
  if ((resolved[1] === "review"
1125
- && ["request", "force-fresh", "retry"].includes(resolved[2] ?? ""))
1181
+ && ["request", "retry"].includes(resolved[2] ?? ""))
1126
1182
  || resolved[1] === "complete") {
1127
1183
  const handoverLock = acquireHandoverLock(home);
1128
1184
  releaseReviewHandoverLock = handoverLock.release;
1129
1185
  }
1130
- let candidateMaterialization;
1131
- let candidateMaterializationCommitted = false;
1132
1186
  try {
1133
1187
  const preparedLanes = await prepareExecutionLaneWorkspacesForCommand(resolved, store, workspacePreparer, process.env);
1134
1188
  if (preparedLanes !== undefined) {
@@ -1136,12 +1190,11 @@ export async function main() {
1136
1190
  laneDispatchRelease = preparedLanes.release;
1137
1191
  laneDispatchProjectPaths = preparedLanes.projectPaths;
1138
1192
  }
1139
- candidateMaterialization = await candidateMaterializationForTaskCommand(resolved, store, workspacePreparer, process.env, taskFinalReviewContract);
1140
- const candidateGitSnapshot = candidateMaterialization === undefined
1141
- ? await candidateSnapshotForTaskCommand(resolved, store, workspacePreparer, process.env, taskFinalReviewContract)
1142
- : candidateMaterialization.snapshot;
1193
+ const candidateGitSnapshot = await candidateSnapshotForTaskCommand(resolved, store, workspacePreparer, process.env, taskFinalReviewContract);
1143
1194
  const directTaskMainSnapshot = await directTaskMainSnapshotForTaskCommand(resolved, store, workspacePreparer, process.env, taskFinalReviewContract);
1144
- const actualTaskReviewCandidate = await actualTaskReviewCandidateForTaskCommand(resolved, store, workspacePreparer, process.env);
1195
+ const actualTaskReviewCandidate = archiveRemoteDeliveryProof === undefined
1196
+ ? await actualTaskReviewCandidateForTaskCommand(resolved, store, workspacePreparer, process.env)
1197
+ : archiveTaskReviewCandidate;
1145
1198
  const deltaRecheckPreflight = await deltaRecheckPreflightForTaskCommand(resolved.slice(1), store, actualTaskReviewCandidate);
1146
1199
  // Physical preparation may precede the durable write, but Task status,
1147
1200
  // workspace identity/cwd, and ManagedWorkspace ownership are adopted by
@@ -1168,9 +1221,6 @@ export async function main() {
1168
1221
  : { completionPublishedTreeProof }),
1169
1222
  ...(workItemIntegrationProof === undefined ? {} : { workItemIntegrationProof }),
1170
1223
  ...(candidateGitSnapshot === undefined ? {} : { candidateGitSnapshot }),
1171
- ...(candidateMaterialization === undefined
1172
- ? {}
1173
- : { candidateWorkspace: candidateMaterialization.workspace ?? null }),
1174
1224
  ...(executionLaneWorkspaces === undefined ? {} : { executionLaneWorkspaces }),
1175
1225
  ...(taskWorkspaceActivation === undefined ? {} : { taskWorkspaceActivation }),
1176
1226
  ...(laneDispatchProjectPaths === undefined ? {} : { laneDispatchProjectPaths }),
@@ -1178,10 +1228,16 @@ export async function main() {
1178
1228
  ...(actualTaskReviewCandidate === undefined
1179
1229
  ? {}
1180
1230
  : { actualTaskReviewCandidate }),
1231
+ ...(archiveRemoteDeliveryProof === undefined
1232
+ ? {}
1233
+ : { archiveRemoteDeliveryProof }),
1181
1234
  ...(deltaRecheckPreflight === undefined
1182
1235
  ? {}
1183
1236
  : { deltaRecheckPreflight }),
1184
- ...(taskRetirementProof === undefined ? {} : { taskRetirementProof })
1237
+ ...(taskRetirementProof === undefined ? {} : { taskRetirementProof }),
1238
+ ...(validateAgentConfiguration === undefined
1239
+ ? {}
1240
+ : { validateAgentConfiguration })
1185
1241
  });
1186
1242
  // The dispatch transaction has now adopted (or rejected) the prepared
1187
1243
  // Lane workspaces. Release the held fence so later output/review
@@ -1190,9 +1246,6 @@ export async function main() {
1190
1246
  laneDispatchRelease();
1191
1247
  laneDispatchRelease = undefined;
1192
1248
  }
1193
- // The command transaction has now durably submitted the Candidate. Any
1194
- // later output/review handling must not roll back its Git snapshot.
1195
- candidateMaterializationCommitted = candidateMaterialization !== undefined;
1196
1249
  if (result.kind === "output") {
1197
1250
  const requestedRound = reviewRoundFromCommandData(result.data);
1198
1251
  const persistedRequestedRound = requestedRound === undefined
@@ -1206,7 +1259,9 @@ export async function main() {
1206
1259
  const reviewDispatchNeeded = requestedRound?.status === "pending"
1207
1260
  || (requestedRound?.status === "running"
1208
1261
  && resumesReviewDispatch
1209
- && persistedRequestedRound?.executionGroup?.lanes.some((lane) => (lane.status === "pending" && lane.turnId === undefined)) === true);
1262
+ && persistedRequestedRound?.executionGroup?.lanes.some((lane) => (lane.disposition === "open"
1263
+ && (lane.currentTurnId === undefined
1264
+ || store.getTurn(requestedRound.taskId, lane.currentTurnId)?.status === "failed"))) === true);
1210
1265
  if (reviewDispatchNeeded) {
1211
1266
  try {
1212
1267
  const workspace = requestedRound.status === "running"
@@ -1242,17 +1297,15 @@ export async function main() {
1242
1297
  })
1243
1298
  });
1244
1299
  reviewOutput = run === null
1245
- ? `Review ${requestedRound.id} retained pending by Resource Broker\n`
1300
+ ? `Review ${requestedRound.id} remains running\n`
1246
1301
  : `Review queued as ${requestedRound.id} (${run.id})\n`;
1247
1302
  reviewData = {
1248
1303
  reviewRequest: run === null
1249
1304
  ? {
1250
- kind: "busy",
1305
+ kind: "running",
1251
1306
  reviewerRoleName: requestedRound.reviewerRoleName,
1252
- phase: "resource-broker",
1253
1307
  activeReviewRoundId: requestedRound.id,
1254
- retryable: true,
1255
- retryAfterSeconds: 5
1308
+ retryable: false
1256
1309
  }
1257
1310
  : {
1258
1311
  kind: "started",
@@ -1311,7 +1364,7 @@ export async function main() {
1311
1364
  agentId: result.agentId,
1312
1365
  adapterId: result.adapterId,
1313
1366
  nativeSessionId: result.nativeSessionId,
1314
- ...(result.launchId === undefined ? {} : { launchId: result.launchId }),
1367
+ ...(result.runtimeGenerationId === undefined ? {} : { runtimeGenerationId: result.runtimeGenerationId }),
1315
1368
  sessionUpdatedAt: result.sessionUpdatedAt
1316
1369
  });
1317
1370
  }
@@ -1405,12 +1458,7 @@ export async function main() {
1405
1458
  return;
1406
1459
  }
1407
1460
  catch (error) {
1408
- if (candidateMaterialization !== undefined && !candidateMaterializationCommitted) {
1409
- await workspacePreparer.restoreExecutionGroupCandidateMaterialization(candidateMaterialization);
1410
- }
1411
- if (!candidateMaterializationCommitted) {
1412
- await workspacePreparer.discardUnadoptedExecutionLaneWorkspaces(executionLaneWorkspaces);
1413
- }
1461
+ await workspacePreparer.discardUnadoptedExecutionLaneWorkspaces(executionLaneWorkspaces);
1414
1462
  if (laneDispatchRelease !== undefined) {
1415
1463
  laneDispatchRelease();
1416
1464
  laneDispatchRelease = undefined;
@@ -1465,10 +1513,9 @@ async function preflightManagedTaskControlPlane() {
1465
1513
  throw new Error(taskFinalReviewInvocation.error);
1466
1514
  }
1467
1515
  const serializedControl = process.env[YUI_CONTROL_PLANE_DESCRIPTOR];
1468
- const serializedRuntime = process.env[YUI_TASK_RUNTIME_DESCRIPTOR];
1469
- const exactAgentRuntime = serializedControl !== undefined || serializedRuntime !== undefined;
1516
+ const exactAgentRuntime = serializedControl !== undefined;
1470
1517
  if (process.env.YUI_SESSION_SCOPE === "task" && !exactAgentRuntime) {
1471
- throw new Error("Exact control-plane invocation requires both frozen descriptors in a managed Task runtime.");
1518
+ throw new Error("Exact control-plane invocation requires its frozen descriptor in a managed Task runtime.");
1472
1519
  }
1473
1520
  if (!exactAgentRuntime) {
1474
1521
  if (exactControlInvocation.digest !== undefined
@@ -1483,7 +1530,7 @@ async function preflightManagedTaskControlPlane() {
1483
1530
  if (process.env.YUI_SESSION_SCOPE !== "task") {
1484
1531
  throw new Error("Exact Task control-plane invocation requires a managed Task runtime.");
1485
1532
  }
1486
- if (serializedControl === undefined || serializedRuntime === undefined) {
1533
+ if (serializedControl === undefined) {
1487
1534
  throw new Error("Exact control-plane invocation is required for this managed Task runtime.");
1488
1535
  }
1489
1536
  const control = parseExactControlPlaneDescriptor(serializedControl);
@@ -1522,20 +1569,16 @@ async function preflightManagedTaskControlPlane() {
1522
1569
  commandControl = control;
1523
1570
  digest = frozenDigest;
1524
1571
  }
1525
- const runtime = assertExactTaskRuntimeEnvironment(serializedRuntime, process.env, frozenDigest, control.yuiHome);
1526
- const runtimeDriverCallback = args[0] === "internal"
1527
- && args[1] === "runtime-hook"
1528
- && process.env.YUI_DRIVER_ID !== undefined
1529
- ? builtinAgentDriverRegistry().require(process.env.YUI_DRIVER_ID)
1530
- : undefined;
1531
1572
  const verifiedStore = openCurrentTaskStore(control.yuiHome);
1532
- // Runtime Hooks have their own event-aware fence, including the valid case
1533
- // where a Provider terminal arrives after its Yui Turn has completed and a
1534
- // later wake is pending. Ordinary managed commands still require the exact
1535
- // current mutable runtime before routing.
1536
- if (runtimeDriverCallback === undefined) {
1537
- assertExactTaskRuntimeState(runtime, verifiedStore);
1538
- }
1573
+ // One authority for "may this process act as this Task Role?". Yui's own
1574
+ // internal callbacks run in the Host process and authenticate through the
1575
+ // exact control-plane digest verified above; an Agent command proves it is
1576
+ // the Role's current runtime with its per-Session caller key. Nothing here
1577
+ // gates on the current Turn: which Turn is active is durable state that the
1578
+ // command needing it reads, never a fact frozen into a process environment.
1579
+ const runtime = internalCallback
1580
+ ? undefined
1581
+ : requireManagedTaskCaller(verifiedStore, process.env);
1539
1582
  const request = taskFinalReviewInvocation.request;
1540
1583
  if (request === undefined) {
1541
1584
  return {
@@ -1544,7 +1587,7 @@ async function preflightManagedTaskControlPlane() {
1544
1587
  verifiedStore
1545
1588
  };
1546
1589
  }
1547
- if (runtime.roleName !== "leader") {
1590
+ if (runtime === undefined || runtime.roleName !== "leader") {
1548
1591
  throw new Error("Only the exact Task Leader invocation may establish a final-review contract.");
1549
1592
  }
1550
1593
  if (request.taskId !== runtime.taskId) {
@@ -1690,6 +1733,25 @@ function cliTaskRecordReference(value, kind, environment) {
1690
1733
  throw usageError(error instanceof Error ? error.message : String(error));
1691
1734
  }
1692
1735
  }
1736
+ function assertWorkItemExecutionDependenciesForCommand(args, store, environment) {
1737
+ let item;
1738
+ if (args[0] === "task" && args[1] === "work" && args[2] === "dispatch"
1739
+ && args[3] !== undefined) {
1740
+ const reference = cliWorkItemReference(args[3], environment);
1741
+ item = store.getWorkItem(reference.taskId, reference.localId) ?? undefined;
1742
+ }
1743
+ else if (args[0] === "task" && args[1] === "turn" && args[2] === "retry"
1744
+ && args[3] !== undefined) {
1745
+ const reference = cliTaskRecordReference(args[3], "turn", environment);
1746
+ const turn = store.getTurn(reference.taskId, reference.localId);
1747
+ item = turn?.purpose === "execution" && turn.workItemId !== undefined
1748
+ ? store.getWorkItem(turn.taskId, turn.workItemId) ?? undefined
1749
+ : undefined;
1750
+ }
1751
+ if (item === undefined)
1752
+ return;
1753
+ assertWorkItemDependenciesCompletedForCommand(store, item);
1754
+ }
1693
1755
  async function candidateSnapshotForTaskCommand(args, store, preparer, environment, taskFinalReviewContract) {
1694
1756
  if (args[0] !== "task")
1695
1757
  return undefined;
@@ -1718,179 +1780,79 @@ async function candidateSnapshotForTaskCommand(args, store, preparer, environmen
1718
1780
  }
1719
1781
  return preparer.snapshotCandidateWorkspace(workspace);
1720
1782
  }
1721
- if (args[1] === "work" && args[2] === "group" && args[3] === "resolve"
1722
- && args[4] !== undefined) {
1723
- // The grouped accept path snapshots only after all selected Lane outputs
1724
- // have been merged by candidateMaterializationForTaskCommand.
1725
- return undefined;
1726
- }
1727
1783
  return undefined;
1728
1784
  }
1729
- async function candidateMaterializationForTaskCommand(args, store, preparer, environment, taskFinalReviewContract) {
1730
- if (args[0] !== "task" || args[1] !== "work" || args[2] !== "group"
1731
- || args[3] !== "resolve" || args[4] === undefined
1732
- || !args.includes("--decision")
1733
- || args[args.indexOf("--decision") + 1] !== "accept")
1734
- return undefined;
1735
- const reference = cliWorkItemReference(args[4], environment);
1736
- const item = store.getWorkItem(reference.taskId, reference.localId);
1737
- const group = item === null || item === undefined
1738
- ? undefined
1739
- : currentWorkItemExecutionGroup(item);
1740
- if (item === null || item === undefined || group === undefined)
1741
- return undefined;
1742
- if (group.stage !== undefined && group.stage.stage !== "resolve")
1743
- return undefined;
1744
- // Early termination first stops never-started spend. Active stragglers are
1745
- // deliberately retained, so the command records that stop and leaves the
1746
- // group unresolved. Do not merge Lane output into the WorkItem Candidate
1747
- // until those active Lanes settle and the Leader resolves the group again.
1748
- if (args.includes("--early-stop")
1749
- && group.lanes.some(({ status }) => status === "running"))
1750
- return undefined;
1751
- const selected = args.flatMap((value, index) => value === "--lane" && args[index + 1] !== undefined ? [args[index + 1]] : []);
1752
- const materializedLaneIds = selected.length === 0
1753
- ? group.lanes
1754
- .filter((lane) => lane.status === "completed")
1755
- .map(({ id }) => id)
1756
- : selected;
1757
- if (group.stage?.convergence !== undefined
1758
- && group.stage.stage === "resolve"
1759
- && materializedLaneIds.length !== 1) {
1760
- throw usageError("Candidate convergence Resolve must select exactly one Lane before materialization.");
1761
- }
1762
- try {
1763
- return await preparer.materializeExecutionGroupCandidate(item.taskId, item.id, group.id, materializedLaneIds);
1764
- }
1765
- catch (error) {
1766
- throw usageError(error instanceof Error ? error.message : String(error));
1767
- }
1768
- }
1769
1785
  async function prepareExecutionLaneWorkspacesForCommand(args, store, preparer, environment) {
1770
- const isDispatch = args[0] === "task" && args[1] === "work" && args[2] === "dispatch" && args[3] !== undefined;
1771
- const isRetry = args[0] === "task" && args[1] === "turn" && args[2] === "retry" && args[3] !== undefined;
1772
- if (!isDispatch && !isRetry)
1786
+ const isDispatch = args[0] === "task"
1787
+ && args[1] === "work"
1788
+ && args[2] === "dispatch"
1789
+ && args[3] !== undefined;
1790
+ if (!isDispatch)
1773
1791
  return undefined;
1774
- const itemRef = isDispatch
1775
- ? cliWorkItemReference(args[3], environment)
1776
- : null;
1777
- const item = itemRef === null
1778
- ? (() => {
1779
- const runRef = cliTaskRecordReference(args[3], "turn", environment);
1780
- const run = store.getTurn(runRef.taskId, runRef.localId);
1781
- return run?.workItemId === undefined ? null : store.getWorkItem(run.taskId, run.workItemId);
1782
- })()
1783
- : store.getWorkItem(itemRef.taskId, itemRef.localId);
1784
- if (item === null)
1792
+ const itemRef = cliWorkItemReference(args[3], environment);
1793
+ const item = store.getWorkItem(itemRef.taskId, itemRef.localId);
1794
+ if (item === null || item.assignee === undefined)
1785
1795
  return undefined;
1786
- const retryRun = isRetry
1787
- ? store.getTurn(item.taskId, cliTaskRecordReference(args[3], "turn", environment).localId)
1788
- : null;
1789
- const currentGroup = currentWorkItemExecutionGroup(item);
1790
- const group = retryRun?.executionGroupId === undefined
1791
- ? (isDispatch && currentGroup?.resolution !== undefined ? undefined : currentGroup)
1792
- : workItemExecutionGroupById(item, retryRun.executionGroupId);
1793
- const roles = args.flatMap((value, index) => (value === "--lane-role" && args[index + 1] !== undefined
1794
- ? [args[index + 1]]
1795
- : []));
1796
- const requestedStrategy = (() => {
1797
- const index = args.indexOf("--strategy");
1798
- const value = index < 0 ? undefined : args[index + 1];
1799
- if (value === undefined)
1800
- return undefined;
1801
- const fixed = /^fixed:([1-9]\d*)$/u.exec(value);
1802
- if (fixed !== null)
1803
- return { mode: "fixed", count: Number(fixed[1]) };
1804
- const adaptive = /^adaptive:([1-9]\d*)$/u.exec(value);
1805
- if (adaptive !== null)
1806
- return { mode: "adaptive", max: Number(adaptive[1]) };
1807
- throw usageError(`Invalid execution strategy: ${value}.`);
1808
- })();
1809
- const retryLaneId = isRetry
1810
- ? store.getTurn(item.taskId, cliTaskRecordReference(args[3], "turn", environment).localId)?.executionLaneId
1811
- : undefined;
1812
- const plan = normalizedExecutionLanePlan({
1813
- assignee: item.assignee ?? "",
1814
- requestedRoles: roles,
1815
- requestedStrategy,
1816
- existingGroup: group === undefined ? undefined : group,
1817
- resolvedRetryGroup: isDispatch
1818
- ? resolvedExecutionStageRetryGroup(currentGroup)
1819
- : undefined,
1820
- status: item.status,
1821
- nextGroupId: `execution-group-${store.peekNextTurnId(item.taskId)}`,
1822
- retryLaneId,
1823
- phase: isRetry ? "retry" : "dispatch"
1824
- });
1825
- const laneRoles = plan.roles;
1826
- if (!isRetry && laneRoles.length === 0) {
1827
- throw usageError("At least one --lane-role is required when expanding an ExecutionGroup.");
1828
- }
1829
- if (group !== undefined && requestedStrategy !== undefined) {
1830
- const same = group.strategy.mode === requestedStrategy.mode
1831
- && (group.strategy.mode === "fixed"
1832
- ? requestedStrategy.mode === "fixed" && group.strategy.count === requestedStrategy.count
1833
- : requestedStrategy.mode === "adaptive" && group.strategy.max === requestedStrategy.max);
1834
- if (!same)
1835
- throw usageError(`ExecutionGroup strategy is frozen: ${group.id}.`);
1836
- }
1837
- const laneCount = plan.requestedCount;
1838
- const adaptive = plan.strategy.mode === "adaptive";
1839
- const needsIsolation = adaptive || laneCount > 1 || (group?.lanes.length ?? 0) > 1;
1840
- if (!needsIsolation)
1796
+ const plan = workItemDispatchLanePlan(args, store, item);
1797
+ if (plan.roles.length === 0)
1841
1798
  return undefined;
1842
- const groupId = group?.id ?? `execution-group-${store.peekNextTurnId(item.taskId)}`;
1843
- const laneIds = plan.laneIds;
1844
- // A new Group's Lanes are not yet durable, so their worktrees would be
1845
- // unadopted between preparation and the dispatch transaction. Hold ONE
1846
- // per-Project maintenance fence across both, so a project migrate cannot
1847
- // switch the catalog in that gap and strand a Lane on the external
1848
- // checkout. An existing Group's Lanes are adopted inside their own fence,
1849
- // so no outer fence is held.
1850
- const held = group === undefined
1851
- ? preparer.acquireTaskProjectMaintenanceLocks(item.taskId)
1852
- : undefined;
1799
+ for (const roleName of plan.roles) {
1800
+ if (store.getRole(item.taskId, roleName) === null) {
1801
+ throw usageError(`Task Role not found: ${item.taskId}/${roleName}.`);
1802
+ }
1803
+ if (store.getActiveTurn(item.taskId, roleName) !== null) {
1804
+ throw usageError(`${item.taskId}/${roleName} already has an active turn.`);
1805
+ }
1806
+ }
1807
+ const held = preparer.acquireTaskProjectMaintenanceLocks(item.taskId);
1853
1808
  const map = new Map();
1854
1809
  try {
1855
- let projectPaths;
1856
- if (held !== undefined) {
1857
- const paths = new Map();
1858
- for (const { projectId } of held.current.projectBindings) {
1859
- const project = store.getProject(projectId);
1860
- if (project === null)
1861
- throw new Error(`Project not found: ${projectId}.`);
1862
- paths.set(projectId, project.path);
1863
- }
1864
- projectPaths = paths;
1810
+ const projectPaths = new Map();
1811
+ for (const { projectId } of held.current.projectBindings) {
1812
+ const project = store.getProject(projectId);
1813
+ if (project === null)
1814
+ throw new Error(`Project not found: ${projectId}.`);
1815
+ projectPaths.set(projectId, project.path);
1865
1816
  }
1866
- for (const laneId of laneIds.filter((value) => value.length > 0)) {
1867
- map.set(laneId, await preparer.prepareExecutionLaneWorkspace(item.taskId, groupId, laneId, {
1868
- purpose: "execution",
1869
- workItemId: item.id
1870
- }, held === undefined ? undefined : { current: held.current }));
1817
+ const source = item.assignee === "leader"
1818
+ ? store.getTaskWorkspace(item.taskId)
1819
+ : store.getWorkItemWorkspace(item.taskId, item.id);
1820
+ if (source === null) {
1821
+ throw usageError(`Execution Lane source workspace is not ready: ${item.id}.`);
1871
1822
  }
1872
- return { workspaces: map, release: held?.release, projectPaths };
1823
+ const inputHeads = await preparer.snapshotExecutionLaneInputHeads(source, item.writeProjectIds);
1824
+ for (const laneId of plan.laneIds) {
1825
+ map.set(laneId, await preparer.prepareExecutionLaneWorkspace(item.taskId, plan.groupId, laneId, { purpose: "execution", workItemId: item.id, inputHeads }, { current: held.current }));
1826
+ }
1827
+ return { workspaces: map, release: held.release, projectPaths };
1873
1828
  }
1874
1829
  catch (error) {
1875
- // Compensate (discard unadopted Lane worktrees) BEFORE releasing the
1876
- // fence: a concurrent project migrate must not switch the catalog while
1877
- // external-backed worktrees are still identifiable for removal.
1878
1830
  await preparer.discardUnadoptedExecutionLaneWorkspaces(map);
1879
- if (held !== undefined)
1880
- held.release();
1831
+ held.release();
1881
1832
  throw error;
1882
1833
  }
1883
1834
  }
1835
+ function workItemDispatchLanePlan(args, store, item) {
1836
+ if (item.assignee === undefined) {
1837
+ throw usageError(`Work Item has no Task Role assignee: ${item.id}.`);
1838
+ }
1839
+ const roles = args.flatMap((value, index) => (value === "--lane-role" && args[index + 1] !== undefined
1840
+ ? [args[index + 1]]
1841
+ : []));
1842
+ const groupId = `execution-group-${store.peekNextTurnId(item.taskId)}`;
1843
+ return {
1844
+ groupId,
1845
+ ...planReplicatedWorkItemLanes(item.assignee, roles, groupId)
1846
+ };
1847
+ }
1884
1848
  async function prepareReviewLaneWorkspaces(taskId, reviewRoundId, store, preparer) {
1885
1849
  const round = store.getReviewRound(taskId, reviewRoundId);
1886
1850
  const group = round?.executionGroup;
1887
1851
  if (round === null || round === undefined || group === undefined)
1888
1852
  return undefined;
1889
- if (group.lanes.length < 2 && group.strategy.mode !== "adaptive")
1890
- return undefined;
1891
1853
  const map = new Map();
1892
1854
  try {
1893
- for (const lane of group.lanes.filter((candidate) => candidate.status === "pending" || candidate.status === "running")) {
1855
+ for (const lane of group.lanes.filter(({ disposition }) => disposition === "open")) {
1894
1856
  map.set(lane.id, await preparer.prepareExecutionLaneWorkspace(taskId, group.id, lane.id, {
1895
1857
  purpose: "review",
1896
1858
  reviewRoundId
@@ -1963,15 +1925,6 @@ async function actualTaskReviewCandidateForTaskCommand(args, store, preparer, en
1963
1925
  taskId = reference.taskId;
1964
1926
  }
1965
1927
  }
1966
- else if (args[1] === "review"
1967
- && args[2] === "force-fresh"
1968
- && args[3] !== undefined) {
1969
- const reference = cliTaskRecordReference(args[3], "reviewRound", environment);
1970
- const round = store.getReviewRound(reference.taskId, reference.localId);
1971
- if (round !== null && (round.scope ?? "work-item") === "task") {
1972
- taskId = reference.taskId;
1973
- }
1974
- }
1975
1928
  else if (args[1] === "work"
1976
1929
  && args[2] === "review"
1977
1930
  && args[3] === "retry"
@@ -1996,12 +1949,21 @@ async function actualTaskReviewCandidateForTaskCommand(args, store, preparer, en
1996
1949
  taskId = reference.taskId;
1997
1950
  }
1998
1951
  }
1999
- else if ((args[1] === "context" || args[1] === "next-action")
1952
+ else if (args[1] === "archive"
1953
+ && args[2] !== undefined
1954
+ && args.includes("--integrated")) {
1955
+ taskId = store.getTask(args[2])?.id;
1956
+ }
1957
+ else if ((args[1] === "show"
1958
+ || args[1] === "context"
1959
+ || args[1] === "next-action"
1960
+ || args[1] === "remote-delivery")
2000
1961
  && args[2] !== undefined) {
2001
1962
  taskId = store.getTask(args[2])?.id;
2002
1963
  decisionSupportRead = true;
2003
1964
  }
2004
- if (taskId === undefined || store.getTask(taskId)?.status !== "active")
1965
+ const status = taskId === undefined ? undefined : store.getTask(taskId)?.status;
1966
+ if (taskId === undefined || (status !== "active" && status !== "retired"))
2005
1967
  return undefined;
2006
1968
  try {
2007
1969
  return await snapshotActualTaskReviewCandidate(taskId, store, preparer);
@@ -2019,6 +1981,9 @@ async function snapshotActualTaskReviewCandidate(taskId, store, preparer) {
2019
1981
  if (task.projectBindings.length === 0) {
2020
1982
  throw usageError(`Final Task Review requires a Project-backed Task: ${task.id}.`);
2021
1983
  }
1984
+ // Reconcile the durable currentCommit facts from the authoritative Task
1985
+ // clones before freezing a completion/review candidate.
1986
+ await preparer.prepareTaskWorkspace(task.id);
2022
1987
  const workspace = store.getTaskWorkspace(task.id);
2023
1988
  if (workspace === null
2024
1989
  || workspace.owner.type !== "task"
@@ -2068,19 +2033,23 @@ async function deltaRecheckPreflightForTaskCommand(args, store, actualTaskReview
2068
2033
  // a head (a full Review or an equivalent-and-accepted delta). A
2069
2034
  // non-accepted delta cannot be the base for a new delta.
2070
2035
  const previous = [...store.listReviewRounds(task.id)]
2071
- .filter((round) => isAcceptedTaskReviewBaseline(store, round))
2036
+ .filter((round) => isCompletedTaskReviewEvidence(store, round))
2072
2037
  .sort((left, right) => left.id.localeCompare(right.id, undefined, { numeric: true }))
2073
2038
  .at(-1);
2074
2039
  if (previous === undefined) {
2075
2040
  throw usageError("Delta-recheck requires a previous completed Task-final Review that accepted a head.");
2076
2041
  }
2077
2042
  const repositoryPaths = {};
2043
+ const taskWorkspace = store.getTaskWorkspace(task.id);
2044
+ if (taskWorkspace === null || taskWorkspace.owner.type !== "task") {
2045
+ throw usageError(`Task has no authoritative main workspace: ${task.id}.`);
2046
+ }
2078
2047
  for (const candidateProject of actualTaskReviewCandidate.projects) {
2079
- const project = store.getProject(candidateProject.projectId);
2080
- if (project === null) {
2081
- throw usageError(`Delta-recheck Project not found: ${candidateProject.projectId}.`);
2048
+ const entry = workspaceProjectEntry(taskWorkspace, candidateProject.projectId);
2049
+ if (entry === undefined) {
2050
+ throw usageError(`Delta-recheck Task workspace Project not found: ${candidateProject.projectId}.`);
2082
2051
  }
2083
- repositoryPaths[candidateProject.projectId] = project.path;
2052
+ repositoryPaths[candidateProject.projectId] = entry.path;
2084
2053
  }
2085
2054
  const assessment = await assessDeltaRecheck({
2086
2055
  repositoryPaths,
@@ -2317,6 +2286,51 @@ function selectionPorts(store, catalogs) {
2317
2286
  };
2318
2287
  }
2319
2288
  async function preflightAgentConfigurationMutation(commandArgs, store, catalogs) {
2289
+ const mutation = profileAgentConfigurationMutation(commandArgs, store)
2290
+ ?? taskRoleAgentConfigurationMutation(commandArgs, store);
2291
+ if (mutation === undefined) {
2292
+ await warmLegacyRoleConfigurationMutation(commandArgs, store, catalogs);
2293
+ return undefined;
2294
+ }
2295
+ const agent = store.getConfiguredAgent(mutation.agentId);
2296
+ if (agent === null)
2297
+ throw agentNotFound(mutation.agentId);
2298
+ const resolved = await catalogs.resolve({
2299
+ agent,
2300
+ cwd: mutation.cwd,
2301
+ config: mutation.config
2302
+ });
2303
+ try {
2304
+ validateAgentLaunchConfiguration(resolved.catalog, mutation.config);
2305
+ }
2306
+ catch (error) {
2307
+ throw usageError(error instanceof Error ? error.message : String(error));
2308
+ }
2309
+ return (candidate) => {
2310
+ if (candidate.agentId !== mutation.agentId
2311
+ || resolve(candidate.cwd) !== resolve(mutation.cwd)
2312
+ || !isDeepStrictEqual(candidate.config, mutation.config)) {
2313
+ throw usageError("Agent configuration changed after capability preflight; retry the command.");
2314
+ }
2315
+ try {
2316
+ validateAgentLaunchConfiguration(resolved.catalog, candidate.config);
2317
+ }
2318
+ catch (error) {
2319
+ throw usageError(error instanceof Error ? error.message : String(error));
2320
+ }
2321
+ };
2322
+ }
2323
+ function profileAgentConfigurationMutation(args, store) {
2324
+ return args[0] === "config" && args[1] === "profile"
2325
+ ? previewProfileAgentConfigurationMutation(args.slice(2), store)
2326
+ : undefined;
2327
+ }
2328
+ function taskRoleAgentConfigurationMutation(args, store) {
2329
+ return args[0] === "task"
2330
+ ? previewTaskRoleAgentConfigurationMutation(args.slice(1), store)
2331
+ : undefined;
2332
+ }
2333
+ async function warmLegacyRoleConfigurationMutation(commandArgs, store, catalogs) {
2320
2334
  if (!hasModelOrEffortMutation(commandArgs))
2321
2335
  return;
2322
2336
  const agentId = configurationMutationAgentId(commandArgs, store);
@@ -2380,8 +2394,11 @@ function selectionCall(store, catalogs, method, params) {
2380
2394
  });
2381
2395
  }
2382
2396
  case "config.get": return store.getConfig();
2383
- case "profile.list": return store.listAgentProfiles();
2384
- case "profile.show": return store.getAgentProfile(String(params.id ?? ""));
2397
+ case "profile.list": return store.listAgentProfiles().map((profile) => profileSelectionRecord(profile, store));
2398
+ case "profile.show": {
2399
+ const profile = store.getAgentProfile(String(params.id ?? ""));
2400
+ return profile === null ? null : profileSelectionRecord(profile, store);
2401
+ }
2385
2402
  case "role.list": return store.listGlobalRoles();
2386
2403
  case "role.show": return store.getGlobalRole(String(params.name ?? ""));
2387
2404
  case "project.list": return callOptional(reader, "listProjects");
@@ -2391,6 +2408,7 @@ function selectionCall(store, catalogs, method, params) {
2391
2408
  case "task.role.list": return callOptional(reader, "listRoles", [params.taskId]);
2392
2409
  case "task.role.show": return callOptional(reader, "getRole", [params.taskId, params.roleName]);
2393
2410
  case "task.work.list": return callOptional(reader, "listWorkItems", [params.taskId]);
2411
+ case "task.message.list": return callOptional(reader, "listMessages", [params.taskId]);
2394
2412
  case "task.input.list": {
2395
2413
  const taskId = typeof params.taskId === "string" ? params.taskId : undefined;
2396
2414
  const requests = taskId === undefined
@@ -2406,6 +2424,25 @@ function selectionCall(store, catalogs, method, params) {
2406
2424
  default: return [];
2407
2425
  }
2408
2426
  }
2427
+ function profileSelectionRecord(profile, store) {
2428
+ const view = resolveAgentProfileView(profile, store);
2429
+ return {
2430
+ ...view.profile,
2431
+ runtimeSource: view.runtime.source,
2432
+ ...(view.runtime.source === "global-worker"
2433
+ ? { workerRevision: view.runtime.workerRevision }
2434
+ : {}),
2435
+ effectiveRuntime: view.runtime,
2436
+ ...(view.runtime.status === "resolved"
2437
+ ? {
2438
+ agentId: view.runtime.binding.agentId,
2439
+ adapterId: view.runtime.binding.adapterId,
2440
+ model: view.runtime.binding.config.model,
2441
+ effort: view.runtime.binding.config.effort
2442
+ }
2443
+ : {})
2444
+ };
2445
+ }
2409
2446
  function presentSelectionTimes(value, store) {
2410
2447
  if (!Array.isArray(value))
2411
2448
  return value;