@zq-silk/yui 0.13.9 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/ARCHITECTURE.md +69 -428
  2. package/README.md +108 -298
  3. package/dist/agent/managedRuntimeEnvironment.js +2 -2
  4. package/dist/cli/commandCatalog.js +39 -77
  5. package/dist/cli/interactionCandidates.js +4 -4
  6. package/dist/cli/interactionPolicy.js +7 -10
  7. package/dist/cli/invocationRouter.js +1 -1
  8. package/dist/cli/updateCommand.js +6 -27
  9. package/dist/cli/updateOrchestrator.js +63 -484
  10. package/dist/cli/updatePorts.js +15 -333
  11. package/dist/cli/upgradeCommand.js +8 -65
  12. package/dist/cli.js +57 -242
  13. package/dist/commands/configCommands.js +11 -62
  14. package/dist/commands/executionAuditCommands.js +23 -23
  15. package/dist/commands/globalRoleCommands.js +2 -7
  16. package/dist/commands/jobCommands.js +1 -4
  17. package/dist/commands/projectCommands.js +5 -5
  18. package/dist/commands/releaseCommands.js +3 -10
  19. package/dist/commands/resourcesCommands.js +2 -2
  20. package/dist/commands/sessionCommands.js +2 -2
  21. package/dist/commands/taskActor.js +31 -39
  22. package/dist/commands/taskChangeSetCommands.js +5 -12
  23. package/dist/commands/taskCommands.js +464 -1249
  24. package/dist/commands/taskContextCommand.js +35 -36
  25. package/dist/commands/taskExecutionCommands.js +33 -46
  26. package/dist/commands/taskInputCommands.js +20 -42
  27. package/dist/commands/taskNextActionCommand.js +9 -9
  28. package/dist/commands/taskOverviewCommand.js +56 -95
  29. package/dist/commands/taskRoleRuntimeStatus.js +77 -107
  30. package/dist/commands/taskUpstreamCommands.js +3 -3
  31. package/dist/commands/taskWorkspaceCommands.js +7 -79
  32. package/dist/commands/telemetryCommands.js +32 -124
  33. package/dist/commands/workflowCommands.js +3 -3
  34. package/dist/completion/fileCompletionManager.js +1 -1
  35. package/dist/config/configCatalog.js +4 -6
  36. package/dist/config/yuiConfig.js +6 -43
  37. package/dist/context/dispatchContext.js +14 -30
  38. package/dist/context/roleSessionContext.js +2 -2
  39. package/dist/context/sessionBootstrapManifest.js +2 -2
  40. package/dist/context/{runContextPack.js → turnContextPack.js} +58 -73
  41. package/dist/context/turnInputContract.js +204 -0
  42. package/dist/context/wakeNotification.js +2 -2
  43. package/dist/controller/agentRuntimeObserver.js +18 -18
  44. package/dist/controller/clientRuntime.js +13 -20
  45. package/dist/controller/controller.js +89 -116
  46. package/dist/controller/fileSchedulerStoreAdapter.js +511 -742
  47. package/dist/controller/jobControl.js +17 -17
  48. package/dist/controller/resourceInventory.js +6 -6
  49. package/dist/controller/resourceInventoryLinux.js +38 -59
  50. package/dist/controller/resourceInventoryRpc.js +3 -3
  51. package/dist/controller/resourceInventoryWorker.js +1 -1
  52. package/dist/controller/runtime.js +46 -53
  53. package/dist/controller/runtimeEventInbox.js +23 -31
  54. package/dist/controller/runtimeEventProcessor.js +15 -15
  55. package/dist/controller/{runtimeHookRunFence.js → runtimeHookTurnFence.js} +87 -81
  56. package/dist/controller/runtimeLaunchCoordinator.js +3 -3
  57. package/dist/controller/runtimeObservationHook.js +11 -8
  58. package/dist/controller/sessionNotify.js +11 -11
  59. package/dist/controller/structuredProviderObservation.js +89 -16
  60. package/dist/coordination/workMailbox.js +309 -579
  61. package/dist/coordination/workMailboxQueue.js +5 -6
  62. package/dist/core/controllerClient.js +1 -91
  63. package/dist/core/controllerServer.js +3 -6
  64. package/dist/doctor/doctor.js +51 -314
  65. package/dist/execution/candidateConvergence.js +3 -3
  66. package/dist/execution/executionGroup.js +32 -33
  67. package/dist/execution/executionHealth.js +36 -38
  68. package/dist/execution/resourceBroker.js +12 -12
  69. package/dist/executor/agentExecutor.js +30 -265
  70. package/dist/executor/effectiveLaunch.js +9 -19
  71. package/dist/executor/executorRegistry.js +43 -17
  72. package/dist/executor/fileRoleLaunchPlanner.js +56 -56
  73. package/dist/executor/launchPlan.js +3 -3
  74. package/dist/executor/workspacePreflightClassification.js +16 -16
  75. package/dist/grant/capabilityGrant.js +20 -25
  76. package/dist/input/inputRequest.js +12 -12
  77. package/dist/integration/changeSet.js +9 -13
  78. package/dist/integration/changeSetManifest.js +1 -1
  79. package/dist/integration/gitIntegrationService.js +2 -2
  80. package/dist/integration/integrationQueueService.js +5 -21
  81. package/dist/integration/overlapDiagnostics.js +2 -2
  82. package/dist/interaction/operatorPresentation.js +1 -1
  83. package/dist/job/jobRunner.js +1 -1
  84. package/dist/lifecycle/canonicalLifecycleEvent.js +25 -25
  85. package/dist/lifecycle/{exactRunTerminalization.js → exactTurnTerminalization.js} +116 -232
  86. package/dist/message/message.js +6 -6
  87. package/dist/observability/executionAudit.js +74 -86
  88. package/dist/observability/faultClassification.js +7 -24
  89. package/dist/observability/orchestrationMetrics.js +5 -5
  90. package/dist/observability/runtimeIdentity.js +34 -101
  91. package/dist/profile/agentProfile.js +1 -1
  92. package/dist/release/cliHomeReleaseFence.js +3 -3
  93. package/dist/release/releaseWorkflowEngine.js +6 -10
  94. package/dist/release/releaseWorkflowPorts.js +2 -2
  95. package/dist/release/workflowFileLock.js +1 -1
  96. package/dist/repository/checkoutSwap.js +1 -1
  97. package/dist/repository/gitWorkspace.js +0 -13
  98. package/dist/repository/project.js +1 -1
  99. package/dist/repository/projectMaintenanceLock.js +1 -2
  100. package/dist/repository/taskBaseFreshness.js +13 -13
  101. package/dist/repository/taskWorkspaceCoordinator.js +21 -25
  102. package/dist/repository/taskWorkspaceIdentity.js +8 -21
  103. package/dist/repository/taskWorkspacePreparer.js +63 -620
  104. package/dist/resources/autoResourceGc.js +3 -3
  105. package/dist/resources/liveReferences.js +1 -1
  106. package/dist/resources/resourceGc.js +1 -1
  107. package/dist/resources/sqliteResourceRegistry.js +1 -1
  108. package/dist/review/reviewDecision.js +7 -7
  109. package/dist/review/reviewFindingLedger.js +1 -1
  110. package/dist/review/reviewOutcomeClassifier.js +35 -50
  111. package/dist/review/reviewRound.js +14 -25
  112. package/dist/review/reviewerAvailability.js +4 -4
  113. package/dist/review/taskFinalReviewContractResolution.js +41 -0
  114. package/dist/role/role.js +6 -3
  115. package/dist/runtime/agentDriver.js +4 -0
  116. package/dist/runtime/agentHost.js +151 -36
  117. package/dist/runtime/builtinAgentDrivers.js +79 -2
  118. package/dist/runtime/codexAppServerRuntime.js +122 -0
  119. package/dist/runtime/continuationManager.js +3 -5
  120. package/dist/runtime/exactControlPlane.js +17 -42
  121. package/dist/runtime/firstProgressAdvisory.js +1 -1
  122. package/dist/runtime/index.js +2 -2
  123. package/dist/runtime/launchDiagnostics.js +2 -2
  124. package/dist/runtime/lifecycleReservation.js +4 -4
  125. package/dist/runtime/processExitObservation.js +1 -1
  126. package/dist/runtime/processExitOutbox.js +0 -32
  127. package/dist/runtime/promptEnvelope.js +9 -9
  128. package/dist/runtime/providerContinuation.js +2 -2
  129. package/dist/runtime/providerContinuationReconciliationService.js +2 -2
  130. package/dist/runtime/providerRuntimeIdentity.js +76 -24
  131. package/dist/runtime/recentTurnIds.js +38 -0
  132. package/dist/runtime/runtimeContinuationProjection.js +2 -2
  133. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  134. package/dist/runtime/runtimeObservation.js +58 -21
  135. package/dist/runtime/runtimeProjection.js +19 -27
  136. package/dist/runtime/sessionLaunchRequest.js +2 -2
  137. package/dist/runtime/structuredProviderHost.js +177 -12
  138. package/dist/runtime/tmuxAdapters.js +46 -11
  139. package/dist/scheduler/activeRoleTurnDelivery.js +245 -0
  140. package/dist/scheduler/leaderWakeupProcessor.js +147 -445
  141. package/dist/scheduler/operatorEvent.js +4 -6
  142. package/dist/scheduler/resourceQueueProjection.js +71 -0
  143. package/dist/scheduler/{roleRunLiveness.js → roleTurnLiveness.js} +24 -28
  144. package/dist/scheduler/{roleRunStall.js → roleTurnStall.js} +162 -247
  145. package/dist/scheduler/taskExecutionProjection.js +99 -99
  146. package/dist/scheduler/taskObservabilityProjection.js +10 -10
  147. package/dist/scheduler/taskWake.js +4 -4
  148. package/dist/scheduler/wakeReason.js +6 -10
  149. package/dist/scheduler/wakeupQueue.js +0 -9
  150. package/dist/setup/setupCommand.js +2 -2
  151. package/dist/storage/currentTaskStore.js +46 -0
  152. package/dist/storage/persistenceWorker.js +1 -1
  153. package/dist/storage/sqliteSchema.js +81 -485
  154. package/dist/storage/sqliteStore.js +206 -365
  155. package/dist/storage/storageSchema.js +1 -15
  156. package/dist/storage/storageVersions.js +8 -9
  157. package/dist/storage/storeRpc.js +10 -34
  158. package/dist/storage/taskStore.js +63 -3415
  159. package/dist/storage/upgrade/recordVersions.js +55 -149
  160. package/dist/storage/upgrade/upgradeOrchestrator.js +89 -1752
  161. package/dist/task/completionReadiness.js +5 -5
  162. package/dist/task/deliveryGuard.js +10 -10
  163. package/dist/task/nextAction.js +74 -77
  164. package/dist/task/task.js +2 -4
  165. package/dist/task/taskRecordReference.js +3 -3
  166. package/dist/task/taskRecordRetirement.js +1 -1
  167. package/dist/telemetry/sqliteTelemetryStore.js +53 -53
  168. package/dist/telemetry/telemetryConfig.js +14 -14
  169. package/dist/telemetry/telemetryWiring.js +8 -14
  170. package/dist/turn/turn.js +356 -0
  171. package/dist/{run/runIdentity.js → turn/turnIdentity.js} +4 -4
  172. package/dist/verification/gateArtifactStore.js +2 -2
  173. package/dist/web/assets/client/app.js +1 -1
  174. package/dist/web/assets/client/components.js +29 -54
  175. package/dist/web/assets/client/i18n.js +17 -37
  176. package/dist/web/assets/client/view.js +37 -37
  177. package/dist/web/assets/shell.js +1 -1
  178. package/dist/web/assets/styles/cards.js +5 -5
  179. package/dist/web/assets/styles/widgets.js +1 -2
  180. package/dist/web/webSnapshot.js +65 -60
  181. package/dist/workItem/workItem.js +11 -11
  182. package/dist/workspace/workItemChangeSetManager.js +1 -1
  183. package/docs/task-local-identity.md +5 -5
  184. package/i18n/README.zh-CN.md +41 -54
  185. package/package.json +1 -1
  186. package/skills/yui-leader/SKILL.md +85 -83
  187. package/skills/yui-operator/SKILL.md +16 -16
  188. package/skills/yui-reviewer/SKILL.md +10 -10
  189. package/skills/yui-runtime/SKILL.md +17 -18
  190. package/skills/yui-worker/SKILL.md +16 -18
  191. package/dist/context/runContextContract.js +0 -162
  192. package/dist/executor/turnCompletion.js +0 -126
  193. package/dist/review/taskFinalReviewContractEvent.js +0 -1
  194. package/dist/review/taskFinalReviewContractRebind.js +0 -367
  195. package/dist/run/agentRun.js +0 -356
  196. package/dist/run/rejectedYieldAttempt.js +0 -221
  197. package/dist/run/runControlRequest.js +0 -50
  198. package/dist/run/yieldReceipt.js +0 -65
  199. package/dist/runtime/turnCompletion.js +0 -3
  200. package/dist/scheduler/actionability.js +0 -323
  201. package/dist/scheduler/activeRoleRunDelivery.js +0 -868
  202. package/dist/scheduler/activeTaskProgress.js +0 -126
  203. package/dist/storage/compatibleTaskStore.js +0 -248
  204. package/dist/storage/migration/baseline.js +0 -78
  205. package/dist/storage/migration/classifier.js +0 -51
  206. package/dist/storage/migration/compatibleCodec.js +0 -53
  207. package/dist/storage/migration/engine.js +0 -147
  208. package/dist/storage/migration/index.js +0 -33
  209. package/dist/storage/migration/planner.js +0 -154
  210. package/dist/storage/migration/productionRegistry.js +0 -3931
  211. package/dist/storage/migration/registry.js +0 -169
  212. package/dist/storage/migration/report.js +0 -54
  213. package/dist/storage/migration/types.js +0 -31
  214. package/dist/storage/upgrade/homeClassification.js +0 -353
  215. package/dist/storage/upgrade/homeMigrationTarget.js +0 -595
  216. package/dist/storage/upgrade/migrationReceipt.js +0 -67
  217. package/dist/storage/upgrade/offlineUpgradeInventory.js +0 -453
  218. package/dist/storage/upgrade/productionMigrationRegistry.js +0 -6
  219. package/dist/storage/upgrade/pseudoLayoutRepair.js +0 -241
  220. package/dist/storage/upgrade/recordVersionScan.js +0 -176
  221. package/dist/storage/upgrade/sqliteFileSet.js +0 -50
  222. package/dist/storage/upgrade/sqliteMigrationTarget.js +0 -375
  223. package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +0 -305
  224. package/dist/storage/upgrade/sqliteStateMigration.js +0 -918
  225. package/dist/storage/upgrade/switchProgress.js +0 -80
  226. package/dist/storage/upgrade/upgradeReceipt.js +0 -161
  227. package/dist/storage/upgradeCoordination.js +0 -187
  228. package/dist/storage/upgradeFence.js +0 -366
  229. package/dist/telemetry/telemetryCompaction.js +0 -252
