@zq-silk/yui 0.13.9 → 0.14.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 (229) hide show
  1. package/ARCHITECTURE.md +69 -428
  2. package/README.md +108 -298
  3. package/dist/agent/managedRuntimeEnvironment.js +2 -2
  4. package/dist/cli/commandCatalog.js +39 -77
  5. package/dist/cli/interactionCandidates.js +4 -4
  6. package/dist/cli/interactionPolicy.js +7 -10
  7. package/dist/cli/invocationRouter.js +1 -1
  8. package/dist/cli/updateCommand.js +6 -27
  9. package/dist/cli/updateOrchestrator.js +63 -484
  10. package/dist/cli/updatePorts.js +15 -333
  11. package/dist/cli/upgradeCommand.js +8 -65
  12. package/dist/cli.js +57 -242
  13. package/dist/commands/configCommands.js +11 -62
  14. package/dist/commands/executionAuditCommands.js +23 -23
  15. package/dist/commands/globalRoleCommands.js +2 -7
  16. package/dist/commands/jobCommands.js +1 -4
  17. package/dist/commands/projectCommands.js +5 -5
  18. package/dist/commands/releaseCommands.js +3 -10
  19. package/dist/commands/resourcesCommands.js +2 -2
  20. package/dist/commands/sessionCommands.js +2 -2
  21. package/dist/commands/taskActor.js +31 -39
  22. package/dist/commands/taskChangeSetCommands.js +5 -12
  23. package/dist/commands/taskCommands.js +464 -1249
  24. package/dist/commands/taskContextCommand.js +35 -36
  25. package/dist/commands/taskExecutionCommands.js +33 -46
  26. package/dist/commands/taskInputCommands.js +20 -42
  27. package/dist/commands/taskNextActionCommand.js +9 -9
  28. package/dist/commands/taskOverviewCommand.js +56 -95
  29. package/dist/commands/taskRoleRuntimeStatus.js +77 -107
  30. package/dist/commands/taskUpstreamCommands.js +3 -3
  31. package/dist/commands/taskWorkspaceCommands.js +7 -79
  32. package/dist/commands/telemetryCommands.js +32 -124
  33. package/dist/commands/workflowCommands.js +3 -3
  34. package/dist/completion/fileCompletionManager.js +1 -1
  35. package/dist/config/configCatalog.js +4 -6
  36. package/dist/config/yuiConfig.js +6 -43
  37. package/dist/context/dispatchContext.js +14 -30
  38. package/dist/context/roleSessionContext.js +2 -2
  39. package/dist/context/sessionBootstrapManifest.js +2 -2
  40. package/dist/context/{runContextPack.js → turnContextPack.js} +58 -73
  41. package/dist/context/turnInputContract.js +204 -0
  42. package/dist/context/wakeNotification.js +2 -2
  43. package/dist/controller/agentRuntimeObserver.js +18 -18
  44. package/dist/controller/clientRuntime.js +13 -20
  45. package/dist/controller/controller.js +89 -116
  46. package/dist/controller/fileSchedulerStoreAdapter.js +511 -742
  47. package/dist/controller/jobControl.js +17 -17
  48. package/dist/controller/resourceInventory.js +6 -6
  49. package/dist/controller/resourceInventoryLinux.js +38 -59
  50. package/dist/controller/resourceInventoryRpc.js +3 -3
  51. package/dist/controller/resourceInventoryWorker.js +1 -1
  52. package/dist/controller/runtime.js +46 -53
  53. package/dist/controller/runtimeEventInbox.js +23 -31
  54. package/dist/controller/runtimeEventProcessor.js +15 -15
  55. package/dist/controller/{runtimeHookRunFence.js → runtimeHookTurnFence.js} +87 -81
  56. package/dist/controller/runtimeLaunchCoordinator.js +3 -3
  57. package/dist/controller/runtimeObservationHook.js +11 -8
  58. package/dist/controller/sessionNotify.js +11 -11
  59. package/dist/controller/structuredProviderObservation.js +89 -16
  60. package/dist/coordination/workMailbox.js +309 -579
  61. package/dist/coordination/workMailboxQueue.js +5 -6
  62. package/dist/core/controllerClient.js +1 -91
  63. package/dist/core/controllerServer.js +3 -6
  64. package/dist/doctor/doctor.js +51 -314
  65. package/dist/execution/candidateConvergence.js +3 -3
  66. package/dist/execution/executionGroup.js +32 -33
  67. package/dist/execution/executionHealth.js +36 -38
  68. package/dist/execution/resourceBroker.js +12 -12
  69. package/dist/executor/agentExecutor.js +30 -265
  70. package/dist/executor/effectiveLaunch.js +9 -19
  71. package/dist/executor/executorRegistry.js +43 -17
  72. package/dist/executor/fileRoleLaunchPlanner.js +56 -56
  73. package/dist/executor/launchPlan.js +3 -3
  74. package/dist/executor/workspacePreflightClassification.js +16 -16
  75. package/dist/grant/capabilityGrant.js +20 -25
  76. package/dist/input/inputRequest.js +12 -12
  77. package/dist/integration/changeSet.js +9 -13
  78. package/dist/integration/changeSetManifest.js +1 -1
  79. package/dist/integration/gitIntegrationService.js +2 -2
  80. package/dist/integration/integrationQueueService.js +5 -21
  81. package/dist/integration/overlapDiagnostics.js +2 -2
  82. package/dist/interaction/operatorPresentation.js +1 -1
  83. package/dist/job/jobRunner.js +1 -1
  84. package/dist/lifecycle/canonicalLifecycleEvent.js +25 -25
  85. package/dist/lifecycle/{exactRunTerminalization.js → exactTurnTerminalization.js} +116 -232
  86. package/dist/message/message.js +6 -6
  87. package/dist/observability/executionAudit.js +74 -86
  88. package/dist/observability/faultClassification.js +7 -24
  89. package/dist/observability/orchestrationMetrics.js +5 -5
  90. package/dist/observability/runtimeIdentity.js +34 -101
  91. package/dist/profile/agentProfile.js +1 -1
  92. package/dist/release/cliHomeReleaseFence.js +3 -3
  93. package/dist/release/releaseWorkflowEngine.js +6 -10
  94. package/dist/release/releaseWorkflowPorts.js +2 -2
  95. package/dist/release/workflowFileLock.js +1 -1
  96. package/dist/repository/checkoutSwap.js +1 -1
  97. package/dist/repository/gitWorkspace.js +0 -13
  98. package/dist/repository/project.js +1 -1
  99. package/dist/repository/projectMaintenanceLock.js +1 -2
  100. package/dist/repository/taskBaseFreshness.js +13 -13
  101. package/dist/repository/taskWorkspaceCoordinator.js +21 -25
  102. package/dist/repository/taskWorkspaceIdentity.js +8 -21
  103. package/dist/repository/taskWorkspacePreparer.js +63 -620
  104. package/dist/resources/autoResourceGc.js +3 -3
  105. package/dist/resources/liveReferences.js +1 -1
  106. package/dist/resources/resourceGc.js +1 -1
  107. package/dist/resources/sqliteResourceRegistry.js +1 -1
  108. package/dist/review/reviewDecision.js +7 -7
  109. package/dist/review/reviewFindingLedger.js +1 -1
  110. package/dist/review/reviewOutcomeClassifier.js +35 -50
  111. package/dist/review/reviewRound.js +14 -25
  112. package/dist/review/reviewerAvailability.js +4 -4
  113. package/dist/review/taskFinalReviewContractResolution.js +41 -0
  114. package/dist/role/role.js +6 -3
  115. package/dist/runtime/agentDriver.js +4 -0
  116. package/dist/runtime/agentHost.js +151 -36
  117. package/dist/runtime/builtinAgentDrivers.js +79 -2
  118. package/dist/runtime/codexAppServerRuntime.js +122 -0
  119. package/dist/runtime/continuationManager.js +3 -5
  120. package/dist/runtime/exactControlPlane.js +17 -42
  121. package/dist/runtime/firstProgressAdvisory.js +1 -1
  122. package/dist/runtime/index.js +2 -2
  123. package/dist/runtime/launchDiagnostics.js +2 -2
  124. package/dist/runtime/lifecycleReservation.js +4 -4
  125. package/dist/runtime/processExitObservation.js +1 -1
  126. package/dist/runtime/processExitOutbox.js +0 -32
  127. package/dist/runtime/promptEnvelope.js +9 -9
  128. package/dist/runtime/providerContinuation.js +2 -2
  129. package/dist/runtime/providerContinuationReconciliationService.js +2 -2
  130. package/dist/runtime/providerRuntimeIdentity.js +76 -24
  131. package/dist/runtime/recentTurnIds.js +38 -0
  132. package/dist/runtime/runtimeContinuationProjection.js +2 -2
  133. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  134. package/dist/runtime/runtimeObservation.js +58 -21
  135. package/dist/runtime/runtimeProjection.js +19 -27
  136. package/dist/runtime/sessionLaunchRequest.js +2 -2
  137. package/dist/runtime/structuredProviderHost.js +177 -12
  138. package/dist/runtime/tmuxAdapters.js +46 -11
  139. package/dist/scheduler/activeRoleTurnDelivery.js +245 -0
  140. package/dist/scheduler/leaderWakeupProcessor.js +147 -445
  141. package/dist/scheduler/operatorEvent.js +4 -6
  142. package/dist/scheduler/resourceQueueProjection.js +71 -0
  143. package/dist/scheduler/{roleRunLiveness.js → roleTurnLiveness.js} +24 -28
  144. package/dist/scheduler/{roleRunStall.js → roleTurnStall.js} +162 -247
  145. package/dist/scheduler/taskExecutionProjection.js +99 -99
  146. package/dist/scheduler/taskObservabilityProjection.js +10 -10
  147. package/dist/scheduler/taskWake.js +4 -4
  148. package/dist/scheduler/wakeReason.js +6 -10
  149. package/dist/scheduler/wakeupQueue.js +0 -9
  150. package/dist/setup/setupCommand.js +2 -2
  151. package/dist/storage/currentTaskStore.js +46 -0
  152. package/dist/storage/persistenceWorker.js +1 -1
  153. package/dist/storage/sqliteSchema.js +81 -485
  154. package/dist/storage/sqliteStore.js +206 -365
  155. package/dist/storage/storageSchema.js +1 -15
  156. package/dist/storage/storageVersions.js +8 -9
  157. package/dist/storage/storeRpc.js +10 -34
  158. package/dist/storage/taskStore.js +63 -3415
  159. package/dist/storage/upgrade/recordVersions.js +55 -149
  160. package/dist/storage/upgrade/upgradeOrchestrator.js +89 -1752
  161. package/dist/task/completionReadiness.js +5 -5
  162. package/dist/task/deliveryGuard.js +10 -10
  163. package/dist/task/nextAction.js +74 -77
  164. package/dist/task/task.js +2 -4
  165. package/dist/task/taskRecordReference.js +3 -3
  166. package/dist/task/taskRecordRetirement.js +1 -1
  167. package/dist/telemetry/sqliteTelemetryStore.js +53 -53
  168. package/dist/telemetry/telemetryConfig.js +14 -14
  169. package/dist/telemetry/telemetryWiring.js +8 -14
  170. package/dist/turn/turn.js +356 -0
  171. package/dist/{run/runIdentity.js → turn/turnIdentity.js} +4 -4
  172. package/dist/verification/gateArtifactStore.js +2 -2
  173. package/dist/web/assets/client/app.js +1 -1
  174. package/dist/web/assets/client/components.js +29 -54
  175. package/dist/web/assets/client/i18n.js +17 -37
  176. package/dist/web/assets/client/view.js +37 -37
  177. package/dist/web/assets/shell.js +1 -1
  178. package/dist/web/assets/styles/cards.js +5 -5
  179. package/dist/web/assets/styles/widgets.js +1 -2
  180. package/dist/web/webSnapshot.js +65 -60
  181. package/dist/workItem/workItem.js +11 -11
  182. package/dist/workspace/workItemChangeSetManager.js +1 -1
  183. package/docs/task-local-identity.md +5 -5
  184. package/i18n/README.zh-CN.md +41 -54
  185. package/package.json +1 -1
  186. package/skills/yui-leader/SKILL.md +85 -83
  187. package/skills/yui-operator/SKILL.md +16 -16
  188. package/skills/yui-reviewer/SKILL.md +10 -10
  189. package/skills/yui-runtime/SKILL.md +17 -18
  190. package/skills/yui-worker/SKILL.md +16 -18
  191. package/dist/context/runContextContract.js +0 -162
  192. package/dist/executor/turnCompletion.js +0 -126
  193. package/dist/review/taskFinalReviewContractEvent.js +0 -1
  194. package/dist/review/taskFinalReviewContractRebind.js +0 -367
  195. package/dist/run/agentRun.js +0 -356
  196. package/dist/run/rejectedYieldAttempt.js +0 -221
  197. package/dist/run/runControlRequest.js +0 -50
  198. package/dist/run/yieldReceipt.js +0 -65
  199. package/dist/runtime/turnCompletion.js +0 -3
  200. package/dist/scheduler/actionability.js +0 -323
  201. package/dist/scheduler/activeRoleRunDelivery.js +0 -868
  202. package/dist/scheduler/activeTaskProgress.js +0 -126
  203. package/dist/storage/compatibleTaskStore.js +0 -248
  204. package/dist/storage/migration/baseline.js +0 -78
  205. package/dist/storage/migration/classifier.js +0 -51
  206. package/dist/storage/migration/compatibleCodec.js +0 -53
  207. package/dist/storage/migration/engine.js +0 -147
  208. package/dist/storage/migration/index.js +0 -33
  209. package/dist/storage/migration/planner.js +0 -154
  210. package/dist/storage/migration/productionRegistry.js +0 -3931
  211. package/dist/storage/migration/registry.js +0 -169
  212. package/dist/storage/migration/report.js +0 -54
  213. package/dist/storage/migration/types.js +0 -31
  214. package/dist/storage/upgrade/homeClassification.js +0 -353
  215. package/dist/storage/upgrade/homeMigrationTarget.js +0 -595
  216. package/dist/storage/upgrade/migrationReceipt.js +0 -67
  217. package/dist/storage/upgrade/offlineUpgradeInventory.js +0 -453
  218. package/dist/storage/upgrade/productionMigrationRegistry.js +0 -6
  219. package/dist/storage/upgrade/pseudoLayoutRepair.js +0 -241
  220. package/dist/storage/upgrade/recordVersionScan.js +0 -176
  221. package/dist/storage/upgrade/sqliteFileSet.js +0 -50
  222. package/dist/storage/upgrade/sqliteMigrationTarget.js +0 -375
  223. package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +0 -305
  224. package/dist/storage/upgrade/sqliteStateMigration.js +0 -918
  225. package/dist/storage/upgrade/switchProgress.js +0 -80
  226. package/dist/storage/upgrade/upgradeReceipt.js +0 -161
  227. package/dist/storage/upgradeCoordination.js +0 -187
  228. package/dist/storage/upgradeFence.js +0 -366
  229. package/dist/telemetry/telemetryCompaction.js +0 -252
@@ -1,16 +1,15 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import { isDeepStrictEqual } from "node:util";
3
- import { createRunAssignment } from "../context/runContextContract.js";
4
- import { buildRunContextPack, buildRunContextDelta, contextSnapshotDeltaRefIds, expandRunContextRef, freezeExecutionStageContextSnapshot, freezeReviewStageContextSnapshot, freezeRunContextSnapshot } from "../context/runContextPack.js";
3
+ import { createTurnInput } from "../context/turnInputContract.js";
4
+ import { buildTurnContextPack, buildTurnContextDelta, contextSnapshotDeltaRefIds, expandTurnContextRef, freezeExecutionStageContextSnapshot, freezeReviewStageContextSnapshot, freezeTurnContextSnapshot } from "../context/turnContextPack.js";
5
5
  import { contextSnapshotRef } from "../context/contextSnapshot.js";
6
6
  import { CliError, dataError, roleNotFound, runtimeError, taskNotFound, usageError } from "../errors/cliError.js";
7
7
  import { createTaskEvent } from "../event/taskEvent.js";
8
8
  import { createTaskRecordRetirement, isTaskRecordRetired, taskRecordRetirement } from "../task/taskRecordRetirement.js";
9
- import { isRoleRunStalled, RUN_PROGRESS_EVENT, RUN_RECOVERED_EVENT } from "../scheduler/roleRunStall.js";
10
- import { routeRoleEvent } from "../scheduler/operatorEvent.js";
9
+ import { isRoleTurnStalled, TURN_PROGRESS_EVENT, TURN_RECOVERED_EVENT } from "../scheduler/roleTurnStall.js";
11
10
  import { readCommandText } from "./textInput.js";
12
11
  import { assertTaskCompletionPublishedTreeProof } from "./taskCompletionGate.js";
13
- import { createRoleSessionSet, roleAgentSessionResumeMode, retireTaskRoleSessionsForWorkspace, terminalizeTaskRoleRunSession, updateTaskRoleProviderRuntime, updateRoleAgentSessionStatus } from "../executor/agentExecutor.js";
12
+ import { createRoleSessionSet, roleAgentSessionResumeMode, updateTaskRoleProviderRuntime } from "../executor/agentExecutor.js";
14
13
  import { currentProviderActivation, transferProviderAuthority } from "../runtime/providerRuntimeIdentity.js";
15
14
  import { resolveEffectiveLaunch } from "../executor/effectiveLaunch.js";
16
15
  import { defaultTableWidth, renderTable } from "../output/table.js";
@@ -18,12 +17,10 @@ import { formatTimestamp } from "../output/timePresentation.js";
18
17
  import { renderRoleDetails } from "../output/rolePresentation.js";
19
18
  import { createTaskMessage, taskMessageAuthorLabel } from "../message/message.js";
20
19
  import { cancelInputRequest } from "../input/inputRequest.js";
21
- import { retireExactActiveAgentRun, terminalizeExactTaskRun, validateExactRunReviewRound } from "../lifecycle/exactRunTerminalization.js";
20
+ import { retireExactActiveTurn, terminalizeExactTaskTurn, validateExactTurnReviewRound } from "../lifecycle/exactTurnTerminalization.js";
22
21
  import { activeRoleAgentBinding, copyGlobalRoleToTaskRole, createRole, createRoleAgentBinding, switchActiveRoleAgent, unbindRoleAgent, updateRole } from "../role/role.js";
23
- import { agentRunDeliveryReceiptId, createAgentRun, failAgentRun, withAgentRunContextSnapshot, yieldAgentRun } from "../run/agentRun.js";
24
- import { matchYieldReceipt } from "../run/yieldReceipt.js";
25
- import { createRejectedYieldAttempt, rejectedYieldAttemptEventPayload, rejectedYieldAttemptFromTaskEvent, RUN_YIELD_REJECTED_EVENT } from "../run/rejectedYieldAttempt.js";
26
- import { createReviewRound, createTaskReviewRound, createTaskDeltaReviewRound, attachReviewExecutionGroup, deltaRecheckBlocksAcceptance, finishReviewRound, parseReviewYieldReport, recordReviewWorkspaceDisposition, retryRunningReviewExecutionLane, retryTaskReviewRound, startReviewRound, updateReviewExecutionGroup, validateTaskReviewCandidate } from "../review/reviewRound.js";
22
+ import { createTurn, withTurnContextSnapshot } from "../turn/turn.js";
23
+ import { createReviewRound, createTaskReviewRound, createTaskDeltaReviewRound, attachReviewExecutionGroup, deltaRecheckBlocksAcceptance, finishReviewRound, recordReviewWorkspaceDisposition, retryRunningReviewExecutionLane, retryTaskReviewRound, startReviewRound, updateReviewExecutionGroup, validateTaskReviewCandidate } from "../review/reviewRound.js";
27
24
  import { buildDeltaRecheckDispatchContext, verifyDeltaRecheckDiff } from "../review/deltaRecheck.js";
28
25
  import { isAcceptedTaskReviewBaseline } from "../review/reviewAcceptance.js";
29
26
  import { projectReviewerAvailability } from "../review/reviewerAvailability.js";
@@ -35,14 +32,13 @@ import { createMilestone } from "../milestone/milestone.js";
35
32
  import { runPublicationCommand } from "./taskPublicationCommands.js";
36
33
  import { enqueueWork, settleExactWorkExecution } from "../coordination/workMailboxQueue.js";
37
34
  import { mailboxHasWork as workMailboxHasWork, nextPendingBatch } from "../coordination/workMailbox.js";
