@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.
- package/ARCHITECTURE.md +69 -428
- package/README.md +108 -298
- package/dist/agent/managedRuntimeEnvironment.js +2 -2
- package/dist/cli/commandCatalog.js +39 -77
- package/dist/cli/interactionCandidates.js +4 -4
- package/dist/cli/interactionPolicy.js +7 -10
- package/dist/cli/invocationRouter.js +1 -1
- package/dist/cli/updateCommand.js +6 -27
- package/dist/cli/updateOrchestrator.js +63 -484
- package/dist/cli/updatePorts.js +15 -333
- package/dist/cli/upgradeCommand.js +8 -65
- package/dist/cli.js +57 -242
- package/dist/commands/configCommands.js +11 -62
- package/dist/commands/executionAuditCommands.js +23 -23
- package/dist/commands/globalRoleCommands.js +2 -7
- package/dist/commands/jobCommands.js +1 -4
- package/dist/commands/projectCommands.js +5 -5
- package/dist/commands/releaseCommands.js +3 -10
- package/dist/commands/resourcesCommands.js +2 -2
- package/dist/commands/sessionCommands.js +2 -2
- package/dist/commands/taskActor.js +31 -39
- package/dist/commands/taskChangeSetCommands.js +5 -12
- package/dist/commands/taskCommands.js +464 -1249
- package/dist/commands/taskContextCommand.js +35 -36
- package/dist/commands/taskExecutionCommands.js +33 -46
- package/dist/commands/taskInputCommands.js +20 -42
- package/dist/commands/taskNextActionCommand.js +9 -9
- package/dist/commands/taskOverviewCommand.js +56 -95
- package/dist/commands/taskRoleRuntimeStatus.js +77 -107
- package/dist/commands/taskUpstreamCommands.js +3 -3
- package/dist/commands/taskWorkspaceCommands.js +7 -79
- package/dist/commands/telemetryCommands.js +32 -124
- package/dist/commands/workflowCommands.js +3 -3
- package/dist/completion/fileCompletionManager.js +1 -1
- package/dist/config/configCatalog.js +4 -6
- package/dist/config/yuiConfig.js +6 -43
- package/dist/context/dispatchContext.js +14 -30
- package/dist/context/roleSessionContext.js +2 -2
- package/dist/context/sessionBootstrapManifest.js +2 -2
- package/dist/context/{runContextPack.js → turnContextPack.js} +58 -73
- package/dist/context/turnInputContract.js +204 -0
- package/dist/context/wakeNotification.js +2 -2
- package/dist/controller/agentRuntimeObserver.js +18 -18
- package/dist/controller/clientRuntime.js +13 -20
- package/dist/controller/controller.js +89 -116
- package/dist/controller/fileSchedulerStoreAdapter.js +511 -742
- package/dist/controller/jobControl.js +17 -17
- package/dist/controller/resourceInventory.js +6 -6
- package/dist/controller/resourceInventoryLinux.js +38 -59
- package/dist/controller/resourceInventoryRpc.js +3 -3
- package/dist/controller/resourceInventoryWorker.js +1 -1
- package/dist/controller/runtime.js +46 -53
- package/dist/controller/runtimeEventInbox.js +23 -31
- package/dist/controller/runtimeEventProcessor.js +15 -15
- package/dist/controller/{runtimeHookRunFence.js → runtimeHookTurnFence.js} +87 -81
- package/dist/controller/runtimeLaunchCoordinator.js +3 -3
- package/dist/controller/runtimeObservationHook.js +11 -8
- package/dist/controller/sessionNotify.js +11 -11
- package/dist/controller/structuredProviderObservation.js +89 -16
- package/dist/coordination/workMailbox.js +309 -579
- package/dist/coordination/workMailboxQueue.js +5 -6
- package/dist/core/controllerClient.js +1 -91
- package/dist/core/controllerServer.js +3 -6
- package/dist/doctor/doctor.js +51 -314
- package/dist/execution/candidateConvergence.js +3 -3
- package/dist/execution/executionGroup.js +32 -33
- package/dist/execution/executionHealth.js +36 -38
- package/dist/execution/resourceBroker.js +12 -12
- package/dist/executor/agentExecutor.js +30 -265
- package/dist/executor/effectiveLaunch.js +9 -19
- package/dist/executor/executorRegistry.js +43 -17
- package/dist/executor/fileRoleLaunchPlanner.js +56 -56
- package/dist/executor/launchPlan.js +3 -3
- package/dist/executor/workspacePreflightClassification.js +16 -16
- package/dist/grant/capabilityGrant.js +20 -25
- package/dist/input/inputRequest.js +12 -12
- package/dist/integration/changeSet.js +9 -13
- package/dist/integration/changeSetManifest.js +1 -1
- package/dist/integration/gitIntegrationService.js +2 -2
- package/dist/integration/integrationQueueService.js +5 -21
- package/dist/integration/overlapDiagnostics.js +2 -2
- package/dist/interaction/operatorPresentation.js +1 -1
- package/dist/job/jobRunner.js +1 -1
- package/dist/lifecycle/canonicalLifecycleEvent.js +25 -25
- package/dist/lifecycle/{exactRunTerminalization.js → exactTurnTerminalization.js} +116 -232
- package/dist/message/message.js +6 -6
- package/dist/observability/executionAudit.js +74 -86
- package/dist/observability/faultClassification.js +7 -24
- package/dist/observability/orchestrationMetrics.js +5 -5
- package/dist/observability/runtimeIdentity.js +34 -101
- package/dist/profile/agentProfile.js +1 -1
- package/dist/release/cliHomeReleaseFence.js +3 -3
- package/dist/release/releaseWorkflowEngine.js +6 -10
- package/dist/release/releaseWorkflowPorts.js +2 -2
- package/dist/release/workflowFileLock.js +1 -1
- package/dist/repository/checkoutSwap.js +1 -1
- package/dist/repository/gitWorkspace.js +0 -13
- package/dist/repository/project.js +1 -1
- package/dist/repository/projectMaintenanceLock.js +1 -2
- package/dist/repository/taskBaseFreshness.js +13 -13
- package/dist/repository/taskWorkspaceCoordinator.js +21 -25
- package/dist/repository/taskWorkspaceIdentity.js +8 -21
- package/dist/repository/taskWorkspacePreparer.js +63 -620
- package/dist/resources/autoResourceGc.js +3 -3
- package/dist/resources/liveReferences.js +1 -1
- package/dist/resources/resourceGc.js +1 -1
- package/dist/resources/sqliteResourceRegistry.js +1 -1
- package/dist/review/reviewDecision.js +7 -7
- package/dist/review/reviewFindingLedger.js +1 -1
- package/dist/review/reviewOutcomeClassifier.js +35 -50
- package/dist/review/reviewRound.js +14 -25
- package/dist/review/reviewerAvailability.js +4 -4
- package/dist/review/taskFinalReviewContractResolution.js +41 -0
- package/dist/role/role.js +6 -3
- package/dist/runtime/agentDriver.js +4 -0
- package/dist/runtime/agentHost.js +151 -36
- package/dist/runtime/builtinAgentDrivers.js +79 -2
- package/dist/runtime/codexAppServerRuntime.js +122 -0
- package/dist/runtime/continuationManager.js +3 -5
- package/dist/runtime/exactControlPlane.js +17 -42
- package/dist/runtime/firstProgressAdvisory.js +1 -1
- package/dist/runtime/index.js +2 -2
- package/dist/runtime/launchDiagnostics.js +2 -2
- package/dist/runtime/lifecycleReservation.js +4 -4
- package/dist/runtime/processExitObservation.js +1 -1
- package/dist/runtime/processExitOutbox.js +0 -32
- package/dist/runtime/promptEnvelope.js +9 -9
- package/dist/runtime/providerContinuation.js +2 -2
- package/dist/runtime/providerContinuationReconciliationService.js +2 -2
- package/dist/runtime/providerRuntimeIdentity.js +76 -24
- package/dist/runtime/recentTurnIds.js +38 -0
- package/dist/runtime/runtimeContinuationProjection.js +2 -2
- package/dist/runtime/runtimeHealthPolicy.js +1 -1
- package/dist/runtime/runtimeObservation.js +58 -21
- package/dist/runtime/runtimeProjection.js +19 -27
- package/dist/runtime/sessionLaunchRequest.js +2 -2
- package/dist/runtime/structuredProviderHost.js +177 -12
- package/dist/runtime/tmuxAdapters.js +46 -11
- package/dist/scheduler/activeRoleTurnDelivery.js +245 -0
- package/dist/scheduler/leaderWakeupProcessor.js +147 -445
- package/dist/scheduler/operatorEvent.js +4 -6
- package/dist/scheduler/resourceQueueProjection.js +71 -0
- package/dist/scheduler/{roleRunLiveness.js → roleTurnLiveness.js} +24 -28
- package/dist/scheduler/{roleRunStall.js → roleTurnStall.js} +162 -247
- package/dist/scheduler/taskExecutionProjection.js +99 -99
- package/dist/scheduler/taskObservabilityProjection.js +10 -10
- package/dist/scheduler/taskWake.js +4 -4
- package/dist/scheduler/wakeReason.js +6 -10
- package/dist/scheduler/wakeupQueue.js +0 -9
- package/dist/setup/setupCommand.js +2 -2
- package/dist/storage/currentTaskStore.js +46 -0
- package/dist/storage/persistenceWorker.js +1 -1
- package/dist/storage/sqliteSchema.js +81 -485
- package/dist/storage/sqliteStore.js +206 -365
- package/dist/storage/storageSchema.js +1 -15
- package/dist/storage/storageVersions.js +8 -9
- package/dist/storage/storeRpc.js +10 -34
- package/dist/storage/taskStore.js +63 -3415
- package/dist/storage/upgrade/recordVersions.js +55 -149
- package/dist/storage/upgrade/upgradeOrchestrator.js +89 -1752
- package/dist/task/completionReadiness.js +5 -5
- package/dist/task/deliveryGuard.js +10 -10
- package/dist/task/nextAction.js +74 -77
- package/dist/task/task.js +2 -4
- package/dist/task/taskRecordReference.js +3 -3
- package/dist/task/taskRecordRetirement.js +1 -1
- package/dist/telemetry/sqliteTelemetryStore.js +53 -53
- package/dist/telemetry/telemetryConfig.js +14 -14
- package/dist/telemetry/telemetryWiring.js +8 -14
- package/dist/turn/turn.js +356 -0
- package/dist/{run/runIdentity.js → turn/turnIdentity.js} +4 -4
- package/dist/verification/gateArtifactStore.js +2 -2
- package/dist/web/assets/client/app.js +1 -1
- package/dist/web/assets/client/components.js +29 -54
- package/dist/web/assets/client/i18n.js +17 -37
- package/dist/web/assets/client/view.js +37 -37
- package/dist/web/assets/shell.js +1 -1
- package/dist/web/assets/styles/cards.js +5 -5
- package/dist/web/assets/styles/widgets.js +1 -2
- package/dist/web/webSnapshot.js +65 -60
- package/dist/workItem/workItem.js +11 -11
- package/dist/workspace/workItemChangeSetManager.js +1 -1
- package/docs/task-local-identity.md +5 -5
- package/i18n/README.zh-CN.md +41 -54
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +85 -83
- package/skills/yui-operator/SKILL.md +16 -16
- package/skills/yui-reviewer/SKILL.md +10 -10
- package/skills/yui-runtime/SKILL.md +17 -18
- package/skills/yui-worker/SKILL.md +16 -18
- package/dist/context/runContextContract.js +0 -162
- package/dist/executor/turnCompletion.js +0 -126
- package/dist/review/taskFinalReviewContractEvent.js +0 -1
- package/dist/review/taskFinalReviewContractRebind.js +0 -367
- package/dist/run/agentRun.js +0 -356
- package/dist/run/rejectedYieldAttempt.js +0 -221
- package/dist/run/runControlRequest.js +0 -50
- package/dist/run/yieldReceipt.js +0 -65
- package/dist/runtime/turnCompletion.js +0 -3
- package/dist/scheduler/actionability.js +0 -323
- package/dist/scheduler/activeRoleRunDelivery.js +0 -868
- package/dist/scheduler/activeTaskProgress.js +0 -126
- package/dist/storage/compatibleTaskStore.js +0 -248
- package/dist/storage/migration/baseline.js +0 -78
- package/dist/storage/migration/classifier.js +0 -51
- package/dist/storage/migration/compatibleCodec.js +0 -53
- package/dist/storage/migration/engine.js +0 -147
- package/dist/storage/migration/index.js +0 -33
- package/dist/storage/migration/planner.js +0 -154
- package/dist/storage/migration/productionRegistry.js +0 -3931
- package/dist/storage/migration/registry.js +0 -169
- package/dist/storage/migration/report.js +0 -54
- package/dist/storage/migration/types.js +0 -31
- package/dist/storage/upgrade/homeClassification.js +0 -353
- package/dist/storage/upgrade/homeMigrationTarget.js +0 -595
- package/dist/storage/upgrade/migrationReceipt.js +0 -67
- package/dist/storage/upgrade/offlineUpgradeInventory.js +0 -453
- package/dist/storage/upgrade/productionMigrationRegistry.js +0 -6
- package/dist/storage/upgrade/pseudoLayoutRepair.js +0 -241
- package/dist/storage/upgrade/recordVersionScan.js +0 -176
- package/dist/storage/upgrade/sqliteFileSet.js +0 -50
- package/dist/storage/upgrade/sqliteMigrationTarget.js +0 -375
- package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +0 -305
- package/dist/storage/upgrade/sqliteStateMigration.js +0 -918
- package/dist/storage/upgrade/switchProgress.js +0 -80
- package/dist/storage/upgrade/upgradeReceipt.js +0 -161
- package/dist/storage/upgradeCoordination.js +0 -187
- package/dist/storage/upgradeFence.js +0 -366
- 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
|
|
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
|
|
65
|
-
subagent launcher or child-session record.
|
|
66
|
-
not a requirement, and repeated
|
|
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
|
-
- `
|
|
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
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
|
129
|
-
Every result
|
|
130
|
-
|
|
131
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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,
|
|
259
|
+
- WorkItems, Roles, Turns, Messages, InputRequests, Events, ChangeSets, and
|
|
258
260
|
integration evidence.
|
|
259
261
|
|
|
260
|
-
The Leader updates the Brief
|
|
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
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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
|
|
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
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
the
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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
|
|
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
|
|
319
|
-
|
|
320
|
-
|
|
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
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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
|