@zq-silk/yui 0.13.10 → 0.14.1

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 (230) hide show
  1. package/ARCHITECTURE.md +69 -428
  2. package/README.md +113 -300
  3. package/dist/agent/argumentPolicy.js +2 -1
  4. package/dist/agent/managedRuntimeEnvironment.js +2 -2
  5. package/dist/cli/commandCatalog.js +39 -77
  6. package/dist/cli/interactionCandidates.js +4 -4
  7. package/dist/cli/interactionPolicy.js +7 -10
  8. package/dist/cli/invocationRouter.js +1 -1
  9. package/dist/cli/updateCommand.js +6 -27
  10. package/dist/cli/updateOrchestrator.js +63 -484
  11. package/dist/cli/updatePorts.js +15 -333
  12. package/dist/cli/upgradeCommand.js +8 -65
  13. package/dist/cli.js +57 -242
  14. package/dist/commands/configCommands.js +11 -62
  15. package/dist/commands/executionAuditCommands.js +23 -23
  16. package/dist/commands/globalRoleCommands.js +2 -7
  17. package/dist/commands/jobCommands.js +1 -4
  18. package/dist/commands/projectCommands.js +5 -5
  19. package/dist/commands/releaseCommands.js +3 -10
  20. package/dist/commands/resourcesCommands.js +2 -2
  21. package/dist/commands/sessionCommands.js +2 -2
  22. package/dist/commands/taskActor.js +31 -39
  23. package/dist/commands/taskChangeSetCommands.js +5 -12
  24. package/dist/commands/taskCommands.js +464 -1249
  25. package/dist/commands/taskContextCommand.js +35 -36
  26. package/dist/commands/taskExecutionCommands.js +33 -46
  27. package/dist/commands/taskInputCommands.js +20 -42
  28. package/dist/commands/taskNextActionCommand.js +9 -9
  29. package/dist/commands/taskOverviewCommand.js +56 -95
  30. package/dist/commands/taskRoleRuntimeStatus.js +77 -107
  31. package/dist/commands/taskUpstreamCommands.js +3 -3
  32. package/dist/commands/taskWorkspaceCommands.js +7 -79
  33. package/dist/commands/telemetryCommands.js +32 -124
  34. package/dist/commands/workflowCommands.js +3 -3
  35. package/dist/completion/fileCompletionManager.js +1 -1
  36. package/dist/config/configCatalog.js +4 -6
  37. package/dist/config/yuiConfig.js +6 -43
  38. package/dist/context/dispatchContext.js +14 -30
  39. package/dist/context/roleSessionContext.js +2 -2
  40. package/dist/context/sessionBootstrapManifest.js +20 -3
  41. package/dist/context/{runContextPack.js → turnContextPack.js} +58 -73
  42. package/dist/context/turnInputContract.js +204 -0
  43. package/dist/context/wakeNotification.js +2 -2
  44. package/dist/controller/agentRuntimeObserver.js +18 -18
  45. package/dist/controller/clientRuntime.js +13 -20
  46. package/dist/controller/controller.js +89 -116
  47. package/dist/controller/fileSchedulerStoreAdapter.js +511 -742
  48. package/dist/controller/jobControl.js +17 -17
  49. package/dist/controller/resourceInventory.js +6 -6
  50. package/dist/controller/resourceInventoryLinux.js +38 -59
  51. package/dist/controller/resourceInventoryRpc.js +3 -3
  52. package/dist/controller/resourceInventoryWorker.js +1 -1
  53. package/dist/controller/runtime.js +46 -53
  54. package/dist/controller/runtimeEventInbox.js +23 -31
  55. package/dist/controller/runtimeEventProcessor.js +15 -15
  56. package/dist/controller/{runtimeHookRunFence.js → runtimeHookTurnFence.js} +87 -81
  57. package/dist/controller/runtimeLaunchCoordinator.js +3 -3
  58. package/dist/controller/runtimeObservationHook.js +11 -8
  59. package/dist/controller/sessionNotify.js +11 -11
  60. package/dist/controller/structuredProviderObservation.js +89 -16
  61. package/dist/coordination/workMailbox.js +309 -579
  62. package/dist/coordination/workMailboxQueue.js +5 -6
  63. package/dist/core/controllerClient.js +1 -91
  64. package/dist/core/controllerServer.js +3 -6
  65. package/dist/doctor/doctor.js +51 -314
  66. package/dist/execution/candidateConvergence.js +3 -3
  67. package/dist/execution/executionGroup.js +32 -33
  68. package/dist/execution/executionHealth.js +36 -38
  69. package/dist/execution/resourceBroker.js +12 -12
  70. package/dist/executor/agentExecutor.js +30 -265
  71. package/dist/executor/effectiveLaunch.js +9 -19
  72. package/dist/executor/executorRegistry.js +43 -17
  73. package/dist/executor/fileRoleLaunchPlanner.js +91 -57
  74. package/dist/executor/launchPlan.js +3 -3
  75. package/dist/executor/workspacePreflightClassification.js +16 -16
  76. package/dist/grant/capabilityGrant.js +20 -25
  77. package/dist/input/inputRequest.js +12 -12
  78. package/dist/integration/changeSet.js +9 -13
  79. package/dist/integration/changeSetManifest.js +1 -1
  80. package/dist/integration/gitIntegrationService.js +2 -2
  81. package/dist/integration/integrationQueueService.js +5 -21
  82. package/dist/integration/overlapDiagnostics.js +2 -2
  83. package/dist/interaction/operatorPresentation.js +1 -1
  84. package/dist/job/jobRunner.js +1 -1
  85. package/dist/lifecycle/canonicalLifecycleEvent.js +25 -25
  86. package/dist/lifecycle/{exactRunTerminalization.js → exactTurnTerminalization.js} +116 -232
  87. package/dist/message/message.js +6 -6
  88. package/dist/observability/executionAudit.js +74 -86
  89. package/dist/observability/faultClassification.js +7 -24
  90. package/dist/observability/orchestrationMetrics.js +5 -5
  91. package/dist/observability/runtimeIdentity.js +34 -101
  92. package/dist/profile/agentProfile.js +1 -1
  93. package/dist/release/cliHomeReleaseFence.js +3 -3
  94. package/dist/release/releaseWorkflowEngine.js +6 -10
  95. package/dist/release/releaseWorkflowPorts.js +2 -2
  96. package/dist/release/workflowFileLock.js +1 -1
  97. package/dist/repository/checkoutSwap.js +1 -1
  98. package/dist/repository/gitWorkspace.js +0 -13
  99. package/dist/repository/project.js +1 -1
  100. package/dist/repository/projectMaintenanceLock.js +1 -2
  101. package/dist/repository/taskBaseFreshness.js +13 -13
  102. package/dist/repository/taskWorkspaceCoordinator.js +21 -25
  103. package/dist/repository/taskWorkspaceIdentity.js +8 -21
  104. package/dist/repository/taskWorkspacePreparer.js +63 -620
  105. package/dist/resources/autoResourceGc.js +3 -3
  106. package/dist/resources/liveReferences.js +1 -1
  107. package/dist/resources/resourceGc.js +1 -1
  108. package/dist/resources/sqliteResourceRegistry.js +1 -1
  109. package/dist/review/reviewDecision.js +7 -7
  110. package/dist/review/reviewFindingLedger.js +1 -1
  111. package/dist/review/reviewOutcomeClassifier.js +35 -50
  112. package/dist/review/reviewRound.js +14 -25
  113. package/dist/review/reviewerAvailability.js +4 -4
  114. package/dist/review/taskFinalReviewContractResolution.js +41 -0
  115. package/dist/role/role.js +6 -3
  116. package/dist/runtime/agentDriver.js +4 -0
  117. package/dist/runtime/agentHost.js +151 -36
  118. package/dist/runtime/builtinAgentDrivers.js +79 -2
  119. package/dist/runtime/codexAppServerRuntime.js +122 -0
  120. package/dist/runtime/continuationManager.js +3 -5
  121. package/dist/runtime/exactControlPlane.js +17 -42
  122. package/dist/runtime/firstProgressAdvisory.js +1 -1
  123. package/dist/runtime/index.js +2 -2
  124. package/dist/runtime/launchDiagnostics.js +2 -2
  125. package/dist/runtime/lifecycleReservation.js +4 -4
  126. package/dist/runtime/processExitObservation.js +1 -1
  127. package/dist/runtime/processExitOutbox.js +0 -32
  128. package/dist/runtime/promptEnvelope.js +9 -9
  129. package/dist/runtime/providerContinuation.js +2 -2
  130. package/dist/runtime/providerContinuationReconciliationService.js +2 -2
  131. package/dist/runtime/providerRuntimeIdentity.js +76 -24
  132. package/dist/runtime/recentTurnIds.js +38 -0
  133. package/dist/runtime/runtimeContinuationProjection.js +2 -2
  134. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  135. package/dist/runtime/runtimeObservation.js +58 -21
  136. package/dist/runtime/runtimeProjection.js +19 -27
  137. package/dist/runtime/sessionLaunchRequest.js +2 -2
  138. package/dist/runtime/structuredProviderHost.js +177 -12
  139. package/dist/runtime/tmuxAdapters.js +46 -11
  140. package/dist/scheduler/activeRoleTurnDelivery.js +245 -0
  141. package/dist/scheduler/leaderWakeupProcessor.js +147 -445
  142. package/dist/scheduler/operatorEvent.js +4 -6
  143. package/dist/scheduler/resourceQueueProjection.js +71 -0
  144. package/dist/scheduler/{roleRunLiveness.js → roleTurnLiveness.js} +24 -28
  145. package/dist/scheduler/{roleRunStall.js → roleTurnStall.js} +162 -247
  146. package/dist/scheduler/taskExecutionProjection.js +99 -99
  147. package/dist/scheduler/taskObservabilityProjection.js +10 -10
  148. package/dist/scheduler/taskWake.js +4 -4
  149. package/dist/scheduler/wakeReason.js +6 -10
  150. package/dist/scheduler/wakeupQueue.js +0 -9
  151. package/dist/setup/setupCommand.js +2 -2
  152. package/dist/storage/currentTaskStore.js +46 -0
  153. package/dist/storage/persistenceWorker.js +1 -1
  154. package/dist/storage/sqliteSchema.js +81 -485
  155. package/dist/storage/sqliteStore.js +206 -365
  156. package/dist/storage/storageSchema.js +1 -15
  157. package/dist/storage/storageVersions.js +8 -9
  158. package/dist/storage/storeRpc.js +10 -34
  159. package/dist/storage/taskStore.js +63 -3415
  160. package/dist/storage/upgrade/recordVersions.js +55 -149
  161. package/dist/storage/upgrade/upgradeOrchestrator.js +89 -1752
  162. package/dist/task/completionReadiness.js +5 -5
  163. package/dist/task/deliveryGuard.js +10 -10
  164. package/dist/task/nextAction.js +74 -77
  165. package/dist/task/task.js +2 -4
  166. package/dist/task/taskRecordReference.js +3 -3
  167. package/dist/task/taskRecordRetirement.js +1 -1
  168. package/dist/telemetry/sqliteTelemetryStore.js +53 -53
  169. package/dist/telemetry/telemetryConfig.js +14 -14
  170. package/dist/telemetry/telemetryWiring.js +8 -14
  171. package/dist/turn/turn.js +356 -0
  172. package/dist/{run/runIdentity.js → turn/turnIdentity.js} +4 -4
  173. package/dist/verification/gateArtifactStore.js +2 -2
  174. package/dist/web/assets/client/app.js +1 -1
  175. package/dist/web/assets/client/components.js +29 -54
  176. package/dist/web/assets/client/i18n.js +17 -37
  177. package/dist/web/assets/client/view.js +37 -37
  178. package/dist/web/assets/shell.js +1 -1
  179. package/dist/web/assets/styles/cards.js +5 -5
  180. package/dist/web/assets/styles/widgets.js +1 -2
  181. package/dist/web/webSnapshot.js +65 -60
  182. package/dist/workItem/workItem.js +11 -11
  183. package/dist/workspace/workItemChangeSetManager.js +1 -1
  184. package/docs/task-local-identity.md +5 -5
  185. package/i18n/README.zh-CN.md +42 -55
  186. package/package.json +1 -1
  187. package/skills/yui-leader/SKILL.md +85 -83
  188. package/skills/yui-operator/SKILL.md +16 -16
  189. package/skills/yui-reviewer/SKILL.md +10 -10
  190. package/skills/yui-runtime/SKILL.md +22 -24
  191. package/skills/yui-worker/SKILL.md +16 -18
  192. package/dist/context/runContextContract.js +0 -162
  193. package/dist/executor/turnCompletion.js +0 -126
  194. package/dist/review/taskFinalReviewContractEvent.js +0 -1
  195. package/dist/review/taskFinalReviewContractRebind.js +0 -367
  196. package/dist/run/agentRun.js +0 -356
  197. package/dist/run/rejectedYieldAttempt.js +0 -221
  198. package/dist/run/runControlRequest.js +0 -50
  199. package/dist/run/yieldReceipt.js +0 -65
  200. package/dist/runtime/turnCompletion.js +0 -3
  201. package/dist/scheduler/actionability.js +0 -323
  202. package/dist/scheduler/activeRoleRunDelivery.js +0 -868
  203. package/dist/scheduler/activeTaskProgress.js +0 -126
  204. package/dist/storage/compatibleTaskStore.js +0 -248
  205. package/dist/storage/migration/baseline.js +0 -78
  206. package/dist/storage/migration/classifier.js +0 -51
  207. package/dist/storage/migration/compatibleCodec.js +0 -53
  208. package/dist/storage/migration/engine.js +0 -147
  209. package/dist/storage/migration/index.js +0 -33
  210. package/dist/storage/migration/planner.js +0 -154
  211. package/dist/storage/migration/productionRegistry.js +0 -3931
  212. package/dist/storage/migration/registry.js +0 -169
  213. package/dist/storage/migration/report.js +0 -54
  214. package/dist/storage/migration/types.js +0 -31
  215. package/dist/storage/upgrade/homeClassification.js +0 -353
  216. package/dist/storage/upgrade/homeMigrationTarget.js +0 -595
  217. package/dist/storage/upgrade/migrationReceipt.js +0 -67
  218. package/dist/storage/upgrade/offlineUpgradeInventory.js +0 -453
  219. package/dist/storage/upgrade/productionMigrationRegistry.js +0 -6
  220. package/dist/storage/upgrade/pseudoLayoutRepair.js +0 -241
  221. package/dist/storage/upgrade/recordVersionScan.js +0 -176
  222. package/dist/storage/upgrade/sqliteFileSet.js +0 -50
  223. package/dist/storage/upgrade/sqliteMigrationTarget.js +0 -375
  224. package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +0 -305
  225. package/dist/storage/upgrade/sqliteStateMigration.js +0 -926
  226. package/dist/storage/upgrade/switchProgress.js +0 -80
  227. package/dist/storage/upgrade/upgradeReceipt.js +0 -161
  228. package/dist/storage/upgradeCoordination.js +0 -187
  229. package/dist/storage/upgradeFence.js +0 -366
  230. package/dist/telemetry/telemetryCompaction.js +0 -252