38
- import { RUNTIME_CLEANUP_REQUIRED_REASON, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
35
+ import { runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
39
36
  import { projectProviderContinuations } from "../runtime/runtimeContinuationProjection.js";
40
37
  import { runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
41
38
  import { addTaskProjectBinding, archiveTask, completeTask, createTask, retireTask, reopenTask, updateTaskMetadata } from "../task/task.js";
42
39
  import { resolveTaskRecordReference } from "../task/taskRecordReference.js";
43
40
  import { projectCompletionReadiness } from "../task/completionReadiness.js";
44
41
  import { assertProjectActive, resolveProject } from "../repository/project.js";
45
- import { acquireProjectMaintenanceLocks } from "../repository/projectMaintenanceLock.js";
46
42
  import { currentWorkItemCandidate, currentWorkItemExecutionGroup, workItemExecutionGroupById, createWorkItem, planWorkItemExplorationStage, attachWorkItemExecutionGroup, updateWorkItemExecutionGroup, retireWorkItem, retryFailedWorkItem, submitWorkItemCandidate, updateWorkItemWriteProjects, updateWorkItemStatus } from "../workItem/workItem.js";
47
43
  import { addExecutionLane, createExecutionGroup, queueExecutionLaneRetry, resolveExecutionGroup, restartExecutionLane, skipPendingExecutionLanes, updateExecutionLane, WORK_ITEM_EXECUTION_MODES } from "../execution/executionGroup.js";
48
44
  import { DEFAULT_EXECUTION_STAGE_TOKEN_BUDGET_PER_LANE, executionStageSpendClosed, observedExecutionResourceUsage, planResourceAdmissions, projectExecutionStageResources, resolveResourceBrokerPolicy, routeExecutionStage } from "../execution/resourceBroker.js";
@@ -50,7 +46,7 @@ import { resolveControllerTaskConcurrency, resolveRuntimeHealth } from "../confi
50
46
  import { assertIndependentVerificationRoles, candidateConvergenceEvidenceSufficient, candidateConvergenceDirective, validateCandidateConvergenceResolution } from "../execution/candidateConvergence.js";
51
47
  import { sameTaskFinalReviewContract, taskFinalReviewConfig, validateTaskFinalReviewContract } from "../review/taskFinalReviewContract.js";
52
48
  import { classifyReviewRoundOutcome } from "../review/reviewOutcomeClassifier.js";
53
- import { TASK_FINAL_REVIEW_CONTRACT_REBOUND_EVENT, createTaskFinalReviewContractRebind, resolveRecordedTaskFinalReviewContract, taskFinalReviewContractRebindPayload } from "../review/taskFinalReviewContractRebind.js";
49
+ import { resolveRecordedTaskFinalReviewContract } from "../review/taskFinalReviewContractResolution.js";
54
50
  import { managedWorkspaceKey } from "../worktree/managedWorkspace.js";
55
51
  import { hasAgentConfigOptions, parseRoleOptions, patchRoleAgentBinding, roleOptionSpecs, roleProfilePatch } from "./roleConfiguration.js";
56
52
  import { hasRoleLaunchContextOptions, validateConfiguredRoleSkills } from "./roleSkillValidation.js";
@@ -58,16 +54,15 @@ import { assertRoleRuntimeMutationAllowed } from "./roleRuntimeGuard.js";
58
54
  import { runTaskContextCommand } from "./taskContextCommand.js";
59
55
  import { runTaskNextActionCommand } from "./taskNextActionCommand.js";
60
56
  import { runDeliveryGuardPreflight, withGuardWarnings } from "./deliveryGuardPreflight.js";
61
- import { inspectTaskRoleRuntimeStatuses, renderTaskRoleRuntimeStatus, taskRoleActiveWorkLabel, taskRoleLastRunLabel, taskRoleNativeSessionLabel, taskRoleOpenInputLabel, taskRoleTmuxLabel } from "./taskRoleRuntimeStatus.js";
62
- import { assertNoOpenInputRequests, isCurrentGlobalOperator, openInputRequestCount, runTaskInputCommand } from "./taskInputCommands.js";
57
+ import { inspectTaskRoleRuntimeStatuses, renderTaskRoleRuntimeStatus, taskRoleActiveWorkLabel, taskRoleLastTurnLabel, taskRoleNativeSessionLabel, taskRoleOpenInputLabel, taskRoleTmuxLabel } from "./taskRoleRuntimeStatus.js";
58
+ import { assertNoOpenInputRequests, openInputRequestCount, runTaskInputCommand } from "./taskInputCommands.js";
63
59
  import { runGrantCommand } from "./grantCommands.js";
64
60
  import { runWorkflowCommand } from "./workflowCommands.js";
65
- import { taskLocalActor as resolveTaskLocalActor, taskLeaderActionRunId } from "./taskActor.js";
61
+ import { taskLocalActor as resolveTaskLocalActor, taskLeaderActionTurnId } from "./taskActor.js";
66
62
  import { enqueueOperatorEvent } from "../scheduler/operatorEvent.js";
67
63
  import { queueLeaderWakeup } from "../scheduler/wakeupQueue.js";
68
64
  import { renderWakeReason, wakeReason } from "../scheduler/wakeReason.js";
69
- import { collectTaskActionability, computeActionabilityDigest, deriveLeaderRunDisposition, projectQueuedResourceLaneIdentities } from "../scheduler/actionability.js";
70
- import { buildTaskExecutionProjection } from "../scheduler/taskExecutionProjection.js";
65
+ import { projectQueuedResourceLaneIdentities } from "../scheduler/resourceQueueProjection.js";
71
66
  import { buildTaskOverview, parseTaskListOptions, renderTaskOverview } from "./taskOverviewCommand.js";
72
67
  const LEADER_ROLE = "leader";
73
68
  /** Exact Task-final identity or evidence changed between queueing and dispatch. */
@@ -82,7 +77,7 @@ function taskFinalReviewDispatchDrift(error) {
82
77
  return new TaskFinalReviewDispatchDriftError(error instanceof Error ? error.message : String(error), error instanceof CliError ? error : undefined);
83
78
  }
84
79
  /** `final` is a Task delivery policy, not a per-WorkItem ReviewRound rule. */
85
- function legacyWorkItemReviewConfig(config) {
80
+ function workItemReviewConfig(config) {
86
81
  return config?.trigger === "final" ? null : config;
87
82
  }
88
83
  function storedTaskFinalReviewContract(store, taskId) {
@@ -90,7 +85,7 @@ function storedTaskFinalReviewContract(store, taskId) {
90
85
  }
91
86
  function storedTaskFinalReviewContractResolution(store, taskId) {
92
87
  try {
93
- return resolveRecordedTaskFinalReviewContract(taskId, store.listWorkItems(taskId), store.listReviewRounds(taskId), store.listEvents(taskId));
88
+ return resolveRecordedTaskFinalReviewContract(taskId, store.listWorkItems(taskId), store.listReviewRounds(taskId));
94
89
  }
95
90
  catch (error) {
96
91
  throw dataError(error instanceof Error
@@ -119,8 +114,8 @@ function taskFinalReviewContractForMutation(store, taskId, options) {
119
114
  }
120
115
  if (stored === undefined)
121
116
  return supplied;
122
- // Historical exact contracts now express only a durable Reviewer policy.
123
- // The persisted contract remains the audit authority, but ordinary
117
+ // The exact contract expresses a durable Reviewer policy. The persisted
118
+ // contract remains the audit authority, but ordinary
124
119
  // protocol/storage-compatible CLIs may continue the Task without presenting
125
120
  // a release-bound capability on every mutation.
126
121
  if (supplied === undefined)
@@ -236,7 +231,7 @@ export function runTaskCommand(args, store, options = {}) {
236
231
  case "role": return taskRoleCommand(rest, store, options);
237
232
  case "work": return taskWorkCommand(rest, store, options);
238
233
  case "review": return taskReviewCommand(rest, store, options);
239
- case "run": return taskRunCommand(rest, store, options);
234
+ case "turn": return taskTurnCommand(rest, store, options);
240
235
  case "brief": return taskBriefCommand(rest, store, options);
241
236
  case "decision": return taskDecisionCommand(rest, store, options);
242
237
  case "milestone": return taskMilestoneCommand(rest, store, options);
@@ -317,7 +312,7 @@ export function normalizedExecutionLanePlan(input) {
317
312
  return { expanding, resumingQueued, roles, strategy, capacity, requestedCount, laneIds };
318
313
  }
319
314
  function activeResourceLaneIdentities(store) {
320
- return store.listActiveTaskIds().flatMap((taskId) => (store.listAgentRuns(taskId).flatMap((run) => {
315
+ return store.listActiveTaskIds().flatMap((taskId) => (store.listTurns(taskId).flatMap((run) => {
321
316
  if (run.status !== "active"
322
317
  || run.executionGroupId === undefined
323
318
  || run.executionLaneId === undefined)
@@ -455,14 +450,6 @@ function updateTaskCommand(args, store, options) {
455
450
  notifyMailbox(options.runtime, taskMailbox(result.id), result.id);
456
451
  return `Updated task ${result.id}\n`;
457
452
  }
458
- /** Compatibility helper for call sites that cannot yet handle foreground enter. */
459
- export function runTaskOutputCommand(args, store, options = {}) {
460
- const execution = runTaskCommand(args, store, options);
461
- if (execution.kind !== "output") {
462
- throw runtimeError("Task Role foreground runtime control requires the CLI.");
463
- }
464
- return execution.output;
465
- }
466
453
  export function submitOperatorMessage(body, taskId, store, options = {}) {
467
454
  const now = clock(options);
468
455
  const result = store.transaction((tx) => {
@@ -601,7 +588,7 @@ function showTaskCommand(args, store) {
601
588
  milestones: milestones.length,
602
589
  events: events.length,
603
590
  workItems: work.length,
604
- agentRuns: store.listAgentRuns(task.id).length,
591
+ turns: store.listTurns(task.id).length,
605
592
  changeSets: changeSets.length,
606
593
  integrations: integrations.length,
607
594
  publications: publications.length,
@@ -640,7 +627,7 @@ function showTaskCommand(args, store) {
640
627
  `Milestones: ${counts.milestones}`,
641
628
  `Events: ${counts.events}`,
642
629
  `Work items: ${counts.workItems}`,
643
- `Agent Runs: ${counts.agentRuns}`,
630
+ `Turns: ${counts.turns}`,
644
631
  `ChangeSets: ${counts.changeSets}`,
645
632
  `Integration Attempts: ${counts.integrations}`,
646
633
  `Publication references: ${counts.publications} (${verifiedMergedPublications} verified merged)`,
@@ -700,7 +687,6 @@ function completeTaskCommand(args, store, options) {
700
687
  return {
701
688
  task,
702
689
  changed: false,
703
- runtimeCleanupTargets: [],
704
690
  completionAdvisories: [],
705
691
  finalReview: undefined
706
692
  };
@@ -722,15 +708,13 @@ function completeTaskCommand(args, store, options) {
722
708
  return {
723
709
  task,
724
710
  changed: false,
725
- runtimeCleanupTargets: [],
726
711
  completionAdvisories: [],
727
712
  finalReview,
728
713
  };
729
714
  }
730
- // Completion is a semantic boundary. Current Runs and Sessions are
731
- // disposable execution attempts; settle them before deriving readiness so
732
- // only durable Task results decide whether the Task can complete.
733
- const runtimeCleanupTargets = settleTaskExecutionForCompletion(tx, task.id, roles, summary, now);
715
+ // Completion is a Task decision only. The current Provider Turn remains
716
+ // responsible for closing its own Turn, and the reusable Session keeps
717
+ // its independent lifecycle.
734
718
  // Issue 06: re-validate the full completion readiness inside the
735
719
  // transaction (the CAS fence) after final-review preparation. This is the
736
720
  // same pure projection `task next-action` displays, now with the finding
@@ -781,7 +765,7 @@ function completeTaskCommand(args, store, options) {
781
765
  // discarded at this lifecycle boundary.
782
766
  tx.removeWorkMailbox(taskMailbox(task.id));
783
767
  // Role mailboxes are also derived wake state. A Worker result or runtime
784
- // signal queued while the final Run was being settled must not survive a
768
+ // signal queued while the final Turn was being settled must not survive a
785
769
  // completed Task and become actionable after a later explicit reopen.
786
770
  for (const role of roles) {
787
771
  tx.removeWorkMailbox(roleMailbox(task.id, role.name));
@@ -789,17 +773,11 @@ function completeTaskCommand(args, store, options) {
789
773
  return {
790
774
  task: completed,
791
775
  changed: true,
792
- runtimeCleanupTargets,
793
776
  completionAdvisories: readiness.advisories,
794
777
  finalReview: undefined
795
778
  };
796
779
  });
797
780
  if (result.changed) {
798
- for (const target of result.runtimeCleanupTargets) {
799
- // Cleanup owns an independent lifecycle lane. Never fall back to the
800
- // Task signal for older runtime ports: a completed Task must not wake.
801
- options.runtime?.notifyMailboxChanged?.(target);
802
- }
803
781
  notifyMailbox(options.runtime, { kind: "operator" }, result.task.id);
804
782
  }
805
783
  if (result.finalReview !== undefined) {
@@ -826,45 +804,29 @@ function completeTaskCommand(args, store, options) {
826
804
  function reopenTaskCommand(args, store, options) {
827
805
  exactPositionals(args, 1, "Task reopen usage: yui task reopen <id>.");
828
806
  const now = clock(options);
829
- // Reopen changes the Task status that archiveLegacyTaskRefs reads before
830
- // deleting the legacy ref. Take the same per-Project maintenance fence so a
831
- // reopen cannot commit between archive's status read and its ref deletion:
832
- // the two operations are mutually exclusive per Project.
833
- const existing = store.getTask(args[0]);
834
- const projectIds = existing === null
835
- ? []
836
- : existing.projectBindings.map(({ projectId }) => projectId);
837
- const release = options.yuiHome === undefined || projectIds.length === 0
838
- ? () => { }
839
- : acquireProjectMaintenanceLocks(options.yuiHome, projectIds);
840
- try {
841
- const result = store.transaction((tx) => {
842
- const task = requireTask(tx, args[0]);
843
- if (task.status === "active")
844
- return { task, changed: false };
845
- if (task.status === "archived")
846
- throw usageError(`Task is archived: ${task.id}.`);
847
- if (task.status !== "completed")
848
- throw usageError(`Task is not completed: ${task.id}.`);
849
- const active = reopenTask(task, now);
850
- tx.saveTask(active);
851
- const reopenedReason = wakeReason("task-reopened");
852
- enqueueWork(tx, leaderMailbox(task.id), reopenedReason, now, [taskRef(task.id)]);
853
- enqueueWork(tx, taskMailbox(task.id), reopenedReason, now, [taskRef(task.id)]);
854
- recordTaskEvent(tx, task.id, "task.reopened", { status: active.status }, now);
855
- return { task: active, changed: true };
856
- });
857
- if (result.changed) {
858
- notifyMailbox(options.runtime, taskMailbox(result.task.id), result.task.id);
859
- notifyMailbox(options.runtime, leaderMailbox(result.task.id), result.task.id);
860
- }
861
- return result.changed
862
- ? `Reopened task ${result.task.id}\n`
863
- : `Task ${result.task.id} is already active\n`;
864
- }
865
- finally {
866
- release();
807
+ const result = store.transaction((tx) => {
808
+ const task = requireTask(tx, args[0]);
809
+ if (task.status === "active")
810
+ return { task, changed: false };
811
+ if (task.status === "archived")
812
+ throw usageError(`Task is archived: ${task.id}.`);
813
+ if (task.status !== "completed")
814
+ throw usageError(`Task is not completed: ${task.id}.`);
815
+ const active = reopenTask(task, now);
816
+ tx.saveTask(active);
817
+ const reopenedReason = wakeReason("task-reopened");
818
+ enqueueWork(tx, leaderMailbox(task.id), reopenedReason, now, [taskRef(task.id)]);
819
+ enqueueWork(tx, taskMailbox(task.id), reopenedReason, now, [taskRef(task.id)]);
820
+ recordTaskEvent(tx, task.id, "task.reopened", { status: active.status }, now);
821
+ return { task: active, changed: true };
822
+ });
823
+ if (result.changed) {
824
+ notifyMailbox(options.runtime, taskMailbox(result.task.id), result.task.id);
825
+ notifyMailbox(options.runtime, leaderMailbox(result.task.id), result.task.id);
867
826
  }
827
+ return result.changed
828
+ ? `Reopened task ${result.task.id}\n`
829
+ : `Task ${result.task.id} is already active\n`;
868
830
  }
869
831
  function archiveTaskCommand(args, store, options) {
870
832
  const request = validateTaskArchiveRequest(args, store, options);
@@ -895,11 +857,20 @@ function archiveTaskCommand(args, store, options) {
895
857
  throw usageError(`Task ${task.id} still has managed worktrees; clean them before archiving.`);
896
858
  }
897
859
  const activeRole = tx.listRoles(task.id)
898
- .find((role) => tx.getActiveAgentRun(task.id, role.name) !== null);
860
+ .find((role) => tx.getActiveTurn(task.id, role.name) !== null);
899
861
  if (activeRole !== undefined) {
900
- throw usageError(`Task ${task.id} still has an active Run for Role ${activeRole.name}; `
862
+ throw usageError(`Task ${task.id} still has an active Turn for Role ${activeRole.name}; `
901
863
  + "stop its runtime before archiving.");
902
864
  }
865
+ const liveSessionRole = tx.listRoles(task.id).find((role) => {
866
+ const sessions = tx.getTaskRoleSessionSet(task.id, role.name);
867
+ const session = sessions?.sessions[sessions.activeAgentId];
868
+ return session !== undefined && session.status !== "ended";
869
+ });
870
+ if (liveSessionRole !== undefined) {
871
+ throw usageError(`Task ${task.id} still has a live Session for Role ${liveSessionRole.name}; `
872
+ + "stop that Session before archiving.");
873
+ }
903
874
  const archived = archiveTask(task, now, { by: actor });
904
875
  tx.saveTask(archived);
905
876
  tx.clearPendingWakeup(task.id);
@@ -960,13 +931,12 @@ function retireTaskCommand(args, store, options) {
960
931
  throw usageError(`Task ${task.id} has an active DurableJob: ${activeRetireJob.id}/${activeRetireJob.status}.`);
961
932
  }
962
933
  assertTaskRetirementProof(tx, task, options.taskRetirementProof);
963
- for (const run of tx.listAgentRuns(task.id).filter(({ status: runStatus }) => (runStatus === "active"))) {
964
- const terminal = terminalizeExactTaskRun(tx, {
934
+ for (const run of tx.listTurns(task.id).filter(({ status: runStatus }) => (runStatus === "active"))) {
935
+ const terminal = terminalizeExactTaskTurn(tx, {
965
936
  taskId: task.id,
966
937
  roleName: run.roleName,
967
938
  agentId: run.effective.agentId,
968
- runId: run.id,
969
- receiptId: agentRunDeliveryReceiptId(run),
939
+ turnId: run.id,
970
940
  mailboxDisposition: "discard",
971
941
  outcome: {
972
942
  status: "failed",
@@ -974,7 +944,7 @@ function retireTaskCommand(args, store, options) {
974
944
  }
975
945
  }, now);
976
946
  if (terminal.disposition !== "applied") {
977
- throw usageError(`Task Run changed during retirement: ${run.id}/${terminal.reason ?? "obsolete"}.`);
947
+ throw usageError(`Task Turn changed during retirement: ${run.id}/${terminal.reason ?? "obsolete"}.`);
978
948
  }
979
949
  }
980
950
  for (const item of tx.listWorkItems(task.id)) {
@@ -1080,8 +1050,8 @@ function reconcileTaskCommand(args, store, options) {
1080
1050
  function taskMessageCommand(args, store, options) {
1081
1051
  const [command, ...rest] = args;
1082
1052
  if (command === "send") {
1083
- const usage = "Task message send usage: yui task message send <id> (<body>|--body-file <path|->) [--wake-policy leader|none] [--delivery-mode followup|steer].";
1084
- const parsed = parseTail(rest, new Set(["--body-file", "--wake-policy", "--delivery-mode"]), usage);
1053
+ const usage = "Task message send usage: yui task message send <id> (<body>|--body-file <path|->) [--wake-policy leader|none].";
1054
+ const parsed = parseTail(rest, new Set(["--body-file", "--wake-policy"]), usage);
1085
1055
  if (parsed.positionals.length < 1 || parsed.positionals.length > 2)
1086
1056
  throw usageError(usage);
1087
1057
  const body = readCommandText(parsed.positionals[1], parsed.options.get("--body-file"), "--body", usage);
@@ -1096,13 +1066,6 @@ function taskMessageCommand(args, store, options) {
1096
1066
  else {
1097
1067
  throw usageError(`--wake-policy must be 'leader' or 'none': ${wakePolicyRaw}.`);
1098
1068
  }
1099
- const deliveryModeRaw = parsed.options.get("--delivery-mode") ?? "followup";
1100
- if (deliveryModeRaw !== "followup" && deliveryModeRaw !== "steer") {
1101
- throw usageError(`--delivery-mode must be 'followup' or 'steer': ${deliveryModeRaw}.`);
1102
- }
1103
- if (deliveryModeRaw === "steer" && wakePolicy === "none") {
1104
- throw usageError("--delivery-mode steer cannot be combined with --wake-policy none.");
1105
- }
1106
1069
  const now = clock(options);
1107
1070
  const result = store.transaction((tx) => {
1108
1071
  const task = requireTask(tx, parsed.positionals[0]);
@@ -1119,19 +1082,10 @@ function taskMessageCommand(args, store, options) {
1119
1082
  if (task.status === "active"
1120
1083
  && actor !== "leader"
1121
1084
  && wakePolicy !== "none") {
1122
- enqueueWork(tx, leaderMailbox(task.id), actor === "operator" ? "operator-input" : "user-message", now, [messageRef(task.id, message.id)], deliveryModeRaw === "steer"
1123
- ? {
1124
- source: actor,
1125
- dedupeKey: `user-correction:${task.id}:${message.id}`,
1126
- deliveryMode: "steer-if-safe",
1127
- lane: "user-correction"
1128
- }
1129
- : {
1130
- source: actor,
1131
- dedupeKey: `message:${task.id}:${message.id}`,
1132
- deliveryMode: "followup",
1133
- lane: "normal"
1134
- });
1085
+ enqueueWork(tx, leaderMailbox(task.id), actor === "operator" ? "operator-input" : "user-message", now, [messageRef(task.id, message.id)], {
1086
+ source: actor,
1087
+ dedupeKey: `message:${task.id}:${message.id}`
1088
+ });
1135
1089
  }
1136
1090
  return { task, message, actor };
1137
1091
  });
@@ -1317,14 +1271,16 @@ function taskRoleSessionCommand(args, store, options) {
1317
1271
  const now = clock(options);
1318
1272
  const request = store.transaction((tx) => {
1319
1273
  const task = requireTask(tx, parsed.positionals[0]);
1320
- assertTaskOpen(task);
1274
+ if (task.status !== "active" && task.status !== "completed") {
1275
+ throw usageError(`Task Role Session stop requires an active or completed Task: ${task.id}.`, usage);
1276
+ }
1321
1277
  const actor = taskActor(tx, options, task.id);
1322
1278
  const role = requireRole(tx, task.id, parsed.positionals[1]);
1323
1279
  if (actor === "leader" && role.name === LEADER_ROLE) {
1324
1280
  throw usageError("A Leader cannot stop the Session executing its own current command.", usage);
1325
1281
  }
1326
- if (tx.getActiveAgentRun(task.id, role.name) !== null) {
1327
- throw usageError(`Task Role has an active Run; settle or retire it before stopping the Session: ${task.id}/${role.name}.`, usage);
1282
+ if (tx.getActiveTurn(task.id, role.name) !== null) {
1283
+ throw usageError(`Task Role has an active Turn; settle or retire it before stopping the Session: ${task.id}/${role.name}.`, usage);
1328
1284
  }
1329
1285
  const sessions = tx.getTaskRoleSessionSet(task.id, role.name);
1330
1286
  const session = sessions?.sessions[sessions.activeAgentId];
@@ -1454,7 +1410,7 @@ function listTaskRoles(args, store, options) {
1454
1410
  { header: "Health", minWidth: 6, maxWidth: 15 },
1455
1411
  { header: "Open input", minWidth: 5, maxWidth: 10 },
1456
1412
  { header: "Active work", minWidth: 10, maxWidth: 34 },
1457
- { header: "Last run", minWidth: 10, maxWidth: 28 },
1413
+ { header: "Last turn", minWidth: 10, maxWidth: 28 },
1458
1414
  { header: "Native session", minWidth: 10, maxWidth: 28 },
1459
1415
  { header: "tmux", minWidth: 6, maxWidth: 22 }
1460
1416
  ], statuses.map((status) => [
@@ -1463,7 +1419,7 @@ function listTaskRoles(args, store, options) {
1463
1419
  status.health,
1464
1420
  taskRoleOpenInputLabel(status),
1465
1421
  taskRoleActiveWorkLabel(status),
1466
- taskRoleLastRunLabel(status),
1422
+ taskRoleLastTurnLabel(status),
1467
1423
  taskRoleNativeSessionLabel(status),
1468
1424
  taskRoleTmuxLabel(status)
1469
1425
  ]), defaultTableWidth())}\n`, { roles: statuses });
@@ -1562,8 +1518,8 @@ function removeTaskRole(args, store, options) {
1562
1518
  taskId: task.id,
1563
1519
  roleName: role.name
1564
1520
  }, "removal");
1565
- if (tx.getActiveAgentRun(task.id, role.name) !== null) {
1566
- throw usageError(`Task Role has an active Run and cannot be removed: ${task.id}/${role.name}.`);
1521
+ if (tx.getActiveTurn(task.id, role.name) !== null) {
1522
+ throw usageError(`Task Role has an active Turn and cannot be removed: ${task.id}/${role.name}.`);
1567
1523
  }
1568
1524
  const sessions = tx.getTaskRoleSessionSet(task.id, role.name);
1569
1525
  if (Object.values(sessions?.sessions ?? {}).some(({ status }) => status === "active")) {
@@ -1612,7 +1568,7 @@ function bindTaskRole(args, store, options) {
1612
1568
  const switched = (() => {
1613
1569
  try {
1614
1570
  return switchActiveRoleAgent(bound, existing, agent.id, {
1615
- activeRun: tx.getActiveAgentRun(task.id, role.name) !== null,
1571
+ activeTurn: tx.getActiveTurn(task.id, role.name) !== null,
1616
1572
  nativeProcessRunning: currentSession !== undefined
1617
1573
  && currentSession.status === "active"
1618
1574
  }, now);
@@ -1705,9 +1661,9 @@ function transferTaskRoleAuthority(args, store, options, action) {
1705
1661
  throw new Error(`Provider Activation is not live: ${task.id}/${role.name}.`);
1706
1662
  }
1707
1663
  if (action === "takeover") {
1708
- const activeRun = tx.getActiveAgentRun(task.id, role.name);
1709
- if (sessions.inFlight === null || activeRun?.id !== sessions.inFlight.runId) {
1710
- throw new Error(`Task Role has no active managed Run for takeover: ${task.id}/${role.name}.`);
1664
+ const activeTurn = tx.getActiveTurn(task.id, role.name);
1665
+ if (activeTurn === null) {
1666
+ throw new Error(`Task Role has no active managed Turn for takeover: ${task.id}/${role.name}.`);
1711
1667
  }
1712
1668
  }
1713
1669
  if (action === "takeover"
@@ -1866,8 +1822,8 @@ function updateWorkScope(args, store, options) {
1866
1822
  const item = requireWorkItem(tx, parsed.positionals[0], options);
1867
1823
  const task = requireTask(tx, item.taskId);
1868
1824
  taskActor(tx, options, task.id);
1869
- if (tx.getActiveAgentRun(task.id, item.assignee ?? "") !== null) {
1870
- throw usageError(`Stop the active Work Item Run before changing scope: ${item.id}.`);
1825
+ if (tx.getActiveTurn(task.id, item.assignee ?? "") !== null) {
1826
+ throw usageError(`Stop the active Work Item Turn before changing scope: ${item.id}.`);
1871
1827
  }
1872
1828
  const requestedProjectIds = (parsed.multiOptions.get("--project") ?? []).map((reference) => {
1873
1829
  const project = resolveProject(task.projectBindings.map(({ projectId }) => requireProject(tx, projectId)), reference);
@@ -1928,7 +1884,7 @@ function updateWork(args, store, options) {
1928
1884
  ? taskFinalReviewContractForMutation(tx, task.id, options)
1929
1885
  : undefined;
1930
1886
  const candidatePolicy = taskFinalContract === undefined
1931
- ? legacyWorkItemReviewConfig(configuredReview)
1887
+ ? workItemReviewConfig(configuredReview)
1932
1888
  : taskFinalReviewConfig(taskFinalContract);
1933
1889
  const projectDelivery = task.projectBindings.length > 0
1934
1890
  && current.writeProjectIds.length > 0;
@@ -1987,14 +1943,10 @@ function updateWork(args, store, options) {
1987
1943
  };
1988
1944
  }
1989
1945
  throw usageError(`Assigned Work Item execution cannot use task work update: ${current.id}. `
1990
- + "Use dispatch and run yield, then let the Task Leader accept or reject the result; "
1946
+ + "Use dispatch, wait for the Turn result, then let the Task Leader accept or reject it; "
1991
1947
  + "use task work retire for an explicit Leader retirement.");
1992
1948
  });
1993
1949
  notifyMailbox(options.runtime, taskMailbox(result.item.taskId), result.item.taskId);
1994
- if (result.reviewDispatch?.run !== null
1995
- && result.reviewDispatch?.run !== undefined) {
1996
- notifyReviewMailbox(options, options.runtime, roleMailbox(result.reviewDispatch.run.taskId, result.reviewDispatch.run.roleName), result.reviewDispatch.run.taskId);
1997
- }
1998
1950
  if (result.item.status === "awaiting_acceptance" && status === "completed") {
1999
1951
  const failure = result.reviewDispatch?.round.status === "failed"
2000
1952
  ? `Review could not start: ${result.reviewDispatch.round.summary}\n`
@@ -2066,7 +2018,7 @@ function dispatchWork(args, store, options) {
2066
2018
  : undefined;
2067
2019
  if (item.status === "failed" && existingGroup !== undefined) {
2068
2020
  throw usageError(`Work Item ${item.id} retains unresolved ExecutionGroup ${existingGroup.id}; `
2069
- + "retry the exact failed AgentRun so completed Lane results remain reusable.");
2021
+ + "retry the exact failed Turn so completed Lane results remain reusable.");
2070
2022
  }
2071
2023
  const expanding = item.status === "running" && existingGroup !== undefined;
2072
2024
  const continuingExploration = currentGroup?.stage !== undefined
@@ -2110,31 +2062,37 @@ function dispatchWork(args, store, options) {
2110
2062
  if (expanding)
2111
2063
  taskActor(tx, options, task.id);
2112
2064
  assertWorkItemDependenciesCompleted(tx, item);
2113
- const workspace = tx.getWorkItemWorkspace(task.id, item.id);
2114
- if (workspace?.owner.type === "work-item"
2065
+ const leaderOwned = item.assignee === "leader";
2066
+ const workspace = leaderOwned
2067
+ ? tx.getTaskWorkspace(task.id)
2068
+ : tx.getWorkItemWorkspace(task.id, item.id);
2069
+ if (!leaderOwned && workspace?.owner.type === "work-item"
2115
2070
  && workspace.owner.workItemId !== item.id) {
2116
2071
  throw usageError(`Task Role ${item.assignee} still uses the isolated worktree for ${workspace.owner.workItemId}; `
2117
2072
  + `cleanup ${workspace.owner.workItemId} before dispatching ${item.id}.`);
2118
2073
  }
2119
2074
  if (task.projectBindings.length > 0) {
2120
- const writable = workspace?.owner.type === "work-item"
2121
- && workspace.owner.workItemId === item.id
2075
+ const ownsExpectedWorkspace = leaderOwned
2076
+ ? workspace?.owner.type === "task" && workspace.owner.taskId === task.id
2077
+ : workspace?.owner.type === "work-item" && workspace.owner.workItemId === item.id;
2078
+ const writable = workspace !== null && ownsExpectedWorkspace
2122
2079
  ? workspace.entries
2123
2080
  .filter(({ access }) => access === "write")
2124
2081
  .map(({ projectId }) => projectId)
2125
2082
  .sort()
2126
2083
  : [];
2127
- const visible = workspace?.owner.type === "work-item"
2128
- && workspace.owner.workItemId === item.id
2084
+ const visible = workspace !== null && ownsExpectedWorkspace
2129
2085
  ? workspace.entries.map(({ projectId }) => projectId).sort()
2130
2086
  : [];
2131
2087
  if (!isDeepStrictEqual(writable, [...item.writeProjectIds].sort())
2132
2088
  || !isDeepStrictEqual(visible, task.projectBindings.map(({ projectId }) => projectId).sort())) {
2133
- throw usageError(`Work Item ${item.id} must be isolated with its approved Project scope before dispatch.`);
2089
+ throw usageError(leaderOwned
2090
+ ? `Leader-owned Work Item ${item.id} must cover the Task main-worktree Project scope.`
2091
+ : `Work Item ${item.id} must be isolated with its approved Project scope before dispatch.`);
2134
2092
  }
2135
2093
  }
2136
2094
  const rawInput = trimmed(parsed.options.get("--input")) ?? item.objective;
2137
- const runWorkspace = workspace ?? tx.getTaskWorkspace(task.id) ?? undefined;
2095
+ const runWorkspace = workspace ?? undefined;
2138
2096
  const lanePlan = normalizedExecutionLanePlan({
2139
2097
  assignee: item.assignee,
2140
2098
  requestedRoles: requestedLaneRoles,
@@ -2142,7 +2100,7 @@ function dispatchWork(args, store, options) {
2142
2100
  existingGroup,
2143
2101
  resolvedRetryGroup,
2144
2102
  status: item.status,
2145
- nextGroupId: `execution-group-${tx.peekNextAgentRunId(task.id)}`,
2103
+ nextGroupId: `execution-group-${tx.peekNextTurnId(task.id)}`,
2146
2104
  retryLaneId: undefined,
2147
2105
  phase: "dispatch"
2148
2106
  });
@@ -2171,17 +2129,17 @@ function dispatchWork(args, store, options) {
2171
2129
  }
2172
2130
  const roles = laneRoles.map((name) => requireRole(tx, task.id, name));
2173
2131
  const plans = roles.map((role) => {
2174
- if (tx.getActiveAgentRun(task.id, role.name) !== null) {
2175
- throw usageError(`${task.id}/${role.name} already has an active run.`);
2132
+ if (tx.getActiveTurn(task.id, role.name) !== null) {
2133
+ throw usageError(`${task.id}/${role.name} already has an active turn.`);
2176
2134
  }
2177
2135
  return {
2178
2136
  role,
2179
- runId: tx.nextAgentRunId(task.id)
2137
+ turnId: tx.nextTurnId(task.id)
2180
2138
  };
2181
2139
  });
2182
2140
  let group = existingGroup;
2183
2141
  if (group === undefined) {
2184
- const groupId = `execution-group-${plans[0].runId}`;
2142
+ const groupId = `execution-group-${plans[0].turnId}`;
2185
2143
  const stage = !startingExploration && !continuingExploration
2186
2144
  ? undefined
2187
2145
  : (() => {
@@ -2248,7 +2206,7 @@ function dispatchWork(args, store, options) {
2248
2206
  : group.lanes.slice(0, plans.length);
2249
2207
  if (lanes.length !== plans.length)
2250
2208
  throw new Error(`ExecutionGroup Lane planning drift: ${group.id}.`);
2251
- const createdRuns = [];
2209
+ const createdTurns = [];
2252
2210
  const queuedAdmissions = [];
2253
2211
  let runningGroup = group;
2254
2212
  const config = tx.getConfig();
@@ -2268,7 +2226,7 @@ function dispatchWork(args, store, options) {
2268
2226
  usage: observedExecutionResourceUsage({
2269
2227
  group,
2270
2228
  stageGroups: [...item.executionGroups, group],
2271
- runs: tx.listAgentRuns(task.id),
2229
+ turns: tx.listTurns(task.id),
2272
2230
  events: tx.listEvents(task.id)
2273
2231
  }),
2274
2232
  now
@@ -2348,30 +2306,21 @@ function dispatchWork(args, store, options) {
2348
2306
  activeResources.push(request);
2349
2307
  const runningLane = lane.status === "failed"
2350
2308
  ? restartExecutionLane(runningGroup, lane.id, {
2351
- runId: plan.runId,
2309
+ turnId: plan.turnId,
2352
2310
  effective,
2353
2311
  workspace: laneWorkspace,
2354
2312
  directive: frozenDirective
2355
2313
  }, now)
2356
2314
  : updateExecutionLane(runningGroup, lane.id, {
2357
2315
  status: "running",
2358
- runId: plan.runId,
2316
+ turnId: plan.turnId,
2359
2317
  effective,
2360
2318
  workspace: laneWorkspace,
2361
2319
  directive: frozenDirective
2362
2320
  }, now);
2363
2321
  runningGroup = runningLane;
2364
- const assignment = createRunAssignment({
2365
- runId: plan.runId,
2366
- roleName: plan.role.name,
2367
- purpose: "execution",
2368
- action: item.status === "failed" ? "repair-work-item" : "execute-work-item",
2369
- subject: {
2370
- taskId: task.id,
2371
- workItemId: item.id,
2372
- executionGroupId: runningGroup.id,
2373
- executionLaneId: lane.id
2374
- },
2322
+ const input = createTurnInput({
2323
+ source: { type: "yui", channel: "workitem-dispatch" },
2375
2324
  directive: `${frozenDirective}\nFrozen target: ${group.target.fingerprint}.`
2376
2325
  + (group.stage === undefined
2377
2326
  ? ""
@@ -2384,7 +2333,7 @@ function dispatchWork(args, store, options) {
2384
2333
  : `\n${candidateConvergenceDirective(group.stage)}`)),
2385
2334
  deltaRefIds: []
2386
2335
  });
2387
- createdRuns.push(createAgentRun(plan.runId, task.id, plan.role.name, dispatchMode, assignment, now, {
2336
+ createdTurns.push(createTurn(plan.turnId, task.id, plan.role.name, dispatchMode, input, now, {
2388
2337
  workItemId: item.id,
2389
2338
  executionGroupId: runningGroup.id,
2390
2339
  executionLaneId: lane.id,
@@ -2439,37 +2388,37 @@ function dispatchWork(args, store, options) {
2439
2388
  tx.saveManagedWorkspace(prepared);
2440
2389
  }
2441
2390
  }
2442
- for (let index = 0; index < createdRuns.length; index += 1) {
2443
- const unboundRun = createdRuns[index];
2444
- const snapshot = freezeRunContextSnapshot(tx, {
2391
+ for (let index = 0; index < createdTurns.length; index += 1) {
2392
+ const unboundTurn = createdTurns[index];
2393
+ const snapshot = freezeTurnContextSnapshot(tx, {
2445
2394
  taskId: task.id,
2446
- roleName: unboundRun.roleName,
2395
+ roleName: unboundTurn.roleName,
2447
2396
  purpose: "execution",
2448
2397
  workItemId: item.id
2449
2398
  }, now, "controller", runningGroup.stage?.contextSnapshotRef);
2450
- const runWithLineage = withAgentRunContextSnapshot(unboundRun, contextSnapshotRef(snapshot), contextSnapshotDeltaRefIds(tx, snapshot));
2451
- createdRuns[index] = runWithLineage;
2452
- const role = requireRole(tx, task.id, unboundRun.roleName);
2453
- tx.saveAgentRun(runWithLineage);
2454
- tx.saveActiveAgentRun(runWithLineage);
2455
- enqueueWork(tx, roleMailbox(task.id, role.name), "run-dispatched", now, [
2456
- runRef(task.id, runWithLineage.id),
2399
+ const turnWithLineage = withTurnContextSnapshot(unboundTurn, contextSnapshotRef(snapshot), contextSnapshotDeltaRefIds(tx, snapshot));
2400
+ createdTurns[index] = turnWithLineage;
2401
+ const role = requireRole(tx, task.id, unboundTurn.roleName);
2402
+ tx.saveTurn(turnWithLineage);
2403
+ tx.saveActiveTurn(turnWithLineage);
2404
+ enqueueWork(tx, roleMailbox(task.id, role.name), "turn-dispatched", now, [
2405
+ turnRef(task.id, turnWithLineage.id),
2457
2406
  workItemRef(task.id, item.id)
2458
2407
  ]);
2459
- recordTaskEvent(tx, task.id, "run.dispatched", runLaunchEventPayload(runWithLineage), now);
2408
+ recordTaskEvent(tx, task.id, "turn.dispatched", turnLaunchEventPayload(turnWithLineage), now);
2460
2409
  }
2461
- return { runs: createdRuns, queuedAdmissions, group: runningGroup };
2410
+ return { turns: createdTurns, queuedAdmissions, group: runningGroup };
2462
2411
  });
2463
- for (const run of dispatch.runs) {
2464
- notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName), run.taskId);
2412
+ for (const turn of dispatch.turns) {
2413
+ notifyMailbox(options.runtime, roleMailbox(turn.taskId, turn.roleName), turn.taskId);
2465
2414
  }
2466
2415
  const backpressure = dispatch.queuedAdmissions.length === 0
2467
2416
  ? ""
2468
2417
  : `; ${dispatch.queuedAdmissions.length} Lane(s) retained pending by Resource Broker`;
2469
- const first = dispatch.runs[0];
2418
+ const first = dispatch.turns[0];
2470
2419
  return first === undefined
2471
2420
  ? `No Lane admitted; ${dispatch.queuedAdmissions.join("; ")}\n`
2472
- : `Dispatch queued for ${first.taskId}/${first.roleName} (${first.id})${dispatch.runs.length > 1 ? `; ${dispatch.runs.length} Lanes` : ""}${backpressure}\n`;
2421
+ : `Dispatch queued for ${first.taskId}/${first.roleName} (${first.id})${dispatch.turns.length > 1 ? `; ${dispatch.turns.length} Lanes` : ""}${backpressure}\n`;
2473
2422
  }
2474
2423
  function resolveWorkExecutionGroup(args, store, options) {
2475
2424
  const usage = "Task work group resolve usage: yui task work group resolve <task>/<work> --decision <accept|reject|retry|blocked> --summary <text> [--lane <lane-id> ...] [--early-stop <marginal-value-percent>].";
@@ -2509,7 +2458,7 @@ function resolveWorkExecutionGroup(args, store, options) {
2509
2458
  }
2510
2459
  const acceptedLaneIds = decision === "accept"
2511
2460
  ? selectedLaneIds ?? group.lanes
2512
- .filter((lane) => lane.status === "yielded" || lane.status === "completed")
2461
+ .filter((lane) => lane.status === "completed")
2513
2462
  .map(({ id }) => id)
2514
2463
  : undefined;
2515
2464
  if (earlyStopMarginalValue !== undefined) {
@@ -2525,7 +2474,7 @@ function resolveWorkExecutionGroup(args, store, options) {
2525
2474
  usage: observedExecutionResourceUsage({
2526
2475
  group,
2527
2476
  stageGroups: item.executionGroups,
2528
- runs: tx.listAgentRuns(task.id),
2477
+ turns: tx.listTurns(task.id),
2529
2478
  events: tx.listEvents(task.id)
2530
2479
  }),
2531
2480
  now
@@ -2594,10 +2543,10 @@ function resolveWorkExecutionGroup(args, store, options) {
2594
2543
  }
2595
2544
  const eligible = resolved.lanes
2596
2545
  .filter((lane) => resolved.resolution?.selectedLaneIds.includes(lane.id) ?? false)
2597
- .map((lane) => lane.runId === undefined ? null : tx.getAgentRun(task.id, lane.runId))
2598
- .find((run) => run !== null && run !== undefined && run.status === "yielded");
2546
+ .map((lane) => lane.turnId === undefined ? null : tx.getTurn(task.id, lane.turnId))
2547
+ .find((run) => run !== null && run !== undefined && run.status === "completed");
2599
2548
  if (eligible === undefined) {
2600
- throw usageError(`ExecutionGroup ${group.id} has no yielded selected Lane Run to capture.`);
2549
+ throw usageError(`ExecutionGroup ${group.id} has no completed selected Lane Turn to capture.`);
2601
2550
  }
2602
2551
  const candidateWorkspace = options.candidateWorkspace;
2603
2552
  if (candidateWorkspace === undefined && options.yuiHome !== undefined) {
@@ -2606,11 +2555,11 @@ function resolveWorkExecutionGroup(args, store, options) {
2606
2555
  const configuredReview = tx.getReviewConfig();
2607
2556
  const taskFinalContract = taskFinalReviewContractForMutation(tx, task.id, options);
2608
2557
  const candidatePolicy = taskFinalContract === undefined
2609
- ? legacyWorkItemReviewConfig(configuredReview)
2558
+ ? workItemReviewConfig(configuredReview)
2610
2559
  : taskFinalReviewConfig(taskFinalContract);
2611
2560
  const candidate = submitWorkItemCandidate(groupedItem, {
2612
2561
  summary: resolutionSummary,
2613
- source: { type: "run", runId: eligible.id },
2562
+ source: { type: "turn", turnId: eligible.id },
2614
2563
  executionGroupId: resolved.id,
2615
2564
  executionLaneId: eligible.executionLaneId,
2616
2565
  ...(candidatePolicy === null ? {} : { reviewPolicy: candidatePolicy }),
@@ -2627,7 +2576,7 @@ function resolveWorkExecutionGroup(args, store, options) {
2627
2576
  ? queueReviewRound(tx, candidate, candidatePolicy, "policy", now)
2628
2577
  : null;
2629
2578
  enqueueWork(tx, leaderMailbox(task.id), "candidate-ready", now, [
2630
- runRef(task.id, eligible.id),
2579
+ turnRef(task.id, eligible.id),
2631
2580
  workItemRef(task.id, item.id)
2632
2581
  ]);
2633
2582
  return {
@@ -2640,9 +2589,6 @@ function resolveWorkExecutionGroup(args, store, options) {
2640
2589
  if (result.waitingForStragglers) {
2641
2590
  return output(`Stopped pending spend for ${result.group.id}; active stragglers are retained until they settle: ${result.group.lanes.filter(({ status }) => status === "running").map(({ id }) => id).join(", ")}\n`, { workItem: result.item, executionGroup: result.group });
2642
2591
  }
2643
- if (result.reviewDispatch?.run !== null && result.reviewDispatch?.run !== undefined) {
2644
- notifyReviewMailbox(options, options.runtime, roleMailbox(result.reviewDispatch.run.taskId, result.reviewDispatch.run.roleName), result.reviewDispatch.run.taskId);
2645
- }
2646
2592
  return output(`Resolved ExecutionGroup ${result.group.id} as ${decision}; Work Item ${result.item.id} is ${result.item.status}.\n`, { workItem: result.item, executionGroup: result.group });
2647
2593
  }
2648
2594
  function aggregateExecutionLaneSummary(leaderSummary, group, selectedLaneIds) {
@@ -2697,7 +2643,7 @@ function executionTargetForReviewRound(task, round, item, candidate) {
2697
2643
  kind: taskScope ? "task-final-review" : "work-item",
2698
2644
  taskId: task.id,
2699
2645
  ...(taskScope ? {} : { workItemId: item.id, candidateId: candidate.id }),
2700
- // A Task-final ReviewRound is itself the immutable semantic target. Runs
2646
+ // A Task-final ReviewRound is itself the immutable semantic target. Turns
2701
2647
  // retry that same revision; a changed frozen Task creates a new Round.
2702
2648
  revision: taskScope ? 1 : candidate.workItemRevision,
2703
2649
  projects,
@@ -2775,8 +2721,8 @@ function acceptWork(args, store, options) {
2775
2721
  recordTaskEvent(tx, item.taskId, "work.accepted", {
2776
2722
  workItemId: item.id,
2777
2723
  candidateId: candidate.id,
2778
- ...(candidate.source.type === "run"
2779
- ? { runId: candidate.source.runId }
2724
+ ...(candidate.source.type === "turn"
2725
+ ? { turnId: candidate.source.turnId }
2780
2726
  : { workItemRevision: String(candidate.workItemRevision) }),
2781
2727
  acceptedBy: actor,
2782
2728
  summary,
@@ -2898,20 +2844,19 @@ function retireWork(args, store, options) {
2898
2844
  + `${activeWorkItemJob.id}/${activeWorkItemJob.status}. `
2899
2845
  + "Cancel or acknowledge it before retiring.");
2900
2846
  }
2901
- for (const run of tx.listAgentRuns(task.id).filter((candidate) => (candidate.status === "active" && candidate.workItemId === item.id))) {
2902
- const terminal = terminalizeExactTaskRun(tx, {
2847
+ for (const run of tx.listTurns(task.id).filter((candidate) => (candidate.status === "active" && candidate.workItemId === item.id))) {
2848
+ const terminal = terminalizeExactTaskTurn(tx, {
2903
2849
  taskId: task.id,
2904
2850
  roleName: run.roleName,
2905
2851
  agentId: run.effective.agentId,
2906
- runId: run.id,
2907
- receiptId: agentRunDeliveryReceiptId(run),
2852
+ turnId: run.id,
2908
2853
  outcome: {
2909
2854
  status: "failed",
2910
2855
  summary: `Work Item retired: ${summary}`
2911
2856
  }
2912
2857
  }, now);
2913
2858
  if (terminal.disposition !== "applied") {
2914
- throw usageError(`Work Item Run changed during retirement: ${run.id}/${terminal.reason ?? "obsolete"}.`);
2859
+ throw usageError(`Work Item Turn changed during retirement: ${run.id}/${terminal.reason ?? "obsolete"}.`);
2915
2860
  }
2916
2861
  }
2917
2862
  const next = retireWorkItem(item, {
@@ -3017,19 +2962,16 @@ function reviewWork(args, store, options) {
3017
2962
  const resourceQueued = activeRound.executionGroup !== undefined
3018
2963
  && activeRound.executionGroup.resolution === undefined
3019
2964
  && activeRound.executionGroup.lanes.some((lane) => (lane.status === "pending"
3020
- && lane.runId === undefined));
3021
- if ((activeRound.status === "pending" && activeRound.reviewerRunId === undefined)
2965
+ && lane.turnId === undefined));
2966
+ if ((activeRound.status === "pending" && activeRound.reviewerTurnId === undefined)
3022
2967
  || (activeRound.status === "running" && resourceQueued)) {
3023
- return { round: activeRound, run: null, resumed: true };
2968
+ return { round: activeRound, resumed: true };
3024
2969
  }
3025
2970
  throw usageError(`ReviewRound is already active: ${activeRound.id}/${activeRound.status}.`);
3026
2971
  }
3027
2972
  const queued = queueReviewRound(tx, item, config, requestedBy, now);
3028
2973
  return { ...queued, resumed: false };
3029
2974
  });
3030
- if (result.run !== null) {
3031
- notifyReviewMailbox(options, options.runtime, roleMailbox(result.run.taskId, result.run.roleName), result.run.taskId);
3032
- }
3033
2975
  if (result.resumed) {
3034
2976
  return output(`Review request ${result.round.id} has pending Lanes; resuming dispatch.\n`, { reviewRound: result.round });
3035
2977
  }
@@ -3039,8 +2981,8 @@ function reviewWork(args, store, options) {
3039
2981
  }
3040
2982
  /**
3041
2983
  * Task-control recovery for a failed Task-final ReviewRound that never
3042
- * created a Reviewer Run. This is deliberately separate from `task run retry`:
3043
- * that command requires an exact failed Agent Run and remains the only retry
2984
+ * created a Reviewer Turn. This is deliberately separate from `task turn retry`:
2985
+ * that command requires an exact failed Turn and remains the only retry
3044
2986
  * path for a failed provider execution. Here the old terminal Round is an
3045
2987
  * immutable anchor and one fresh Round is created only after the same frozen
3046
2988
  * committed Integration/ChangeSet provenance and Reviewer independence fences
@@ -3048,8 +2990,6 @@ function reviewWork(args, store, options) {
3048
2990
  */
3049
2991
  function taskReviewCommand(args, store, options) {
3050
2992
  const [command, ...rest] = args;
3051
- if (command === "rebind")
3052
- return rebindTaskFinalReviewContract(rest, store, options);
3053
2993
  if (command === "request")
3054
2994
  return requestTaskReviewRound(rest, store, options);
3055
2995
  if (command === "force-fresh")
@@ -3064,115 +3004,6 @@ function taskReviewCommand(args, store, options) {
3064
3004
  ? "Task review command is required."
3065
3005
  : `Unknown command: task review ${command}`);
3066
3006
  }
3067
- export function parseTaskFinalReviewContractRebindRequest(args) {
3068
- const usage = "Task review rebind usage: yui task review rebind <task> "
3069
- + "--from-control <digest> --to-control <digest> "
3070
- + "--from-release <release-id> --to-release <release-id>.";
3071
- const parsed = parseTail(args, new Set(["--from-control", "--to-control", "--from-release", "--to-release"]), usage);
3072
- exactPositionals(parsed.positionals, 1, usage);
3073
- return {
3074
- taskId: parsed.positionals[0],
3075
- fromControlPlaneDigest: requiredOption(parsed.options, "--from-control"),
3076
- toControlPlaneDigest: requiredOption(parsed.options, "--to-control"),
3077
- fromReleaseId: requiredOption(parsed.options, "--from-release"),
3078
- toReleaseId: requiredOption(parsed.options, "--to-release")
3079
- };
3080
- }
3081
- function rebindTaskFinalReviewContract(args, store, options) {
3082
- const request = parseTaskFinalReviewContractRebindRequest(args);
3083
- const environment = options.environment ?? {};
3084
- const usage = "Task review rebind usage: yui task review rebind <task> "
3085
- + "--from-control <digest> --to-control <digest> "
3086
- + "--from-release <release-id> --to-release <release-id>.";
3087
- if (!isCurrentGlobalOperator(store, environment)) {
3088
- throw usageError("Task-final Review contract rebind requires the authenticated global Operator session.", usage);
3089
- }
3090
- const proof = options.taskFinalReviewRebindProof;
3091
- if (proof === undefined
3092
- || proof.schemaVersion !== 1
3093
- || proof.taskId !== request.taskId
3094
- || proof.fromControlPlaneDigest !== request.fromControlPlaneDigest
3095
- || proof.toControlPlaneDigest !== request.toControlPlaneDigest
3096
- || proof.fromRelease.releaseId !== request.fromReleaseId
3097
- || proof.toRelease.releaseId !== request.toReleaseId) {
3098
- throw usageError("Task-final Review contract rebind proof is missing or does not match the explicit request.", usage);
3099
- }
3100
- const now = clock(options);
3101
- const result = store.transaction((tx) => {
3102
- const task = requireTask(tx, request.taskId);
3103
- if (task.status !== "active") {
3104
- throw usageError(`Task is not active: ${task.id}.`);
3105
- }
3106
- if (task.projectBindings.length === 0) {
3107
- throw usageError(`Task final-review contract requires a Project-backed Task: ${task.id}.`);
3108
- }
3109
- if (!isCurrentGlobalOperator(tx, environment)) {
3110
- throw usageError("Task-final Review contract rebind Operator identity drifted before commit.");
3111
- }
3112
- const resolution = storedTaskFinalReviewContractResolution(tx, task.id);
3113
- if (resolution === undefined) {
3114
- throw usageError(`Task final-review contract is missing for ${task.id}.`);
3115
- }
3116
- const exactExisting = resolution.rebinds.at(-1);
3117
- if (resolution.effective.controlPlaneDigest === request.toControlPlaneDigest) {
3118
- if (exactExisting !== undefined
3119
- && exactExisting.fromContract.controlPlaneDigest === request.fromControlPlaneDigest
3120
- && exactExisting.toContract.controlPlaneDigest === request.toControlPlaneDigest
3121
- && exactExisting.fromRelease.releaseId === request.fromReleaseId
3122
- && exactExisting.toRelease.releaseId === request.toReleaseId
3123
- && exactExisting.handoverId === proof.handoverId) {
3124
- return { rebind: exactExisting, changed: false };
3125
- }
3126
- throw usageError(`Task final-review contract already targets ${request.toControlPlaneDigest} without the requested proof tuple.`);
3127
- }
3128
- if (resolution.effective.controlPlaneDigest !== request.fromControlPlaneDigest) {
3129
- throw usageError(`Task final-review contract source control-plane digest drifted for ${task.id}.`);
3130
- }
3131
- const activeRound = tx.listReviewRounds(task.id).find((round) => ((round.scope ?? "work-item") === "task"
3132
- && (round.status === "pending" || round.status === "running")));
3133
- if (activeRound !== undefined) {
3134
- throw usageError(`Task final-review contract cannot rebind while ReviewRound ${activeRound.id} is ${activeRound.status}.`);
3135
- }
3136
- const activeLeaderRun = tx.getActiveAgentRun(task.id, "leader");
3137
- if (activeLeaderRun !== null) {
3138
- throw usageError(`Task final-review contract cannot rebind while Leader Run ${activeLeaderRun.id} is active.`);
3139
- }
3140
- const leaderSessions = tx.getTaskRoleSessionSet(task.id, "leader");
3141
- if (leaderSessions !== null) {
3142
- if (leaderSessions.inFlight !== null) {
3143
- throw usageError("Task final-review contract cannot rebind while the Leader runtime has unsettled Run state.");
3144
- }
3145
- const liveLeaderSession = Object.values(leaderSessions.sessions).find(({ status }) => status === "active");
3146
- if (liveLeaderSession !== undefined) {
3147
- throw usageError(`Task final-review contract cannot rebind while Leader Session ${liveLeaderSession.agentId} is ${liveLeaderSession.status}.`);
3148
- }
3149
- if (Object.keys(leaderSessions.sessions).length > 0
3150
- || leaderSessions.providerBinding !== null) {
3151
- tx.saveTaskRoleSessionSet(retireTaskRoleSessionsForWorkspace(leaderSessions, now));
3152
- }
3153
- }
3154
- const rebind = createTaskFinalReviewContractRebind({
3155
- taskId: task.id,
3156
- reviewerRoleName: resolution.effective.reviewerRoleName,
3157
- fromContract: resolution.effective,
3158
- toControlPlaneDigest: proof.toControlPlaneDigest,
3159
- fromRelease: proof.fromRelease,
3160
- toRelease: proof.toRelease,
3161
- handoverId: proof.handoverId,
3162
- authorizedBy: `operator:${environment.YUI_AGENT_ID ?? "unknown"}`
3163
- });
3164
- const event = recordTaskEventRecord(tx, task.id, TASK_FINAL_REVIEW_CONTRACT_REBOUND_EVENT, taskFinalReviewContractRebindPayload(rebind), now);
3165
- enqueueWork(tx, leaderMailbox(task.id), wakeReason("review-contract-rebound", event.id), now, [eventRef(task.id, event.id)]);
3166
- return { rebind, event, changed: true };
3167
- });
3168
- if (result.changed) {
3169
- notifyMailbox(options.runtime, leaderMailbox(request.taskId), request.taskId);
3170
- }
3171
- return output(result.changed
3172
- ? `Rebound Task-final Review contract for ${request.taskId} from `
3173
- + `${request.fromControlPlaneDigest} to ${request.toControlPlaneDigest}.\n`
3174
- : `Task-final Review contract rebind is already recorded for ${request.taskId}.\n`, result);
3175
- }
3176
3007
  function resolveReviewExecutionGroup(args, store, options) {
3177
3008
  const usage = "Task review group resolve usage: yui task review group resolve <task>/<review-round> --decision <accept|reject|blocked> --summary <text> [--lane <lane-id> ...].";
3178
3009
  if (args[0] !== "resolve")
@@ -3203,7 +3034,7 @@ function resolveReviewExecutionGroup(args, store, options) {
3203
3034
  ...(decision === "accept"
3204
3035
  ? {
3205
3036
  selectedLaneIds: selectedLaneIds ?? group.lanes
3206
- .filter((lane) => lane.status === "yielded" || lane.status === "completed")
3037
+ .filter((lane) => lane.status === "completed")
3207
3038
  .map(({ id }) => id)
3208
3039
  }
3209
3040
  : selectedLaneIds === undefined ? {} : { selectedLaneIds })
@@ -3323,7 +3154,7 @@ function disposeReviewFindingCommand(args, store, options) {
3323
3154
  const command = {
3324
3155
  disposition,
3325
3156
  by: actor === "leader"
3326
- ? taskLeaderActionRunId(tx, task.id, options.environment, options.yuiHome) ?? actor
3157
+ ? taskLeaderActionTurnId(tx, task.id, options.environment, options.yuiHome) ?? actor
3327
3158
  : actor,
3328
3159
  ...(parsed.options.get("--note") === undefined ? {} : { note: parsed.options.get("--note") }),
3329
3160
  ...(parsed.options.get("--work-item") === undefined ? {} : { workItemId: parsed.options.get("--work-item") }),
@@ -3496,7 +3327,7 @@ function requestTaskReviewRound(args, store, options) {
3496
3327
  if (requestedLaneRoles.length === 0) {
3497
3328
  if (exact.status === "running"
3498
3329
  && exact.executionGroup?.resolution === undefined
3499
- && exact.executionGroup?.lanes.some((lane) => (lane.status === "pending" && lane.runId === undefined)))
3330
+ && exact.executionGroup?.lanes.some((lane) => (lane.status === "pending" && lane.turnId === undefined)))
3500
3331
  return exact;
3501
3332
  assertTaskReviewRequestLane(tx, task.id, reviewerRoleName, exact);
3502
3333
  return exact;
@@ -3525,7 +3356,7 @@ function requestTaskReviewRound(args, store, options) {
3525
3356
  for (const laneRoleName of laneRoles) {
3526
3357
  const existingLane = exact.executionGroup.lanes.find(({ roleName }) => (roleName === laneRoleName));
3527
3358
  if (existingLane !== undefined) {
3528
- if (existingLane.runId === undefined)
3359
+ if (existingLane.turnId === undefined)
3529
3360
  continue;
3530
3361
  if (existingLane.status !== "running")
3531
3362
  continue;
@@ -3651,7 +3482,7 @@ function requestTaskReviewRound(args, store, options) {
3651
3482
  });
3652
3483
  if ("kind" in round && round.kind === "busy") {
3653
3484
  return output(`Reviewer ${round.reviewerRoleName} is busy (${round.phase}`
3654
- + `${round.activeRunId === undefined ? "" : `; Run ${round.activeRunId}`}); `
3485
+ + `${round.activeTurnId === undefined ? "" : `; Turn ${round.activeTurnId}`}); `
3655
3486
  + `${round.activeReviewRoundId === undefined
3656
3487
  ? ""
3657
3488
  : `active ReviewRound ${round.activeReviewRoundId}; `}`
@@ -3668,7 +3499,7 @@ const TASK_FINAL_FORCE_FRESH_EVENT = "review.task-final-force-fresh-requested";
3668
3499
  /**
3669
3500
  * Creates a distinct full Task-final ReviewRound only when the exact previous
3670
3501
  * terminal Round durably proves that no semantic review was produced. The
3671
- * source Round, Run, findings, workspace, and terminal report remain immutable
3502
+ * source Round, Turn, findings, workspace, and terminal report remain immutable
3672
3503
  * history; the linking Event is both the audit record and the idempotence key.
3673
3504
  */
3674
3505
  function forceFreshTaskReviewRound(args, store, options) {
@@ -3769,7 +3600,7 @@ function forceFreshTaskReviewRound(args, store, options) {
3769
3600
  tx.saveReviewRound(task.id, created);
3770
3601
  recordTaskEvent(tx, task.id, TASK_FINAL_FORCE_FRESH_EVENT, {
3771
3602
  sourceReviewRoundId: source.id,
3772
- ...(source.reviewerRunId === undefined ? {} : { sourceReviewerRunId: source.reviewerRunId }),
3603
+ ...(source.reviewerTurnId === undefined ? {} : { sourceReviewerTurnId: source.reviewerTurnId }),
3773
3604
  reviewRoundId: created.id,
3774
3605
  reviewerRoleName: created.reviewerRoleName,
3775
3606
  taskCandidate: JSON.stringify(created.taskCandidate),
@@ -3787,7 +3618,7 @@ function forceFreshTaskReviewRound(args, store, options) {
3787
3618
  * Conservatively classifies an immutable Task-final Review as replaceable.
3788
3619
  * A failed Round keeps the existing no-semantic-evidence behavior. A completed
3789
3620
  * Round needs stronger, mutually corroborating evidence: an explicit internal
3790
- * context/workspace failure, its exact yielded Run and receipt, matching Lane
3621
+ * context/workspace failure, its exact completed Turn, matching Lane
3791
3622
  * output, and the mechanically emitted empty completion Event.
3792
3623
  * This is command eligibility only; it never rewrites the source outcome or
3793
3624
  * changes the global semantic classifier used by the finding ledger.
@@ -3809,7 +3640,7 @@ export function classifyForceFreshReviewRecovery(store, round) {
3809
3640
  */
3810
3641
  function validateDeltaRecheckRequest(store, taskId, candidate, preflight) {
3811
3642
  if (preflight === undefined) {
3812
- throw usageError("Delta-recheck assessment is missing; the CLI preflight did not run. "
3643
+ throw usageError("Delta-recheck assessment is missing; the CLI preflight did not turn. "
3813
3644
  + "Request a full Review or retry with a current CLI.");
3814
3645
  }
3815
3646
  const previous = store.getReviewRound(taskId, preflight.record.previousReviewRoundId);
@@ -3840,42 +3671,42 @@ function assertTaskReviewRequestLane(store, taskId, reviewerRoleName, reusableRo
3840
3671
  roleName: reviewerRoleName
3841
3672
  }));
3842
3673
  const hasMailboxWork = (mailbox) => (mailbox !== null && workMailboxHasWork(mailbox));
3843
- const activePointer = store.getActiveAgentRun(taskId, reviewerRoleName);
3844
- const activeRuns = store.listAgentRuns(taskId).filter((entry) => (entry.roleName === reviewerRoleName && entry.status === "active"));
3674
+ const activePointer = store.getActiveTurn(taskId, reviewerRoleName);
3675
+ const activeTurns = store.listTurns(taskId).filter((entry) => (entry.roleName === reviewerRoleName && entry.status === "active"));
3845
3676
  if (reusableRound === undefined || reusableRound.status === "pending") {
3846
- if (activePointer !== null || activeRuns.length > 0
3677
+ if (activePointer !== null || activeTurns.length > 0
3847
3678
  || hasMailboxWork(reviewerMailbox) || hasMailboxWork(runtimeMailbox)) {
3848
3679
  throw usageError(`Reviewer has unrelated active execution: ${reviewerRoleName}.`);
3849
3680
  }
3850
3681
  return;
3851
3682
  }
3852
3683
  if (reusableRound.status === "completed") {
3853
- if (activePointer !== null || activeRuns.length > 0
3684
+ if (activePointer !== null || activeTurns.length > 0
3854
3685
  || hasMailboxWork(reviewerMailbox) || hasMailboxWork(runtimeMailbox)) {
3855
3686
  throw usageError(`Reviewer has unrelated active execution: ${reviewerRoleName}.`);
3856
3687
  }
3857
3688
  return;
3858
3689
  }
3859
- const reviewerRunId = reusableRound.reviewerRunId;
3860
- const activeMatches = reviewerRunId !== undefined
3861
- && activePointer?.id === reviewerRunId
3690
+ const reviewerTurnId = reusableRound.reviewerTurnId;
3691
+ const activeMatches = reviewerTurnId !== undefined
3692
+ && activePointer?.id === reviewerTurnId
3862
3693
  && activePointer.status === "active"
3863
- && activeRuns.length === 1
3864
- && activeRuns[0].id === reviewerRunId;
3694
+ && activeTurns.length === 1
3695
+ && activeTurns[0].id === reviewerTurnId;
3865
3696
  if (!activeMatches) {
3866
3697
  throw usageError(`Existing Task-final ReviewRound ${reusableRound.id} is running without its exact Reviewer execution.`);
3867
3698
  }
3868
- const exactRun = runRef(taskId, reviewerRunId);
3699
+ const exactTurn = turnRef(taskId, reviewerTurnId);
3869
3700
  const reviewerPending = reviewerMailbox === null ? null : nextPendingBatch(reviewerMailbox);
3870
3701
  const processingMatches = reviewerMailbox?.processing !== null
3871
3702
  && reviewerMailbox?.processing !== undefined
3872
3703
  && reviewerPending === null
3873
3704
  && reviewerMailbox.processing.executionRef !== undefined
3874
- && isDeepStrictEqual(reviewerMailbox.processing.executionRef, exactRun);
3705
+ && isDeepStrictEqual(reviewerMailbox.processing.executionRef, exactTurn);
3875
3706
  const pendingMatches = reviewerPending !== null
3876
3707
  && reviewerMailbox?.processing === null
3877
3708
  && reviewerPending.requestCount === 1
3878
- && reviewerPending.refs.some((ref) => isDeepStrictEqual(ref, exactRun));
3709
+ && reviewerPending.refs.some((ref) => isDeepStrictEqual(ref, exactTurn));
3879
3710
  if (hasMailboxWork(reviewerMailbox) && !processingMatches && !pendingMatches) {
3880
3711
  throw usageError(`Reviewer mailbox has unrelated active work: ${reviewerRoleName}.`);
3881
3712
  }
@@ -3899,11 +3730,11 @@ function retryFailedTaskReviewRound(args, store, options) {
3899
3730
  if ((round.scope ?? "work-item") !== "task") {
3900
3731
  throw usageError(`ReviewRound ${round.id} is not a failed Task-final ReviewRound.`);
3901
3732
  }
3902
- if (round.reviewerRunId !== undefined) {
3733
+ if (round.reviewerTurnId !== undefined) {
3903
3734
  if (round.status === "completed") {
3904
3735
  throw usageError(`ReviewRound ${round.id} is not retryable from ${round.status}.`);
3905
3736
  }
3906
- throw usageError(`ReviewRound ${round.id} has Reviewer Run ${round.reviewerRunId}; use task run retry instead.`);
3737
+ throw usageError(`ReviewRound ${round.id} has Reviewer Turn ${round.reviewerTurnId}; use task turn retry instead.`);
3907
3738
  }
3908
3739
  if (round.status !== "failed" && round.status !== "pending") {
3909
3740
  throw usageError(`ReviewRound ${round.id} is not retryable from ${round.status}.`);
@@ -3962,10 +3793,10 @@ function retryFailedTaskReviewRound(args, store, options) {
3962
3793
  roleName: reviewer.name
3963
3794
  }));
3964
3795
  const hasMailboxWork = (mailbox) => (mailbox !== null && workMailboxHasWork(mailbox));
3965
- const activeReviewerRuns = tx.listAgentRuns(task.id).filter((entry) => (entry.roleName === reviewer.name && entry.status === "active"));
3966
- const activePointer = tx.getActiveAgentRun(task.id, reviewer.name);
3967
- if (activePointer !== null || activeReviewerRuns.length > 0) {
3968
- throw usageError(`Reviewer Role already has an active run: ${reviewer.name}.`);
3796
+ const activeReviewerTurns = tx.listTurns(task.id).filter((entry) => (entry.roleName === reviewer.name && entry.status === "active"));
3797
+ const activePointer = tx.getActiveTurn(task.id, reviewer.name);
3798
+ if (activePointer !== null || activeReviewerTurns.length > 0) {
3799
+ throw usageError(`Reviewer Role already has an active Turn: ${reviewer.name}.`);
3969
3800
  }
3970
3801
  if (hasMailboxWork(reviewerMailbox) || hasMailboxWork(runtimeMailbox)) {
3971
3802
  throw usageError(`Reviewer has unrelated mailbox work: ${reviewer.name}.`);
@@ -3988,32 +3819,28 @@ function retryFailedTaskReviewRound(args, store, options) {
3988
3819
  ? `Task-final Review retry requested as ${result.round.id}\n`
3989
3820
  : `Task-final Review retry already requested as ${result.round.id} (${result.round.status})\n`, { reviewRound: result.round });
3990
3821
  }
3991
- function taskRunCommand(args, store, options) {
3822
+ function taskTurnCommand(args, store, options) {
3992
3823
  const [command, ...rest] = args;
3993
3824
  if (command === "list")
3994
- return output(listRuns(rest, store, options));
3825
+ return output(listTurns(rest, store, options));
3995
3826
  if (command === "show")
3996
- return showRun(rest, store, options);
3827
+ return showTurn(rest, store, options);
3997
3828
  if (command === "context")
3998
- return runContextCommand(rest, store, options);
3829
+ return turnContextCommand(rest, store, options);
3999
3830
  if (command === "retry")
4000
- return retryRun(rest, store, options);
3831
+ return retryTurn(rest, store, options);
4001
3832
  if (command === "settle")
4002
- return settleStaleFinalReviewRun(rest, store, options);
4003
- if (command === "yield")
4004
- return yieldRun(rest, store, options);
4005
- if (command === "yield-status")
4006
- return yieldRunStatus(rest, store, options);
3833
+ return settleStaleFinalReviewTurn(rest, store, options);
4007
3834
  if (command === "checkpoint")
4008
- return output(checkpointRun(rest, store, options));
3835
+ return output(checkpointTurn(rest, store, options));
4009
3836
  if (command === "retire")
4010
- return retireRun(rest, store, options);
3837
+ return retireTurn(rest, store, options);
4011
3838
  throw usageError(command === undefined
4012
- ? "Task run command is required."
4013
- : `Unknown command: task run ${command}`);
3839
+ ? "Task turn command is required."
3840
+ : `Unknown command: task turn ${command}`);
4014
3841
  }
4015
- function retireRun(args, store, options) {
4016
- const usage = "Task run retire usage: yui task run retire <task>/<run> --reason <text> [--expected-progress-at <timestamp>] [--agent-id <id>] [--adapter-id <id>] [--native-session-id <id>] [--launch-id <id>].";
3842
+ function retireTurn(args, store, options) {
3843
+ const usage = "Task turn retire usage: yui task turn retire <task>/<turn> --reason <text> [--expected-progress-at <timestamp>] [--agent-id <id>] [--adapter-id <id>] [--native-session-id <id>] [--launch-id <id>].";
4017
3844
  const parsed = parseTail(args, new Set([
4018
3845
  "--reason",
4019
3846
  "--expected-progress-at",
@@ -4025,23 +3852,23 @@ function retireRun(args, store, options) {
4025
3852
  ]), usage);
4026
3853
  exactPositionals(parsed.positionals, 1, usage);
4027
3854
  const reason = requiredOption(parsed.options, "--reason");
4028
- const reference = taskRecordReference(parsed.positionals[0], "agentRun", "Agent Run reference", options);
3855
+ const reference = taskRecordReference(parsed.positionals[0], "turn", "Turn reference", options);
4029
3856
  const now = clock(options);
4030
3857
  const result = store.transaction((tx) => {
4031
3858
  const task = requireTask(tx, reference.taskId);
4032
3859
  assertTaskOpen(task);
4033
3860
  const actor = taskActor(tx, options, task.id);
4034
- let run = tx.getAgentRun(task.id, reference.localId);
3861
+ let run = tx.getTurn(task.id, reference.localId);
4035
3862
  if (run === null)
4036
- throw dataError(`Agent Run not found: ${task.id}/${reference.localId}.`);
3863
+ throw dataError(`Turn not found: ${task.id}/${reference.localId}.`);
4037
3864
  const events = tx.listEvents(task.id);
4038
- if (isTaskRecordRetired(events, "agent-run", run.id)) {
4039
- return { task, run, changed: false };
3865
+ if (isTaskRecordRetired(events, "turn", run.id)) {
3866
+ return { task, turn: run, changed: false };
4040
3867
  }
4041
3868
  if (run.status === "active") {
4042
3869
  if (actor === "leader"
4043
- && taskLeaderActionRunId(tx, task.id, options.environment, options.yuiHome) === run.id) {
4044
- throw usageError("A Task Leader cannot retire its own current authority Run.", usage);
3870
+ && taskLeaderActionTurnId(tx, task.id, options.environment, options.yuiHome) === run.id) {
3871
+ throw usageError("A Task Leader cannot retire its own current authority Turn.", usage);
4045
3872
  }
4046
3873
  const expectedProgressAt = requiredOption(parsed.options, parsed.options.has("--expected-progress-at")
4047
3874
  ? "--expected-progress-at"
@@ -4056,31 +3883,31 @@ function retireRun(args, store, options) {
4056
3883
  const sessions = tx.getTaskRoleSessionSet(task.id, run.roleName);
4057
3884
  const session = sessions?.sessions[run.effective.agentId];
4058
3885
  if (session?.nativeSessionId !== undefined && nativeSessionId === undefined) {
4059
- throw usageError("--native-session-id is required for this active Run.", usage);
3886
+ throw usageError("--native-session-id is required for this active Turn.", usage);
4060
3887
  }
4061
3888
  if (session?.nativeSessionId === undefined && launchId === undefined) {
4062
- throw usageError("--launch-id is required for an opaque active Run.", usage);
3889
+ throw usageError("--launch-id is required for an opaque active Turn.", usage);
4063
3890
  }
4064
- const terminal = retireExactActiveAgentRun(tx, {
3891
+ const terminal = retireExactActiveTurn(tx, {
4065
3892
  taskId: task.id,
4066
3893
  roleName: run.roleName,
4067
- runId: run.id,
3894
+ turnId: run.id,
4068
3895
  agentId,
4069
3896
  adapterId,
4070
3897
  ...(nativeSessionId === undefined ? {} : { nativeSessionId }),
4071
3898
  ...(launchId === undefined ? {} : { launchId }),
4072
3899
  expectedProgressAt,
4073
- reason: `Agent Run retired: ${reason}`
3900
+ reason: `Turn retired: ${reason}`
4074
3901
  }, now);
4075
- if (terminal.disposition !== "applied" || terminal.run === null) {
3902
+ if (terminal.disposition !== "applied" || terminal.turn === null) {
4076
3903
  throw usageError(terminal.disposition === "blocked"
4077
- ? `Agent Run retirement is blocked: ${run.id}/${terminal.reason ?? "unsafe"}.`
4078
- : `Agent Run changed during retirement: ${run.id}/${terminal.reason ?? "obsolete"}.`);
3904
+ ? `Turn retirement is blocked: ${run.id}/${terminal.reason ?? "unsafe"}.`
3905
+ : `Turn changed during retirement: ${run.id}/${terminal.reason ?? "obsolete"}.`);
4079
3906
  }
4080
- run = terminal.run;
3907
+ run = terminal.turn;
4081
3908
  }
4082
- recordTaskEvent(tx, task.id, "run.retired", {
4083
- runId: run.id,
3909
+ recordTaskEvent(tx, task.id, "turn.retired", {
3910
+ turnId: run.id,
4084
3911
  reason,
4085
3912
  ...(parsed.options.get("--expected-progress-at") === undefined
4086
3913
  && parsed.options.get("--progress-at") === undefined
@@ -4102,69 +3929,69 @@ function retireRun(args, store, options) {
4102
3929
  tx.saveEvent(task.id, createTaskRecordRetirement({
4103
3930
  eventId: tx.nextEventId(task.id),
4104
3931
  taskId: task.id,
4105
- recordKind: "agent-run",
3932
+ recordKind: "turn",
4106
3933
  recordId: run.id,
4107
3934
  reason,
4108
3935
  retiredBy: actor
4109
3936
  }, now));
4110
- return { task, run, changed: true };
3937
+ return { task, turn: run, changed: true };
4111
3938
  });
4112
3939
  if (result.changed)
4113
3940
  options.runtime?.notifyStateChanged(result.task.id);
4114
- return output(`Retired Agent Run ${result.task.id}/${result.run.id}\n`, {
4115
- agentRun: result.run,
3941
+ return output(`Retired Turn ${result.task.id}/${result.turn.id}\n`, {
3942
+ turn: result.turn,
4116
3943
  retired: true
4117
3944
  });
4118
3945
  }
4119
- function runContextCommand(args, store, options) {
3946
+ function turnContextCommand(args, store, options) {
4120
3947
  const [first, ...rest] = args;
4121
3948
  if (first === "expand") {
4122
- const usage = "Task run context expand usage: yui task run context expand <task>/<run> <ref-id> [--store <store>] [--mode full].";
3949
+ const usage = "Task turn context expand usage: yui task turn context expand <task>/<turn> <ref-id> [--store <store>] [--mode full].";
4123
3950
  const parsed = parseTail(rest, new Set(["--store", "--mode"]), usage);
4124
3951
  exactPositionals(parsed.positionals, 2, usage);
4125
3952
  const mode = parsed.options.get("--mode");
4126
3953
  if (mode !== undefined && mode !== "full") {
4127
- throw usageError("Run Context expansion mode must be full.", usage);
3954
+ throw usageError("Turn Context expansion mode must be full.", usage);
4128
3955
  }
4129
- const { taskId, runId } = parseRunContextReference(parsed.positionals[0]);
4130
- authorizeRunContext(store, taskId, runId, options.environment);
4131
- const expanded = store.transaction((tx) => expandRunContextRef(tx, taskId, runId, parsed.positionals[1], optionalNonEmptyOption(parsed.options, "--store")));
3956
+ const { taskId, turnId } = parseTurnContextReference(parsed.positionals[0]);
3957
+ authorizeTurnContext(store, taskId, turnId, options.environment);
3958
+ const expanded = store.transaction((tx) => expandTurnContextRef(tx, taskId, turnId, parsed.positionals[1], optionalNonEmptyOption(parsed.options, "--store")));
4132
3959
  return output(`${JSON.stringify(expanded, null, 2)}\n`, { context: expanded });
4133
3960
  }
4134
3961
  if (first === "delta") {
4135
3962
  if (rest.length !== 3 || rest[1] !== "--after") {
4136
- throw usageError("Task run context delta usage: yui task run context delta <task>/<run> --after <cursor>.");
3963
+ throw usageError("Task turn context delta usage: yui task turn context delta <task>/<turn> --after <cursor>.");
4137
3964
  }
4138
- const { taskId, runId } = parseRunContextReference(rest[0]);
4139
- authorizeRunContext(store, taskId, runId, options.environment);
4140
- const delta = store.transaction((tx) => (buildRunContextDelta(tx, taskId, runId, rest[2])));
3965
+ const { taskId, turnId } = parseTurnContextReference(rest[0]);
3966
+ authorizeTurnContext(store, taskId, turnId, options.environment);
3967
+ const delta = store.transaction((tx) => (buildTurnContextDelta(tx, taskId, turnId, rest[2])));
4141
3968
  return output(`${JSON.stringify(delta, null, 2)}\n`, { contextDelta: delta });
4142
3969
  }
4143
3970
  if (first === undefined || rest.length !== 0) {
4144
- throw usageError("Task run context usage: yui task run context <task>/<run>.");
3971
+ throw usageError("Task turn context usage: yui task turn context <task>/<turn>.");
4145
3972
  }
4146
- const { taskId, runId } = parseRunContextReference(first);
4147
- authorizeRunContext(store, taskId, runId, options.environment);
4148
- const pack = store.transaction((tx) => buildRunContextPack(tx, taskId, runId));
3973
+ const { taskId, turnId } = parseTurnContextReference(first);
3974
+ authorizeTurnContext(store, taskId, turnId, options.environment);
3975
+ const pack = store.transaction((tx) => buildTurnContextPack(tx, taskId, turnId));
4149
3976
  return output(`${JSON.stringify(pack, null, 2)}\n`, { context: pack });
4150
3977
  }
4151
- function parseRunContextReference(value) {
4152
- const [taskId, runId, extra] = value.split("/");
4153
- if (taskId === undefined || taskId.length === 0 || runId === undefined || runId.length === 0
3978
+ function parseTurnContextReference(value) {
3979
+ const [taskId, turnId, extra] = value.split("/");
3980
+ if (taskId === undefined || taskId.length === 0 || turnId === undefined || turnId.length === 0
4154
3981
  || extra !== undefined) {
4155
- throw usageError(`Run context reference is invalid: ${value}.`);
3982
+ throw usageError(`Turn context reference is invalid: ${value}.`);
4156
3983
  }
4157
- return { taskId, runId };
3984
+ return { taskId, turnId };
4158
3985
  }
4159
- function authorizeRunContext(store, taskId, runId, environment) {
3986
+ function authorizeTurnContext(store, taskId, turnId, environment) {
4160
3987
  const managed = environment?.YUI_SESSION_SCOPE !== undefined
4161
3988
  || environment?.YUI_TASK_ID !== undefined
4162
3989
  || environment?.YUI_ROLE !== undefined
4163
- || environment?.YUI_RUN_ID !== undefined;
3990
+ || environment?.YUI_TURN_ID !== undefined;
4164
3991
  if (!managed)
4165
3992
  return;
4166
- const run = store.getAgentRun(taskId, runId);
4167
- const active = run === null ? null : store.getActiveAgentRun(taskId, run.roleName);
3993
+ const run = store.getTurn(taskId, turnId);
3994
+ const active = run === null ? null : store.getActiveTurn(taskId, run.roleName);
4168
3995
  if (environment?.YUI_SESSION_SCOPE !== "task"
4169
3996
  || environment.YUI_TASK_ID !== taskId
4170
3997
  || run === null
@@ -4172,27 +3999,32 @@ function authorizeRunContext(store, taskId, runId, environment) {
4172
3999
  || environment.YUI_AGENT_ID !== run.effective.agentId
4173
4000
  || environment.YUI_ADAPTER_ID !== run.effective.adapterId
4174
4001
  || run.status !== "active"
4175
- || active?.id !== runId) {
4176
- throw usageError(`Run Context access is not authorized: ${taskId}/${runId}.`);
4002
+ || active?.id !== turnId) {
4003
+ throw usageError(`Turn Context access is not authorized: ${taskId}/${turnId}.`);
4177
4004
  }
4178
4005
  const callerKey = environment.YUI_JOB_CALLER_KEY;
4179
4006
  const expectedCallerKeyHash = store.getJobCallerKeyHash(taskId, run.roleName, run.effective.agentId);
4180
4007
  if (callerKey === undefined
4181
4008
  || expectedCallerKeyHash === null
4182
4009
  || createHash("sha256").update(callerKey).digest("hex") !== expectedCallerKeyHash) {
4183
- throw usageError(`Run Context caller key is not authorized: ${taskId}/${runId}.`);
4010
+ throw usageError(`Turn Context caller key is not authorized: ${taskId}/${turnId}.`);
4184
4011
  }
4185
4012
  }
4186
- function listRuns(args, store, options) {
4187
- exactPositionals(args, 1, "Task run list usage: yui task run list <task>/<work>.");
4188
- const item = requireWorkItem(store, args[0], options);
4189
- const runs = store.listAgentRuns(item.taskId).filter((run) => run.workItemId === item.id);
4190
- if (runs.length === 0)
4191
- return "No runs found.\n";
4192
- const events = store.listEvents(item.taskId);
4193
- return `${renderTable(`Runs: ${item.id}`, [
4194
- { header: "Run", minWidth: 6, maxWidth: 20 },
4013
+ function listTurns(args, store, options) {
4014
+ const usage = "Task turn list usage: yui task turn list <task|task/work>.";
4015
+ exactPositionals(args, 1, usage);
4016
+ const reference = args[0];
4017
+ const task = store.getTask(reference);
4018
+ const item = task === null ? requireWorkItem(store, reference, options) : null;
4019
+ const taskId = task?.id ?? item.taskId;
4020
+ const turns = store.listTurns(taskId).filter((turn) => (item === null || turn.workItemId === item.id));
4021
+ if (turns.length === 0)
4022
+ return "No Turns found.\n";
4023
+ const events = store.listEvents(taskId);
4024
+ return `${renderTable(`Turns: ${item?.id ?? taskId}`, [
4025
+ { header: "Turn", minWidth: 6, maxWidth: 20 },
4195
4026
  { header: "Role", minWidth: 4, maxWidth: 22 },
4027
+ { header: "Subject", minWidth: 7, maxWidth: 24 },
4196
4028
  { header: "Purpose", minWidth: 6, maxWidth: 10 },
4197
4029
  { header: "Mode", minWidth: 4, maxWidth: 8 },
4198
4030
  { header: "Effective", minWidth: 10, maxWidth: 30 },
@@ -4201,38 +4033,39 @@ function listRuns(args, store, options) {
4201
4033
  { header: "Status", minWidth: 6, maxWidth: 12 },
4202
4034
  { header: "History", minWidth: 7, maxWidth: 9 },
4203
4035
  { header: "Summary", minWidth: 8, maxWidth: 58 }
4204
- ], runs.map((run) => [
4036
+ ], turns.map((run) => [
4205
4037
  run.id,
4206
4038
  run.roleName,
4039
+ run.workItemId ?? (run.reviewRoundId === undefined ? "task" : `review:${run.reviewRoundId}`),
4207
4040
  run.purpose,
4208
4041
  run.mode,
4209
4042
  `${run.effective.agentId}/${run.effective.adapterId} r${run.effective.sourceDesiredRevision}`,
4210
4043
  run.effective.profileAccess,
4211
4044
  run.effective.permission.strategy,
4212
4045
  run.status,
4213
- isTaskRecordRetired(events, "agent-run", run.id) ? "retired" : "active",
4214
- run.summary ?? "-"
4046
+ isTaskRecordRetired(events, "turn", run.id) ? "retired" : "active",
4047
+ run.result?.output ?? "-"
4215
4048
  ]), defaultTableWidth())}\n`;
4216
4049
  }
4217
4050
  /**
4218
- * Settles only the known bootstrap split where a failed Task-final Run
4051
+ * Settles only the known bootstrap split where a failed Task-final Turn
4219
4052
  * still owns a running ReviewRound, but the committed Task heads have moved
4220
4053
  * on. This is deliberately narrower than retry: it cannot manufacture a
4221
4054
  * review or fail an arbitrary Round, and every identity/mailbox fence is
4222
4055
  * checked before the old Round changes. The next normal Task completion then
4223
4056
  * creates one fresh Round over the newer frozen Task heads.
4224
4057
  */
4225
- function settleStaleFinalReviewRun(args, store, options) {
4226
- exactPositionals(args, 1, "Task run settle usage: yui task run settle <task>/<run>.");
4058
+ function settleStaleFinalReviewTurn(args, store, options) {
4059
+ exactPositionals(args, 1, "Task turn settle usage: yui task turn settle <task>/<turn>.");
4227
4060
  const now = clock(options);
4228
- const previous = store.transaction((tx) => requireRun(tx, args[0], options));
4061
+ const previous = store.transaction((tx) => requireTurn(tx, args[0], options));
4229
4062
  const result = store.transaction((tx) => {
4230
- const run = tx.getAgentRun(previous.taskId, previous.id);
4063
+ const run = tx.getTurn(previous.taskId, previous.id);
4231
4064
  if (run === null || run.status !== "failed" || run.purpose !== "review") {
4232
- throw usageError(`Run ${previous.id} is not a failed review run.`);
4065
+ throw usageError(`Turn ${previous.id} is not a failed review Turn.`);
4233
4066
  }
4234
4067
  if (run.reviewRoundId === undefined) {
4235
- throw usageError(`Review Run ${run.id} has no ReviewRound.`);
4068
+ throw usageError(`Review Turn ${run.id} has no ReviewRound.`);
4236
4069
  }
4237
4070
  const task = requireTask(tx, run.taskId);
4238
4071
  if (task.status !== "active")
@@ -4240,30 +4073,30 @@ function settleStaleFinalReviewRun(args, store, options) {
4240
4073
  const actor = taskActor(tx, options, task.id);
4241
4074
  const round = tx.getReviewRound(task.id, run.reviewRoundId);
4242
4075
  if (round === null) {
4243
- throw dataError(`ReviewRound not found for run ${run.id}: ${run.reviewRoundId}.`);
4076
+ throw dataError(`ReviewRound not found for Turn ${run.id}: ${run.reviewRoundId}.`);
4244
4077
  }
4245
4078
  if ((round.scope ?? "work-item") !== "task") {
4246
- throw usageError(`Review Run ${run.id} is not a Task-final review; request a new WorkItem review `
4079
+ throw usageError(`Review Turn ${run.id} is not a Task-final review; request a new WorkItem review `
4247
4080
  + "for a new Candidate.");
4248
4081
  }
4249
4082
  const taskFinalContract = taskFinalReviewContractForMutation(tx, task.id, options);
4250
4083
  if (!sameTaskFinalReviewContract(round.taskFinalReviewContract, taskFinalContract)) {
4251
4084
  throw usageError(`Task final-review contract does not match ReviewRound ${round.id}.`);
4252
4085
  }
4253
- // This read-only compare-and-swap fence covers the exact Run/Round,
4086
+ // This read-only compare-and-swap fence covers the exact Turn/Round,
4254
4087
  // Candidate, stored Review workspace, frozen Project scope, and frozen
4255
4088
  // Project heads before any mailbox or Round write.
4256
- const validation = validateExactRunReviewRound(tx, run, { allowTerminal: true });
4089
+ const validation = validateExactTurnReviewRound(tx, run, { allowTerminal: true });
4257
4090
  if (validation.disposition !== "applied" || validation.round === null) {
4258
- throw usageError(`Review Run ${run.id} identity does not match its ReviewRound or frozen Task state changed: ${validation.reason ?? "mismatch"}.`);
4091
+ throw usageError(`Review Turn ${run.id} identity does not match its ReviewRound or frozen Task state changed: ${validation.reason ?? "mismatch"}.`);
4259
4092
  }
4260
- const activeReviewerRun = tx.listAgentRuns(task.id).find((entry) => (entry.purpose === "review"
4093
+ const activeReviewerTurn = tx.listTurns(task.id).find((entry) => (entry.purpose === "review"
4261
4094
  && entry.roleName === round.reviewerRoleName
4262
4095
  && entry.status === "active"));
4263
- const activeRoleRun = tx.getActiveAgentRun(task.id, round.reviewerRoleName);
4264
- if (activeReviewerRun !== undefined || activeRoleRun !== null) {
4265
- const active = activeReviewerRun ?? activeRoleRun;
4266
- throw usageError(`${task.id}/${round.reviewerRoleName} already has active Run ${active.id}.`);
4096
+ const activeRoleTurn = tx.getActiveTurn(task.id, round.reviewerRoleName);
4097
+ if (activeReviewerTurn !== undefined || activeRoleTurn !== null) {
4098
+ const active = activeReviewerTurn ?? activeRoleTurn;
4099
+ throw usageError(`${task.id}/${round.reviewerRoleName} already has active Turn ${active.id}.`);
4267
4100
  }
4268
4101
  const taskRounds = reviewRoundsByIdentity(tx.listReviewRounds(task.id)
4269
4102
  .filter((entry) => (entry.scope ?? "work-item") === "task"));
@@ -4280,13 +4113,13 @@ function settleStaleFinalReviewRun(args, store, options) {
4280
4113
  throw usageError(`Final ReviewRound ${round.id} freezes the current Task candidate; use exact retry.`);
4281
4114
  }
4282
4115
  if (round.status === "failed") {
4283
- const summary = run.summary?.trim();
4116
+ const summary = run.result?.output.trim();
4284
4117
  if (summary !== undefined
4285
4118
  && round.summary === summary
4286
4119
  && round.report === summary
4287
4120
  && (round.checks?.length ?? 0) === 0
4288
4121
  && round.evidenceCommit === undefined) {
4289
- return { run, round, changed: false };
4122
+ return { turn: run, round, changed: false };
4290
4123
  }
4291
4124
  throw usageError(`Final ReviewRound is already terminal: ${round.id}/${round.status}.`);
4292
4125
  }
@@ -4296,11 +4129,11 @@ function settleStaleFinalReviewRun(args, store, options) {
4296
4129
  const reviewerTarget = roleMailbox(task.id, round.reviewerRoleName);
4297
4130
  const reviewerMailbox = tx.getWorkMailbox(reviewerTarget);
4298
4131
  const reviewerPending = reviewerMailbox === null ? null : nextPendingBatch(reviewerMailbox);
4299
- const exactRunRef = runRef(task.id, run.id);
4132
+ const exactTurnRef = turnRef(task.id, run.id);
4300
4133
  if (reviewerMailbox?.processing !== null && reviewerMailbox?.processing !== undefined) {
4301
4134
  const processing = reviewerMailbox.processing;
4302
4135
  if (processing.executionRef === undefined
4303
- || !isDeepStrictEqual(processing.executionRef, exactRunRef)
4136
+ || !isDeepStrictEqual(processing.executionRef, exactTurnRef)
4304
4137
  || reviewerPending !== null) {
4305
4138
  throw usageError(`Reviewer mailbox has unrelated processing work: ${round.reviewerRoleName}.`);
4306
4139
  }
@@ -4308,7 +4141,7 @@ function settleStaleFinalReviewRun(args, store, options) {
4308
4141
  else if (reviewerPending !== null) {
4309
4142
  const pending = reviewerPending;
4310
4143
  if (pending.requestCount !== 1
4311
- || !pending.refs.some((ref) => isDeepStrictEqual(ref, exactRunRef))) {
4144
+ || !pending.refs.some((ref) => isDeepStrictEqual(ref, exactTurnRef))) {
4312
4145
  throw usageError(`Reviewer mailbox has unrelated pending work: ${round.reviewerRoleName}.`);
4313
4146
  }
4314
4147
  }
@@ -4323,10 +4156,10 @@ function settleStaleFinalReviewRun(args, store, options) {
4323
4156
  if (runtimeMailbox !== null && workMailboxHasWork(runtimeMailbox)) {
4324
4157
  throw usageError(`Reviewer runtime lifecycle has pending work: ${round.reviewerRoleName}.`);
4325
4158
  }
4326
- // A matching pending/processing dispatch belongs to this failed Run and
4159
+ // A matching pending/processing dispatch belongs to this failed Turn and
4327
4160
  // is safe to settle here. It is never merged with unrelated mailbox work.
4328
4161
  if (reviewerMailbox !== null && reviewerMailbox !== undefined) {
4329
- const settlement = settleExactWorkExecution(tx, reviewerTarget, exactRunRef);
4162
+ const settlement = settleExactWorkExecution(tx, reviewerTarget, exactTurnRef);
4330
4163
  if (settlement === "absent") {
4331
4164
  throw usageError(`Reviewer mailbox changed before stale final settlement: ${round.reviewerRoleName}.`);
4332
4165
  }
@@ -4334,53 +4167,53 @@ function settleStaleFinalReviewRun(args, store, options) {
4334
4167
  // Preserve any report/check/evidence already attached to the old Round;
4335
4168
  // terminalization only adds the missing failure boundary and end time.
4336
4169
  const summary = round.summary
4337
- ?? run.summary
4338
- ?? `Review Run ${run.id} failed before delivery; committed Task heads changed.`;
4170
+ ?? run.result?.output
4171
+ ?? `Review Turn ${run.id} failed before delivery; committed Task heads changed.`;
4339
4172
  const terminal = finishReviewRound(round, "failed", summary, now, {
4340
4173
  report: round.report ?? summary,
4341
4174
  checks: round.checks ?? [],
4342
4175
  ...(round.evidenceCommit === undefined ? {} : { evidenceCommit: round.evidenceCommit })
4343
4176
  });
4344
4177
  tx.saveReviewRound(task.id, terminal);
4345
- recordTaskEvent(tx, task.id, "run.review-stale-settled", {
4346
- runId: run.id,
4178
+ recordTaskEvent(tx, task.id, "turn.review-stale-settled", {
4179
+ turnId: run.id,
4347
4180
  reviewRoundId: round.id,
4348
4181
  previousTaskCandidate: JSON.stringify(round.taskCandidate),
4349
4182
  currentTaskCandidate: JSON.stringify(currentTaskCandidate),
4350
4183
  settledBy: actor,
4351
4184
  ...(actor === "leader" ? leaderActionEventPayload(tx, task.id, options) : {})
4352
4185
  }, now);
4353
- return { run, round: terminal, changed: true };
4186
+ return { turn: run, round: terminal, changed: true };
4354
4187
  });
4355
4188
  return output(result.changed
4356
- ? `Settled obsolete final Review ${result.round.id} from failed Run ${result.run.id}\n`
4357
- : `Obsolete final Review already settled: ${result.round.id}/${result.run.id}\n`, { reviewRound: result.round, reviewRun: result.run });
4189
+ ? `Settled obsolete final Review ${result.round.id} from failed Turn ${result.turn.id}\n`
4190
+ : `Obsolete final Review already settled: ${result.round.id}/${result.turn.id}\n`, { reviewRound: result.round, reviewTurn: result.turn });
4358
4191
  }
4359
- function retryRun(args, store, options) {
4360
- exactPositionals(args, 1, "Task run retry usage: yui task run retry <task>/<run>.");
4192
+ function retryTurn(args, store, options) {
4193
+ exactPositionals(args, 1, "Task turn retry usage: yui task turn retry <task>/<turn>.");
4361
4194
  const now = clock(options);
4362
- const previous = store.transaction((tx) => requireRun(tx, args[0], options));
4195
+ const previous = store.transaction((tx) => requireTurn(tx, args[0], options));
4363
4196
  if (previous.purpose === "review") {
4364
4197
  return retryFailedReviewRun(previous, store, options, now);
4365
4198
  }
4366
4199
  const retried = store.transaction((tx) => {
4367
4200
  if (previous.status !== "failed") {
4368
- throw usageError(`Run ${previous.id} is not retryable from ${previous.status}.`);
4201
+ throw usageError(`Turn ${previous.id} is not retryable from ${previous.status}.`);
4369
4202
  }
4370
4203
  const task = requireTask(tx, previous.taskId);
4371
4204
  if (task.status !== "active")
4372
4205
  throw usageError(`Task is not active: ${task.id}.`);
4373
- assertTaskExecutionEnabled(task, "retrying a Run");
4206
+ assertTaskExecutionEnabled(task, "retrying a Turn");
4374
4207
  const role = requireRole(tx, task.id, previous.roleName);
4375
- if (tx.getActiveAgentRun(task.id, role.name) !== null) {
4376
- throw usageError(`${task.id}/${role.name} already has an active run.`);
4208
+ if (tx.getActiveTurn(task.id, role.name) !== null) {
4209
+ throw usageError(`${task.id}/${role.name} already has an active turn.`);
4377
4210
  }
4378
4211
  const sessions = tx.getTaskRoleSessionSet(task.id, role.name);
4379
4212
  const retryItem = previous.workItemId === undefined
4380
4213
  ? null
4381
4214
  : tx.getWorkItem(task.id, previous.workItemId);
4382
4215
  if (previous.workItemId !== undefined && retryItem === null) {
4383
- throw dataError(`Work item not found for run ${previous.id}: ${previous.workItemId}.`);
4216
+ throw dataError(`Work item not found for Turn ${previous.id}: ${previous.workItemId}.`);
4384
4217
  }
4385
4218
  const retryLaneBefore = previous.executionLaneId === undefined
4386
4219
  ? undefined
@@ -4398,9 +4231,9 @@ function retryRun(args, store, options) {
4398
4231
  && retryLaneBefore !== undefined
4399
4232
  && retryLaneBefore.id === previous.executionLaneId
4400
4233
  && retryLaneBefore.status === "failed"
4401
- && retryLaneBefore.runId === previous.id);
4234
+ && retryLaneBefore.turnId === previous.id);
4402
4235
  if (!exactCurrentLane) {
4403
- throw usageError(`Run ${previous.id} no longer owns the current failed Execution Lane.`);
4236
+ throw usageError(`Turn ${previous.id} no longer owns the current failed Execution Lane.`);
4404
4237
  }
4405
4238
  const groupedRunningRetry = retryItem?.status === "running"
4406
4239
  && retriesExecutionLane
@@ -4425,7 +4258,7 @@ function retryRun(args, store, options) {
4425
4258
  && (retryGroup === undefined
4426
4259
  || retryGroup.id !== previous.executionGroupId
4427
4260
  || retryLane === undefined))) {
4428
- throw dataError(`Run ${previous.id} execution lineage no longer matches its Work Item.`);
4261
+ throw dataError(`Turn ${previous.id} execution lineage no longer matches its Work Item.`);
4429
4262
  }
4430
4263
  const retryManagedWorkspace = retryLane === undefined
4431
4264
  ? runWorkspace
@@ -4443,9 +4276,9 @@ function retryRun(args, store, options) {
4443
4276
  writableProjectIds: [...(retryItem?.writeProjectIds ?? [])]
4444
4277
  };
4445
4278
  const retryDirective = retryLane?.directive
4446
- ?? previous.assignment.directive
4279
+ ?? previous.inputs[0].input.directive
4447
4280
  ?? retryItem?.objective
4448
- ?? `Retry Run ${previous.id}.`;
4281
+ ?? `Retry Turn ${previous.id}.`;
4449
4282
  let queuedReason;
4450
4283
  if (retryGroup !== undefined && retryLane !== undefined && retryItem !== null) {
4451
4284
  const config = tx.getConfig();
@@ -4466,7 +4299,7 @@ function retryRun(args, store, options) {
4466
4299
  usage: observedExecutionResourceUsage({
4467
4300
  group: retryGroup,
4468
4301
  stageGroups,
4469
- runs: tx.listAgentRuns(task.id),
4302
+ turns: tx.listTurns(task.id),
4470
4303
  events: tx.listEvents(task.id)
4471
4304
  }),
4472
4305
  now
@@ -4496,12 +4329,12 @@ function retryRun(args, store, options) {
4496
4329
  if (admission.decision === "queued")
4497
4330
  queuedReason = admission.reason;
4498
4331
  }
4499
- const runId = queuedReason === undefined ? tx.nextAgentRunId(task.id) : undefined;
4332
+ const turnId = queuedReason === undefined ? tx.nextTurnId(task.id) : undefined;
4500
4333
  const runningGroup = retryGroup === undefined || retryLane === undefined
4501
4334
  ? undefined
4502
4335
  : queuedReason === undefined
4503
4336
  ? restartExecutionLane(retryGroup, retryLane.id, {
4504
- runId: runId,
4337
+ turnId: turnId,
4505
4338
  effective,
4506
4339
  ...(laneWorkspace === undefined ? {} : { workspace: laneWorkspace }),
4507
4340
  directive: retryDirective
@@ -4540,32 +4373,24 @@ function retryRun(args, store, options) {
4540
4373
  reason: queuedReason
4541
4374
  };
4542
4375
  }
4543
- const retrySnapshot = freezeRunContextSnapshot(tx, {
4376
+ const retrySnapshot = freezeTurnContextSnapshot(tx, {
4544
4377
  taskId: task.id,
4545
4378
  roleName: role.name,
4546
4379
  purpose: "execution",
4547
4380
  ...(previous.workItemId === undefined ? {} : { workItemId: previous.workItemId })
4548
4381
  }, now, "controller", retryGroup?.stage?.contextSnapshotRef);
4549
- const assignment = createRunAssignment({
4550
- runId: runId,
4551
- roleName: role.name,
4552
- purpose: previous.assignment.purpose,
4553
- action: previous.workItemId === undefined ? previous.assignment.action : "repair-work-item",
4554
- subject: {
4555
- taskId: task.id,
4556
- ...(previous.workItemId === undefined ? {} : { workItemId: previous.workItemId }),
4557
- ...(runningGroup === undefined ? {} : {
4558
- executionGroupId: runningGroup.id,
4559
- executionLaneId: retryLane.id
4560
- })
4382
+ const input = createTurnInput({
4383
+ source: {
4384
+ type: "yui",
4385
+ channel: previous.workItemId === undefined ? "task-dispatch" : "workitem-dispatch"
4561
4386
  },
4562
- ...(previous.assignment.directive === undefined
4387
+ ...(previous.inputs[0].input.directive === undefined
4563
4388
  ? {}
4564
- : { directive: previous.assignment.directive }),
4389
+ : { directive: previous.inputs[0].input.directive }),
4565
4390
  contextSnapshotRef: contextSnapshotRef(retrySnapshot),
4566
4391
  deltaRefIds: contextSnapshotDeltaRefIds(tx, retrySnapshot)
4567
4392
  });
4568
- const created = createAgentRun(runId, task.id, role.name, roleAgentSessionResumeMode(sessions, effective.agentId, effective), assignment, now, {
4393
+ const created = createTurn(turnId, task.id, role.name, roleAgentSessionResumeMode(sessions, effective.agentId, effective), input, now, {
4569
4394
  ...(previous.workItemId === undefined ? {} : { workItemId: previous.workItemId }),
4570
4395
  ...(runningGroup === undefined ? {} : {
4571
4396
  executionGroupId: runningGroup.id,
@@ -4574,8 +4399,8 @@ function retryRun(args, store, options) {
4574
4399
  ...(retryManagedWorkspace === undefined ? {} : { workspace: retryManagedWorkspace }),
4575
4400
  effective
4576
4401
  });
4577
- tx.saveAgentRun(created);
4578
- tx.saveActiveAgentRun(created);
4402
+ tx.saveTurn(created);
4403
+ tx.saveActiveTurn(created);
4579
4404
  if (previous.workItemId !== undefined && retriedItemWithGroup !== null) {
4580
4405
  const item = retriedItemWithGroup;
4581
4406
  const workspace = tx.getWorkItemWorkspace(task.id, item.id);
@@ -4585,18 +4410,18 @@ function retryRun(args, store, options) {
4585
4410
  + `cannot retry ${item.id}.`);
4586
4411
  }
4587
4412
  }
4588
- enqueueWork(tx, roleMailbox(task.id, role.name), "run-retried", now, [runRef(task.id, created.id)]);
4589
- recordTaskEvent(tx, task.id, "run.retried", {
4590
- ...runLaunchEventPayload(created),
4591
- previousRunId: previous.id
4413
+ enqueueWork(tx, roleMailbox(task.id, role.name), "turn-retried", now, [turnRef(task.id, created.id)]);
4414
+ recordTaskEvent(tx, task.id, "turn.retried", {
4415
+ ...turnLaunchEventPayload(created),
4416
+ previousTurnId: previous.id
4592
4417
  }, now);
4593
- return { kind: "run", run: created };
4418
+ return { kind: "turn", turn: created };
4594
4419
  });
4595
4420
  if (retried.kind === "queued") {
4596
4421
  return output(`Retry retained pending for ${retried.taskId}/${retried.laneId}: ${retried.reason}\n`);
4597
4422
  }
4598
- notifyMailbox(options.runtime, roleMailbox(retried.run.taskId, retried.run.roleName), retried.run.taskId);
4599
- return output(`Retry queued as ${retried.run.id} for ${retried.run.taskId}/${retried.run.roleName}\n`);
4423
+ notifyMailbox(options.runtime, roleMailbox(retried.turn.taskId, retried.turn.roleName), retried.turn.taskId);
4424
+ return output(`Retry queued as ${retried.turn.id} for ${retried.turn.taskId}/${retried.turn.roleName}\n`);
4600
4425
  }
4601
4426
  function actualTaskReviewCandidateForMutation(store, task, options) {
4602
4427
  if (options.actualTaskReviewCandidate === undefined) {
@@ -4646,7 +4471,7 @@ function taskReviewProducerCollision(provenance, reviewerRoleName) {
4646
4471
  *
4647
4472
  * When `expected` is supplied this is also the final dispatch compare-and-swap
4648
4473
  * fence: every bound Project must still point at the exact frozen physical
4649
- * head. Drift fails closed before a Reviewer Run is created.
4474
+ * head. Drift fails closed before a Reviewer Turn is created.
4650
4475
  */
4651
4476
  function taskReviewProvenance(store, task, options, expected) {
4652
4477
  const candidate = actualTaskReviewCandidateForMutation(store, task, options);
@@ -4707,13 +4532,13 @@ function taskReviewProvenance(store, task, options, expected) {
4707
4532
  recordProducer(LEADER_ROLE, item.id);
4708
4533
  continue;
4709
4534
  }
4710
- const sourceRun = store.getAgentRun(task.id, itemCandidate.source.runId);
4535
+ const sourceRun = store.getTurn(task.id, itemCandidate.source.turnId);
4711
4536
  if (sourceRun === null
4712
4537
  || sourceRun.workItemId !== item.id
4713
4538
  || sourceRun.purpose !== "execution"
4714
- || sourceRun.status !== "yielded") {
4715
- throw dataError(`Committed producer Candidate Run is unavailable: `
4716
- + `${item.id}/${itemCandidate.source.runId}.`);
4539
+ || sourceRun.status !== "completed") {
4540
+ throw dataError(`Committed producer Candidate Turn is unavailable: `
4541
+ + `${item.id}/${itemCandidate.source.turnId}.`);
4717
4542
  }
4718
4543
  recordProducer(sourceRun.roleName, item.id);
4719
4544
  }
@@ -4771,7 +4596,7 @@ function queueTaskReviewRound(store, task, config, taskCandidate, options, now,
4771
4596
  const availability = projectReviewerAvailability(store, task.id, config.roleName);
4772
4597
  if (availability.kind === "busy") {
4773
4598
  throw usageError(`Reviewer ${config.roleName} is busy (${availability.phase}`
4774
- + `${availability.activeRunId === undefined ? "" : `; Run ${availability.activeRunId}`}); `
4599
+ + `${availability.activeTurnId === undefined ? "" : `; Turn ${availability.activeTurnId}`}); `
4775
4600
  + `retry after ${availability.retryAfterSeconds}s.`);
4776
4601
  }
4777
4602
  let reviewer = store.getRole(task.id, config.roleName);
@@ -4837,8 +4662,8 @@ function resumablePendingFinalTaskReview(store, task, round, config, taskCandida
4837
4662
  || round.reviewerRoleName !== taskFinalContract.reviewerRoleName) {
4838
4663
  throw usageError(`Pending final ReviewRound Reviewer identity changed: ${round.id}.`);
4839
4664
  }
4840
- if (round.reviewerRunId !== undefined) {
4841
- throw usageError(`Pending final ReviewRound already records Reviewer Run ${round.reviewerRunId}: ${round.id}.`);
4665
+ if (round.reviewerTurnId !== undefined) {
4666
+ throw usageError(`Pending final ReviewRound already records Reviewer Turn ${round.reviewerTurnId}: ${round.id}.`);
4842
4667
  }
4843
4668
  if (!isSameTaskReviewCandidate(round.taskCandidate, taskCandidate)) {
4844
4669
  throw usageError(`Final ReviewRound ${round.id} freezes a candidate that is no longer the current Task candidate.`);
@@ -4848,8 +4673,8 @@ function resumablePendingFinalTaskReview(store, task, round, config, taskCandida
4848
4673
  if (reviewer === null || reviewer.name !== round.reviewerRoleName) {
4849
4674
  throw usageError(`Pending final ReviewRound Reviewer identity changed: ${round.id}.`);
4850
4675
  }
4851
- if (store.getActiveAgentRun(task.id, reviewer.name) !== null) {
4852
- throw usageError(`Reviewer Role already has an active run: ${reviewer.name}.`);
4676
+ if (store.getActiveTurn(task.id, reviewer.name) !== null) {
4677
+ throw usageError(`Reviewer Role already has an active Turn: ${reviewer.name}.`);
4853
4678
  }
4854
4679
  assertPendingFinalReviewWorkspaceEvidence(store, task, round);
4855
4680
  const provenance = taskReviewProvenance(store, task, options, taskCandidate);
@@ -4890,20 +4715,20 @@ function assertPendingFinalReviewWorkspaceEvidence(store, task, round) {
4890
4715
  }
4891
4716
  }
4892
4717
  /**
4893
- * Task-control retry of an exact failed Task-final review Run. The old failed
4894
- * Run remains the attempt trail, while the semantic ReviewRound is reset to
4718
+ * Task-control retry of an exact failed Task-final review Turn. The old failed
4719
+ * Turn remains the attempt trail, while the semantic ReviewRound is reset to
4895
4720
  * pending under its existing identity. Every identity and frozen-head fence is
4896
4721
  * checked inside one transaction so a partial fail-old-without-reset state can
4897
4722
  * never be committed.
4898
4723
  */
4899
4724
  function retryFailedReviewRun(previous, store, options, now) {
4900
4725
  const result = store.transaction((tx) => {
4901
- const run = tx.getAgentRun(previous.taskId, previous.id);
4726
+ const run = tx.getTurn(previous.taskId, previous.id);
4902
4727
  if (run === null || run.status !== "failed" || run.purpose !== "review") {
4903
- throw usageError(`Run ${previous.id} is not a failed review run.`);
4728
+ throw usageError(`Turn ${previous.id} is not a failed review Turn.`);
4904
4729
  }
4905
4730
  if (run.reviewRoundId === undefined) {
4906
- throw usageError(`Review Run ${run.id} has no ReviewRound.`);
4731
+ throw usageError(`Review Turn ${run.id} has no ReviewRound.`);
4907
4732
  }
4908
4733
  const task = requireTask(tx, run.taskId);
4909
4734
  if (task.status !== "active")
@@ -4914,7 +4739,7 @@ function retryFailedReviewRun(previous, store, options, now) {
4914
4739
  throw dataError(`ReviewRound not found for run ${run.id}: ${run.reviewRoundId}.`);
4915
4740
  }
4916
4741
  if ((round.scope ?? "work-item") !== "task") {
4917
- throw usageError(`Review Run ${run.id} is not a Task-final review; request a new WorkItem review `
4742
+ throw usageError(`Review Turn ${run.id} is not a Task-final review; request a new WorkItem review `
4918
4743
  + "for a new Candidate.");
4919
4744
  }
4920
4745
  const retryLane = run.executionLaneId === undefined
@@ -4922,9 +4747,9 @@ function retryFailedReviewRun(previous, store, options, now) {
4922
4747
  : round.executionGroup?.lanes.find(({ id }) => id === run.executionLaneId);
4923
4748
  if (run.executionGroupId !== undefined && (round.executionGroup?.id !== run.executionGroupId
4924
4749
  || retryLane === undefined
4925
- || retryLane.runId !== run.id
4750
+ || retryLane.turnId !== run.id
4926
4751
  || retryLane.roleName !== run.roleName)) {
4927
- throw usageError(`Review Run ${run.id} no longer owns its exact Review Lane attempt.`);
4752
+ throw usageError(`Review Turn ${run.id} no longer owns its exact Review Lane attempt.`);
4928
4753
  }
4929
4754
  const panelGroup = round.executionGroup !== undefined
4930
4755
  && (round.executionGroup.lanes.length > 1
@@ -4933,7 +4758,7 @@ function retryFailedReviewRun(previous, store, options, now) {
4933
4758
  && panelGroup
4934
4759
  && retryLane?.status === "failed";
4935
4760
  if (round.status === "running" && panelGroup && !runningPanelLaneRetry) {
4936
- throw usageError(`Review Run ${run.id} is not the current failed Lane attempt in running Round ${round.id}.`);
4761
+ throw usageError(`Review Turn ${run.id} is not the current failed Lane attempt in running Round ${round.id}.`);
4937
4762
  }
4938
4763
  const retryReviewerRoleName = retryLane?.roleName ?? round.reviewerRoleName;
4939
4764
  const taskFinalContract = taskFinalReviewContractForMutation(tx, task.id, options);
@@ -4977,12 +4802,12 @@ function retryFailedReviewRun(previous, store, options, now) {
4977
4802
  }));
4978
4803
  const hasMailboxWork = (mailbox) => (mailbox !== null && workMailboxHasWork(mailbox));
4979
4804
  const reviewer = requireRole(tx, task.id, retryReviewerRoleName);
4980
- const activePointer = tx.getActiveAgentRun(task.id, reviewer.name);
4981
- const activeReviewerRuns = tx.listAgentRuns(task.id).filter((entry) => (entry.roleName === reviewer.name && entry.status === "active"));
4805
+ const activePointer = tx.getActiveTurn(task.id, reviewer.name);
4806
+ const activeReviewerTurns = tx.listTurns(task.id).filter((entry) => (entry.roleName === reviewer.name && entry.status === "active"));
4982
4807
  // Issue 06: a completed same-Round retry is a no-write idempotent result.
4983
4808
  if (round.status === "completed") {
4984
- if (activePointer !== null || activeReviewerRuns.length > 0) {
4985
- throw usageError(`Reviewer Role already has an active run: ${reviewer.name}.`);
4809
+ if (activePointer !== null || activeReviewerTurns.length > 0) {
4810
+ throw usageError(`Reviewer Role already has an active Turn: ${reviewer.name}.`);
4986
4811
  }
4987
4812
  if (hasMailboxWork(reviewerMailbox) || hasMailboxWork(runtimeMailbox)) {
4988
4813
  throw usageError(`Reviewer has unrelated mailbox work: ${reviewer.name}.`);
@@ -4990,24 +4815,24 @@ function retryFailedReviewRun(previous, store, options, now) {
4990
4815
  return { round, previousRun: run, created: false };
4991
4816
  }
4992
4817
  // Issue 06: a running same Round is reusable only with its exact active
4993
- // Run and mailbox execution. A stranded Run (no active pointer) falls
4818
+ // Turn and mailbox execution. A stranded Turn (no active pointer) falls
4994
4819
  // through and resets the Round after the identity fences below.
4995
4820
  if (round.status === "running" && !runningPanelLaneRetry) {
4996
- const reviewerRunId = round.reviewerRunId;
4997
- const activeMatches = reviewerRunId !== undefined
4821
+ const reviewerTurnId = round.reviewerTurnId;
4822
+ const activeMatches = reviewerTurnId !== undefined
4998
4823
  && activePointer !== null
4999
- && activePointer.id === reviewerRunId
4824
+ && activePointer.id === reviewerTurnId
5000
4825
  && activePointer.status === "active"
5001
- && activeReviewerRuns.length === 1
5002
- && activeReviewerRuns[0].id === reviewerRunId;
5003
- const processingMatches = reviewerRunId !== undefined
4826
+ && activeReviewerTurns.length === 1
4827
+ && activeReviewerTurns[0].id === reviewerTurnId;
4828
+ const processingMatches = reviewerTurnId !== undefined
5004
4829
  && reviewerMailbox?.processing?.executionRef !== undefined
5005
- && isDeepStrictEqual(reviewerMailbox.processing.executionRef, runRef(task.id, reviewerRunId))
4830
+ && isDeepStrictEqual(reviewerMailbox.processing.executionRef, turnRef(task.id, reviewerTurnId))
5006
4831
  && reviewerPending === null;
5007
- const pendingMatches = reviewerRunId !== undefined
4832
+ const pendingMatches = reviewerTurnId !== undefined
5008
4833
  && reviewerMailbox?.processing === null
5009
4834
  && reviewerPending?.requestCount === 1
5010
- && reviewerPending.refs.some((ref) => (isDeepStrictEqual(ref, runRef(task.id, reviewerRunId))));
4835
+ && reviewerPending.refs.some((ref) => (isDeepStrictEqual(ref, turnRef(task.id, reviewerTurnId))));
5011
4836
  if (activePointer !== null
5012
4837
  && (!activeMatches
5013
4838
  || (!processingMatches && !pendingMatches)
@@ -5019,16 +4844,16 @@ function retryFailedReviewRun(previous, store, options, now) {
5019
4844
  }
5020
4845
  // Issue 06: an already-pending Round is the idempotent retry result.
5021
4846
  if (round.status === "pending") {
5022
- if (activePointer !== null || activeReviewerRuns.length > 0) {
5023
- throw usageError(`Reviewer Role already has an active run: ${reviewer.name}.`);
4847
+ if (activePointer !== null || activeReviewerTurns.length > 0) {
4848
+ throw usageError(`Reviewer Role already has an active Turn: ${reviewer.name}.`);
5024
4849
  }
5025
4850
  if (hasMailboxWork(reviewerMailbox) || hasMailboxWork(runtimeMailbox)) {
5026
4851
  throw usageError(`Reviewer has unrelated mailbox work: ${reviewer.name}.`);
5027
4852
  }
5028
4853
  return { round, previousRun: run, created: false };
5029
4854
  }
5030
- if (activePointer !== null || activeReviewerRuns.length > 0) {
5031
- throw usageError(`Reviewer Role already has an active run: ${reviewer.name}.`);
4855
+ if (activePointer !== null || activeReviewerTurns.length > 0) {
4856
+ throw usageError(`Reviewer Role already has an active Turn: ${reviewer.name}.`);
5032
4857
  }
5033
4858
  if (runtimeMailbox?.processing !== null && runtimeMailbox?.processing !== undefined) {
5034
4859
  throw usageError(`Reviewer runtime lifecycle is pending: ${reviewer.name}.`);
@@ -5036,36 +4861,36 @@ function retryFailedReviewRun(previous, store, options, now) {
5036
4861
  if (runtimeMailbox !== null && workMailboxHasWork(runtimeMailbox)) {
5037
4862
  throw usageError(`Reviewer runtime lifecycle has pending work: ${reviewer.name}.`);
5038
4863
  }
5039
- const validation = validateExactRunReviewRound(tx, run, { allowTerminal: true });
4864
+ const validation = validateExactTurnReviewRound(tx, run, { allowTerminal: true });
5040
4865
  if (validation.disposition !== "applied" || validation.round === null) {
5041
- throw usageError(`Review Run ${run.id} identity does not match its ReviewRound or frozen Task state changed: ${validation.reason ?? "mismatch"}.`);
4866
+ throw usageError(`Review Turn ${run.id} identity does not match its ReviewRound or frozen Task state changed: ${validation.reason ?? "mismatch"}.`);
5042
4867
  }
5043
- // A stranded pre-delivery Run can leave its exact pending or processing
4868
+ // A stranded pre-delivery Turn can leave its exact pending or processing
5044
4869
  // dispatch behind; settle only that exact reference while holding the
5045
4870
  // same aggregate lock. Any merged or unrelated batch fails closed.
5046
- const exactOldRunRef = runRef(task.id, run.id);
4871
+ const exactOldTurnRef = turnRef(task.id, run.id);
5047
4872
  if (reviewerMailbox?.processing !== null && reviewerMailbox?.processing !== undefined) {
5048
4873
  if (reviewerMailbox.processing.executionRef === undefined
5049
- || !isDeepStrictEqual(reviewerMailbox.processing.executionRef, exactOldRunRef)
4874
+ || !isDeepStrictEqual(reviewerMailbox.processing.executionRef, exactOldTurnRef)
5050
4875
  || reviewerPending !== null) {
5051
4876
  throw usageError(`Reviewer mailbox is busy: ${reviewer.name}.`);
5052
4877
  }
5053
- settleExactWorkExecution(tx, reviewerMailboxTarget, exactOldRunRef);
4878
+ settleExactWorkExecution(tx, reviewerMailboxTarget, exactOldTurnRef);
5054
4879
  }
5055
4880
  else if (reviewerPending !== null) {
5056
- const exact = reviewerPending.refs.some((ref) => (isDeepStrictEqual(ref, exactOldRunRef)));
4881
+ const exact = reviewerPending.refs.some((ref) => (isDeepStrictEqual(ref, exactOldTurnRef)));
5057
4882
  if (!exact)
5058
4883
  throw usageError(`Reviewer mailbox has unrelated pending work: ${reviewer.name}.`);
5059
4884
  if (reviewerPending.requestCount !== 1) {
5060
4885
  throw usageError(`Reviewer mailbox has merged pending work: ${reviewer.name}.`);
5061
4886
  }
5062
- settleExactWorkExecution(tx, reviewerMailboxTarget, exactOldRunRef);
4887
+ settleExactWorkExecution(tx, reviewerMailboxTarget, exactOldTurnRef);
5063
4888
  }
5064
4889
  if (runningPanelLaneRetry) {
5065
4890
  const resetRound = retryRunningReviewExecutionLane(round, retryLane.id, run.id, now);
5066
4891
  tx.saveReviewRound(task.id, resetRound);
5067
- recordTaskEvent(tx, task.id, "run.review-retried", {
5068
- runId: run.id,
4892
+ recordTaskEvent(tx, task.id, "turn.review-retried", {
4893
+ turnId: run.id,
5069
4894
  reviewRoundId: round.id,
5070
4895
  executionLaneId: retryLane.id
5071
4896
  }, now);
@@ -5076,8 +4901,8 @@ function retryFailedReviewRun(previous, store, options, now) {
5076
4901
  let roundToReset = round;
5077
4902
  if (round.status !== "failed") {
5078
4903
  const summary = round.summary
5079
- ?? run.summary
5080
- ?? `Review Run ${run.id} failed before delivery.`;
4904
+ ?? run.result?.output
4905
+ ?? `Review Turn ${run.id} failed before delivery.`;
5081
4906
  roundToReset = finishReviewRound(round, "failed", summary, now, {
5082
4907
  report: round.report ?? summary,
5083
4908
  checks: round.checks ?? [],
@@ -5090,8 +4915,8 @@ function retryFailedReviewRun(previous, store, options, now) {
5090
4915
  // stable across execution-attempt failures.
5091
4916
  const resetRound = retryTaskReviewRound(roundToReset, requestedBy, run.executionLaneId);
5092
4917
  tx.saveReviewRound(task.id, resetRound);
5093
- recordTaskEvent(tx, task.id, "run.review-retried", {
5094
- runId: run.id,
4918
+ recordTaskEvent(tx, task.id, "turn.review-retried", {
4919
+ turnId: run.id,
5095
4920
  reviewRoundId: round.id
5096
4921
  }, now);
5097
4922
  return { round: resetRound, previousRun: run, created: true };
@@ -5100,39 +4925,31 @@ function retryFailedReviewRun(previous, store, options, now) {
5100
4925
  ? `Review retry requested as ${result.round.id}\n`
5101
4926
  : `Review retry already requested as ${result.round.id} (${result.round.status})\n`, { reviewRound: result.round });
5102
4927
  }
5103
- /** Run details are retained audit evidence; continuation uses durable Task state. */
5104
- function showRun(args, store, options) {
5105
- const usage = "Task run show usage: yui task run show <task>/<run> [--json].";
4928
+ /** Turn details are retained audit evidence; continuation uses durable Task state. */
4929
+ function showTurn(args, store, options) {
4930
+ const usage = "Task turn show usage: yui task turn show <task>/<turn> [--json].";
5106
4931
  const asJson = args.includes("--json");
5107
4932
  const positionals = args.filter((arg) => arg !== "--json");
5108
4933
  exactPositionals(positionals, 1, usage);
5109
4934
  const data = store.transaction((tx) => {
5110
- const run = requireRun(tx, positionals[0], options);
4935
+ const run = requireTurn(tx, positionals[0], options);
5111
4936
  const retirement = tx.listEvents(run.taskId)
5112
4937
  .map(taskRecordRetirement)
5113
- .find((entry) => entry?.recordKind === "agent-run" && entry.recordId === run.id) ?? null;
5114
- const allRejectedYieldAttempts = tx.listEvents(run.taskId)
5115
- .map(rejectedYieldAttemptFromTaskEvent)
5116
- .filter((attempt) => (attempt !== null && attempt.runId === run.id));
5117
- return {
5118
- run,
5119
- retirement,
5120
- rejectedYieldAttemptCount: allRejectedYieldAttempts.length,
5121
- rejectedYieldAttempts: allRejectedYieldAttempts.slice(-16)
5122
- };
4938
+ .find((entry) => entry?.recordKind === "turn" && entry.recordId === run.id) ?? null;
4939
+ return { turn: run, retirement };
5123
4940
  });
5124
4941
  if (asJson) {
5125
4942
  return { kind: "output", output: `${JSON.stringify(data, null, 2)}\n`, data };
5126
4943
  }
5127
4944
  return {
5128
4945
  kind: "output",
5129
- output: renderRunShow(data.run, data.retirement, data.rejectedYieldAttemptCount, data.rejectedYieldAttempts),
4946
+ output: renderTurnShow(data.turn, data.retirement),
5130
4947
  data
5131
4948
  };
5132
4949
  }
5133
- function renderRunShow(run, retirement, rejectedYieldAttemptCount, rejectedYieldAttempts) {
4950
+ function renderTurnShow(run, retirement) {
5134
4951
  const lines = [
5135
- `Run: ${run.id}`,
4952
+ `Turn: ${run.id}`,
5136
4953
  `Task: ${run.taskId}`,
5137
4954
  `Role: ${run.roleName}`,
5138
4955
  `Purpose: ${run.purpose}`,
@@ -5144,589 +4961,48 @@ function renderRunShow(run, retirement, rejectedYieldAttemptCount, rejectedYield
5144
4961
  ]),
5145
4962
  `Effective: ${run.effective.agentId}/${run.effective.adapterId} r${run.effective.sourceDesiredRevision}`,
5146
4963
  `Created: ${run.createdAt}`,
5147
- ...(run.endedAt === undefined ? [] : [`Ended: ${run.endedAt}`]),
5148
- ...(run.pushedAt === undefined ? [] : [`Pushed: ${run.pushedAt}`]),
5149
- ...(run.deliveredAt === undefined
4964
+ ...(run.result === undefined ? [] : [`Ended: ${run.result.completedAt}`]),
4965
+ ...(run.result === undefined || run.result.output.trim().length === 0
5150
4966
  ? []
5151
- : [`Provider accepted (durable): ${run.deliveredAt}`]),
5152
- ...(run.summary === undefined || run.summary.trim().length === 0
5153
- ? []
5154
- : [`Summary: ${run.summary}`]),
5155
- ...(rejectedYieldAttemptCount === 0
5156
- ? []
5157
- : [
5158
- `Rejected yield attempts: ${rejectedYieldAttempts.length} shown of ${rejectedYieldAttemptCount} (unaccepted diagnostic evidence only)`,
5159
- ...rejectedYieldAttempts.slice(-5).flatMap((attempt) => [
5160
- ` ${attempt.attemptedAt}: ${attempt.rejectionReason} (${attempt.attemptDigest.slice(0, 16)})`,
5161
- ` Reviewer output: ${truncateRunShowText(attempt.summary)}`,
5162
- ` Checks: ${attempt.checks.length === 0
5163
- ? "none"
5164
- : attempt.checks.map(({ name, outcome }) => `${name}:${outcome}`).join(", ")}`
5165
- ])
5166
- ])
4967
+ : [`Summary: ${run.result.output}`])
5167
4968
  ];
5168
4969
  return `${lines.join("\n")}\n`;
5169
4970
  }
5170
- function truncateRunShowText(value) {
5171
- const normalized = value.replaceAll(/\s+/gu, " ").trim();
5172
- return normalized.length <= 400 ? normalized : `${normalized.slice(0, 399)}…`;
5173
- }
5174
- /**
5175
- * Issue 04: builds the terminal yield outcome from the command inputs. The
5176
- * same construction feeds both the first commit and the idempotent replay, so
5177
- * a resend always hashes to the same digest.
5178
- */
5179
- function buildYieldOutcome(run, inputSummary, options) {
5180
- let yieldedReport;
5181
- if (run.purpose === "review"
5182
- || (run.purpose === "execution" && run.executionGroupId !== undefined)) {
5183
- yieldedReport = parseReviewYieldReport(inputSummary);
5184
- }
5185
- const summary = yieldedReport?.summary ?? inputSummary;
5186
- if (yieldedReport === undefined)
5187
- return { summary };
5188
- return {
5189
- summary,
5190
- reviewResult: {
5191
- report: yieldedReport.report,
5192
- checks: yieldedReport.checks,
5193
- ...(yieldedReport.findings === undefined ? {} : { findings: yieldedReport.findings }),
5194
- ...(yieldedReport.evidence === undefined ? {} : { evidence: yieldedReport.evidence }),
5195
- ...(run.purpose === "review"
5196
- && options.reviewWorkspaceResult?.evidenceCommit === undefined
5197
- ? {}
5198
- : run.purpose === "review"
5199
- ? { evidenceCommit: options.reviewWorkspaceResult.evidenceCommit }
5200
- : yieldedReport.evidenceCommit === undefined
5201
- ? {}
5202
- : { evidenceCommit: yieldedReport.evidenceCommit }),
5203
- ...(options.executionLaneGitSnapshot === undefined
5204
- || options.executionLaneGitSnapshot === null
5205
- ? {}
5206
- : { gitSnapshot: options.executionLaneGitSnapshot }),
5207
- ...(yieldedReport.deltaDisposition === undefined
5208
- ? {}
5209
- : { deltaDisposition: yieldedReport.deltaDisposition }),
5210
- ...(yieldedReport.deltaReasoning === undefined
5211
- ? {}
5212
- : { deltaReasoning: yieldedReport.deltaReasoning })
5213
- }
5214
- };
5215
- }
5216
- /**
5217
- * Issue 04: replays an already-committed yield. Returns the committed receipt
5218
- * for the same outcome, fails closed for a different outcome, or returns
5219
- * `null` to keep the legacy "already terminal" behavior.
5220
- */
5221
- function replayYieldReceipt(run, inputSummary, options) {
5222
- if (run.yieldReceipt === undefined)
5223
- return null;
5224
- const outcome = buildYieldOutcome(run, inputSummary, options);
5225
- const match = matchYieldReceipt(run.yieldReceipt, {
5226
- status: "yielded",
5227
- summary: outcome.summary,
5228
- ...(outcome.reviewResult === undefined ? {} : { reviewResult: outcome.reviewResult })
5229
- });
5230
- if (match === null)
5231
- return null;
5232
- if (match.kind === "digest-mismatch") {
5233
- throw usageError(`Run ${run.id} is already terminal with a different yield outcome. `
5234
- + `Existing receipt: ${match.existing.receiptId} (request ${match.existing.requestId}).`);
5235
- }
5236
- return output(`Run ${run.id} yield already committed.\n`
5237
- + `Receipt: ${match.receipt.receiptId}\n`
5238
- + `Request: ${match.receipt.requestId}\n`, { receipt: match.receipt });
5239
- }
5240
- /**
5241
- * Issue 04: `yui task run yield-status <task>/<run>` — returns the committed
5242
- * yield receipt for a terminal Run, or the current status for an active Run.
5243
- */
5244
- function yieldRunStatus(args, store, options) {
5245
- const usage = "Task run yield-status usage: yui task run yield-status <task>/<run>.";
5246
- const parsed = parseTail(args, new Set(), usage);
5247
- exactPositionals(parsed.positionals, 1, usage);
5248
- const run = requireRun(store, parsed.positionals[0], options);
5249
- if (run.status === "active") {
5250
- return output(`Run ${run.id} is active; no yield receipt yet.\n`, {
5251
- runId: run.id,
5252
- status: run.status
5253
- });
5254
- }
5255
- if (run.yieldReceipt === undefined) {
5256
- return output(`Run ${run.id} is ${run.status}; no yield receipt recorded.\n`, {
5257
- runId: run.id,
5258
- status: run.status
5259
- });
5260
- }
5261
- return output(`Run ${run.id} yield receipt:\n`
5262
- + `Receipt: ${run.yieldReceipt.receiptId}\n`
5263
- + `Request: ${run.yieldReceipt.requestId}\n`
5264
- + `Committed: ${run.yieldReceipt.committedAt}\n`, { receipt: run.yieldReceipt });
5265
- }
5266
- function recordRejectedReviewYield(store, input) {
5267
- if (input.run.purpose !== "review" || input.run.reviewRoundId === undefined) {
5268
- throw new Error(`Only a Review Run can record rejected yield evidence: ${input.run.id}.`);
5269
- }
5270
- const receiptId = agentRunDeliveryReceiptId(input.run);
5271
- const sessions = store.getTaskRoleSessionSet(input.run.taskId, input.run.roleName);
5272
- const durableSession = sessions?.sessions[input.run.effective.agentId];
5273
- const attempt = createRejectedYieldAttempt({
5274
- taskId: input.run.taskId,
5275
- runId: input.run.id,
5276
- roleName: input.run.roleName,
5277
- reviewRoundId: input.run.reviewRoundId,
5278
- receiptId,
5279
- rejectionReason: input.rejectionReason,
5280
- summary: input.outcome.summary,
5281
- ...(input.outcome.reviewResult === undefined
5282
- ? {}
5283
- : { reviewResult: input.outcome.reviewResult }),
5284
- ...(input.nativeSessionId === undefined
5285
- ? {}
5286
- : { nativeSessionId: input.nativeSessionId }),
5287
- ...(input.launchId === undefined ? {} : { launchId: input.launchId }),
5288
- ...(durableSession?.nativeSessionId === undefined
5289
- ? {}
5290
- : { durableNativeSessionId: durableSession.nativeSessionId }),
5291
- ...(durableSession?.launchId === undefined
5292
- ? {}
5293
- : { durableLaunchId: durableSession.launchId }),
5294
- ...(sessions?.inFlight?.runId === undefined
5295
- ? {}
5296
- : { inFlightRunId: sessions.inFlight.runId }),
5297
- ...(sessions?.inFlight?.receiptId === undefined
5298
- ? {}
5299
- : { inFlightReceiptId: sessions.inFlight.receiptId }),
5300
- ...(input.run.assignment.contextSnapshotRef === undefined
5301
- ? {}
5302
- : { contextSnapshot: input.run.assignment.contextSnapshotRef }),
5303
- activeRun: input.activeRun === null
5304
- ? null
5305
- : {
5306
- id: input.activeRun.id,
5307
- receiptId: agentRunDeliveryReceiptId(input.activeRun),
5308
- ...(input.activeRun.assignment.contextSnapshotRef === undefined
5309
- ? {}
5310
- : { contextSnapshot: input.activeRun.assignment.contextSnapshotRef })
5311
- },
5312
- attemptedAt: input.now
5313
- });
5314
- const existing = store.listEvents(input.run.taskId).find((event) => (event.type === RUN_YIELD_REJECTED_EVENT
5315
- && event.payload.runId === input.run.id
5316
- && event.payload.receiptId === receiptId
5317
- && event.payload.attemptDigest === attempt.attemptDigest));
5318
- if (existing !== undefined) {
5319
- const persisted = rejectedYieldAttemptFromTaskEvent(existing);
5320
- if (persisted === null) {
5321
- throw new Error(`Rejected yield attempt is malformed: ${existing.id}.`);
5322
- }
5323
- return { attempt: persisted, event: existing, created: false };
5324
- }
5325
- const event = createTaskEvent(store.nextEventId(input.run.taskId), input.run.taskId, RUN_YIELD_REJECTED_EVENT, rejectedYieldAttemptEventPayload(attempt), input.now);
5326
- store.saveEvent(input.run.taskId, event);
5327
- routeRoleEvent(store, event, input.run.roleName, "run-yield-rejected", input.now);
5328
- return { attempt, event, created: true };
5329
- }
5330
- function yieldRun(args, store, options) {
5331
- const usage = "Task run yield usage: yui task run yield <task>/<run> (--summary <text>|--summary-file <path|->).";
5332
- const parsed = parseTail(args, new Set(["--summary", "--summary-file"]), usage);
5333
- exactPositionals(parsed.positionals, 1, usage);
5334
- const inputSummary = readCommandText(parsed.options.get("--summary"), parsed.options.get("--summary-file"), "--summary", usage);
5335
- const now = clock(options);
5336
- // Issue 04: an already-terminal Run may be a lost-response resend. Match
5337
- // the presented outcome against the committed receipt before opening the
5338
- // transaction; the receipt is immutable once committed.
5339
- const existing = requireRun(store, parsed.positionals[0], options);
5340
- if (existing.status !== "active") {
5341
- const replayed = replayYieldReceipt(existing, inputSummary, options);
5342
- if (replayed !== null)
5343
- return replayed;
5344
- throw usageError(`Run ${existing.id} is already terminal: ${existing.status}.`);
5345
- }
5346
- const yielded = store.transaction((tx) => {
5347
- const active = requireRun(tx, parsed.positionals[0], options);
5348
- if (active.status !== "active") {
5349
- throw usageError(`Run ${active.id} is already terminal: ${active.status}.`);
5350
- }
5351
- if (active.pushedAt === undefined) {
5352
- throw usageError(`Run ${active.id} delivery is still pending.`);
5353
- }
5354
- const task = requireTask(tx, active.taskId);
5355
- if (task.status !== "active")
5356
- throw usageError(inactiveTaskMessage(task, "yielding a run"));
5357
- const role = requireRole(tx, task.id, active.roleName);
5358
- const pointer = activeRunPointer(tx, active);
5359
- const taskFinalContract = active.purpose === "execution"
5360
- && active.workItemId !== undefined
5361
- ? taskFinalReviewContractForMutation(tx, task.id, options)
5362
- : undefined;
5363
- const wasStalled = isRoleRunStalled(tx.listEvents(task.id), active.id);
5364
- let yieldedReport;
5365
- if (active.purpose === "review"
5366
- || (active.purpose === "execution" && active.executionGroupId !== undefined)) {
5367
- try {
5368
- yieldedReport = parseReviewYieldReport(inputSummary);
5369
- }
5370
- catch (error) {
5371
- throw usageError(error instanceof Error ? error.message : String(error));
5372
- }
5373
- }
5374
- const groupedLaneRun = active.executionGroupId !== undefined
5375
- && active.executionLaneId !== undefined;
5376
- if (active.purpose !== "review"
5377
- && groupedLaneRun
5378
- && options.yuiHome !== undefined
5379
- && options.executionLaneGitSnapshot === undefined) {
5380
- throw usageError(`Managed Execution Lane Git snapshot preflight is missing: ${active.id}.`);
5381
- }
5382
- if (active.purpose === "review") {
5383
- if (options.reviewWorkspaceResult === undefined) {
5384
- throw usageError(`Review Run requires managed workspace preflight: ${active.id}.`);
5385
- }
5386
- if (yieldedReport?.evidenceCommit !== undefined
5387
- && yieldedReport.evidenceCommit !== options.reviewWorkspaceResult.evidenceCommit) {
5388
- throw usageError(`Reported Review evidence commit does not match the managed workspace: ${active.id}.`);
5389
- }
5390
- }
5391
- const yieldOutcome = buildYieldOutcome(active, inputSummary, options);
5392
- const summary = yieldOutcome.summary;
5393
- const terminalization = terminalizeExactTaskRun(tx, {
5394
- taskId: task.id,
5395
- roleName: role.name,
5396
- agentId: active.effective.agentId,
5397
- runId: active.id,
5398
- receiptId: agentRunDeliveryReceiptId(active),
5399
- ...(active.purpose !== "review" || options.environment?.YUI_NATIVE_SESSION_ID === undefined
5400
- ? {}
5401
- : { nativeSessionId: options.environment.YUI_NATIVE_SESSION_ID }),
5402
- ...(active.purpose !== "review" || options.environment?.YUI_LAUNCH_ID === undefined
5403
- ? {}
5404
- : { launchId: options.environment.YUI_LAUNCH_ID }),
5405
- outcome: { status: "yielded", summary },
5406
- ...(yieldOutcome.reviewResult === undefined
5407
- ? {}
5408
- : { reviewResult: yieldOutcome.reviewResult })
5409
- }, now);
5410
- if (terminalization.disposition !== "applied" || terminalization.run === null) {
5411
- if (active.purpose === "review" && active.reviewRoundId !== undefined) {
5412
- const rejected = recordRejectedReviewYield(tx, {
5413
- run: active,
5414
- activeRun: pointer,
5415
- rejectionReason: terminalization.reason ?? "obsolete",
5416
- outcome: yieldOutcome,
5417
- ...(options.environment?.YUI_NATIVE_SESSION_ID === undefined
5418
- ? {}
5419
- : { nativeSessionId: options.environment.YUI_NATIVE_SESSION_ID }),
5420
- ...(options.environment?.YUI_LAUNCH_ID === undefined
5421
- ? {}
5422
- : { launchId: options.environment.YUI_LAUNCH_ID }),
5423
- now
5424
- });
5425
- return {
5426
- kind: "rejected",
5427
- run: active,
5428
- rejected,
5429
- notifyLeader: rejected.created
5430
- };
5431
- }
5432
- throw usageError(`Run ${active.id} no longer matches its exact execution fence: `
5433
- + `${terminalization.reason ?? "obsolete"}.`);
5434
- }
5435
- const terminal = terminalization.run;
5436
- let reviewGroupPending = false;
5437
- let reviewGroupReady = false;
5438
- if (wasStalled) {
5439
- recordTaskEvent(tx, task.id, RUN_RECOVERED_EVENT, {
5440
- runId: terminal.id,
5441
- roleName: role.name,
5442
- progressAt: now.toISOString(),
5443
- kind: "yield"
5444
- }, now);
5445
- }
5446
- if (active.purpose === "review") {
5447
- const round = active.reviewRoundId === undefined
5448
- ? null
5449
- : tx.getReviewRound(task.id, active.reviewRoundId);
5450
- const panel = round?.executionGroup !== undefined
5451
- && (round.executionGroup.lanes.length > 1
5452
- || round.executionGroup.strategy.mode === "adaptive");
5453
- if (round === null || round.reviewerRunId === undefined) {
5454
- throw usageError(`ReviewRun did not complete its exact ReviewRound: ${active.id}.`);
5455
- }
5456
- if (panel) {
5457
- reviewGroupPending = round.status !== "completed";
5458
- reviewGroupReady = reviewGroupPending
5459
- && round.executionGroup.lanes.every(({ status }) => (["yielded", "completed", "failed"].includes(status)));
5460
- if (reviewGroupReady) {
5461
- const panelGroup = round.executionGroup;
5462
- recordTaskEvent(tx, task.id, "review-group-ready", {
5463
- reviewRoundId: round.id,
5464
- ...(round.workItemId === undefined ? {} : { workItemId: round.workItemId }),
5465
- ...(round.candidateId === undefined ? {} : { candidateId: round.candidateId }),
5466
- executionGroupId: panelGroup.id,
5467
- terminalLanes: String(panelGroup.lanes
5468
- .filter(({ status }) => ["yielded", "completed", "failed"].includes(status)).length),
5469
- laneCount: String(panelGroup.lanes.length)
5470
- }, now);
5471
- }
5472
- }
5473
- else {
5474
- if (round.status !== "completed" || round.reviewerRunId !== active.id) {
5475
- throw usageError(`ReviewRun did not complete its exact ReviewRound: ${active.id}.`);
5476
- }
5477
- recordTaskEvent(tx, task.id, "review.completed", {
5478
- reviewRoundId: round.id,
5479
- ...(round.workItemId === undefined ? {} : { workItemId: round.workItemId }),
5480
- ...(round.candidateId === undefined ? {} : { candidateId: round.candidateId }),
5481
- reviewBaseCommit: round.reviewBaseCommit,
5482
- evidenceCommit: round.evidenceCommit ?? "none",
5483
- checks: round.checks?.map(({ name, outcome }) => `${name}:${outcome}`)
5484
- .join(",") || "none",
5485
- ...(round.deltaRecheck === undefined
5486
- ? {}
5487
- : {
5488
- reviewMode: "delta-recheck",
5489
- deltaDisposition: round.deltaRecheck.disposition ?? "requires-full-review",
5490
- previousReviewRoundId: round.deltaRecheck.previousReviewRoundId,
5491
- diffDigest: round.deltaRecheck.diffDigest
5492
- })
5493
- }, now);
5494
- }
5495
- }
5496
- let automaticReview = null;
5497
- let submittedItem = null;
5498
- let executionGroupPending = false;
5499
- let executionGroupReady = false;
5500
- if (active.purpose === "review") {
5501
- // Saving the terminal review Run completes its ReviewRound in the same
5502
- // aggregate transaction. The WorkItem remains the candidate under review.
5503
- }
5504
- else if (active.workItemId !== undefined) {
5505
- const item = tx.getWorkItem(task.id, active.workItemId);
5506
- if (item === null)
5507
- throw dataError(`Work item not found for run ${active.id}: ${active.workItemId}.`);
5508
- const currentGroup = currentWorkItemExecutionGroup(item);
5509
- const multiLaneGroup = currentGroup !== undefined
5510
- && (currentGroup.stage !== undefined
5511
- || currentGroup.lanes.length > 1
5512
- || currentGroup.strategy.mode === "adaptive")
5513
- && currentGroup.resolution === undefined;
5514
- if (multiLaneGroup) {
5515
- executionGroupPending = true;
5516
- executionGroupReady = currentGroup.lanes.every(({ status }) => (["yielded", "completed", "failed", "skipped"].includes(status)));
5517
- if (executionGroupReady) {
5518
- recordTaskEvent(tx, task.id, "execution-group-ready", {
5519
- workItemId: item.id,
5520
- executionGroupId: currentGroup.id,
5521
- terminalLanes: String(currentGroup.lanes.length),
5522
- laneCount: String(currentGroup.lanes.length)
5523
- }, now);
5524
- }
5525
- }
5526
- else {
5527
- const configuredReview = tx.getReviewConfig();
5528
- const candidatePolicy = taskFinalContract === undefined
5529
- ? legacyWorkItemReviewConfig(configuredReview)
5530
- : taskFinalReviewConfig(taskFinalContract);
5531
- const projectDelivery = task.projectBindings.length > 0
5532
- && item.writeProjectIds.length > 0;
5533
- const candidateRequired = role.name !== LEADER_ROLE
5534
- || candidatePolicy !== null
5535
- || projectDelivery;
5536
- let candidateSourceItem = item;
5537
- if (candidateRequired
5538
- && currentGroup !== undefined
5539
- && currentGroup.resolution === undefined) {
5540
- const resolvedSingle = resolveExecutionGroup(currentGroup, {
5541
- decision: "accept",
5542
- summary,
5543
- selectedLaneIds: [active.executionLaneId]
5544
- }, now);
5545
- candidateSourceItem = updateWorkItemExecutionGroup(item, resolvedSingle, now);
5546
- tx.saveWorkItem(task.id, candidateSourceItem);
5547
- }
5548
- const yieldedItem = candidateRequired
5549
- ? submitWorkItemCandidate(candidateSourceItem, {
5550
- summary,
5551
- source: { type: "run", runId: terminal.id },
5552
- ...(candidatePolicy === null ? {} : { reviewPolicy: candidatePolicy }),
5553
- ...(taskFinalContract === undefined
5554
- ? {}
5555
- : { taskFinalReviewContract: taskFinalContract }),
5556
- ...(active.executionGroupId === undefined
5557
- ? {}
5558
- : {
5559
- executionGroupId: active.executionGroupId,
5560
- executionLaneId: active.executionLaneId
5561
- }),
5562
- ...(active.workspace === undefined
5563
- || (active.workspace.owner.type === "task"
5564
- && active.workspace.entries.length === 0)
5565
- ? {}
5566
- : { workspace: active.workspace }),
5567
- ...(options.candidateGitSnapshot === undefined
5568
- ? {}
5569
- : { gitSnapshot: options.candidateGitSnapshot })
5570
- }, now)
5571
- : updateWorkItemStatus(item, "completed", now, summary);
5572
- tx.saveWorkItem(task.id, yieldedItem);
5573
- if (candidateRequired)
5574
- submittedItem = yieldedItem;
5575
- if (candidatePolicy?.trigger === "always") {
5576
- automaticReview = { item: yieldedItem, config: candidatePolicy };
5577
- }
5578
- }
5579
- }
5580
- else if (role.name !== LEADER_ROLE) {
5581
- throw usageError(`Run ${active.id} is not a work run.`);
5582
- }
5583
- const reviewDispatch = automaticReview === null
5584
- ? null
5585
- : queueReviewRound(tx, automaticReview.item, automaticReview.config, "policy", now);
5586
- const leaderHandoff = active.purpose === "review"
5587
- ? (reviewGroupPending
5588
- ? (reviewGroupReady ? "review-group-ready" : null)
5589
- : "review-result")
5590
- : submittedItem === null
5591
- && executionGroupPending
5592
- ? (executionGroupReady ? "execution-group-ready" : null)
5593
- : submittedItem === null
5594
- || (reviewDispatch !== null && reviewDispatch.run !== null)
5595
- ? null
5596
- : reviewDispatch?.round.status === "failed"
5597
- ? "review-failed"
5598
- : "candidate-ready";
5599
- if (leaderHandoff !== null) {
5600
- enqueueWork(tx, leaderMailbox(task.id), leaderHandoff, now, [
5601
- runRef(task.id, terminal.id),
5602
- ...(terminal.workItemId === undefined ? [] : [workItemRef(task.id, terminal.workItemId)])
5603
- ]);
5604
- }
5605
- // Issue 05: record the Leader's terminal receipt so the Scheduler can
5606
- // suppress no-change `task-orphaned` wakes. The disposition and digest are
5607
- // machine-derived from the post-yield projection; the Leader does not
5608
- // need to cooperate for the admission check to work.
5609
- if (role.name === LEADER_ROLE) {
5610
- const receipt = leaderYieldReceipt(tx, task, terminal, now);
5611
- if (receipt !== null) {
5612
- tx.saveAgentRun(receipt);
5613
- return {
5614
- kind: "yielded",
5615
- run: receipt,
5616
- reviewDispatch,
5617
- notifyLeader: leaderHandoff !== null
5618
- };
5619
- }
5620
- }
5621
- return {
5622
- kind: "yielded",
5623
- run: terminal,
5624
- reviewDispatch,
5625
- notifyLeader: leaderHandoff !== null
5626
- };
5627
- });
5628
- if (yielded.kind === "rejected") {
5629
- if (yielded.notifyLeader) {
5630
- notifyMailbox(options.runtime, leaderMailbox(yielded.run.taskId), yielded.run.taskId);
5631
- }
5632
- throw usageError(`Run ${yielded.run.id} no longer matches its exact execution fence: `
5633
- + `${yielded.rejected.attempt.rejectionReason}. `
5634
- + `Rejected Reviewer output was recorded as ${yielded.rejected.event.id} `
5635
- + "(unaccepted diagnostic evidence only).");
5636
- }
5637
- if (yielded.notifyLeader) {
5638
- notifyMailbox(options.runtime, leaderMailbox(yielded.run.taskId), yielded.run.taskId);
5639
- }
5640
- if (yielded.reviewDispatch?.run !== null
5641
- && yielded.reviewDispatch?.run !== undefined) {
5642
- notifyReviewMailbox(options, options.runtime, roleMailbox(yielded.reviewDispatch.run.taskId, yielded.reviewDispatch.run.roleName), yielded.reviewDispatch.run.taskId);
5643
- }
5644
- return output(`Yielded ${yielded.run.id}: ${yielded.run.summary ?? inputSummary}\n`, {
5645
- run: yielded.run,
5646
- ...(yielded.reviewDispatch === null
5647
- ? {}
5648
- : { reviewRound: yielded.reviewDispatch.round })
5649
- });
5650
- }
5651
4971
  /**
5652
- * Issue 05: compute the Leader Run terminal receipt (disposition + observed
5653
- * actionability digest) from the post-yield projection. Returns the updated
5654
- * Run, or null when the receipt cannot be computed (the caller keeps the
5655
- * unmodified terminal Run in that case; the Scheduler fails open).
5656
- */
5657
- function leaderYieldReceipt(tx, task, terminal, now) {
5658
- try {
5659
- const projection = buildTaskExecutionProjection(tx, task.id, task);
5660
- if (projection === null)
5661
- return null;
5662
- const disposition = deriveLeaderRunDisposition(projection.status, task.status);
5663
- const digest = computeActionabilityDigest(collectTaskActionability(tx, task.id, now));
5664
- const waitReason = disposition === "waiting" || disposition === "blocked"
5665
- ? leaderWaitReason(projection)
5666
- : undefined;
5667
- return {
5668
- ...terminal,
5669
- disposition,
5670
- observedActionabilityDigest: digest,
5671
- ...(waitReason === undefined ? {} : { waitReason }),
5672
- updatedAt: now.toISOString()
5673
- };
5674
- }
5675
- catch {
5676
- return null;
5677
- }
5678
- }
5679
- function leaderWaitReason(projection) {
5680
- const blocker = projection.blockers[0];
5681
- if (blocker !== undefined) {
5682
- return { kind: blocker.kind, ref: blocker.id };
5683
- }
5684
- if (projection.status === "waiting-on-agents") {
5685
- return { kind: "delegated-work" };
5686
- }
5687
- if (projection.status === "waiting-user") {
5688
- return { kind: "input" };
5689
- }
5690
- return undefined;
5691
- }
5692
- /**
5693
- * Records a structured progress checkpoint for an active Run. This is a durable
5694
- * run fact, not a Task Message: it advances the Run's durable-progress clock so
5695
- * a healthy but long-running Run keeps proving it is alive without adding
5696
- * collaboration-narrative noise. It never yields, mutates the Run, or wakes the
4972
+ * Records a structured progress checkpoint for an active Turn. This is a durable
4973
+ * Turn fact, not a Task Message: it advances the Turn's durable-progress clock so
4974
+ * a healthy but long-running Turn keeps proving it is alive without adding
4975
+ * collaboration-narrative noise. It never completes, mutates the Turn, or wakes the
5697
4976
  * Leader.
5698
4977
  */
5699
- function checkpointRun(args, store, options) {
5700
- const usage = "Task run checkpoint usage: yui task run checkpoint <run> (--note <text>|--note-file <path|->).";
4978
+ function checkpointTurn(args, store, options) {
4979
+ const usage = "Task turn checkpoint usage: yui task turn checkpoint <turn> (--note <text>|--note-file <path|->).";
5701
4980
  const parsed = parseTail(args, new Set(["--note", "--note-file"]), usage);
5702
4981
  exactPositionals(parsed.positionals, 1, usage);
5703
4982
  const note = readCommandText(parsed.options.get("--note"), parsed.options.get("--note-file"), "--note", usage);
5704
4983
  const now = clock(options);
5705
4984
  const event = store.transaction((tx) => {
5706
- const run = requireRun(tx, parsed.positionals[0], options);
4985
+ const run = requireTurn(tx, parsed.positionals[0], options);
5707
4986
  if (run.status !== "active") {
5708
- throw usageError(`Run ${run.id} is already terminal: ${run.status}.`);
5709
- }
5710
- if (run.deliveredAt === undefined) {
5711
- throw usageError(`Run ${run.id} delivery is still pending.`);
4987
+ throw usageError(`Turn ${run.id} is already terminal: ${run.status}.`);
5712
4988
  }
5713
4989
  const task = requireTask(tx, run.taskId);
5714
4990
  if (task.status !== "active")
5715
- throw usageError(inactiveTaskMessage(task, "checkpointing a run"));
5716
- const pointer = activeRunPointer(tx, run);
4991
+ throw usageError(inactiveTaskMessage(task, "checkpointing a Turn"));
4992
+ const pointer = activeTurnPointer(tx, run);
5717
4993
  if (pointer?.id !== run.id) {
5718
- throw usageError(`Run is not active for ${task.id}/${run.roleName}: ${run.id}.`);
4994
+ throw usageError(`Turn is not active for ${task.id}/${run.roleName}: ${run.id}.`);
5719
4995
  }
5720
4996
  const events = tx.listEvents(task.id);
5721
- const recovered = isRoleRunStalled(events, run.id);
5722
- const progress = recordTaskEventRecord(tx, task.id, RUN_PROGRESS_EVENT, {
5723
- runId: run.id,
4997
+ const recovered = isRoleTurnStalled(events, run.id);
4998
+ const progress = recordTaskEventRecord(tx, task.id, TURN_PROGRESS_EVENT, {
4999
+ turnId: run.id,
5724
5000
  note: truncateEventNote(note),
5725
5001
  ...(run.workItemId === undefined ? {} : { workItemId: run.workItemId })
5726
5002
  }, now);
5727
5003
  if (recovered) {
5728
- recordTaskEventRecord(tx, task.id, RUN_RECOVERED_EVENT, {
5729
- runId: run.id,
5004
+ recordTaskEventRecord(tx, task.id, TURN_RECOVERED_EVENT, {
5005
+ turnId: run.id,
5730
5006
  roleName: run.roleName,
5731
5007
  progressAt: now.toISOString(),
5732
5008
  kind: "checkpoint"
@@ -5744,13 +5020,13 @@ export function queueReviewRound(store, item, config, requestedBy, now) {
5744
5020
  const pending = createReviewRound(store.nextReviewRoundId(item.taskId), item.taskId, item.id, candidate.id, config.roleName, requestedBy, candidate.gitSnapshot.reviewBaseCommit, now);
5745
5021
  store.saveReviewRound(item.taskId, pending);
5746
5022
  const producerRoleName = item.assignee
5747
- ?? (candidate.source.type === "run"
5748
- ? store.getAgentRun(item.taskId, candidate.source.runId)?.roleName
5023
+ ?? (candidate.source.type === "turn"
5024
+ ? store.getTurn(item.taskId, candidate.source.turnId)?.roleName
5749
5025
  : LEADER_ROLE);
5750
5026
  if (producerRoleName === config.roleName) {
5751
5027
  const failed = finishReviewRound(pending, "failed", `Reviewer Role must be separate from the Candidate producer: ${config.roleName}.`, now);
5752
5028
  store.saveReviewRound(item.taskId, failed);
5753
- return { round: failed, run: null };
5029
+ return { round: failed };
5754
5030
  }
5755
5031
  let reviewer = store.getRole(item.taskId, config.roleName);
5756
5032
  if (reviewer === null) {
@@ -5758,18 +5034,18 @@ export function queueReviewRound(store, item, config, requestedBy, now) {
5758
5034
  if (globalRole === null) {
5759
5035
  const failed = finishReviewRound(pending, "failed", `Global Role not found: ${config.roleName}.`, now);
5760
5036
  store.saveReviewRound(item.taskId, failed);
5761
- return { round: failed, run: null };
5037
+ return { round: failed };
5762
5038
  }
5763
5039
  const task = requireTask(store, item.taskId);
5764
5040
  reviewer = createTaskRole(store, task, config.roleName, undefined, now, config.roleName);
5765
5041
  store.saveRole(task.id, reviewer);
5766
5042
  }
5767
- if (store.getActiveAgentRun(item.taskId, reviewer.name) !== null) {
5768
- const failed = finishReviewRound(pending, "failed", `Reviewer Role already has an active run: ${reviewer.name}.`, now);
5043
+ if (store.getActiveTurn(item.taskId, reviewer.name) !== null) {
5044
+ const failed = finishReviewRound(pending, "failed", `Reviewer Role already has an active Turn: ${reviewer.name}.`, now);
5769
5045
  store.saveReviewRound(item.taskId, failed);
5770
- return { round: failed, run: null };
5046
+ return { round: failed };
5771
5047
  }
5772
- return { round: pending, run: null };
5048
+ return { round: pending };
5773
5049
  }
5774
5050
  export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, options = {}) {
5775
5051
  const now = clock(options);
@@ -5834,8 +5110,8 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
5834
5110
  && round.reviewerRoleName !== taskFinalContract.reviewerRoleName) {
5835
5111
  throw new TaskFinalReviewDispatchDriftError(`Task final-review Reviewer identity does not match ReviewRound ${round.id}.`);
5836
5112
  }
5837
- if (round.status === "pending" && round.reviewerRunId !== undefined) {
5838
- throw new TaskFinalReviewDispatchDriftError(`Pending final ReviewRound already records Reviewer Run ${round.reviewerRunId}: `
5113
+ if (round.status === "pending" && round.reviewerTurnId !== undefined) {
5114
+ throw new TaskFinalReviewDispatchDriftError(`Pending final ReviewRound already records Reviewer Turn ${round.reviewerTurnId}: `
5839
5115
  + `${round.id}.`);
5840
5116
  }
5841
5117
  let currentTaskCandidate;
@@ -5885,8 +5161,8 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
5885
5161
  && (entry.executionGroup?.lanes.some(({ roleName }) => requestedReviewers.has(roleName))
5886
5162
  ?? requestedReviewers.has(entry.reviewerRoleName))
5887
5163
  && !(entry.status === "running"
5888
- && entry.reviewerRunId !== undefined
5889
- && tx.getAgentRun(task.id, entry.reviewerRunId)?.status === "failed")));
5164
+ && entry.reviewerTurnId !== undefined
5165
+ && tx.getTurn(task.id, entry.reviewerTurnId)?.status === "failed")));
5890
5166
  if (conflicting !== undefined) {
5891
5167
  throw new TaskFinalReviewDispatchDriftError(`Another active Task-final ReviewRound already exists: ${conflicting.id}/${conflicting.reviewerRoleName}.`);
5892
5168
  }
@@ -5913,8 +5189,8 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
5913
5189
  }
5914
5190
  const candidateLabel = taskScope
5915
5191
  ? "frozen Task candidate"
5916
- : candidate.source.type === "run"
5917
- ? `candidate Run ${candidate.source.runId}`
5192
+ : candidate.source.type === "turn"
5193
+ ? `candidate Turn ${candidate.source.turnId}`
5918
5194
  : `revision ${candidate.workItemRevision}`;
5919
5195
  const frozenHeads = taskScope
5920
5196
  ? round.taskCandidate.projects
@@ -5932,7 +5208,7 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
5932
5208
  }
5933
5209
  const diffByProject = options.deltaRecheckDiff;
5934
5210
  if (diffByProject === undefined) {
5935
- throw new TaskFinalReviewDispatchDriftError(`Delta-recheck diff is missing for ${round.id}; the CLI preflight did not run.`);
5211
+ throw new TaskFinalReviewDispatchDriftError(`Delta-recheck diff is missing for ${round.id}; the CLI preflight did not turn.`);
5936
5212
  }
5937
5213
  try {
5938
5214
  verifyDeltaRecheckDiff(round.deltaRecheck, diffByProject);
@@ -5976,8 +5252,8 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
5976
5252
  : []),
5977
5253
  "You may freely edit source/tests, run local build or test commands, and optionally commit diagnostic evidence only inside this stable Reviewer workspace at the exact ReviewRound snapshot.",
5978
5254
  "Do not push, integrate, mutate Task state, touch the Candidate or Worker workspace, another Task/workspace, a stable checkout, or the real Yui control-plane home.",
5979
- "Use the exact --summary-file - body to report complete findings, evidence, checks actually run, uncertainty, and recommended next actions in clear Markdown or JSON. Yui preserves the full report; no fixed wording or field list is required. If you include evidenceCommit, it must match the managed Review workspace.",
5980
- "Report reviewBaseCommit, exact checks/results, material findings, and uncertainty. Review yield completes only this Round and creates no Candidate or ChangeSet.",
5255
+ "End the Provider turn with complete findings, evidence, checks actually run, uncertainty, and recommended next actions in clear Markdown or JSON. Yui preserves the full report automatically; no fixed wording or field list is required. If you include evidenceCommit, it must match the managed Review workspace.",
5256
+ "Report reviewBaseCommit, exact checks/results, material findings, and uncertainty. This Turn result completes only the Round and creates no Candidate or ChangeSet.",
5981
5257
  "The Leader alone interprets and routes evidence: original Worker when open, a small Repair WorkItem when needed, or Leader/Integration for merge and local fixes; never merge review evidence yourself."
5982
5258
  ].join("\n");
5983
5259
  const executionTarget = executionTargetForReviewRound(task, round, item, candidate);
@@ -5987,19 +5263,19 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
5987
5263
  strategy: { mode: "fixed", count: 1 },
5988
5264
  lanes: [{ roleName: reviewer.name, reviewRoundId: round.id }]
5989
5265
  }, now);
5990
- const dispatchLanes = runningGroup.lanes.filter((lane) => (lane.status === "pending" && lane.runId === undefined));
5266
+ const dispatchLanes = runningGroup.lanes.filter((lane) => (lane.status === "pending" && lane.turnId === undefined));
5991
5267
  const laneRoles = dispatchLanes.map(({ roleName }) => roleName);
5992
5268
  const reviewers = laneRoles.map((roleName) => {
5993
5269
  const laneReviewer = tx.getRole(taskId, roleName);
5994
5270
  if (laneReviewer === null) {
5995
5271
  throw usageError(`Reviewer Role not found: ${taskId}/${roleName}.`);
5996
5272
  }
5997
- if (tx.getActiveAgentRun(taskId, roleName) !== null) {
5998
- throw usageError(`Reviewer Role already has an active run: ${roleName}.`);
5273
+ if (tx.getActiveTurn(taskId, roleName) !== null) {
5274
+ throw usageError(`Reviewer Role already has an active Turn: ${roleName}.`);
5999
5275
  }
6000
5276
  return laneReviewer;
6001
5277
  });
6002
- const createdRuns = [];
5278
+ const createdTurns = [];
6003
5279
  const config = tx.getConfig();
6004
5280
  const controllerConcurrency = resolveControllerTaskConcurrency(config.controllerTaskConcurrency);
6005
5281
  const capacity = runningGroup.strategy.mode === "fixed"
@@ -6077,30 +5353,23 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
6077
5353
  continue;
6078
5354
  }
6079
5355
  activeResources.push(request);
6080
- const runId = tx.nextAgentRunId(taskId);
6081
- const assignment = createRunAssignment({
6082
- runId,
6083
- roleName: laneReviewer.name,
6084
- purpose: "review",
6085
- action: "review-round",
6086
- subject: {
6087
- taskId,
6088
- ...(item === undefined ? {} : { workItemId: item.id }),
6089
- reviewRoundId: round.id,
6090
- executionGroupId: runningGroup.id,
6091
- executionLaneId: lane.id
5356
+ const turnId = tx.nextTurnId(taskId);
5357
+ const input = createTurnInput({
5358
+ source: {
5359
+ type: "yui",
5360
+ channel: item === undefined ? "task-dispatch" : "workitem-dispatch"
6092
5361
  },
6093
5362
  directive: `${rawInput}\nFrozen target: ${runningGroup.target.fingerprint}.`,
6094
5363
  deltaRefIds: []
6095
5364
  });
6096
5365
  runningGroup = updateExecutionLane(runningGroup, lane.id, {
6097
5366
  status: "running",
6098
- runId,
5367
+ turnId,
6099
5368
  reviewRoundId: round.id,
6100
5369
  effective,
6101
5370
  workspace: laneWorkspace
6102
5371
  }, now);
6103
- createdRuns.push(createAgentRun(runId, taskId, laneReviewer.name, roleAgentSessionResumeMode(sessions, effective.agentId, effective), assignment, now, {
5372
+ createdTurns.push(createTurn(turnId, taskId, laneReviewer.name, roleAgentSessionResumeMode(sessions, effective.agentId, effective), input, now, {
6104
5373
  ...(item === undefined ? {} : { workItemId: item.id }),
6105
5374
  purpose: "review",
6106
5375
  reviewRoundId: round.id,
@@ -6113,8 +5382,8 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
6113
5382
  const roundWithGroup = round.executionGroup === undefined
6114
5383
  ? attachReviewExecutionGroup(round, runningGroup)
6115
5384
  : updateReviewExecutionGroup(round, runningGroup);
6116
- const persistedRound = round.status === "pending" && createdRuns.length > 0
6117
- ? startReviewRound(roundWithGroup, createdRuns[0].id)
5385
+ const persistedRound = round.status === "pending" && createdTurns.length > 0
5386
+ ? startReviewRound(roundWithGroup, createdTurns[0].id)
6118
5387
  : roundWithGroup;
6119
5388
  // Save the aggregate before adopting prepared Lane workspaces. The
6120
5389
  // managed-workspace validator deliberately requires durable Group/Lane
@@ -6132,29 +5401,29 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
6132
5401
  tx.saveManagedWorkspace(prepared);
6133
5402
  }
6134
5403
  }
6135
- for (let index = 0; index < createdRuns.length; index += 1) {
6136
- const unboundRun = createdRuns[index];
6137
- const snapshot = freezeRunContextSnapshot(tx, {
5404
+ for (let index = 0; index < createdTurns.length; index += 1) {
5405
+ const unboundTurn = createdTurns[index];
5406
+ const snapshot = freezeTurnContextSnapshot(tx, {
6138
5407
  taskId,
6139
- roleName: unboundRun.roleName,
5408
+ roleName: unboundTurn.roleName,
6140
5409
  purpose: "review",
6141
5410
  ...(item === undefined ? {} : { workItemId: item.id }),
6142
5411
  reviewRoundId: round.id
6143
5412
  }, now, "controller", reviewBaseline === undefined
6144
5413
  ? undefined
6145
5414
  : contextSnapshotRef(reviewBaseline));
6146
- const created = withAgentRunContextSnapshot(unboundRun, contextSnapshotRef(snapshot), contextSnapshotDeltaRefIds(tx, snapshot));
6147
- createdRuns[index] = created;
6148
- const laneReviewer = requireRole(tx, taskId, unboundRun.roleName);
6149
- tx.saveAgentRun(created);
6150
- tx.saveActiveAgentRun(created);
5415
+ const created = withTurnContextSnapshot(unboundTurn, contextSnapshotRef(snapshot), contextSnapshotDeltaRefIds(tx, snapshot));
5416
+ createdTurns[index] = created;
5417
+ const laneReviewer = requireRole(tx, taskId, unboundTurn.roleName);
5418
+ tx.saveTurn(created);
5419
+ tx.saveActiveTurn(created);
6151
5420
  enqueueWork(tx, roleMailbox(taskId, laneReviewer.name), "review-requested", now, [
6152
- runRef(taskId, created.id),
5421
+ turnRef(taskId, created.id),
6153
5422
  ...(item === undefined ? [] : [workItemRef(taskId, item.id)])
6154
5423
  ]);
6155
- recordTaskEvent(tx, taskId, "run.review-dispatched", runLaunchEventPayload(created), now);
5424
+ recordTaskEvent(tx, taskId, "turn.review-dispatched", turnLaunchEventPayload(created), now);
6156
5425
  }
6157
- return createdRuns;
5426
+ return createdTurns;
6158
5427
  });
6159
5428
  for (const run of runs) {
6160
5429
  notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName), run.taskId);
@@ -6283,7 +5552,7 @@ function appendMessage(store, taskId, body, kind, author, now, context = {}) {
6283
5552
  recordTaskEvent(store, taskId, "message.sent", {
6284
5553
  messageId: message.id,
6285
5554
  kind: message.kind,
6286
- ...(message.runId === undefined ? {} : { runId: message.runId })
5555
+ ...(message.turnId === undefined ? {} : { turnId: message.turnId })
6287
5556
  }, now);
6288
5557
  return message;
6289
5558
  }
@@ -6291,15 +5560,15 @@ function recordTaskEvent(store, taskId, type, payload, now) {
6291
5560
  return recordTaskEventRecord(store, taskId, type, payload, now);
6292
5561
  }
6293
5562
  function leaderActionEventPayload(store, taskId, options) {
6294
- const runId = taskLeaderActionRunId(store, taskId, options.environment, options.yuiHome);
6295
- return runId === undefined ? {} : { leaderRunId: runId };
5563
+ const turnId = taskLeaderActionTurnId(store, taskId, options.environment, options.yuiHome);
5564
+ return turnId === undefined ? {} : { leaderTurnId: turnId };
6296
5565
  }
6297
5566
  function recordTaskEventRecord(store, taskId, type, payload, now) {
6298
5567
  const event = createTaskEvent(store.nextEventId(taskId), taskId, type, payload, now);
6299
5568
  store.saveEvent(taskId, event);
6300
5569
  return event;
6301
5570
  }
6302
- /** Keeps a free-text run-fact note bounded so an event payload stays compact. */
5571
+ /** Keeps a free-text Turn-fact note bounded so an event payload stays compact. */
6303
5572
  function truncateEventNote(note) {
6304
5573
  const normalized = note.trim();
6305
5574
  return normalized.length <= 280 ? normalized : `${normalized.slice(0, 279)}…`;
@@ -6321,9 +5590,9 @@ function roleLaunchEventPayload(role, sessions) {
6321
5590
  : "pending-next-launch"
6322
5591
  };
6323
5592
  }
6324
- function runLaunchEventPayload(run) {
5593
+ function turnLaunchEventPayload(run) {
6325
5594
  return {
6326
- runId: run.id,
5595
+ turnId: run.id,
6327
5596
  role: run.roleName,
6328
5597
  purpose: run.purpose,
6329
5598
  mode: run.mode,
@@ -6381,18 +5650,18 @@ function requireWorkItem(store, workItemId, options) {
6381
5650
  }
6382
5651
  return item;
6383
5652
  }
6384
- function requireRun(store, runId, options) {
6385
- const reference = taskRecordReference(runId, "agentRun", "Agent Run reference", options);
6386
- const run = store.getAgentRun(reference.taskId, reference.localId);
5653
+ function requireTurn(store, turnId, options) {
5654
+ const reference = taskRecordReference(turnId, "turn", "Turn reference", options);
5655
+ const run = store.getTurn(reference.taskId, reference.localId);
6387
5656
  if (run === null) {
6388
- throw usageError(`Agent Run not found: ${reference.taskId}/${reference.localId}.`);
5657
+ throw usageError(`Turn not found: ${reference.taskId}/${reference.localId}.`);
6389
5658
  }
6390
5659
  return run;
6391
5660
  }
6392
- function activeRunPointer(store, run) {
5661
+ function activeTurnPointer(store, run) {
6393
5662
  return run.executionGroupId !== undefined && run.executionLaneId !== undefined
6394
- ? store.getActiveExecutionLaneRun(run.taskId, run.executionGroupId, run.executionLaneId)
6395
- : store.getActiveAgentRun(run.taskId, run.roleName);
5663
+ ? store.getActiveExecutionLaneTurn(run.taskId, run.executionGroupId, run.executionLaneId)
5664
+ : store.getActiveTurn(run.taskId, run.roleName);
6396
5665
  }
6397
5666
  function requireReviewRound(store, reviewRoundId, options) {
6398
5667
  const reference = taskRecordReference(reviewRoundId, "reviewRound", "ReviewRound reference", options);
@@ -6425,8 +5694,8 @@ function assertWorkItemDependenciesCompleted(store, item) {
6425
5694
  }
6426
5695
  }
6427
5696
  }
6428
- function chronologicalAgentRuns(runs) {
6429
- return [...runs].sort((left, right) => (left.createdAt.localeCompare(right.createdAt)
5697
+ function chronologicalTurns(turns) {
5698
+ return [...turns].sort((left, right) => (left.createdAt.localeCompare(right.createdAt)
6430
5699
  || left.id.localeCompare(right.id)));
6431
5700
  }
6432
5701
  function isTerminalWorkItemStatus(status) {
@@ -6903,7 +6172,7 @@ function taskContinuationCommand(args, store) {
6903
6172
  continuationId: identity.continuationId,
6904
6173
  generation: identity.generation,
6905
6174
  driver: identity.providerNamespace,
6906
- runId: continuation.runId,
6175
+ turnId: continuation.turnId,
6907
6176
  execution: continuation.execution,
6908
6177
  outcome: continuation.outcome,
6909
6178
  attachment: continuation.attachment,
@@ -6994,13 +6263,13 @@ function taskWakeInspectionCommand(args, store) {
6994
6263
  { header: "Wake", minWidth: 8, maxWidth: 18 },
6995
6264
  { header: "Status", minWidth: 8, maxWidth: 12 },
6996
6265
  { header: "Reasons", minWidth: 10, maxWidth: 40 },
6997
- { header: "Run", minWidth: 10, maxWidth: 20 },
6266
+ { header: "Turn", minWidth: 10, maxWidth: 20 },
6998
6267
  { header: "Dispatched", minWidth: 10, maxWidth: 28 }
6999
6268
  ], wakes.map((wake) => [
7000
6269
  wake.id,
7001
6270
  wake.status,
7002
6271
  wake.reasons.map(renderWakeReason).join(", "),
7003
- wake.runId ?? "-",
6272
+ wake.turnId ?? "-",
7004
6273
  presentTime(wake.createdAt, timeZone)
7005
6274
  ]), defaultTableWidth())}\n`, { taskId: task.id, wakes });
7006
6275
  }
@@ -7020,14 +6289,14 @@ function taskWakeInspectionCommand(args, store) {
7020
6289
  };
7021
6290
  const events = store.listEvents(task.id).filter((e) => inWindow(e.createdAt));
7022
6291
  const messages = store.listMessages(task.id).filter((m) => inWindow(m.createdAt));
7023
- const runs = store.listAgentRuns(task.id).filter((r) => inWindow(r.createdAt));
6292
+ const turns = store.listTurns(task.id).filter((turn) => inWindow(turn.createdAt));
7024
6293
  const lines = [
7025
6294
  `Wake: ${wake.id}`,
7026
6295
  `Task: ${task.id}`,
7027
6296
  `Status: ${wake.status}`,
7028
6297
  `Reasons: ${wake.reasons.map(renderWakeReason).join(", ")}`,
7029
6298
  `Delta window: ${wake.fromCursor} → ${wake.toCursor}`,
7030
- ...(wake.runId === undefined ? [] : [`Run: ${wake.runId}`]),
6299
+ ...(wake.turnId === undefined ? [] : [`Turn: ${wake.turnId}`]),
7031
6300
  `Dispatched: ${presentTime(wake.createdAt, timeZone)}`,
7032
6301
  ...(wake.consumedAt === undefined
7033
6302
  ? []
@@ -7036,15 +6305,15 @@ function taskWakeInspectionCommand(args, store) {
7036
6305
  ...events.map((e) => ` ${e.id} ${e.type} ${presentTime(e.createdAt, timeZone)}`),
7037
6306
  `Messages (${messages.length}):`,
7038
6307
  ...messages.map((m) => ` ${m.id} [${taskMessageAuthorLabel(m.author)}] ${presentTime(m.createdAt, timeZone)}`),
7039
- `Runs (${runs.length}):`,
7040
- ...runs.map((r) => ` ${r.id} [${r.status}/${r.purpose}] ${r.roleName} ${presentTime(r.createdAt, timeZone)}`)
6308
+ `Turns (${turns.length}):`,
6309
+ ...turns.map((turn) => ` ${turn.id} [${turn.status}/${turn.purpose}] ${turn.roleName} ${presentTime(turn.createdAt, timeZone)}`)
7041
6310
  ];
7042
6311
  return output(lines.join("\n").concat("\n"), {
7043
6312
  taskId: task.id,
7044
6313
  wake,
7045
6314
  events,
7046
6315
  messages,
7047
- runs
6316
+ turns
7048
6317
  });
7049
6318
  }
7050
6319
  throw usageError(command === undefined
@@ -7218,60 +6487,6 @@ function messageOf(error) {
7218
6487
  function taskMailbox(taskId) {
7219
6488
  return { kind: "task", taskId };
7220
6489
  }
7221
- function settleTaskExecutionForCompletion(store, taskId, roles, summary, now) {
7222
- const activeRuns = store.listAgentRuns(taskId).filter((run) => run.status === "active");
7223
- for (const run of activeRuns) {
7224
- store.saveAgentRun(run.roleName === LEADER_ROLE && run.pushedAt !== undefined
7225
- ? yieldAgentRun(run, summary, now)
7226
- : failAgentRun(run, `Task completed: ${summary}`, now));
7227
- if (run.executionGroupId !== undefined && run.executionLaneId !== undefined) {
7228
- store.clearActiveExecutionLaneRun(taskId, run.executionGroupId, run.executionLaneId);
7229
- }
7230
- }
7231
- // Historical pointer projections are execution state, not Task progress.
7232
- for (const run of store.listAgentRuns(taskId)) {
7233
- if (run.executionGroupId !== undefined && run.executionLaneId !== undefined) {
7234
- store.clearActiveExecutionLaneRun(taskId, run.executionGroupId, run.executionLaneId);
7235
- }
7236
- }
7237
- const targets = [];
7238
- for (const role of roles) {
7239
- store.clearActiveAgentRun(taskId, role.name);
7240
- let sessions = store.getTaskRoleSessionSet(taskId, role.name);
7241
- const activeSession = sessions?.sessions[sessions.activeAgentId];
7242
- const cleanupRequired = ownedRuntimeSessionRequiresCleanup(activeSession);
7243
- if (sessions !== null) {
7244
- if (sessions.inFlight !== null) {
7245
- sessions = terminalizeTaskRoleRunSession(sessions, sessions.inFlight, now);
7246
- }
7247
- const current = sessions.sessions[sessions.activeAgentId];
7248
- if (current !== undefined && current.status === "active") {
7249
- sessions = updateRoleAgentSessionStatus(sessions, sessions.activeAgentId, "ended", now, "stopped");
7250
- }
7251
- store.saveTaskRoleSessionSet(sessions);
7252
- }
7253
- if (!cleanupRequired)
7254
- continue;
7255
- const target = runtimeLifecycleTarget({
7256
- scope: "task",
7257
- taskId,
7258
- roleName: role.name
7259
- });
7260
- enqueueWork(store, target, RUNTIME_CLEANUP_REQUIRED_REASON, now, [taskRef(taskId)]);
7261
- targets.push(target);
7262
- }
7263
- return targets;
7264
- }
7265
- function ownedRuntimeSessionRequiresCleanup(session) {
7266
- if (session === undefined || session.status === "ended") {
7267
- return false;
7268
- }
7269
- // Older synthetic session fixtures can represent a ready native session
7270
- // without a lifecycle generation. Do not manufacture a cleanup obligation
7271
- // for those records; coordinated launches always carry launchId, while a
7272
- // running session remains actionable even when an older record lacks it.
7273
- return session.launchId !== undefined;
7274
- }
7275
6490
  function roleMailbox(taskId, roleName) {
7276
6491
  return { kind: "role", taskId, roleName };
7277
6492
  }
@@ -7281,8 +6496,8 @@ function leaderMailbox(taskId) {
7281
6496
  function taskRef(id) {
7282
6497
  return { type: "task", id };
7283
6498
  }
7284
- function runRef(taskId, id) {
7285
- return { type: "run", taskId, id };
6499
+ function turnRef(taskId, id) {
6500
+ return { type: "turn", taskId, id };
7286
6501
  }
7287
6502
  function workItemRef(taskId, id) {
7288
6503
  return { type: "work-item", taskId, id };