@@ -7,10 +7,10 @@
7
7
  * `npm install --global --prefix <tmp>`, so the live global install is never
8
8
  * touched until the binary-activation step. Preflight invokes the STAGED binary's
9
9
  * internal `yui upgrade --update-preflight` contract so the target version
10
- * classifies the Home, validates a compatible source in memory, or reads the
11
- * authoritative offline inventory as required by that path. After the parent
12
- * stops the exact old Controller, storage activation performs the full staged
13
- * validation/switch. Post-verify invokes the actually activated global binary.
10
+ * proves that the Home already implements the exact current contract. After
11
+ * the parent stops the exact old Controller, only the binary is promoted;
12
+ * update never rewrites or switches storage. Post-verify invokes the actually
13
+ * activated global binary.
14
14
  *
15
15
  * Two hardening guarantees this module enforces:
16
16
  *
@@ -23,26 +23,16 @@
23
23
  * global `yui` (via `npm prefix -g`), runs its `--json doctor`, AND confirms
24
24
  * the promoted binary's reported version matches the staged version. A
25
25
  * mismatch fails closed.
26
- *
27
- * And the ambiguity guarantee (P1-2): `activateStorage` returns `ambiguous`
28
- * (never a false "unchanged") when the child leaves no parseable receipt, and
29
- * `probeStorage` reads the durable receipt + backup + schema so the orchestrator
30
- * can resolve the true state.
31
26
  */