package/ARCHITECTURE.md CHANGED
@@ -56,14 +56,14 @@ polling protocol is required to follow work.
56
56
  `WorkItem` means one substantial, independently acceptable requirement with a
57
57
  clear owner. Create multiple WorkItems only when multiple Workers can own and
58
58
  advance those requirements independently, normally in parallel. Internal
59
- implementation steps, test runs, review findings, and local fixes remain Run,
59
+ implementation steps, test runs, review findings, and local fixes remain Turn,
60
60
  Event, report, or commit evidence under the existing Task or WorkItem; they are
61
61
  not new WorkItems.
62
62
 
63
63
  Each WorkItem may use a native subagent inside the Leader conversation or a
64
- Task Role AgentRun backed by a durable Worker Session. There is no Yui
65
- subagent launcher or child-session record. AgentRun is an execution attempt,
66
- not a requirement, and repeated Runs may continue the same compatible Role
64
+ Task Role Turn backed by a durable Worker Session. There is no Yui
65
+ subagent launcher or child-session record. Turn is an execution attempt,
66
+ not a requirement, and repeated Turns may continue the same compatible Role
67
67
  Session.
68
68
 
69
69
  ## Profiles, Roles, and Agents
@@ -77,14 +77,14 @@ Session.
77
77
  the active Agent binding. Its versioned desired launch configuration is
