@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
@@ -75,7 +75,7 @@ export function builtinAgentProfileInputs() {
75
75
  {
76
76
  id: "reviewer",
77
77
  description: "Review one candidate against the user's core outcome, supported behavior, and direct evidence.",
78
- instructions: "Start from user intent and acceptance criteria. Inspect the complete relevant change and report only reachable, material, actionable problems with direct evidence. Separate defects from verification gaps, and prefer the smallest sufficient correction. Follow the bound Project's Policy and Knowledge for build, test, migration, release, and review expectations; do not import rules from another Project or Task. For normal software delivery, review the frozen Task result as one final ReviewRound rather than inventing a per-WorkItem protocol unless the Project Policy explicitly requires one. A Task-final Round has no synthetic WorkItem anchor, and the Reviewer Session and physical workspace continue across changed-head Rounds without reusing an earlier verdict. The current Run's frozen candidate remains the only scope even if Task main advances. For Delta Recheck, return equivalent-and-accepted, finding, or requires-full-review with explicit reasoning; never create the next Round. Do not turn speculative or extreme edge cases into new state, retries, fallbacks, or protocol. In the Review workspace you may edit source or tests, run local checks, and optionally commit diagnostic evidence. Never push, integrate, mutate Task state, touch another workspace or stable checkout, or write the real Yui control-plane home. Report complete findings, checks actually run, uncertainty, and bounded next actions through the exact Review yield; Yui preserves the full free-form report. Expose evidence and options to the Leader, who decides.",
78
+ instructions: "Start from user intent and acceptance criteria. Inspect the complete relevant change and report only reachable, material, actionable problems with direct evidence. Separate defects from verification gaps, and prefer the smallest sufficient correction. Follow the bound Project's Policy and Knowledge for build, test, migration, release, and review expectations; do not import rules from another Project or Task. For normal software delivery, review the frozen Task result as one final ReviewRound rather than inventing a per-WorkItem protocol unless the Project Policy explicitly requires one. A Task-final Round has no synthetic WorkItem anchor, and the Reviewer Session and physical workspace continue across changed-head Rounds without reusing an earlier verdict. The current Turn's frozen candidate remains the only scope even if Task main advances. For Delta Recheck, return equivalent-and-accepted, finding, or requires-full-review with explicit reasoning; never create the next Round. Do not turn speculative or extreme edge cases into new state, retries, fallbacks, or protocol. In the Review workspace you may edit source or tests, run local checks, and optionally commit diagnostic evidence. Never push, integrate, mutate Task state, touch another workspace or stable checkout, or write the real Yui control-plane home. End the Provider turn with complete findings, checks actually run, uncertainty, and bounded next actions; Yui preserves the full free-form report automatically. Expose evidence and options to the Leader, who decides.",
79
79
  defaultAccess: "write"
80
80
  }
81
81
  ];
@@ -1,7 +1,7 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { UNSUPPORTED, collectRuntimeBuildIdentity, createProductionRuntimeIdentityPorts } from "../observability/runtimeIdentity.js";
4
- import { readCompatibleHomeIdentity } from "../storage/compatibleTaskStore.js";
4
+ import { readCurrentHomeIdentity } from "../storage/currentTaskStore.js";
5
5
  import { detectRunningRelease, readActiveReleasePointer, releaseDirectoryName } from "./runtimeRelease.js";