32
27
  import { spawnSync } from "node:child_process";
33
28
  import { accessSync, constants, existsSync, mkdtempSync, readFileSync, realpathSync, rmSync } from "node:fs";
34
29
  import { tmpdir } from "node:os";
35
30
  import { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
36
31
  import { fileURLToPath } from "node:url";
37
- import Database from "better-sqlite3";
38
32
  import { runtimeError } from "../errors/cliError.js";
39
33
  import { isConcreteVersion } from "../domain/validation.js";
40
34
  import { STORAGE_DOCTOR_CHECK_NAMES } from "../doctor/doctor.js";
41
35
  import { acquireHandoverLock } from "../release/runtimeRelease.js";
42
- import { inspectStorageSchema } from "../storage/storageSchema.js";
43
- import { placeUpgradeFence } from "../storage/upgradeFence.js";
44
- import { correlateUpgradeReceipt } from "../storage/upgrade/upgradeOrchestrator.js";
45
- import { readSwitchProgress } from "../storage/upgrade/switchProgress.js";
46
36
  const PACKAGE_NAME = "@zq-silk/yui";
47
37
  const PACKAGE_SPEC = `${PACKAGE_NAME}@latest`;
48
38
  function resolveExecutable(command, environmentPath) {
@@ -94,29 +84,11 @@ export function createUpdatePorts(environment, spawn = spawnSync, stagingRoot =
94
84
  // deliberately not persisted as a retry or recovery protocol.
95
85
  let verifiedActivatedBinary;
96
86
  let verifiedActivatedVersion;
97
- let storageFenceOwnerPid;
98
87
  const stopReplacementController = (home, pid) => (stopReplacementControllerForUpdate(home, pid, environment, run));
99
88
  return {
100
89
  beginControllerHandover(home) {
101
90
  return acquireHandoverLock(home).release;
102
91
  },
103
- beginStorageWriteFence(home) {
104
- const release = placeUpgradeFence(home, {
105
- reason: "update storage activation in progress",
106
- createdAt: new Date().toISOString(),
107
- ownerPid: process.pid
108
- });
109
- storageFenceOwnerPid = process.pid;
110
- let released = false;
111
- return () => {
112
- if (released)
113
- return;
114
- release();
115
- released = true;
116
- if (storageFenceOwnerPid === process.pid)
117
- storageFenceOwnerPid = undefined;
118
- };
119
- },
120
92
  stage(version) {
121
93
  // A caller that names a version (the release workflow, which freezes the
122
94
  // exact version in its plan) installs THAT version — never a moving
@@ -165,25 +137,6 @@ export function createUpdatePorts(environment, spawn = spawnSync, stagingRoot =
165
137
  const result = run(staged.binaryPath, ["--json", "upgrade", "--update-preflight"], { cwd: process.cwd(), env: { ...environment, YUI_HOME: home }, shell: false });
166
138
  return interpretPreflight(result);
167
139
  },
168
- activateStorage(staged, home) {
169
- const result = run(staged.binaryPath, ["--json", "upgrade"], {
170
- cwd: process.cwd(),
171
- // The parent update process captures/stops/drains the old Controller
172
- // before invoking the staged child. Mark this internal call so the
173
- // child performs storage migration only and never starts a Controller
174
- // from the temporary staging installation.
175
- env: {
176
- ...environment,
177
- YUI_HOME: home,
178
- YUI_UPDATE_EXTERNALLY_QUIESCED: "1",
179
- ...(storageFenceOwnerPid === undefined
180
- ? {}
181
- : { YUI_UPDATE_HANDOVER_OWNER_PID: String(storageFenceOwnerPid) })
182
- },
183
- shell: false
184
- });
185
- return interpretActivation(result);
186
- },
187
140
  activateBinary(staged) {
188
141
  verifiedActivatedBinary = undefined;
189
142
  verifiedActivatedVersion = undefined;
@@ -200,7 +153,7 @@ export function createUpdatePorts(environment, spawn = spawnSync, stagingRoot =
200
153
  if (activeBinary === null || !existsSync(activeBinary)) {
201
154
  throw runtimeError("Post-update health check failed: could not locate the activated global `yui` binary.");
202
155
  }
203
- // 1) Health check the migrated Home through the activated binary's loader.
156
+ // 1) Health check the unchanged current Home through the activated binary.
204
157
  // POST-VERIFY PARSES THE MACHINE-READABLE RESULT FIRST, THEN THE EXIT STATUS
205
158
  // (R2-F2). `yui --json doctor` deliberately sets a non-zero exit when storage
206
159
  // is unhealthy, so interpreting the exit status before the envelope would
@@ -237,64 +190,6 @@ export function createUpdatePorts(environment, spawn = spawnSync, stagingRoot =
237
190
  verifiedActivatedBinary = activeBinary;
238
191
  verifiedActivatedVersion = activeVersion;
239
192
  },
240
- probeStorage(home) {
241
- // Resolve an ambiguous activation from durable on-disk evidence (P1-2),
242
- // but only trust a receipt that CORRESPONDS to the current Home/backup
243
- // (P2-6): a leftover receipt from a prior attempt, a different Home, or one
244
- // whose backup was already restored/cleaned is NOT evidence that THIS
245
- // attempt's switch committed. When it does not correspond, fall back to the
246
- // on-disk schema and report `switched: false` so the caller re-probes the
247
- // real state instead of giving a recovery instruction from a stale receipt.
248
- const schema = inspectStorageSchema(home);
249
- const sqliteSchemaHead = inspectSqliteSchemaHead(home);
250
- // A crash mid-switch leaves a durable progress marker. A marker of ANY phase
251
- // — `backing-up`, `promoting`, or `interrupted` — is only actionable as an
252
- // interrupted switch when the FILESYSTEM still corroborates it: the backup
253
- // exists AND the Home is not in place (missing/uninitialized), so the
254
- // authoritative data lives only at the backup and the recovery is a precise
255
- // restore. This gate now applies to `interrupted` too (R2-F3): a leftover
256
- // `interrupted` marker after a manual recovery — Home already restored, or
257
- // the backup already gone — must NOT be trusted to emit a restore path; we
258
- // ignore the stale marker and re-probe the real state below.
259
- const progress = readSwitchProgress(home);
260
- if (progress !== null) {
261
- const backupPresent = progress.backupPath !== undefined
262
- && existsSync(progress.backupPath);
263
- // The Home is "in place" only when storage is actually initialized there;
264
- // a missing/uninitialized Home after a mid-switch crash means the data is
265
- // at the backup.
266
- const homeInitialized = schema.status !== "uninitialized";
267
- if (backupPresent && !homeInitialized) {
268
- // Original at the backup, Home missing/uninitialized: recover by restore.
269
- // The Home is uninitialized here, so it is definitively not current.
270
- return {
271
- switched: false,
272
- interrupted: true,
273
- schemaCurrent: false,
274
- ...(sqliteSchemaHead === undefined ? {} : { sqliteSchemaHead }),
275
- ...(progress.backupPath === undefined ? {} : { backupPath: progress.backupPath })
276
- };
277
- }
278
- // Otherwise the marker is stale relative to the current filesystem (Home
279
- // intact, or no backup to restore). Do not emit a restore path from it —
280
- // fall through and reconcile against the receipt/schema below.
281
- }
282
- const correlation = correlateUpgradeReceipt(home);
283
- if (!correlation.corresponds) {
284
- return {
285
- switched: false,
286
- schemaCurrent: schema.status === "current",
287
- ...(sqliteSchemaHead === undefined ? {} : { sqliteSchemaHead })
288
- };
289
- }
290
- const receipt = correlation.receipt;
291
- return {
292
- switched: true,
293
- schemaCurrent: schema.status === "current",
294
- ...(sqliteSchemaHead === undefined ? {} : { sqliteSchemaHead }),
295
- ...(receipt.backupPath === undefined ? {} : { backupPath: receipt.backupPath })
296
- };
297
- },
298
193
  cleanup(staged) {
299
194
  if (staged.stagingPath !== undefined) {
300
195
  rmSync(staged.stagingPath, { recursive: true, force: true });
@@ -312,7 +207,6 @@ export function createUpdatePorts(environment, spawn = spawnSync, stagingRoot =
312
207
  stopController(home, expectedPid) {
313
208
  return stopControllerForUpdate(home, expectedPid, environment, spawn);
314
209
  },
315
- stopReplacementController,
316
210
  startController(home) {
317
211
  if (verifiedActivatedBinary === undefined || verifiedActivatedVersion === undefined) {
318
212
  throw runtimeError("Replacement Controller cannot start before the activated global binary has "
@@ -325,39 +219,6 @@ export function createUpdatePorts(environment, spawn = spawnSync, stagingRoot =
325
219
  }
326
220
  };
327
221
  }
328
- /**
329
- * Read the generic durable SQLite ledger without asking the old parent binary
330
- * to understand the staged release's migration registry. The staged preflight
331
- * supplies the validated source and target heads used to interpret this value.
332
- */
333
- function inspectSqliteSchemaHead(home) {
334
- const path = join(home, "yui.db");
335
- if (!existsSync(path))
336
- return undefined;
337
- const db = new Database(path, { readonly: true, fileMustExist: true });
338
- try {
339
- db.pragma("query_only = ON");
340
- const rows = db.prepare("SELECT version, checksum FROM schema_migrations ORDER BY version").all();
341
- if (rows.length === 0)
342
- throw new Error("SQLite migration ledger is empty.");
343
- for (let index = 0; index < rows.length; index += 1) {
344
- const row = rows[index];
345
- if (row.version !== index + 1
346
- || typeof row.checksum !== "string"
347
- || row.checksum.length === 0) {
348
- throw new Error("SQLite migration ledger is not a contiguous checksummed prefix.");
349
- }
350
- }
351
- const head = rows.at(-1);
352
- return {
353
- version: head.version,
354
- checksum: head.checksum
355
- };
356
- }
357
- finally {
358
- db.close();
359
- }
360
- }
361
222
  const UPDATE_CLI_PATH = fileURLToPath(new URL("../cli.js", import.meta.url));
362
223
  const UPDATE_CLIENT_RUNTIME_PATH = fileURLToPath(new URL("../controller/clientRuntime.js", import.meta.url));
363
224
  const UPDATE_CONTROLLER_RECONCILIATION_PATH = fileURLToPath(new URL("../controller/updateReconciliation.js", import.meta.url));
@@ -921,90 +782,19 @@ function interpretPreflight(result) {
921
782
  ...(data.sceneUnchanged === true ? { sceneUnchanged: true } : {})
922
783
  };
923
784
  }
924
- /** Strictly parse the green states of the internal update preflight. */
785
+ /** Only the exact current storage contract is a green update preflight. */
925
786
  function parseUpdatePreflightResult(data) {
926
- const status = data.status;
927
- const stepCount = data.stepCount;
928
- if ((status !== "already-current"
929
- && status !== "compatible"
930
- && status !== "in-place-migration"
931
- && status !== "migration-required")
932
- || !Number.isSafeInteger(stepCount)
933
- || stepCount < 0) {
787
+ if (data.status !== "already-current" || data.stepCount !== 0)
934
788
  return null;
935
- }
936
789
  const homeClassification = data.classification;
937
790
  if (!isRecord(homeClassification) || !isRecord(homeClassification.classification))
938
791
  return null;
939
792
  const classification = homeClassification.classification;
940
- const expected = status === "already-current"
941
- ? { verdict: "USABLE", classificationStatus: "current" }
942
- : status === "compatible"
943
- ? { verdict: "COMPATIBLE", classificationStatus: "compatible-old" }
944
- : { verdict: "MIGRATABLE", classificationStatus: "migration-required" };
945
- if (classification.verdict !== expected.verdict
946
- || classification.status !== expected.classificationStatus
947
- || (status === "already-current"
948
- ? stepCount !== 0
949
- : classification.stepCount !== stepCount || stepCount < 1)) {
793
+ if (classification.verdict !== "USABLE"
794
+ || classification.status !== "current") {
950
795
  return null;
951
796
  }
952
- if (status === "already-current")
953
- return { status };
954
- const evidence = status === "compatible"
955
- ? `${stepCount} compatible step(s) classified and the compatible source validated in memory`
956
- : status === "in-place-migration"
957
- ? `${stepCount} SQLite migration step(s) classified for one in-place transaction and the offline runtime inventory confirmed clear`
958
- : `${stepCount} offline migration step(s) classified and the offline runtime inventory confirmed clear`;
959
- const summary = `${evidence}. `
960
- + "No staged Home or staged-output loader validation was performed during update preflight.";
961
- if (status === "in-place-migration") {
962
- const sqliteMigration = parseSqliteMigrationBoundary(data.sqliteMigration, stepCount);
963
- if (sqliteMigration === null)
964
- return null;
965
- return {
966
- status,
967
- summary,
968
- sqliteMigration
969
- };
970
- }
971
- return {
972
- status,
973
- summary
974
- };
975
- }
976
- function parseSqliteMigrationBoundary(value, stepCount) {
977
- if (!isRecord(value))
978
- return null;
979
- const currentVersion = value.currentVersion;
980
- const currentChecksum = value.currentChecksum;
981
- const targetVersion = value.targetVersion;
982
- const targetChecksum = value.targetChecksum;
983
- const pendingVersions = value.pendingVersions;
984
- if (!Number.isSafeInteger(currentVersion)
985
- || currentVersion < 1
986
- || typeof currentChecksum !== "string"
987
- || currentChecksum.length === 0
988
- || !Number.isSafeInteger(targetVersion)
989
- || targetVersion <= currentVersion
990
- || typeof targetChecksum !== "string"
991
- || targetChecksum.length === 0
992
- || !Array.isArray(pendingVersions)
993
- || pendingVersions.length !== stepCount
994
- || targetVersion !== currentVersion + stepCount
995
- || pendingVersions.some((version, index) => !Number.isSafeInteger(version) || version !== currentVersion + index + 1)) {
996
- return null;
997
- }
998
- return {
999
- current: {
1000
- version: currentVersion,
1001
- checksum: currentChecksum
1002
- },
1003
- target: {
1004
- version: targetVersion,
1005
- checksum: targetChecksum
1006
- }
1007
- };
797
+ return { status: "already-current" };
1008
798
  }
1009
799
  function parseUpdateBlockers(value) {
1010
800
  if (value === undefined)
@@ -1016,14 +806,14 @@ function parseUpdateBlockers(value) {
1016
806
  if (!isRecord(item) || typeof item.reason !== "string" || item.reason.length === 0) {
1017
807
  return undefined;
1018
808
  }
1019
- const optional = ["taskId", "roleName", "runId", "nativeSessionId", "launchId"];
809
+ const optional = ["taskId", "roleName", "turnId", "nativeSessionId", "launchId"];
1020
810
  if (optional.some((key) => item[key] !== undefined && typeof item[key] !== "string")) {
1021
811
  return undefined;
1022
812
  }
1023
813
  parsed.push({
1024
814
  ...(typeof item.taskId === "string" ? { taskId: item.taskId } : {}),
1025
815
  ...(typeof item.roleName === "string" ? { roleName: item.roleName } : {}),
1026
- ...(typeof item.runId === "string" ? { runId: item.runId } : {}),
816
+ ...(typeof item.turnId === "string" ? { turnId: item.turnId } : {}),
1027
817
  ...(typeof item.nativeSessionId === "string"
1028
818
  ? { nativeSessionId: item.nativeSessionId }
1029
819
  : {}),
@@ -1033,114 +823,6 @@ function parseUpdateBlockers(value) {
1033
823
  }
1034
824
  return parsed;
1035
825
  }
1036
- function interpretActivation(result) {
1037
- // A spawn transport error (could not even run) is a clean pre-switch failure.
1038
- if (result.error !== undefined) {
1039
- return {
1040
- status: "ambiguous",
1041
- detail: `the activation process could not be run: ${result.error.message}`
1042
- };
1043
- }
1044
- // Require a valid `{ ok:true, data }` success envelope (R3-F3). Killed by a
1045
- // signal, no parseable JSON, or an `ok:false`/malformed envelope: the child may
1046
- // have died after the atomic switch but before printing a valid result. This is
1047
- // AMBIGUOUS, never a false "recoverable/unchanged" (P1-2).
1048
- const data = parseSuccessEnvelopeData(result);
1049
- if (data === null) {
1050
- const how = result.signal !== null
1051
- ? `terminated by ${result.signal}`
1052
- : result.status === null
1053
- ? "terminated without an exit code"
1054
- : `exited with status ${result.status} and no valid success envelope`;
1055
- return {
1056
- status: "ambiguous",
1057
- detail: `the activation process ${how}`
1058
- };
1059
- }
1060
- const outcome = typeof data.outcome === "string" ? data.outcome : undefined;
1061
- // EXIT STATUS / OUTCOME CONSISTENCY (P1-2)
1062
- // A success-class outcome is only trustworthy when the child ALSO exited 0. A
1063
- // contradiction (e.g. stdout says `upgraded` but the process exited non-zero)
1064
- // means the child's own contract was violated mid-flight — the switch may or
1065
- // may not have committed — so it is AMBIGUOUS, never a false success. Blocker-
1066
- // class outcomes are exempt: `yui upgrade` deliberately exits non-zero (5) for
1067
- // a clean `blocked`, so a non-zero exit there is expected and consistent.
1068
- if ((outcome === "already-current" || outcome === "upgraded") && result.status !== 0) {
1069
- return {
1070
- status: "ambiguous",
1071
- detail: `the activation process reported outcome=${outcome} but exited with status `
1072
- + `${result.status ?? "null"} (a success outcome must exit 0); the switch state is unknown`
1073
- };
1074
- }
1075
- if (outcome === "already-current")
1076
- return { status: "already-current" };
1077
- if (outcome === "upgraded") {
1078
- if (data.migrationMode === "in-place" && data.backupPath === undefined) {
1079
- return { status: "migrated-in-place" };
1080
- }
1081
- const backupPath = data.backupPath;
1082
- if (typeof backupPath !== "string"
1083
- || backupPath.length === 0
1084
- || backupPath.trim() !== backupPath
1085
- || backupPath.includes("\0")
1086
- || !isAbsolute(backupPath)) {
1087
- return {
1088
- status: "ambiguous",
1089
- detail: "the activation process reported outcome=upgraded without a non-empty absolute backupPath"
1090
- };
1091
- }
1092
- return {
1093
- status: "migrated",
1094
- backupPath
1095
- };
1096
- }
1097
- if (outcome === "blocked") {
1098
- const stage = typeof data?.stage === "string" ? data.stage : "activate-storage";
1099
- // A `switch-ambiguous` blocker is NOT a clean, recoverable refusal (P1-4):
1100
- // the upgrade's atomic switch was left partially applied (original moved to
1101
- // backup, promotion + rollback both failed). Route it to AMBIGUOUS so the
1102
- // orchestrator probes the interrupted marker and reports a restore, never a
1103
- // false "the current install and Home remain usable".
1104
- if (stage === "switch-ambiguous") {
1105
- return {
1106
- status: "ambiguous",
1107
- detail: typeof data?.message === "string"
1108
- ? data.message
1109
- : "the storage switch was left partially applied"
1110
- };
1111
- }
1112
- // A post-verify blocker is emitted only after the atomic Home switch has
1113
- // committed. Treat it as AMBIGUOUS in the parent update flow so a stopped
1114
- // old Controller is never restored against the migrated Home; the receipt,
1115
- // backup, and switch-progress marker are the recovery evidence.
1116
- if (stage === "post-verify") {
1117
- return {
1118
- status: "ambiguous",
1119
- detail: typeof data?.message === "string"
1120
- ? data.message
1121
- : "storage switched but post-switch verification did not complete"
1122
- };
1123
- }
1124
- const blockers = parseUpdateBlockers(data.blockers);
1125
- return {
1126
- status: "blocked",
1127
- stage,
1128
- message: typeof data?.message === "string" ? data.message : "Storage activation was refused.",
1129
- action: typeof data?.action === "string"
1130
- ? data.action
1131
- : "Resolve the reported condition and retry.",
1132
- ...(blockers === undefined ? {} : { blockers }),
1133
- ...(typeof data.retryCommand === "string" ? { retryCommand: data.retryCommand } : {}),
1134
- ...(data.sceneUnchanged === true ? { sceneUnchanged: true } : {})
1135
- };
1136
- }
1137
- // Parseable JSON but an unrecognized outcome: we cannot classify it, so it is
1138
- // ambiguous rather than silently treated as a clean refusal.
1139
- return {
1140
- status: "ambiguous",
1141
- detail: `the activation process returned an unrecognized outcome (${String(outcome)})`
1142
- };
1143
- }
1144
826
  /**
1145
827
  * Extract the validated `data` object from a spawn result's `{ ok: true, data }`
1146
828
  * JSON envelope, or `null` when the result is not a trustworthy success envelope.
@@ -1243,7 +925,7 @@ function resolveGlobalBinary(environment, spawn) {
1243
925
  return join(prefix, "bin", "yui");
1244
926
  }
1245
927
  /**
1246
- * Fail closed unless the doctor result PROVES the migrated Home's storage is
928
+ * Fail closed unless the doctor result proves the current Home's storage is
1247
929
  * healthy (P1-3 / R2-F2). The envelope and storage checks are validated BEFORE
1248
930
  * the exit status is interpreted, because `yui --json doctor` deliberately exits
1249
931
  * non-zero on unhealthy storage — so keying off the exit first would reduce a
@@ -1344,7 +1026,7 @@ function assertDoctorStorageHealthy(result) {
1344
1026
  const detail = [...blockingChecks, ...declaredBlockingChecks]
1345
1027
  .map((c) => `${String(c.name)}=${String(c.status)} (${String(c.detail)})`)
1346
1028
  .join("; ");
1347
- throw runtimeError("Post-update health check failed: the migrated Home is not healthy per the activated "
1029
+ throw runtimeError("Post-update health check failed: the current Home is not healthy per the activated "
1348
1030
  + `binary's doctor: ${detail || "(no detail)"}. The storage did not come up cleanly on `
1349
1031
  + "the new version. Restore the timestamped backup to recover the original Home before "
1350
1032
  + "resuming writes.");
@@ -1358,7 +1040,7 @@ function assertDoctorStorageHealthy(result) {
1358
1040
  }
1359
1041
  /** A fail-closed post-update health-check error for an unverifiable doctor result. */
1360
1042
  function doctorUnverifiable(reason) {
1361
- return runtimeError(`Post-update health check failed: ${reason}, so the migrated Home cannot be confirmed `
1043
+ return runtimeError(`Post-update health check failed: ${reason}, so the current Home cannot be confirmed `
1362
1044
  + "healthy. Investigate with `yui doctor` before resuming; if storage was migrated, restore "
1363
1045
  + "the timestamped backup to recover.");
1364
1046
  }
@@ -1,22 +1,7 @@
1
1
  /**
2
- * `yui upgrade` the diagnostic/manual storage upgrade entry point.
3
- *
4
- * `yui upgrade --dry-run` first classifies read-only. Compatible-old validates
5
- * through the in-memory current-model gate and returns without staging a Home;
6
- * migration-required also proves the offline runtime inventory before it stages
7
- * and validates a fresh copy. The hidden `--update-preflight` contract stops at
8
- * classification, compatible-source validation, or offline inventory as required
9
- * by the classified path, so a staged updater can run it while the old Controller
10
- * remains live without a false staged-output validation claim. Execute enters
11
- * fence/quiesce/switch only for the offline path. Only explicitly declared
12
- * production paths can run; the aggregate `16→17` and execution record-family
13
- * transitions remain offline migrations.
14
- *
15
- * Like `doctor`/`controller`, this command needs a Home but manages its own
16
- * schema check (it must run against a non-current Home), so it is dispatched
17
- * before the unconditional `requireStorageSchema` gate in `cli.ts`.
2
+ * `yui upgrade` is now a read-only storage admission diagnostic. This release
3
+ * opens only its exact current contract and never rewrites an historical Home.
18
4
  */
19
- import { createProductionStorageRegistry } from "../storage/compatibleTaskStore.js";
20
5
  import { latestStorageVersionState } from "../storage/upgrade/recordVersions.js";
21
6
  import { runStorageUpgrade } from "../storage/upgrade/upgradeOrchestrator.js";
22
7
  import { usageError } from "../errors/cliError.js";
@@ -25,20 +10,12 @@ import { usageError } from "../errors/cliError.js";
25
10
  * updater's internal `--update-preflight` form, then returns rendered text,
26
11
  * structured data, and an exit code (0 for a safe result, 5 for a blocker).
27
12
  */
28
- export async function runUpgradeCommand(args, home, options = {}) {
13
+ export async function runUpgradeCommand(args, home) {
29
14
  const mode = parseUpgradeArgs(args);
30
15
  const result = await runStorageUpgrade({
31
16
  home,
32
- registry: createProductionStorageRegistry(),
33
17
  latest: latestStorageVersionState(),
34
- mode,
35
- ...(options.controllerLifecycle === undefined
36
- ? {}
37
- : { controllerLifecycle: options.controllerLifecycle }),
38
- ...(options.externalUpgradeFenceOwnerPid === undefined
39
- ? {}
40
- : { externalUpgradeFenceOwnerPid: options.externalUpgradeFenceOwnerPid }),
41
- ...(options.now === undefined ? {} : { now: options.now })
18
+ mode
42
19
  });
43
20
  return {
44
21
  output: renderUpgradeResult(result, mode),
@@ -52,7 +29,7 @@ function parseUpgradeArgs(args) {
52
29
  if (args.length === 1 && args[0] === "--dry-run")
53
30
  return "dry-run";
54
31
  // Intentionally omitted from public command help: this is the machine contract
55
- // used by a staged `yui update`, not a replacement for user-facing dry-run.
32
+ // used by a staged `yui update`, not a replacement for user-facing dry-turn.
56
33
  if (args.length === 1 && args[0] === "--update-preflight")
57
34
  return "update-preflight";
58
35
  throw usageError("Upgrade usage: yui upgrade [--dry-run]");
@@ -63,48 +40,14 @@ export function renderUpgradeResult(result, mode) {
63
40
  switch (result.outcome) {
64
41
  case "already-current":
65
42
  return `${header}\nStorage is already at the current version; nothing to upgrade.`;
66
- case "compatible":
67
- return `${header}\nStorage is compatible-old and can be opened by the current loader. `
68
- + "No Home migration, backup, or switch is required.";
69
- case "update-preflight": {
70
- const evidence = result.status === "already-current"
71
- ? "four-state classification"
72
- : result.status === "compatible"
73
- ? "four-state classification plus compatible-source validation"
74
- : result.status === "in-place-migration"
75
- ? "SQLite ledger classification plus a clear offline runtime inventory"
76
- : "four-state classification plus a clear offline runtime inventory";
77
- return `${header}\nUpdate preflight: ${result.status} (${result.stepCount} step(s)); `
78
- + `${evidence}. No staged Home was created, `
79
- + "no staged-output loader validation was performed, and storage was not switched.";
80
- }
81
- case "dry-run": {
82
- const steps = result.classification.sqliteMigration?.pendingVersions.length
83
- ?? (result.report.outcome === "dry-run" ? result.report.steps.length : 0);
84
- return `${header}\nDry run: validated ${steps} migration step(s) through the loader gate. `
85
- + "Staged output discarded; storage was not switched.";
86
- }
87
- case "upgraded":
88
- return result.migrationMode === "in-place"
89
- ? `${header}\nUpgraded SQLite in place in one transaction; no database rebuild or backup copy was created.`
90
- : `${header}\nUpgraded storage. Original Home backed up at `
91
- + `${result.backupPath ?? "(unspecified)"}.`;
43
+ case "update-preflight":
44
+ return `${header}\nUpdate preflight: already-current (0 steps). Storage was not modified.`;
92
45
  case "blocked": {
93
- // Most blockers guarantee the source is untouched. A partial switch or a
94
- // post-switch ambiguity explicitly carries the committed boundary and
95
- // named recovery evidence; never print a false unchanged claim there.
96
- const unchangedNote = result.storageCommitted === true
97
- ? "The SQLite transaction committed in place; the old Controller was not restored (see Action)."
98
- : result.switchCommitted === true
99
- ? "The storage switch committed, but post-switch completion is ambiguous; the old Controller was not restored (see Action and recovery evidence)."
100
- : result.stage === "switch-ambiguous"
101
- ? "The switch did not complete and could not be rolled back; the authoritative Home is NOT intact (see Action)."
102
- : "Storage was not switched; the authoritative Home is unchanged.";
103
46
  return [
104
47
  header,
105
48
  `${mode === "dry-run" ? "Dry run" : mode === "update-preflight" ? "Update preflight" : "Upgrade"} blocked at ${result.stage}: ${result.message}`,
106
49
  `Action: ${result.action}`,
107
- unchangedNote
50
+ "The authoritative Home is unchanged."
108
51
  ].join("\n");
109
52
  }
110
53
  }