78
78
  next-launch-only. The Role may bind multiple Agents; every binding retains
79
79
  independent runtime configuration.
80
- - `AgentRun` records one managed dispatch and an immutable effective snapshot:
80
+ - `Turn` records one managed dispatch and an immutable effective snapshot:
81
81
  actual Agent, adapter, model, effort, Profile behavior intent, exact writable
82
82
  Projects, provider permission strategy and native options, workspace, Role
83
83
  context, and source desired revision. A native Role Session stores the same snapshot; running processes
84
84
  are never hot-mutated by later Role edits.
85
85
  - A `WorkItemCandidate` is the explicit result currently awaiting Leader
86
86
  acceptance. It snapshots the WorkItem revision, summary, and either a
87
- yielded execution Run or a Leader-managed direct source.
87
+ completed execution Turn or a Leader-managed direct source.
88
88
  - `ReviewRound` records one semantic judgment. A WorkItem Review references
89
89
  that WorkItem's immutable Candidate. A Task-final Review references the
90
90
  frozen Task heads directly and has no synthetic WorkItem/Candidate anchor.
@@ -118,17 +118,19 @@ todo -> running -> awaiting Leader review
118
118
  | reject -> failed -> redispatch -> running
119
119
  ```
120
120
 
121
- Worker yield ends the AgentRun and submits its result for review. It never
122
- accepts the WorkItem. The Leader checks semantics, evidence, and Git state,
123
- then accepts or rejects with bounded feedback. A rejected isolated WorkItem
124
- keeps its workspace so the next Run can repair the same result.
121
+ The Provider's native Turn terminal ends its associated Turn and stores the
122
+ final response as immutable Turn evidence. It never accepts the WorkItem. The
123
+ Leader checks semantics, evidence, and Git state, then resolves the execution
124
+ result and accepts or rejects it with bounded feedback. A rejected isolated
125
+ WorkItem keeps its workspace so the next Turn can repair the same result.
125
126
 
126
127
  An optional global review rule names one existing Global Role and chooses
127
128
  `always`, `leader`, or `final`. Candidate rules remain live defaults; each
128
- WorkItem Candidate snapshots the effective legacy rule when submitted.
129
- Every result awaiting acceptance is stored as an explicit WorkItem candidate.
130
- `always` dispatches a review AgentRun for every candidate, whether it comes
131
- from a yielded execution Run or a Leader-managed direct result; `leader`
129
+ WorkItem Candidate snapshots the effective review rule when submitted.
130
+ Every result is stored first on its exact Turn/Lane. A Candidate is created only
131
+ when the Leader resolves the execution output for acceptance.
132
+ `always` dispatches a review Turn for every candidate, whether it comes
133
+ from a completed execution Turn or a Leader-managed direct result; `leader`
132
134
  leaves every candidate for the Leader to accept directly or review explicitly.
133
135
  `final` keeps WorkItem acceptance and Integration independent and supplies the
134
136
  default Reviewer Role when the Leader decides the frozen Task result warrants
@@ -137,7 +139,7 @@ Review. A Leader-requested Round remains evidence without becoming policy: a
137
139
  later Task head does not require another Round unless the Leader requests one
138
140
  or an explicit Task contract requires it. This final Reviewer evaluates the whole
139
141
  Task, so normal delivery does not pay for a complete review of every WorkItem.
140
- Review Runs complete only their exact ReviewRound, leave the WorkItem awaiting
142
+ Review Turns complete only their exact ReviewRound, leave the WorkItem awaiting
141
143
  acceptance, and never trigger another review or append a Candidate. Successful
142
144
  and failed review attempts both wake the Leader and remain evidence for
143
145
  judgment, not a machine verdict. The ReviewRound stores its frozen Candidate
@@ -147,16 +149,16 @@ never merges it automatically.
147
149
 
148
150
  Roles describe Agent capability, but they do not own repository workspaces. A
149
151
  `ManagedWorkspace` is keyed by its durable owner (`Task`, `WorkItem`,
150
- `ReviewRound`, or `IntegrationAttempt`); an AgentRun carries only a launch
152
+ `ReviewRound`, or `IntegrationAttempt`); an Turn carries only a launch
151
153
  snapshot. Review workspaces are writable copies at the frozen commit, so
152
154
  diagnostics cannot redirect Develop or become a ChangeSet source. Task-final
153
155
  Rounds keep independent immutable records but may reassign one clean physical
154
156
  workspace to the next Round for the same Reviewer Role. This lets the native
155
- Reviewer Session continue while every Run remains bound to its exact Round and
157
+ Reviewer Session continue while every Turn remains bound to its exact Round and
156
158
  head.
157
159
 
158
160
  Dependencies are enforced at dispatch. A Role cannot have overlapping active
159
- Runs, and terminal Task state fences new messages, dispatches, retries, and
161
+ Turns, and terminal Task state fences new messages, dispatches, retries, and
160
162
  late results until explicitly reopened.
161
163
 
162
164
  ## Project workspaces and integration
@@ -206,7 +208,7 @@ WorkItem, or adds the Project to the Task.
206
208
 
207
209
  An isolated result is handled in this order:
208
210
 
209
- 1. the Worker yields;
211
+ 1. the Worker Provider Turn ends and its Turn result is recorded;
210
212
  2. the Leader reviews semantics and evidence;
211
213
  3. Yui captures each writable Project HEAD as an immutable Project ChangeSet;
212
214
  4. each Project integration applies its latest reviewed ChangeSet in a candidate worktree;
@@ -223,7 +225,7 @@ project-specific engineering rules; and the Task Contract owns the requested
223
225
  outcome. Yui injects only its own generic Role Skills. It never scans or copies
224
226
  Project Skills into managed context; the selected Agent discovers them through
225
227
  its native project mechanism. Execution and review select their generic Skill
226
- by durable Run purpose. A Reviewer finding routes to the original Worker while
228
+ by durable Turn purpose. A Reviewer finding routes to the original Worker while
227
229
  open, one consolidated Repair WorkItem when closed, Leader/Integration for
228
230
  merge or local fixes, and an architecture WorkItem only for a genuinely
229
231
  cross-cutting design change. Parallel repair is explicit and requires
@@ -254,10 +256,10 @@ and knowledge needed to resume and audit work:
254
256
  - Decisions: material choices and supersession;
255
257
  - Milestones: independently useful phase outcomes;
256
258
  - Project Knowledge: stable facts reusable across Tasks;
257
- - WorkItems, Roles, AgentRuns, Messages, InputRequests, Events, ChangeSets, and
259
+ - WorkItems, Roles, Turns, Messages, InputRequests, Events, ChangeSets, and
258
260
  integration evidence.
259
261
 
260
- The Leader updates the Brief before every yield, records material choices as
262
+ The Leader updates the Brief when durable Task context changes, records material choices as
261
263
  Decisions, records phase outcomes as Milestones, and promotes only cross-Task
262
264
  stable facts to Project Knowledge. `task context` is the consolidated recovery
263
265
  read; launches and wake messages carry record pointers rather than copied
@@ -270,30 +272,37 @@ Skill and Session Manifest pointer, then uses provider-native requests for
270
272
  durable Task delivery; it does not own or mirror the full transcript. The
271
273
  Controller owns mailbox delivery, wakeups, Role liveness, recovery decisions,
272
274
  and exact receipts. tmux keeps Yui's client attachment observable where the
273
- provider path needs one.
274
-
275
- Run, Conversation, Activation, and Turn identities are independent. A
276
- Conversation can span Runs and client attachments; one Activation identifies
277
- Yui's current attachment, not exclusive ownership of the Provider thread. One
278
- Turn identifies one provider-native execution. Yui's authority epoch fences
279
- only Yui's own submissions and retries.
280
-
281
- `AgentRun` is the single durable scheduling authority for a Role. Provider
282
- runtime persistence has no independently writable current-Run field; a Turn's
283
- Run id is correlation evidence for receipts and terminal observations only.
275
+ provider path needs one. The Controller owns durable wake consumption and
276
+ Provider submission; the Provider Runtime Binding owns the only Turn receipt.
277
+
278
+ Session, Activation, and Turn identities are independent. A Session can span
279
+ Turns and client attachments; one Activation identifies Yui's current
280
+ attachment, not exclusive ownership of the Provider thread. One Turn identifies
281
+ one provider-native execution, whether its input arrived through Yui or directly
282
+ through the Provider UI. Yui's authority epoch fences only Yui's own submissions
283
+ and retries.
284
+
285
+ `Turn` is the single durable scheduling authority for a Role. It records the
286
+ visible inputs, their source and channel, and the final Provider output; it does
287
+ not copy reasoning or tool traffic. All input relayed or generated by Yui has
288
+ source `yui`, while direct Provider input has source `user` and explicit Goal
289
+ continuations have source `provider`.
284
290
  `TaskRole` likewise stores configuration and identity, not a writable runtime
285
- status. CLI and Web status views derive activity from the active AgentRun and
291
+ status. CLI and Web status views derive activity from the active Turn and
286
292
  add Session/Driver facts only as lifecycle and diagnostic detail.
287
- `AgentHost` is the serialized consumer: while a native Turn is active, the next
288
- AgentRun and mailbox batch remain durable and unsubmitted. When that Turn ends,
289
- the Host makes the Conversation ready and the retained delivery continues.
290
- This remains true when the Agent declared the old Run's semantic outcome before
291
- the Provider emitted its terminal event.
292
-
293
- Codex Task threads remain ordinary native sessions and can be opened and used
294
- directly in Desktop. If a direct user Turn is active, Yui keeps its pending
295
- Run/message until that Turn settles. Global interactive entry remains a native
296
- session-lifecycle operation outside the Task delivery contract.
293
+ `AgentHost` is the serialized consumer: while a Provider Turn is active, the
294
+ next mailbox wake remains durable and unsubmitted. When that Turn ends, Yui
295
+ atomically stores the result. Worker and Reviewer completion enters the bounded
296
+ Leader wake aggregation window; a later dispatch creates a new Turn while
297
+ reusing the same live Session whenever its configuration remains compatible.
298
+ Task and WorkItem completion remain Leader decisions and never follow merely
299
+ from Provider termination.
300
+
301
+ Codex Task threads remain ordinary native Sessions and can be opened and used
302
+ directly in Desktop. Direct user Turns are recorded in the same Turn history. If
303
+ one is active, Yui keeps its pending message until that Turn settles. Global
304
+ interactive entry remains a native Session-lifecycle operation outside the Task
305
+ delivery contract.
297
306
 
298
307
  Codex establishes an App Server WebSocket through the byte-forwarding
299
308
  `app-server proxy` to create or resume a normal thread on the shared daemon.
@@ -309,15 +318,15 @@ transport with exact user-message replay acknowledgement. In both cases, Yui
309
318
  records Turn intent before writing, accepts only exact Provider evidence, and
310
319
  maps an uncertain write to `delivery-unknown` without automatic resubmission.
311
320
 
312
- Role desired revisions and Run/Session effective snapshots keep configuration
321
+ Role desired revisions and Turn/Session effective snapshots keep configuration
313
322
  history explicit. Resume compares the complete effective snapshot and
314
323
  workspace compatibility rather than revision alone. Desired drift is expected
315
324
  while an old process is running and becomes effective only on a later launch;
316
325
  control-plane wakes continue through the live Session's actual snapshot, and
317
326
  fresh replacement archives the stopped snapshot instead of rewriting it.
318
- Mailbox generations, reservations, liveness, native Turn Hooks, and exact yield
319
- remain the control-plane authority; configuration snapshots do not replace any
320
- of those completion fences. Lifecycle code uses structured Hook data, persisted identities, tmux
327
+ Mailbox generations, reservations, liveness, and native Turn terminals remain
328
+ the control-plane authority; configuration snapshots do not replace those
329
+ execution facts. Lifecycle code uses structured Hook data, persisted identities, tmux
321
330
  process state, receipts, and pane fences. It never parses Agent terminal glyphs,
322
331
  progress text, trust dialogs, or final prose to infer readiness or success.
323
332
 
@@ -328,386 +337,18 @@ cleanup revalidates ownership and fails safely when concurrent state changes;
328
337
  manual retry is the recovery boundary rather than another durable state
329
338
  machine.
330
339
 
331
- Storage compatibility is modeled on three independent, monotonic version axes:
332
- `layout` (on-disk `schema.json`, `state.json`, locks), `aggregate` (the
333
- authoritative document), and `record` a `recordKind -> version` map so each
334
- record family versions on its own. A centralized compatibility framework
335
- (registry planner compatible loader or migration engine) is generic and
336
- domain-free: the engine is parameterized over an injected `MigrationTarget` and
337
- never hardcodes a Yui record list. Compatibility is decided **only** by explicit
338
- adjacent transition declarations, never by version magnitude or semver.
339
-
340
- The registry separates transition intent from executable transformation. A
341
- `compatible` declaration is legal only on a single `record` axis and carries
342
- three obligations: deterministic named defaults, `validateSource` for the exact
343
- old shape (including rejection of unknown fields), and a normalizer that returns
344
- a fresh value in the next/current domain model. Layout, aggregate, identity or
345
- reference meaning, record splits/merges, and transactional semantic changes must
346
- be declared `offline-migration`; that declaration is runnable only when the
347
- matching adjacent migration step is registered. A transform without a
348
- declaration is `missing-declaration`; an offline declaration without a step is
349
- `missing-step`. Both fail closed. The planner chooses `compatible` only if every
350
- hop on every changed axis is compatible; one offline hop selects the migration
351
- engine. Future versions and damaged structures remain unsupported. The
352
- production registry contains the explicit aggregate `16→17` offline transition;
353
- no historical record-family normalization is implicitly authorized. A frozen
354
- post-baseline descriptor snapshot (versions and locators) plus the shared planner
355
- form the delivery gate: any current-axis advance, locator drift, or new target
356
- family must have its full declared path before the registry can be constructed.
357
-
358
- `doctor`, staged `update` preflight, ordinary store opening, and `upgrade` share
359
- the same classification: **current** (`USABLE`), **compatible-old**
360
- (`COMPATIBLE`), **migration-required** (`MIGRATABLE`), or **unsupported**
361
- (`NEEDS_NEW_VERSION`/`CORRUPTED`). The legacy uppercase verdict remains an
362
- internal/result compatibility label; the product meaning is the four-state
363
- vocabulary above.
364
-
365
- The staged updater uses a dedicated internal preflight contract, not the user's
366
- `upgrade --dry-run`. It stops after four-state classification for current Homes,
367
- strict source/current-model validation for compatible-old Homes, or the
368
- authoritative offline inventory for migration-required Homes. It does not create
369
- a migration target, copy or back up the Home, place a fence, touch Controller
370
- lifecycle, or claim staged-output validation, so the exact old Controller may
371
- still be running. After this preflight is clear, the update parent captures and
372
- stops that exact Controller PID; only then does staged activation run the full
373
- stage, loader validation, and atomic switch. Machine results carry one explicit
374
- `update-preflight` outcome plus a consistent current/compatible/migration-required
375
- status; malformed or contradictory combinations fail closed.
376
-
377
- The three axes are genuinely independent, including the record axis. The
378
- durable `schema.json#/recordVersions` map is authoritative for each persisted
379
- family version; a family absent from that map is explicit pre-introduction
380
- version `0`, even when its `state.json` locator is empty. Raw `state.json` is
381
- traversed only to cross-check the manifest against persisted records, never to
382
- infer that a missing target family is current. The planner can advance version
383
- `0` only through an explicitly marked record-family `0->1` introduction; a
384
- missing declaration or offline transform fails closed. For compatible-old,
385
- `openCompatibleFileTaskStore` normalizes a fresh
386
- in-memory snapshot hop by hop, validates the resulting current state with the
387
- same strict graph gate, and exposes only the current domain model. Commits use
388
- the existing current `FileTaskStore` writer, so the first write emits only
389
- current records and advances the durable manifest to the same current versions;
390
- there is no dual write, no preservation of unknown old fields, and no old writer
391
- permitted against a newly written Home. For current Homes the
392
- ordinary strict loader remains the direct path. CORRUPTED is reserved for real
393
- structural JSON damage: an unparseable `state.json`, a container whose shape does
394
- not match its locator, a record with a missing/invalid `schemaVersion`, or a
395
- reference graph that fails the appropriate strict gate.
396
-
397
- `yui upgrade` is the transactional entry point only for
398
- **migration-required** Homes. Before constructing a migration target or touching
399
- the Controller, fence, binary, staging directory, or Home, both dry-run and
400
- execute re-read an authoritative offline inventory. The blocking facts are an
401
- active AgentRun, an in-flight Run, a live native Session, a native Session whose
402
- health cannot be determined, pending turn-completion ownership, a lifecycle
403
- mailbox, or a durable inbox event. Stopped/history-only Sessions, an idle Role
404
- with no native process, and an open Input alone are non-blocking. Every blocker
405
- returns the count plus the available Task/Role/Run/native-session/launch identity
406
- and reason, asserts the scene is unchanged, and names `yui update` as the user
407
- re-run boundary. The inspection never kills, resets, rebinds, retries, or drains
408
- anything. An unreadable inventory is unknown activity and fails closed.
409
-
410
- Once that inventory is clear (including on the user's later re-run after a
411
- block), execute mode places an **admission fence** honored at every authoritative
412
- write choke point, so baseline CLI writers and the Controller (which mutate
413
- through the same store) refuse to begin a new write while an upgrade owns the
414
- Home; the fencing process itself is exempt. Durable runtime-inbox `publish`
415
- participates in a separate,
416
- shared sibling coordination boundary: `<home>.upgrade-coordination.lock` lives
417
- outside the Home and serializes the complete inbox write with the final
418
- snapshot/copy/two-step switch. A publish acquires that lock, then checks the
419
- fence and any unresolved `<home>.upgrade-switch.json` marker before its
420
- temp/link/fsync sequence. Upgrade acquires the same lock after Controller drain,
421
- proves both runtime lanes, re-pins under `.state.lock`, stages the complete
422
- Home, and holds the coordination lock through `home -> backup` and
423
- `staging -> home`. A hook that passed admission before the fence therefore either
424
- finishes under the lock and is copied into promoted Home, or waits and receives a
425
- structured `UpgradeFenceError` that permits re-delivery; it cannot be silently
426
- dropped into backup-only storage. With no fence, normal hook behavior is unchanged
427
- apart from this shared serialization point. **Fence acquisition is a single atomic
428
- `O_CREAT|O_EXCL` create** — the kernel guarantees exactly one of any number of
429
- concurrent upgraders wins that create, so there is no check-then-write window in
430
- which two upgraders both believe they acquired; a loser either re-enters (it
431
- already owns the fence), reclaims a *provably-dead* owner's stale fence and
432
- retries, or fails closed for a live/undeterminable owner. **Stale-fence reclaim
433
- is itself atomic (compare-and-delete under a `mkdir` critical section):** the
434
- reclaim re-reads the fence bytes under the lock and deletes *only* the exact
435
- dead-owner bytes it observed, so a racer that slipped a fresh live fence into the
436
- same path between the observe and the delete is never clobbered — closing the
437
- reclaim TOCTOU that could otherwise let two entrants both acquire. **That
438
- critical-section lock is itself crash-recoverable** (mirroring the storage lock's
439
- dead-owner reclaim): it records its owner pid, and a lock left behind by a
440
- crashed holder is reclaimed by a later entrant once its owner is provably dead
441
- (or it is older than a small age bound), so a mid-reclaim crash can never
442
- permanently orphan the lock and strand admission (R2-F4). When a reclaim cannot
443
- be proven complete, `assertHomeWritable` re-verifies and refuses rather than
444
- falsely reporting the home writable, and a dead-owner fence is never left
445
- indefinitely stranding writers. There is no lease or multi-round negotiation.
446
- The coordination lock uses the same bounded crash-recovery rule as other Home
447
- locks: it records an owner PID, waits only a bounded interval, and atomically
448
- renames aside a lock whose owner is provably dead (or whose owner-less directory
449
- is older than the conservative acquisition window). A live or undeterminable
450
- holder fails closed; a switch-progress marker blocks hook admission when the
451
- Home is missing or uninitialized (including a malformed marker), while a stale
452
- marker beside an intact Home is ignored after filesystem corroboration. Lock
453
- ordering is one-way — coordination lock, then `.state.lock`; inbox writers
454
- never acquire `.state.lock` — so the cutover cannot deadlock on a reverse order.
455
- The fence is enforced by every writer built from this release forward (its check
456
- lives in the shared store-commit path); it cannot retroactively bind an
457
- already-installed older binary, so cross-release
458
- concurrency is instead handled by the quiesce step and the recommendation to
459
- stop all Yui activity for the home before upgrading. It then drains the
460
- Controller with the public `controller.stop`/shutdownAndDrain (never a broad
461
- kill, never a TTL or idle heuristic), fails closed if any foreign writer, live
462
- Controller, or held `.state.lock` remains, and proves BOTH durable runtime lanes
463
- empty — the aggregate `state.json` runtime-lifecycle mailboxes AND the durable
464
- runtime inbox `runtime/inbox/*` (authoritative not-yet-applied native-hook
465
- events; per task-1 / message-8 §3, either non-empty is a `drain-incomplete`
466
- blocker). The inbox is proven empty **read-only** (a plain directory scan for
467
- committed `*.json` events, in-progress `.tmp-*` writes, and quarantined
468
- `runtime/inbox-invalid` entries) — never via the inbox's own `list()`, which
469
- would quarantine as a side effect, so the check never mutates the source; an
470
- unreadable inbox directory fails closed. This matters because the no-Controller
471
- / stale-event path reaches quiesce with inbox entries still on disk, and an
472
- atomic switch must never silently drop them. The read-only quiesce proof is
473
- performed only after acquiring the shared coordination lock; an admitted hook
474
- that was still completing cannot cross that lock, and a hook that waits sees the
475
- fence and fails explicitly. The cutover then re-pins the committed revision
476
- under the write lock after the drain (avoiding a
477
- check-then-migrate race), migrates the immutable source into a fresh staged home,
478
- validates it
479
- through the real `FileTaskStore` loader gate (record parse + reference graph),
480
- then atomically switches into place with a timestamped backup and a post-switch
481
- health check. Any blocked or failed step leaves the authoritative home
482
- byte-for-byte unchanged and reports the exact stage and recovery action;
483
- User-facing `--dry-run` runs through the validation gate and reports success only
484
- when the migration engine itself returns its exact `dry-run` evidence. A live
485
- runtime or any other earlier engine result remains a blocker; it is never wrapped
486
- as validated. Successful dry-run discards the staged output without switching.
487
- The aggregate `16→17` transition is the only production offline path in this
488
- release; compatible record-family normalization remains explicitly declaration-gated.
489
-
490
- **Uninitialized home is an actionable blocker, not a no-op.** An
491
- uninitialized home (never `yui setup`) has no storage to migrate. The classifier
492
- reports it as USABLE (nothing is *wrong* with it, so `doctor` may present it
493
- as-is), but the *upgrade* path would otherwise collapse that verdict into a
494
- silent no-op against a home that was never set up. Upgrade therefore returns a
495
- structured `uninitialized` blocker ("run `yui setup`") — never an unclassified
496
- runtime error and never a false success.
497
-
498
- **Complete home content preservation contract.** A migration only *transforms*
499
- `schema.json` + `state.json`, but the atomic switch replaces the **whole** home
500
- directory (`home -> backup`, `staging -> home`). Staging that held only those two
501
- files would silently drop everything else the real home persists — `runtime/`
502
- discovery, `runtime/inbox/*` (AUTHORITATIVE, not-yet-applied events), `cache/`,
503
- `artifacts/`. The chosen contract (implemented in `writeFreshOutput`) is that
504
- **staging carries a complete copy of the home**: every other entry (any depth:
505
- dirs, files, symlinks) is copied verbatim, and only `schema.json`/`state.json`
506
- are overwritten with their migrated bytes. So the switch preserves all
507
- authoritative and rebuildable content — and the timestamped backup retains the
508
- original of everything too. The transient `.state.lock` is the one exception: a
509
- lock is per-instance coordination state, never authoritative content, so it is
510
- not promoted into the migrated home. The staging directory is required to live
511
- *outside* the home (an in-home staging layout is refused at construction), so the
512
- copy never excludes a home entry merely because it shares the staging directory's
513
- name — a real home entry named `home.upgrade-staging` is preserved like any other.
514
-
515
- **Partial (two-step) switch is reported honestly, never as "unchanged".** The
516
- atomic switch is two renames — `home -> backup`, then `staging -> home` — with one
517
- non-atomic window between them, tracked by a durable sibling progress marker
518
- (`<home>.upgrade-switch.json`) whose phase distinguishes *not-started* /
519
- *backing-up* / *promoting* / *interrupted* / *complete*. The invariant that drives
520
- error handling: **before** the first rename commits the home is intact and any
521
- failure is a clean pre-switch error ("source unchanged", which is true);
522
- **after** it commits, *every* subsequent operation — the post-rename fsync, the
523
- `promoting` marker write, the promote rename, and the post-promote fsync/marker
524
- clear — is phase-aware, so an fsync or marker failure can never escape as a plain
525
- error that the engine would render as "source unchanged". On any pre-promotion
526
- failure the code attempts an automatic rollback (`backup -> home`); when that
527
- succeeds the original is restored and the failure is reported with the home
528
- genuinely unchanged. **Only if the rollback also fails** is the switch left
529
- partially applied: the marker records `interrupted`, the engine surfaces a
530
- distinct `switch-ambiguous` outcome, and the upgrade blocks at a dedicated
531
- `switch-ambiguous` stage that states the home is **not** intact and prints the
532
- exact `mv "<backup>" "<home>"` recovery. A failure of the *post-promotion*
533
- fsync/marker-clear, by contrast, does **not** fail the switch — the new home is
534
- already in place and correct, and those steps are best-effort durability, so a
535
- good migrated home is never rolled back. No completion receipt is written for an
536
- interrupted switch (it did not commit); the `interrupted` marker is the durable
537
- signal.
538
-
539
- **Crash-window recovery keys off the marker plus filesystem evidence.** A process
540
- that dies mid-switch leaves a durable marker (`backing-up`, `promoting`, or
541
- `interrupted`), with the original at the backup and the home path missing. `yui
542
- update`'s probe treats a marker of **any** phase as an interrupted switch **only
543
- when the filesystem still corroborates it** — the backup exists AND the home is
544
- missing/uninitialized — and then prints the exact backup-restore path, never a
545
- generic "most likely did not commit, retry/setup" that would send the operator to
546
- re-initialize a missing home. Crucially this evidence gate applies to the
547
- `interrupted` phase too (R2-F3): a stale `interrupted` marker left over after a
548
- manual recovery — the home already restored, or the backup already removed — is
549
- **not** trusted to emit a restore path; the probe ignores the stale marker and
550
- reconciles against the real on-disk state instead. A pre-start marker whose home
551
- is still intact (or that has no usable backup) is likewise not treated as
552
- interrupted: there is nothing to recover.
553
-
554
- **Quiesce fails closed on any undeterminable signal.** The `.state.lock` is
555
- acquired mkdir-first with its `owner` file written a moment later, so a lock
556
- directory that exists but whose owner is missing, empty, non-integer, or
557
- unreadable is *not* proof of "no writer" — it may be a writer mid-acquisition.
558
- Quiesce therefore treats such a lock as **unknown-active** and refuses to proceed
559
- (reporting an `active-runtime` blocker); only a lock whose owner is clearly
560
- readable *and* names a dead PID is reclaimable. A `runtime/controller.json` that
561
- exists but is malformed/unparseable is treated the same way — a live Controller
562
- cannot be ruled out, so it fails closed rather than being read as "no
563
- controller". A lock or discovery file that is provably absent is the only "no
564
- runtime" case.
565
-
566
- `yui update` stages the published package side by side (never replacing the live
567
- install first) and runs that staged binary's read-only classification against the
568
- Home. Current and compatible-old Homes take the **fast path**: no Home target is
569
- constructed, copied, backed up, renamed, or replayed, and no Provider Session is
570
- waited on. The parent captures the exact executable/argv/version identity of the
571
- old Controller, stops it once with authenticated lifecycle control, promotes the
572
- same staged artifact, validates the activated binary and compatible loader, then
573
- starts and authenticates the replacement Controller. Existing managed Sessions
574
- retain their frozen executable/CLI path, Home, control digest, and exact
575
- Task/Run/launch/native-Session fence; neither binary promotion nor Controller
576
- replacement retargets them through PATH. The managed continuity preflight treats
577
- package-version drift alone as expected for that in-place path, but keeps
578
- protocol, layout, aggregate, path, Home, digest, and runtime identity strict.
579
- This lets the old Session record progress and yield through the replacement
580
- Controller. It does not authorize migration-required storage: that path still
581
- requires the offline inventory to prove zero live Sessions. A compatible Home
582
- remains byte-for-byte old until an ordinary new-CLI commit; that first
583
- current-only write is also the no-auto-downgrade boundary.
584
-
585
- Migration-required Homes take the **offline path**. Staged preflight applies the
586
- offline inventory before the parent stops the Controller, and storage activation
587
- rechecks it before the child may fence, stage, or mutate the Home. Execute then
588
- closes pre-admitted writers through `.state.lock` while the fence is held and
589
- rechecks the same inventory once more before staging. A newly active Run or
590
- native Session therefore blocks the race window and the parent restores the exact
591
- captured Controller identity on a clean pre-switch refusal. Only a clear user
592
- invocation proceeds through the existing complete-Home migration, backup,
593
- validation, and switch. Neither path writes Task Messages as a heartbeat or
594
- performs background automatic upgrades.
595
-
596
- **Same-artifact promotion:** the version resolved at stage time is pinned, and
597
- binary activation installs that exact `@zq-silk/yui@<version>` — never a second
598
- bare `@latest` that could resolve to a different build than the one that passed
599
- preflight. **Only a CONCRETE version is accepted** (R3-F1): the resolver
600
- requires a semver-shaped `X.Y.Z` (optional pre-release/build suffix) — a dist-tag
601
- sentinel like `latest`, an empty/malformed value, or a version probe that does
602
- not come back in a valid `{ ok:true, data }` envelope at exit 0 all yield "no
603
- version", and the stage then FAILS closed (the live install is untouched, fully
604
- recoverable) rather than splicing a `latest` sentinel into an activation spec.
605
- **Verify the activated binary:** the post-update health check runs the
606
- *actually-activated* global binary (resolved via `npm prefix -g`), not the
607
- staging path, and **requires** its reported version to be concrete and equal to
608
- the staged version — a missing, unparseable, or mismatched version fails closed
609
- (never skipped), so a build whose identity cannot be positively confirmed is
610
- never trusted.
611
-
612
- **A success envelope is required before any outcome is trusted.** Every
613
- interpretation of a spawned staged-binary result first requires a valid
614
- `{ ok: true, data: <object> }` success envelope (R3-F3). The parser guards the
615
- top-level shape *before* reading any field: a body that parses to `null`, an
616
- array, or a primitive (`JSON.parse("null")`/`"[]"`/`"5"` all succeed) is rejected
617
- as no-envelope rather than crashing on a `.ok` access (R4-F1); likewise an
618
- `ok:false` error envelope, a non-object `data`, unparseable output, a kill, or a
619
- transport error is unresolved — preflight treats it as **blocked**, activation as
620
- **ambiguous**, and a version probe as "no version". The `runUpdate` orchestrator
621
- also wraps the preflight/activation port calls so an unexpected throw becomes a
622
- blocked preflight / ambiguous activation, never an uncaught error that could hide
623
- a committed switch. Only then does the outcome/exit consistency rule apply: a
624
- *success-class* outcome (`upgraded`, `compatible`, `already-current`, or a
625
- `dry-run` preflight) is trusted **only when the process also
626
- exited 0**. A contradiction — stdout says `upgraded` but the process exited
627
- non-zero — means the child's own contract was violated mid-flight, so it is
628
- treated as **ambiguous** (activation) or **blocked** (preflight), never a false
629
- success. Blocker-class outcomes are exempt: `yui upgrade` deliberately exits
630
- non-zero (5) for a clean `blocked`, so a non-zero exit there is expected and
631
- consistent. A parseable result with **no** recognized outcome is likewise never
632
- read as success.
633
-
634
- **Post-verify parses the doctor machine-readable result before the exit status.**
635
- The post-update health check validates the structured `yui --json doctor` verdict
636
- FIRST, then the exit status (R2-F2) — because `--json doctor` deliberately exits
637
- non-zero on unhealthy storage, so keying off the exit first would reduce a precise
638
- "storage unsupported/corrupted" verdict to a generic "exited with status N".
639
- Storage is healthy only when ALL hold: a valid `{ ok: true, data: { checks,
640
- storage } }` success envelope, **every expected storage check present exactly once
641
- and `ok`** (a missing, duplicated, or malformed check fails closed — the `healthy`
642
- flag is never trusted over the authoritative checks array, R3-F2), a
643
- `storage.blocking` that is **a well-formed array of check-shaped objects** (a
644
- missing field, a non-array value, or a malformed element fails closed rather than
645
- being silently coerced to an empty array, R4-F2), `storage.healthy === true` with
646
- no blocking checks, AND exit 0. A parseable-
647
- but-unhealthy result (typically exit 5) throws a precise, recovery-oriented
648
- blocker; an unparseable, non-success, or self-contradictory envelope (e.g.
649
- `healthy: true` alongside a non-`ok` storage check, or `ok: false`) fails closed —
650
- an unverifiable health check must never pass silently. The `--json` doctor path
651
- additionally exits non-zero when storage is unhealthy, so even a naive exit-code
652
- consumer fails closed; text-mode `doctor` keeps its existing presentation.
653
-
654
- **Activation ambiguity.** Storage activation runs in a spawned staged-binary
655
- child. If that child is killed (SIGTERM/OOM) or crashes *after* the atomic switch
656
- commits but *before* it prints its result JSON, the parent cannot tell "nothing
657
- happened" from "storage already switched". This is reported as a distinct
658
- **ambiguous** outcome — never a false "recoverable/unchanged". The switch writes
659
- a durable completion **receipt** at a sibling path (`<home>.upgrade-receipt.json`)
660
- the instant it commits, and clears it only on a clean, fully-verified return; so
661
- its presence proves the switch committed even when stdout was lost. On an
662
- ambiguous result the orchestrator probes the receipt + timestamped backup +
663
- current schema and prints precise manual-recovery steps (verify with `yui doctor`;
664
- restore the named backup with `mv` if needed), and the CLI exits non-zero with a
665
- dedicated code so the ambiguity is never mistaken for success.
666
-
667
- **A receipt is only trusted when it genuinely corresponds to the current home
668
- AND its backup.** A leftover receipt from a prior attempt is not unconditional
669
- proof that *this* attempt's switch committed, and existence alone is not
670
- correspondence (R3-F6). Before using a receipt for a recovery decision, the probe
671
- requires the current protocol's correlating fields and a real backup: it is
672
- rejected (the caller re-probes the real on-disk state instead) when it lacks a
673
- `homePath` (a legacy/degraded marker), names a **different home**, lacks a
674
- `backupPath`, names a backup that is **not this home's expected
675
- `<home>.backup-*` timestamped sibling** (unrelated/foreign evidence), or whose
676
- backup is **absent or not a real directory** (already restored or cleaned). A
677
- non-corresponding receipt reads as "not switched" so recovery advice is never
678
- derived from stale, legacy, or unrelated evidence.
679
-
680
- **Rollback boundary (narrowed):** the managed Session launcher is an in-place
681
- forwarder to the currently activated CLI, not a versioned package pointer, so
682
- this release still makes no binary+Home dual-resource atomicity claim. It
683
- guarantees isolated staging (a stage/preflight failure leaves binary and Home
684
- unchanged), a no-Home-mutation fast path, a recoverable atomic storage switch on
685
- the offline path (timestamped backup, restorable until the new version resumes
686
- writes), and no auto-downgrade after writes resume. The offline path's single
687
- non-atomic window — storage switched, binary promotion then failing — is surfaced
688
- with the exact backup-restore recovery, and the version-gated axes make the old
689
- binary fail-close on the new Home rather than misread it. This release exercises
690
- the contracts only against isolated synthetic Homes; the production registry
691
- contains the aggregate `16→17` offline transition, but this Task does not run a
692
- migration against any real Home.
693
-
694
- **Cross-Task schema scheduling.** Storage schema work is not globally serialized.
695
- Any module or Task may advance a storage version axis (`layout`, `aggregate`, or
696
- a `record` family) on its own isolated branch without waiting for another Task's
697
- schema change to land — branches do not block each other. The cost of that
698
- parallelism is assigned, by design, to whichever branch integrates later: the
699
- later-integrating branch is responsible for rebasing onto the latest project
700
- head, resolving all schema and code conflicts, re-advancing whatever schema
701
- versions and record-version-map entries the rebase requires, rebuilding and
702
- re-validating the real wiring, and fully re-running the isolated migration/upgrade
703
- E2E and its documentation. This rework-and-reconcile duty belongs to the later
704
- integrator; it is a deliberate scheduling trade-off (authorized by the user) that
705
- avoids cross-Task blocking rather than an accident to be repaired ad hoc.
706
- Concretely, the current manifest descriptor map is re-derived against the newest
707
- head, while the post-baseline descriptor snapshot remains frozen. If another
708
- Task lands a record-schema change, the integrating branch must reconcile both:
709
- existing-family advances need a complete adjacent path, and a new target family
710
- needs an explicit `0->1` introduction before re-testing to convergence.
340
+ Storage still records layout, aggregate, and record-family versions, but this
341
+ release deliberately re-baselines all three axes at the current contract. The
342
+ production migration registry is empty. Ordinary opening, Controller startup,
343
+ doctor, update preflight, and the storage upgrade entry point therefore accept
344
+ only an exact current manifest and current record shapes. An older Home is
345
+ unsupported and must not be normalized, rewritten, or switched in place.
346
+
347
+ SQLite bootstrap DDL is an implementation detail for initializing a fresh Home.
348
+ Its ledger must be complete on every later open; a partial or older ledger is
349
+ rejected rather than advanced. This keeps one durable model for Turn,
350
+ TaskRoleSessionSet, WorkMailbox, and Provider Runtime Binding and prevents an
351
+ old writer or migration transform from recreating removed delivery state.
711
352
 
712
353
  The Web control room is loopback-only and never receives Controller socket
713
354
  credentials. It presents durable records and native terminal access without