6
6
  /**
7
7
  * Prevent a checkout or otherwise unverified CLI from entering a Home that is
@@ -47,7 +47,7 @@ export function describeCliHomeInvocation(input) {
47
47
  : "unverified-local";
48
48
  let homeId = "unknown";
49
49
  try {
50
- homeId = readCompatibleHomeIdentity(input.home).homeId;
50
+ homeId = readCurrentHomeIdentity(input.home).homeId;
51
51
  }
52
52
  catch {
53
53
  // The primary migration error remains authoritative when identity is unreadable.
@@ -102,7 +102,7 @@ function readPackageDistribution(packageRoot) {
102
102
  function fenceError(input, active, source, release, collected = collectRuntimeBuildIdentity(createProductionRuntimeIdentityPorts(input.packageRoot, input.entryPath))) {
103
103
  let homeId = "unknown";
104
104
  try {
105
- homeId = readCompatibleHomeIdentity(input.home).homeId;
105
+ homeId = readCurrentHomeIdentity(input.home).homeId;
106
106
  }
107
107
  catch {
108
108
  // The release fence must still diagnose and reject an unreadable Home.
@@ -30,8 +30,8 @@ const IRREVERSIBILITY_RANK = {
30
30
  * Concurrency guard: prevents two runs of the same workflow from submitting
31
31
  * duplicate external effects. Each CLI invocation is a fresh Node process with
32
32
  * its own module instance, so an in-process map cannot coordinate across
33
- * processes. When the store is a FileTaskStore (it exposes its root directory),
34
- * the guard is a file lock under that root; otherwise (in-memory test stores)
33
+ * processes. When the store exposes its root directory, the guard is a file
34
+ * lock under that root; otherwise (in-memory test stores)
35
35
  * it falls back to an in-process map. The second run waits for the first to
36
36
  * finish, then re-reads the (now-updated) workflow state.
37
37
  */
@@ -86,8 +86,8 @@ async function acquireWorkflowLock(store, taskId, workflowId) {
86
86
  * crash between the two is healed on resume as described above.
87
87
  *
88
88
  * Concurrent runs of the same workflow are serialized by a cross-process file
89
- * lock when the store is a FileTaskStore (each CLI invocation is a fresh
90
- * process), so a second run cannot submit a duplicate effect.
89
+ * lock when the store exposes its durable root, so a second CLI process cannot
90
+ * submit a duplicate effect.
91
91
  */
92
92
  export async function runReleaseWorkflow(store, taskId, workflowId, ports, options = {}) {
93
93
  // Prevent concurrent runs of the same workflow from submitting duplicate
@@ -381,7 +381,7 @@ async function runReleaseWorkflowLocked(store, taskId, workflowId, ports, option
381
381
  // For irreversible steps, mark as unknown and stop so the effect is
382
382
  // never re-submitted blindly. For reversible steps, mark as failed.
383
383
  // The exception is re-thrown so the caller sees the crash; the workflow
384
- // state is already persisted for recovery on the next run.
384
+ // state is already persisted for recovery on the next turn.
385
385
  const message = error instanceof Error ? error.message : String(error);
386
386
  const crashNow = clock();
387
387
  if (effectiveIrreversibility === "irreversible") {
@@ -461,11 +461,7 @@ function attemptReservationKey(workflowId, stepId, attempt) {
461
461
  * by starting a second workflow against the same grant.
462
462
  */
463
463
  function grantUseRecognized(grant, workflowId, stepId, attempt) {
464
- const reservations = grant.useReservations;
465
- if (Array.isArray(reservations)) {
466
- return reservations.includes(attemptReservationKey(workflowId, stepId, attempt));
467
- }
468
- return false;
464
+ return grant.useReservations.includes(attemptReservationKey(workflowId, stepId, attempt));
469
465
  }
470
466
  /**
471
467
  * Records one grant use after a successful authorization decision. The engine
@@ -186,7 +186,7 @@ export function createReleaseWorkflowPorts(deps) {
186
186
  logs: [`cli-update: ${result.outcome} (${result.version})`]
187
187
  };
188
188
  }
189
- if (result.outcome === "ambiguous" || (result.outcome === "aborted" && !result.recoverable)) {
189
+ if (result.outcome === "aborted" && !result.recoverable) {
190
190
  // P1-2 (rr20): An aborted activation with recoverable=false means
191
191
  // npm activation began but its outcome is unknowable — the global
192
192
  // install may or may not have landed. Treat it as ambiguous:
@@ -203,7 +203,7 @@ export function createReleaseWorkflowPorts(deps) {
203
203
  // externalIdentity so the engine marks the step as `unknown`
204
204
  // and stops — resume must not continue until the expected
205
205
  // Controller lifecycle/identity handoff is proven complete.
206
- if (result.outcome === "aborted" && result.controllerOwnershipUnknown === true) {
206
+ if (result.controllerOwnershipUnknown === true) {
207
207
  return {
208
208
  outcome: "timeout",
209
209
  logs: [`cli-update Controller ownership unknown at phase ${result.phase}: ${result.message}`]
@@ -4,7 +4,7 @@ import { join } from "node:path";
4
4
  /**
5
5
  * Cross-process mutual exclusion for one workflow's engine runs. Each CLI
6
6
  * invocation is a fresh Node process, so an in-process map cannot coordinate
7
- * across processes; this lock lives under the FileTaskStore root instead.
7
+ * across processes; this lock lives under the current Home root instead.
8
8
  *
9
9
  * The lock is a directory created atomically with mkdir(2): its existence is
10
10
  * the lock, so a second acquirer fails with EEXIST while the first holds it.
@@ -25,7 +25,7 @@ export async function healCheckoutSwap(request, ports = realPorts) {
25
25
  * leaving the registered path without a valid repository. The previous
26
26
  * checkout is moved to the backup path first; if the promotion fails it is
27
27
  * restored and the staging clone is removed. A crash between the two renames
28
- * is healed by {@link healCheckoutSwap} on the next run.
28
+ * is healed by {@link healCheckoutSwap} on the next turn.
29
29
  */
30
30
  export async function swapManagedCheckout(request, ports = realPorts) {
31
31
  await healCheckoutSwap(request, ports);
@@ -121,19 +121,6 @@ export class NodeGitWorkspace {
121
121
  }
122
122
  return { addedLines, deletedLines };
123
123
  }
124
- /**
125
- * Files deleted between two commits. Uses `--diff-filter=D` so a rename
126
- * reports its source path here (and its destination in `changedFilesBetween`),
127
- * letting the containment proof verify both sides of a rename.
128
- */
129
- async deletedFilesBetween(input) {
130
- const output = await git([
131
- "-C", input.repositoryPath,
132
- "diff", "--name-only", "--diff-filter=D", "--no-renames",
133
- input.fromCommit, input.toCommit
134
- ]);
135
- return output.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
136
- }
137
124
  /**
138
125
  * Resolve the configured Project branch directly from its remote. This is
139
126
  * deliberately read-only: unlike `refresh`, it never advances the stable
@@ -31,7 +31,7 @@ export function validateProjectName(value) {
31
31
  }
32
32
  /**
33
33
  * Soft-retire a Project: mark it retired and record the audit trail. The
34
- * catalog record, checkout, and every historical Task/Run/Review/Integration/
34
+ * catalog record, checkout, and every historical Task/Turn/Review/Integration/
35
35
  * Publication reference are retained. Retiring an already-retired Project
36
36
  * fails closed; a retired Project must be deleted (or kept) as-is.
37
37
  */
@@ -3,8 +3,7 @@ import { join } from "node:path";
3
3
  /**
4
4
  * Per-Project maintenance fence.
5
5
  *
6
- * `project migrate`, Task workspace rebuild, legacy ref archive, and Task
7
- * archive cleanup are separate CLI processes that all mutate a Project's
6
+ * `project migrate` and Task archive cleanup are separate CLI processes that mutate a Project's
8
7
  * Git repository. Without coordination they can interleave with each other
9
8
  * and with the Controller's worktree preparation. The fence is an exclusive
10
9
  * directory under the Home's `locks/` area (O_EXCL via mkdir), so it works
@@ -61,10 +61,10 @@ export async function inspectTaskBaseFreshness(taskId, store, options = {}) {
61
61
  const task = requireTask(store, taskId);
62
62
  const git = options.git ?? newGitWorkspace();
63
63
  const workspace = store.getTaskWorkspace(taskId);
64
- // Collect active Run workspace snapshots once so every Project entry can
65
- // report whether a live Run is pinned to a different commit.
66
- const activeRuns = store.listAgentRuns(taskId)
67
- .filter((run) => run.status === "active" && run.workspace !== undefined);
64
+ // Collect active Turn workspace snapshots once so every Project entry can
65
+ // report whether a live Turn is pinned to a different commit.
66
+ const activeTurns = store.listTurns(taskId)
67
+ .filter((turn) => turn.status === "active" && turn.workspace !== undefined);
68
68
  const entries = await Promise.all(task.projectBindings.map(async (binding) => {
69
69
  const project = requireProject(store, binding.projectId);
70
70
  const entry = workspace === null ? undefined : workspaceProjectEntry(workspace, project.id);
@@ -77,12 +77,12 @@ export async function inspectTaskBaseFreshness(taskId, store, options = {}) {
77
77
  catch {
78
78
  physicalHead = undefined;
79
79
  }
80
- // Find the first active Run whose workspace snapshot covers this Project.
81
- const runSnapshot = activeRuns.find((run) => {
82
- if (run.workspace === undefined)
80
+ // Find the first active Turn whose workspace snapshot covers this Project.
81
+ const turnSnapshot = activeTurns.find((turn) => {
82
+ if (turn.workspace === undefined)
83
83
  return false;
84
- const runEntry = workspaceProjectEntry(run.workspace, project.id);
85
- return runEntry !== undefined;
84
+ const turnEntry = workspaceProjectEntry(turn.workspace, project.id);
85
+ return turnEntry !== undefined;
86
86
  });
87
87
  const provenance = latestBaseProvenance(store.listEvents(taskId), project.id);
88
88
  const tracked = await resolveTracked(git, project, workspacePath, options.refresh === true);
@@ -99,11 +99,11 @@ export async function inspectTaskBaseFreshness(taskId, store, options = {}) {
99
99
  workspacePath,
100
100
  workspaceClean,
101
101
  ...(physicalHead === undefined ? {} : { physicalHead }),
102
- ...(runSnapshot?.workspace === undefined
102
+ ...(turnSnapshot?.workspace === undefined
103
103
  ? {}
104
104
  : {
105
- runSnapshotCommit: workspaceProjectEntry(runSnapshot.workspace, project.id)?.baseCommit,
106
- runSnapshotRunId: runSnapshot.id
105
+ turnSnapshotCommit: workspaceProjectEntry(turnSnapshot.workspace, project.id)?.baseCommit,
106
+ turnSnapshotId: turnSnapshot.id
107
107
  }),
108
108
  ...(tracked === undefined ? {} : {
109
109
  trackedRef: tracked.ref,
@@ -161,7 +161,7 @@ export function renderTaskBaseFreshnessReport(report) {
161
161
  `Task base freshness: ${report.taskId} (source: ${report.refreshed ? "remote refresh" : "local tracking"})`
162
162
  ];
163
163
  for (const entry of report.entries) {
164
- lines.push(`- ${entry.directory}: ${entry.projectId} @ ${entry.baseCommit}`, ` status: ${entry.status}; workspace: ${entry.workspaceClean === null ? "unknown" : entry.workspaceClean ? "clean" : "dirty"}`, ` physical HEAD: ${entry.physicalHead ?? "-"}`, ` Run snapshot: ${entry.runSnapshotCommit ?? "-"}${entry.runSnapshotRunId === undefined ? "" : ` (${entry.runSnapshotRunId})`}`, ` observed remote: ${entry.observedRemoteUrl ?? "-"}`, ` tracked: ${entry.trackedCommit ?? "-"}${entry.trackedRef === undefined ? "" : ` (${entry.trackedRef})`}`, ` observed: ${entry.observedTrackingCommit ?? "-"}${entry.observedAt === undefined ? "" : ` at ${entry.observedAt}`}`);
164
+ lines.push(`- ${entry.directory}: ${entry.projectId} @ ${entry.baseCommit}`, ` status: ${entry.status}; workspace: ${entry.workspaceClean === null ? "unknown" : entry.workspaceClean ? "clean" : "dirty"}`, ` physical HEAD: ${entry.physicalHead ?? "-"}`, ` Turn snapshot: ${entry.turnSnapshotCommit ?? "-"}${entry.turnSnapshotId === undefined ? "" : ` (${entry.turnSnapshotId})`}`, ` observed remote: ${entry.observedRemoteUrl ?? "-"}`, ` tracked: ${entry.trackedCommit ?? "-"}${entry.trackedRef === undefined ? "" : ` (${entry.trackedRef})`}`, ` observed: ${entry.observedTrackingCommit ?? "-"}${entry.observedAt === undefined ? "" : ` at ${entry.observedAt}`}`);
165
165
  if (entry.risk !== undefined)
166
166
  lines.push(` risk: ${entry.risk}`);
167
167
  if (entry.error !== undefined)
@@ -23,10 +23,10 @@ export class TaskWorkspaceCoordinator {
23
23
  if (item === null)
24
24
  throw new Error(`Work item not found: ${taskId}/${workItemId}.`);
25
25
  const assignee = this.#workItemIsolationAssignee(item);
26
- const activeDevelopRun = this.store.listAgentRuns(item.taskId)
26
+ const activeDevelopTurn = this.store.listTurns(item.taskId)
27
27
  .find((run) => run.status === "active" && run.workItemId === item.id);
28
- if (activeDevelopRun !== undefined) {
29
- throw new Error(`Work Item already has an active Develop Run: ${activeDevelopRun.id}.`);
28
+ if (activeDevelopTurn !== undefined) {
29
+ throw new Error(`Work Item already has an active Develop Turn: ${activeDevelopTurn.id}.`);
30
30
  }
31
31
  const task = this.store.getTask(item.taskId);
32
32
  const taskProjectIds = task.projectBindings.map(({ projectId }) => projectId);
@@ -72,8 +72,8 @@ export class TaskWorkspaceCoordinator {
72
72
  }
73
73
  if (item.workspaceDisposition === disposition)
74
74
  return "missing";
75
- // Hold the per-Project maintenance fence so a concurrent migrate/rebuild/
76
- // archive cannot interleave with worktree removal.
75
+ // Hold the per-Project maintenance fence so concurrent Project maintenance
76
+ // or Task archive cannot interleave with worktree removal.
77
77
  const workspace = this.store.getWorkItemWorkspace(item.taskId, item.id);
78
78
  const projectIds = workspace === null
79
79
  ? []
@@ -124,8 +124,8 @@ export class TaskWorkspaceCoordinator {
124
124
  }
125
125
  if (round.workspaceDisposition?.kind === "reassigned")
126
126
  return "missing";
127
- // Hold the per-Project maintenance fence so a concurrent migrate/rebuild/
128
- // archive cannot interleave with worktree removal.
127
+ // Hold the per-Project maintenance fence so concurrent Project maintenance
128
+ // or Task archive cannot interleave with worktree removal.
129
129
  const workspace = this.store.getReviewRoundWorkspace(taskId, reviewRoundId);
130
130
  const projectIds = workspace === null
131
131
  ? []
@@ -163,7 +163,7 @@ export class TaskWorkspaceCoordinator {
163
163
  .localeCompare(managedWorkspaceKey(right.owner)));
164
164
  // Archive cleanup removes worktrees from every Project the Task uses:
165
165
  // hold each Project's maintenance fence so the Controller defers
166
- // preparation and no migrate/rebuild/archive interleaves.
166
+ // preparation and no Project maintenance or Task archive interleaves.
167
167
  const projectIds = new Set(task.projectBindings.map(({ projectId }) => projectId));
168
168
  for (const workspace of managedWorkspaces) {
169
169
  for (const entry of workspace.entries)
@@ -253,9 +253,9 @@ export class TaskWorkspaceCoordinator {
253
253
  };
254
254
  }
255
255
  const activeRole = this.store.listRoles(taskId)
256
- .find((role) => this.store.getActiveAgentRun(taskId, role.name) !== null);
256
+ .find((role) => this.store.getActiveTurn(taskId, role.name) !== null);
257
257
  if (activeRole !== undefined) {
258
- throw new WorkspaceCleanupBlockedError("active-run", `role:${task.id}/${activeRole.name}`, true, `Task Role still has an active Run: ${task.id}/${activeRole.name}.`);
258
+ throw new WorkspaceCleanupBlockedError("active-turn", `role:${task.id}/${activeRole.name}`, true, `Task Role still has an active Turn: ${task.id}/${activeRole.name}.`);
259
259
  }
260
260
  const roleNames = this.store.listRoles(taskId).map(({ name }) => name);
261
261
  if (roleNames.length > 0) {
@@ -326,10 +326,10 @@ export class TaskWorkspaceCoordinator {
326
326
  }
327
327
  }
328
328
  #assertWorkItemRuntimeQuiescent(item) {
329
- const activeRun = this.store.listAgentRuns(item.taskId)
329
+ const activeTurn = this.store.listTurns(item.taskId)
330
330
  .find((run) => run.status === "active" && run.workItemId === item.id);
331
- if (activeRun !== undefined) {
332
- throw new WorkspaceCleanupBlockedError("active-run", `work-item:${item.taskId}/${item.id}`, true, `Work item still has an active Run: ${item.taskId}/${item.id}.`);
331
+ if (activeTurn !== undefined) {
332
+ throw new WorkspaceCleanupBlockedError("active-turn", `work-item:${item.taskId}/${item.id}`, true, `Work item still has an active Turn: ${item.taskId}/${item.id}.`);
333
333
  }
334
334
  }
335
335
  /**
@@ -368,14 +368,10 @@ export class TaskWorkspaceCoordinator {
368
368
  }
369
369
  async #stopLiveRoles(taskId, roleNames) {
370
370
  const targets = [...new Set(roleNames)];
371
- const getActiveRun = this.store.getActiveAgentRun?.bind(this.store);
371
+ const getActiveTurn = this.store.getActiveTurn?.bind(this.store);
372
372
  for (const roleName of targets) {
373
- if (getActiveRun !== undefined && getActiveRun(taskId, roleName) !== null) {
374
- throw new Error(`Role has an active Run: ${taskId}/${roleName}.`);
375
- }
376
- const sessions = this.store.getTaskRoleSessionSet(taskId, roleName);
377
- if (sessions?.inFlight !== null && sessions?.inFlight !== undefined) {
378
- throw new Error(`Role has unsettled Run state: ${taskId}/${roleName}.`);
373
+ if (getActiveTurn !== undefined && getActiveTurn(taskId, roleName) !== null) {
374
+ throw new Error(`Role has an active Turn: ${taskId}/${roleName}.`);
379
375
  }
380
376
  if (this.store.getWorkMailbox !== undefined && hasRuntimeLifecycleWork(this.store.getWorkMailbox(runtimeLifecycleTarget({ scope: "task", taskId, roleName })))) {
381
377
  throw new Error(`Role has unsettled runtime lifecycle state: ${taskId}/${roleName}.`);
@@ -397,7 +393,7 @@ export class TaskWorkspaceCoordinator {
397
393
  // The aggregate-16 dormant Claude placeholder is the sole exception to
398
394
  // strict workspace-session retirement. The synchronous pane inspection is
399
395
  // performed while holding the Task store transaction so a normal launch
400
- // cannot reserve a Run/Session between absence proof and terminalization.
396
+ // cannot reserve a Turn/Session between absence proof and terminalization.
401
397
  if (inspect === undefined || this.store.transaction === undefined)
402
398
  return;
403
399
  this.store.transaction((tx) => {
@@ -406,8 +402,8 @@ export class TaskWorkspaceCoordinator {
406
402
  if (panes.some((pane) => pane.roleName === roleName && !pane.dead)) {
407
403
  throw new Error(`Task Role native pane must stop before workspace migration: ${roleName}.`);
408
404
  }
409
- if (tx.getActiveAgentRun(taskId, roleName) !== null) {
410
- throw new Error(`Role has an active Run: ${taskId}/${roleName}.`);
405
+ if (tx.getActiveTurn(taskId, roleName) !== null) {
406
+ throw new Error(`Role has an active Turn: ${taskId}/${roleName}.`);
411
407
  }
412
408
  if (tx.getWorkMailbox !== undefined && hasRuntimeLifecycleWork(tx.getWorkMailbox(runtimeLifecycleTarget({ scope: "task", taskId, roleName })))) {
413
409
  throw new Error(`Role has unsettled runtime lifecycle state: ${taskId}/${roleName}.`);
@@ -436,8 +432,8 @@ export class TaskWorkspaceCoordinator {
436
432
  if (isTerminalWorkItem(item)) {
437
433
  throw new Error(`Work item is already terminal: ${item.id}.`);
438
434
  }
439
- if (item.assignee !== undefined && this.store.getActiveAgentRun(task.id, item.assignee) !== null) {
440
- throw new Error(`Role has an active Run: ${task.id}/${item.assignee}.`);
435
+ if (item.assignee !== undefined && this.store.getActiveTurn(task.id, item.assignee) !== null) {
436
+ throw new Error(`Role has an active Turn: ${task.id}/${item.assignee}.`);
441
437
  }
442
438
  if (item.assignee !== undefined && this.store.getRole(task.id, item.assignee) === null) {
443
439
  throw new Error(`Role not found: ${task.id}/${item.assignee}.`);
@@ -46,20 +46,18 @@ export function taskWorkspaceRefSegmentFromIdentity(identity) {
46
46
  return `${valid.taskId}-${valid.token}`;
47
47
  }
48
48
  /**
49
- * The durable ref segment for a Task. A Task with a persisted workspace
50
- * identity always uses its token-bearing segment; a pre-identity record (a
51
- * valid v4 Task that never had a managed Git workspace, or one awaiting the
52
- * controlled rebuild) keeps its bare Task id so its existing worktrees remain
53
- * addressable until they are rebuilt.
49
+ * The durable ref segment for a Task. Every managed Git workspace uses the
50
+ * token-bearing identity minted before its first adoption.
54
51
  */
55
52
  export function taskWorkspaceRefSegment(task) {
56
- if (task.workspaceIdentity === undefined)
57
- return task.id;
53
+ if (task.workspaceIdentity === undefined) {
54
+ throw new Error(`Task workspace identity is required: ${task.id}.`);
55
+ }
58
56
  return taskWorkspaceRefSegmentFromIdentity(task.workspaceIdentity);
59
57
  }
60
58
  /**
61
59
  * The strict main-branch ref for a Task workspace. The main branch is always
62
- * `yui/task-N-<8hex>/main`; any other shape is a foreign or legacy ref.
60
+ * `yui/task-N-<8hex>/main`; any other shape is foreign to the current contract.
63
61
  */
64
62
  export function taskMainBranch(refSegment) {
65
63
  if (!TASK_WORKSPACE_REF_SEGMENT_PATTERN.test(refSegment)) {
@@ -89,11 +87,9 @@ export function taskIntegrationBranch(refSegment, integrationId) {
89
87
  return taskDerivedBranch(refSegment, `integration/${integrationId}`);
90
88
  }
91
89
  /**
92
- * The non-colliding, auditable archive ref for a legacy Task ref. The Home id
93
- * in the path keeps two Homes' archives apart even when they share a Project
90
+ * The non-colliding, auditable archive ref for a current managed Task ref.
91
+ * The Home id keeps two Homes' archives apart when they share a Project
94
92
  * repository; the full original ref name is preserved verbatim.
95
- * Legacy refs live at `refs/heads/yui/task-N/...`; archives at
96
- * `refs/yui/archive/<homeId>/heads/yui/task-N/...`.
97
93
  */
98
94
  export function taskArchiveRef(homeId, sourceRef) {
99
95
  const id = requireIdentityPart(homeId, "Home id");
@@ -105,15 +101,6 @@ export function taskArchiveRef(homeId, sourceRef) {
105
101
  }
106
102
  return `refs/yui/archive/${id}/${ref.slice("refs/".length)}`;
107
103
  }
108
- /**
109
- * Whether a managed branch belongs to the legacy (pre-identity) layout:
110
- * `yui/task-N/...` with a bare Task id segment. Identity-bearing branches
111
- * (`yui/task-N-<8hex>/...`) are never legacy.
112
- */
113
- export function isLegacyTaskRef(refName) {
114
- const match = /^refs\/heads\/(yui\/(task-[0-9]+)\/.+)$/.exec(refName);
115
- return match !== null && !TASK_WORKSPACE_REF_SEGMENT_PATTERN.test(match[2]);
116
- }
117
104
  export function validateTaskWorkspaceIdentity(identity) {
118
105
  if (identity.schemaVersion !== 1) {
119
106
  throw new Error("Task workspace identity must use schemaVersion 1.");