@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
@@ -22,15 +22,15 @@
22
22
  * by SQLite (DDL is transactional) and re-applied on the next open.
23
23
  */
24
24
  import { createHash } from "node:crypto";
25
- /** The SQLite layout version (state.json layout 6 -> SQLite layout 7, per §8.1). */
26
- export const SQLITE_LAYOUT_VERSION = 7;
25
+ /** The current SQLite layout version. */
26
+ export const SQLITE_LAYOUT_VERSION = 8;
27
27
  /** The aggregate version of the normalized SQLite schema. */
28
- export const SQLITE_AGGREGATE_VERSION = 1;
28
+ export const SQLITE_AGGREGATE_VERSION = 2;
29
29
  /** The current schema migration version. */
30
- export const SQLITE_SCHEMA_VERSION = 18;
30
+ export const SQLITE_SCHEMA_VERSION = 1;
31
31
  /** Telemetry retention bounds (§4.4). Open question 3 in §11; defaults from the design. */
32
32
  export const TELEMETRY_KEEP_PER_GENERATION = 200;
33
- export const TELEMETRY_RUN_CAP = 50_000;
33
+ export const TELEMETRY_TURN_CAP = 50_000;
34
34
  /**
35
35
  * Version 1 migration: creates every table and index.
36
36
  *
@@ -38,11 +38,10 @@ export const TELEMETRY_RUN_CAP = 50_000;
38
38
  * the store; `journal_mode=WAL` is a persistent database property set on open.
39
39
  * The migration itself only contains schema objects.
40
40
  *
41
- * This released SQL string, including its comments, is checksummed persistent
42
- * history and must remain byte-for-byte unchanged. The `operator-notification`
43
- * projection kind it creates is retained only for old layout-7 Homes.
41
+ * This is the direct current baseline for a new Home. Its checksum rejects any
42
+ * database initialized from a different physical contract.
44
43
  */
45
- const MIGRATION_1_SQL = `
44
+ const BASELINE_CORE_SQL = `
46
45
  -- Global catalog and coordination (§4.1) -------------------------------------
47
46
 
48
47
  CREATE TABLE IF NOT EXISTS home_meta (
@@ -182,10 +181,14 @@ CREATE TABLE IF NOT EXISTS mailboxes (
182
181
  next_sequence INTEGER NOT NULL,
183
182
  processing TEXT,
184
183
  pending TEXT,
184
+ recent_dedupe_keys TEXT NOT NULL DEFAULT '[]',
185
185
  UNIQUE (target_kind, task_id, role_name)
186
186
  );
187
187
  CREATE UNIQUE INDEX IF NOT EXISTS idx_mailboxes_target_key
188
188
  ON mailboxes(target_key);
189
+ CREATE INDEX IF NOT EXISTS idx_mailboxes_ready
190
+ ON mailboxes(target_key)
191
+ WHERE processing IS NOT NULL OR json_type(pending) <> 'null';
189
192
 
190
193
  -- Task-partitioned tables (§4.3). Every task-local read constrains task_id. ----
191
194
 
@@ -232,22 +235,22 @@ CREATE TABLE IF NOT EXISTS work_item_candidates (
232
235
  PRIMARY KEY (task_id, candidate_id)
233
236
  );
234
237
 
235
- CREATE TABLE IF NOT EXISTS agent_runs (
238
+ CREATE TABLE IF NOT EXISTS turns (
236
239
  task_id TEXT NOT NULL,
237
- run_id TEXT NOT NULL,
240
+ turn_id TEXT NOT NULL,
238
241
  role_name TEXT NOT NULL,
239
242
  status TEXT NOT NULL,
240
243
  payload TEXT NOT NULL,
241
244
  updated_at TEXT NOT NULL,
242
- PRIMARY KEY (task_id, run_id)
245
+ PRIMARY KEY (task_id, turn_id)
243
246
  );
244
- CREATE INDEX IF NOT EXISTS idx_agent_runs_role_status ON agent_runs(task_id, role_name, status);
247
+ CREATE INDEX IF NOT EXISTS idx_turns_role_status ON turns(task_id, role_name, status);
245
248
 
246
- -- Active-run pointers (getActiveAgentRun / execution-lane runs).
247
- CREATE TABLE IF NOT EXISTS active_runs (
249
+ -- Active-Turn pointers (getActiveTurn / execution-lane Turns).
250
+ CREATE TABLE IF NOT EXISTS active_turns (
248
251
  task_id TEXT NOT NULL,
249
252
  pointer TEXT NOT NULL,
250
- run_id TEXT NOT NULL,
253
+ turn_id TEXT NOT NULL,
251
254
  payload TEXT NOT NULL,
252
255
  updated_at TEXT NOT NULL,
253
256
  PRIMARY KEY (task_id, pointer)
@@ -303,6 +306,9 @@ CREATE TABLE IF NOT EXISTS input_requests (
303
306
  PRIMARY KEY (task_id, input_id)
304
307
  );
305
308
  CREATE INDEX IF NOT EXISTS idx_input_open ON input_requests(task_id, status) WHERE status <> 'resolved';
309
+ CREATE INDEX IF NOT EXISTS idx_input_requests_open_hot
310
+ ON input_requests(task_id, input_id)
311
+ WHERE status = 'open';
306
312
 
307
313
  CREATE TABLE IF NOT EXISTS decisions (
308
314
  task_id TEXT NOT NULL,
@@ -344,30 +350,27 @@ CREATE TABLE IF NOT EXISTS task_projections (
344
350
  CREATE TABLE IF NOT EXISTS telemetry (
345
351
  task_id TEXT NOT NULL,
346
352
  role_name TEXT NOT NULL,
347
- run_id TEXT NOT NULL,
353
+ turn_id TEXT NOT NULL,
348
354
  generation TEXT NOT NULL,
349
355
  progress_id TEXT NOT NULL,
350
356
  sequence INTEGER,
351
357
  payload TEXT NOT NULL,
352
358
  received_at TEXT NOT NULL,
353
- PRIMARY KEY (task_id, role_name, run_id, generation, progress_id)
359
+ PRIMARY KEY (task_id, role_name, turn_id, generation, progress_id)
354
360
  ) WITHOUT ROWID;
355
- CREATE INDEX IF NOT EXISTS idx_telemetry_run ON telemetry(task_id, run_id);
361
+ CREATE INDEX IF NOT EXISTS idx_telemetry_turn ON telemetry(task_id, turn_id);
356
362
  `;
357
- /**
358
- * Migration 2: post-baseline task-scoped record families. This single
359
- * migration creates the DurableJob, CapabilityGrant, and ReleaseWorkflow
360
- * tables so a fresh database receives the complete merged schema atomically.
361
- */
362
- const MIGRATION_2_SQL = `
363
+ /** Task-scoped execution and release record families. */
364
+ const BASELINE_JOB_AND_RELEASE_SQL = `
363
365
  CREATE TABLE IF NOT EXISTS durable_jobs (
364
- job_id TEXT PRIMARY KEY,
366
+ job_id TEXT NOT NULL,
365
367
  task_id TEXT NOT NULL,
366
368
  idempotency_key TEXT,
367
369
  status TEXT NOT NULL,
368
370
  payload TEXT NOT NULL,
369
371
  created_at TEXT NOT NULL,
370
- updated_at TEXT NOT NULL
372
+ updated_at TEXT NOT NULL,
373
+ PRIMARY KEY (task_id, job_id)
371
374
  );
372
375
  CREATE INDEX IF NOT EXISTS idx_durable_jobs_task ON durable_jobs(task_id);
373
376
  CREATE INDEX IF NOT EXISTS idx_durable_jobs_status ON durable_jobs(status);
@@ -389,12 +392,8 @@ CREATE TABLE IF NOT EXISTS release_workflows (
389
392
  PRIMARY KEY (task_id, workflow_id)
390
393
  );
391
394
  `;
392
- /**
393
- * Migration 3: Job caller key hashes (task-14, rr13). Durable SHA-256 hashes of
394
- * the caller key bound to each launched Session, used to fail-closed verify a
395
- * DurableJob's caller against durable Run state.
396
- */
397
- const MIGRATION_3_SQL = `
395
+ /** Durable caller-key hashes used to verify DurableJob ownership. */
396
+ const BASELINE_JOB_CALLER_SQL = `
398
397
  CREATE TABLE IF NOT EXISTS job_caller_key_hashes (
399
398
  task_id TEXT NOT NULL,
400
399
  role_name TEXT NOT NULL,
@@ -405,48 +404,17 @@ CREATE TABLE IF NOT EXISTS job_caller_key_hashes (
405
404
  );
406
405
  `;
407
406
  /**
408
- * Migration 4: DurableJob IDs are Task-local. Migration 2 accidentally made
409
- * job_id the global primary key, so two Tasks allocating their first `job-1`
410
- * could overwrite one another through the upsert path. Rebuild the table with
411
- * the actual record identity `(task_id, job_id)` and restore its indexes.
412
- * Never edit migration 2's checksum: existing Homes reach this repair through
413
- * this adjacent schema migration.
414
- */
415
- const MIGRATION_4_SQL = `
416
- CREATE TABLE durable_jobs_v4 (
417
- job_id TEXT NOT NULL,
418
- task_id TEXT NOT NULL,
419
- idempotency_key TEXT,
420
- status TEXT NOT NULL,
421
- payload TEXT NOT NULL,
422
- created_at TEXT NOT NULL,
423
- updated_at TEXT NOT NULL,
424
- PRIMARY KEY (task_id, job_id)
425
- );
426
- INSERT INTO durable_jobs_v4
427
- (job_id, task_id, idempotency_key, status, payload, created_at, updated_at)
428
- SELECT job_id, task_id, idempotency_key, status, payload, created_at, updated_at
429
- FROM durable_jobs;
430
- DROP TABLE durable_jobs;
431
- ALTER TABLE durable_jobs_v4 RENAME TO durable_jobs;
432
- CREATE INDEX idx_durable_jobs_task ON durable_jobs(task_id);
433
- CREATE INDEX idx_durable_jobs_status ON durable_jobs(status);
434
- CREATE UNIQUE INDEX idx_durable_jobs_idempotency
435
- ON durable_jobs(task_id, idempotency_key) WHERE idempotency_key IS NOT NULL;
436
- `;
437
- /**
438
- * Migration 5: telemetry aggregate (Issue 09). The `telemetry` table (migration
439
- * 1, §4.4) stores the bounded latest-per-key progress window; this companion
440
- * table holds the authoritative per-Run/generation summary (count, first/last,
407
+ * The `telemetry` table stores the bounded latest-per-key progress window; this companion
408
+ * table holds the authoritative per-Turn/generation summary (count, first/last,
441
409
  * max sequence, error count) so aggregates stay accurate after the window is
442
410
  * pruned. Triggers maintain it on telemetry INSERT/UPDATE; DELETE intentionally
443
411
  * leaves it untouched because pruned rows were still observed.
444
412
  */
445
- const MIGRATION_5_SQL = `
413
+ const BASELINE_TELEMETRY_AGGREGATE_SQL = `
446
414
  CREATE TABLE IF NOT EXISTS telemetry_aggregate (
447
415
  task_id TEXT NOT NULL,
448
416
  role_name TEXT NOT NULL,
449
- run_id TEXT NOT NULL,
417
+ turn_id TEXT NOT NULL,
450
418
  generation TEXT NOT NULL,
451
419
  first_at TEXT NOT NULL,
452
420
  last_at TEXT NOT NULL,
@@ -454,21 +422,21 @@ CREATE TABLE IF NOT EXISTS telemetry_aggregate (
454
422
  max_sequence INTEGER,
455
423
  error_count INTEGER NOT NULL DEFAULT 0,
456
424
  updated_at TEXT NOT NULL,
457
- PRIMARY KEY (task_id, role_name, run_id, generation)
425
+ PRIMARY KEY (task_id, role_name, turn_id, generation)
458
426
  ) WITHOUT ROWID;
459
427
 
460
428
  CREATE TRIGGER IF NOT EXISTS telemetry_ai AFTER INSERT ON telemetry
461
429
  BEGIN
462
430
  INSERT INTO telemetry_aggregate
463
- (task_id, role_name, run_id, generation, first_at, last_at, count, max_sequence, error_count, updated_at)
431
+ (task_id, role_name, turn_id, generation, first_at, last_at, count, max_sequence, error_count, updated_at)
464
432
  VALUES
465
- (NEW.task_id, NEW.role_name, NEW.run_id, NEW.generation, NEW.received_at, NEW.received_at, 1, NEW.sequence,
433
+ (NEW.task_id, NEW.role_name, NEW.turn_id, NEW.generation, NEW.received_at, NEW.received_at, 1, NEW.sequence,
466
434
  CASE WHEN json_valid(NEW.payload)
467
435
  AND (COALESCE(json_extract(NEW.payload, '$.error'), '') <> ''
468
436
  OR COALESCE(json_extract(NEW.payload, '$.errorKind'), '') <> '')
469
437
  THEN 1 ELSE 0 END,
470
438
  NEW.received_at)
471
- ON CONFLICT(task_id, role_name, run_id, generation) DO UPDATE SET
439
+ ON CONFLICT(task_id, role_name, turn_id, generation) DO UPDATE SET
472
440
  first_at = MIN(telemetry_aggregate.first_at, excluded.first_at),
473
441
  last_at = MAX(telemetry_aggregate.last_at, excluded.last_at),
474
442
  count = telemetry_aggregate.count + 1,
@@ -489,15 +457,15 @@ BEGIN
489
457
  THEN NEW.sequence ELSE max_sequence END,
490
458
  updated_at = NEW.received_at
491
459
  WHERE task_id = NEW.task_id AND role_name = NEW.role_name
492
- AND run_id = NEW.run_id AND generation = NEW.generation;
460
+ AND turn_id = NEW.turn_id AND generation = NEW.generation;
493
461
  END;
494
462
  `;
495
463
  /**
496
- * Migration 6: Issue 06 ReviewFinding ledger. Cross-Round semantic review
464
+ * Cross-Round semantic review
497
465
  * findings with stable keys and Leader dispositions. Failed execution attempts
498
466
  * never create rows; only completed Rounds feed the ledger.
499
467
  */
500
- const MIGRATION_6_SQL = `
468
+ const BASELINE_REVIEW_FINDING_SQL = `
501
469
  CREATE TABLE IF NOT EXISTS review_findings (
502
470
  task_id TEXT NOT NULL,
503
471
  finding_id TEXT NOT NULL,
@@ -511,13 +479,13 @@ CREATE INDEX IF NOT EXISTS idx_review_findings_task ON review_findings(task_id);
511
479
  CREATE INDEX IF NOT EXISTS idx_review_findings_stable_key ON review_findings(task_id, stable_key);
512
480
  `;
513
481
  /**
514
- * Version 5 migration: session owner physical identity records (Issue 03).
482
+ * Session owner physical identity records.
515
483
  *
516
484
  * One row per runtime generation, keyed by launch id. The payload column
517
485
  * stores the full versioned JSON record; typed columns support the
518
486
  * reconciliation queries (task/role lookup, PID liveness).
519
487
  */
520
- const MIGRATION_7_SQL = `
488
+ const BASELINE_SESSION_OWNER_SQL = `
521
489
  CREATE TABLE IF NOT EXISTS session_owners (
522
490
  launch_id TEXT PRIMARY KEY,
523
491
  scope TEXT NOT NULL CHECK (scope IN ('task','global')),
@@ -533,14 +501,14 @@ CREATE INDEX IF NOT EXISTS idx_session_owners_task
533
501
  ON session_owners(task_id, role_name);
534
502
  `;
535
503
  /**
536
- * Migration 8: Resource GC registry (Issue 10).
504
+ * Resource GC registry.
537
505
  *
538
506
  * GC-owned table for resource lifecycle records. The registry is GC's own
539
507
  * state — it is not part of the aggregate and never participates in aggregate
540
508
  * versioning. Records are stored as full versioned JSON in `payload`, with
541
509
  * typed columns for the fields GC queries (disposition, kind, task_id).
542
510
  */
543
- const MIGRATION_8_SQL = `
511
+ const BASELINE_RESOURCE_REGISTRY_SQL = `
544
512
  CREATE TABLE IF NOT EXISTS resource_registry (
545
513
  id TEXT PRIMARY KEY,
546
514
  kind TEXT NOT NULL,
@@ -557,14 +525,14 @@ CREATE INDEX IF NOT EXISTS idx_resource_registry_task
557
525
  ON resource_registry(task_id);
558
526
  `;
559
527
  /**
560
- * Migration 9: GateArtifact storage (Issue 08). Content-addressed gate
528
+ * Content-addressed GateArtifact storage. Content-addressed gate
561
529
  * evidence records with per-step logs stored as BLOBs. The artifact key is
562
530
  * the SHA-256 of the identity tuple (Project + commit + plan digest +
563
531
  * toolchain digest + L2 boundary), so the same tuple always maps to one row.
564
532
  * Typed columns support the reuse lookup paths (exact-commit L2 search,
565
533
  * Project-level prune) without scanning payloads.
566
534
  */
567
- const MIGRATION_9_SQL = `
535
+ const BASELINE_GATE_ARTIFACT_SQL = `
568
536
  CREATE TABLE IF NOT EXISTS gate_artifacts (
569
537
  key TEXT PRIMARY KEY,
570
538
  project_id TEXT NOT NULL,
@@ -597,29 +565,8 @@ CREATE TABLE IF NOT EXISTS gate_artifact_logs (
597
565
  FOREIGN KEY (artifact_key) REFERENCES gate_artifacts(key) ON DELETE CASCADE
598
566
  );
599
567
  `;
600
- /**
601
- * Migration 10: bounded ready-mailbox lookup for the Controller hot path.
602
- *
603
- * Historical empty mailboxes remain durable, but only mailboxes with a
604
- * processing or pending batch require scheduling. The partial index contains
605
- * exactly that unsettled set in target-key order, so the Controller's recovery
606
- * query is O(log H + ready) instead of scanning every historical mailbox.
607
- */
608
- const MIGRATION_10_SQL = `
609
- CREATE INDEX IF NOT EXISTS idx_mailboxes_ready
610
- ON mailboxes(target_key)
611
- WHERE processing IS NOT NULL OR pending IS NOT NULL;
612
- `;
613
- /**
614
- * Migration 11: bounded current-Session projection for runtime cleanup.
615
- *
616
- * RoleSessionSet payloads remain authoritative. This table contains only the
617
- * current active Agent Session while it is non-stopped, so terminal Session
618
- * history cannot enlarge Controller cleanup discovery. Runtime writes maintain
619
- * it in the same transaction as the source payload; this migration performs
620
- * the one-time deterministic backfill for existing layout-7 Homes.
621
- */
622
- const MIGRATION_11_SQL = `
568
+ /** Bounded current-Session projection for runtime cleanup. */
569
+ const BASELINE_RUNTIME_SESSION_SQL = `
623
570
  CREATE TABLE IF NOT EXISTS runtime_session_candidates (
624
571
  scope TEXT NOT NULL CHECK (scope IN ('task','global')),
625
572
  task_id TEXT NOT NULL,
@@ -628,7 +575,6 @@ CREATE TABLE IF NOT EXISTS runtime_session_candidates (
628
575
  adapter_id TEXT NOT NULL,
629
576
  native_session_id TEXT NOT NULL,
630
577
  launch_id TEXT,
631
- status TEXT NOT NULL CHECK (status IN ('reserved','ready','running','broken')),
632
578
  session_updated_at TEXT NOT NULL,
633
579
  cleanup_required INTEGER NOT NULL CHECK (cleanup_required IN (0,1)),
634
580
  PRIMARY KEY (scope, task_id, role_name),
@@ -637,234 +583,20 @@ CREATE TABLE IF NOT EXISTS runtime_session_candidates (
637
583
  OR (scope = 'global' AND task_id = '')
638
584
  ),
639
585
  CHECK (
640
- cleanup_required = CASE
641
- WHEN status NOT IN ('stopped','broken')
642
- AND (status = 'running' OR launch_id IS NOT NULL)
643
- THEN 1 ELSE 0
644
- END
586
+ cleanup_required = CASE WHEN launch_id IS NOT NULL THEN 1 ELSE 0 END
645
587
  )
646
588
  );
647
589
 
648
590
  CREATE INDEX IF NOT EXISTS idx_runtime_session_cleanup_required
649
591
  ON runtime_session_candidates(scope, task_id, role_name)
650
592
  WHERE cleanup_required = 1;
651
-
652
- INSERT INTO runtime_session_candidates (
653
- scope, task_id, role_name, agent_id, adapter_id, native_session_id,
654
- launch_id, status, session_updated_at, cleanup_required
655
- )
656
- SELECT
657
- 'task', source.task_id, source.role_name,
658
- json_extract(active.value, '$.agentId'),
659
- json_extract(active.value, '$.adapterId'),
660
- json_extract(active.value, '$.nativeSessionId'),
661
- json_extract(active.value, '$.launchId'),
662
- json_extract(active.value, '$.status'),
663
- json_extract(active.value, '$.updatedAt'),
664
- CASE
665
- WHEN json_extract(active.value, '$.status') NOT IN ('stopped','broken')
666
- AND (
667
- json_extract(active.value, '$.status') = 'running'
668
- OR json_type(active.value, '$.launchId') = 'text'
669
- )
670
- THEN 1 ELSE 0
671
- END
672
- FROM role_session_sets AS source
673
- JOIN json_each(source.payload, '$.sessions') AS active
674
- ON active.key = json_extract(source.payload, '$.activeAgentId')
675
- WHERE json_extract(active.value, '$.status') <> 'stopped';
676
-
677
- INSERT INTO runtime_session_candidates (
678
- scope, task_id, role_name, agent_id, adapter_id, native_session_id,
679
- launch_id, status, session_updated_at, cleanup_required
680
- )
681
- SELECT
682
- 'global', '', source.name,
683
- json_extract(active.value, '$.agentId'),
684
- json_extract(active.value, '$.adapterId'),
685
- json_extract(active.value, '$.nativeSessionId'),
686
- json_extract(active.value, '$.launchId'),
687
- json_extract(active.value, '$.status'),
688
- json_extract(active.value, '$.updatedAt'),
689
- CASE
690
- WHEN json_extract(active.value, '$.status') NOT IN ('stopped','broken')
691
- AND (
692
- json_extract(active.value, '$.status') = 'running'
693
- OR json_type(active.value, '$.launchId') = 'text'
694
- )
695
- THEN 1 ELSE 0
696
- END
697
- FROM global_role_session_sets AS source
698
- JOIN json_each(source.payload, '$.sessions') AS active
699
- ON active.key = json_extract(source.payload, '$.activeAgentId')
700
- WHERE json_extract(active.value, '$.status') <> 'stopped';
701
- `;
702
- /**
703
- * Migration 12: bounded open-InputRequest lookup for Controller deadlines.
704
- *
705
- * The baseline index's historical predicate predates the current
706
- * open/answered/cancelled status contract and retains terminal rows. This
707
- * partial index contains only the live InputRequest set, so deadline arming and
708
- * targeted auto-resolution never scan terminal request history.
709
- */
710
- const MIGRATION_12_SQL = `
711
- CREATE INDEX IF NOT EXISTS idx_input_requests_open_hot
712
- ON input_requests(task_id, input_id)
713
- WHERE status = 'open';
714
- `;
715
- /**
716
- * Migration 13: durable pending native Turn-completion hot projection.
717
- *
718
- * A Task Role may have an unsettled Turn completion after the native Session
719
- * has ended. That completion is independent lifecycle state, so it cannot be
720
- * discovered through the current-Session projection (which intentionally
721
- * removes stopped Sessions). Keep one typed row per Task Role and maintain it
722
- * in the same transaction as `role_session_sets`; deadline discovery can then
723
- * read this table without parsing a RoleSessionSet or its historical Sessions.
724
- */
725
- const MIGRATION_13_SQL = `
726
- CREATE TABLE IF NOT EXISTS pending_runtime_turn_completions (
727
- task_id TEXT NOT NULL,
728
- role_name TEXT NOT NULL,
729
- schema_version INTEGER NOT NULL CHECK (schema_version = 1),
730
- agent_id TEXT NOT NULL,
731
- native_session_id TEXT NOT NULL,
732
- turn_id TEXT NOT NULL,
733
- run_id TEXT NOT NULL,
734
- summary TEXT NOT NULL,
735
- observed_at TEXT NOT NULL,
736
- due_at TEXT NOT NULL,
737
- PRIMARY KEY (task_id, role_name),
738
- CHECK (due_at >= observed_at),
739
- FOREIGN KEY (task_id, role_name)
740
- REFERENCES role_session_sets(task_id, role_name) ON DELETE CASCADE
741
- );
742
-
743
- CREATE INDEX IF NOT EXISTS idx_pending_runtime_turn_completions_due
744
- ON pending_runtime_turn_completions(task_id, due_at, role_name);
745
-
746
- INSERT INTO pending_runtime_turn_completions (
747
- task_id, role_name, schema_version, agent_id, native_session_id,
748
- turn_id, run_id, summary, observed_at, due_at
749
- )
750
- SELECT
751
- source.task_id,
752
- source.role_name,
753
- json_extract(source.payload, '$.pendingTurnCompletion.schemaVersion'),
754
- json_extract(source.payload, '$.pendingTurnCompletion.agentId'),
755
- json_extract(source.payload, '$.pendingTurnCompletion.nativeSessionId'),
756
- json_extract(source.payload, '$.pendingTurnCompletion.turnId'),
757
- json_extract(source.payload, '$.pendingTurnCompletion.runId'),
758
- json_extract(source.payload, '$.pendingTurnCompletion.summary'),
759
- json_extract(source.payload, '$.pendingTurnCompletion.observedAt'),
760
- json_extract(source.payload, '$.pendingTurnCompletion.dueAt')
761
- FROM role_session_sets AS source
762
- WHERE json_type(source.payload, '$.pendingTurnCompletion') = 'object';
763
- `;
764
- /** Migration 14: WorkMailbox v2 lanes and the single durable model-input delivery. */
765
- const MIGRATION_14_SQL = `
766
- ALTER TABLE mailboxes ADD COLUMN input_delivery TEXT;
767
-
768
- UPDATE mailboxes
769
- SET processing = CASE
770
- WHEN processing IS NULL THEN NULL
771
- ELSE json_set(
772
- processing,
773
- '$.lane', 'normal',
774
- '$.batch.sources', json_array('migration-v1'),
775
- '$.batch.dedupeKeys', json_array(
776
- 'mailbox-v1:' || target_key || ':'
777
- || json_extract(processing, '$.batch.fromSequence') || '-'
778
- || json_extract(processing, '$.batch.toSequence')
779
- ),
780
- '$.batch.deliveryModes', json_array('followup')
781
- )
782
- END;
783
-
784
- UPDATE mailboxes
785
- SET input_delivery = json_object(
786
- 'attemptId', json_extract(processing, '$.batchId'),
787
- 'lane', 'normal',
788
- 'mode', 'followup',
789
- 'batch', json(json_extract(processing, '$.batch')),
790
- 'owner', json_extract(processing, '$.owner'),
791
- 'status', 'delivery-unknown',
792
- 'startedAt', json_extract(processing, '$.startedAt'),
793
- 'pushedAt', json_extract(processing, '$.startedAt'),
794
- 'unknownReason', 'migrated-unconfirmed-provider-acceptance',
795
- 'executionRef', json(json_extract(processing, '$.executionRef')),
796
- 'providerFence', json_object(
797
- 'conversationId', (
798
- SELECT json_extract(
799
- json_extract(role_session_sets.payload, '$.sessions'),
800
- '$.' || json_quote(json_extract(role_session_sets.payload, '$.activeAgentId'))
801
- || '.nativeSessionId'
802
- )
803
- FROM role_session_sets
804
- WHERE role_session_sets.task_id = mailboxes.task_id
805
- AND role_session_sets.role_name = mailboxes.role_name
806
- ),
807
- 'activationId', COALESCE((
808
- SELECT json_extract(
809
- json_extract(role_session_sets.payload, '$.sessions'),
810
- '$.' || json_quote(json_extract(role_session_sets.payload, '$.activeAgentId'))
811
- || '.launchId'
812
- )
813
- FROM role_session_sets
814
- WHERE role_session_sets.task_id = mailboxes.task_id
815
- AND role_session_sets.role_name = mailboxes.role_name
816
- ), (
817
- SELECT json_extract(role_session_sets.payload, '$.inFlight.receiptId')
818
- FROM role_session_sets
819
- WHERE role_session_sets.task_id = mailboxes.task_id
820
- AND role_session_sets.role_name = mailboxes.role_name
821
- ))
822
- )
823
- )
824
- WHERE target_kind = 'role'
825
- AND json_extract(processing, '$.batchId') LIKE 'agent-input:%';
826
-
827
- UPDATE mailboxes
828
- SET processing = NULL
829
- WHERE input_delivery IS NOT NULL;
830
-
831
- UPDATE mailboxes
832
- SET pending = json_object(
833
- 'normal', CASE
834
- WHEN pending IS NULL THEN json('null')
835
- ELSE json(json_set(
836
- pending,
837
- '$.sources', json_array('migration-v1'),
838
- '$.dedupeKeys', json_array(
839
- 'mailbox-v1:' || target_key || ':'
840
- || json_extract(pending, '$.fromSequence') || '-'
841
- || json_extract(pending, '$.toSequence')
842
- ),
843
- '$.deliveryModes', json_array('followup')
844
- ))
845
- END,
846
- 'userCorrection', json('null'),
847
- 'cursors', json_object('normal', 0, 'userCorrection', 0),
848
- 'recentDedupeKeys', json_array()
849
- );
850
-
851
- DROP INDEX idx_mailboxes_ready;
852
- CREATE INDEX idx_mailboxes_ready
853
- ON mailboxes(target_key)
854
- WHERE processing IS NOT NULL
855
- OR input_delivery IS NOT NULL
856
- OR json_type(pending, '$.normal') <> 'null'
857
- OR json_type(pending, '$.userCorrection') <> 'null';
858
-
859
- DROP TABLE pending_runtime_turn_completions;
860
- DROP TABLE IF EXISTS mailbox_signals;
861
593
  `;
862
594
  /**
863
- * Migration 15: Issue 11 external publication evidence. Records are immutable;
595
+ * External publication evidence. Records are immutable;
864
596
  * a corrected MR/PR state appends a superseding record with the same
865
597
  * external_key, so only the unsuperseded root is globally unique.
866
598
  */
867
- const MIGRATION_15_SQL = `
599
+ const BASELINE_PUBLICATION_REFERENCE_SQL = `
868
600
  CREATE TABLE IF NOT EXISTS publication_references (
869
601
  task_id TEXT NOT NULL,
870
602
  publication_id TEXT NOT NULL,
@@ -895,7 +627,7 @@ CREATE INDEX IF NOT EXISTS idx_publication_references_external
895
627
  CREATE UNIQUE INDEX IF NOT EXISTS idx_publication_references_external_root
896
628
  ON publication_references(external_key) WHERE supersedes IS NULL;
897
629
  `;
898
- const MIGRATION_16_SQL = `
630
+ const BASELINE_TASK_WAKE_SQL = `
899
631
  -- Durable Leader wake ledger (Issue 04 long-term design). A wake is a
900
632
  -- notification envelope, not a context dump: the record holds the aggregated
901
633
  -- reason tags and the delta window; the Agent reads delta content on demand.
@@ -904,7 +636,7 @@ CREATE TABLE IF NOT EXISTS task_wakes (
904
636
  wake_id TEXT NOT NULL,
905
637
  seq INTEGER NOT NULL,
906
638
  status TEXT NOT NULL CHECK (status IN ('dispatched','consumed')),
907
- run_id TEXT,
639
+ turn_id TEXT,
908
640
  from_cursor TEXT NOT NULL,
909
641
  to_cursor TEXT NOT NULL,
910
642
  reasons TEXT NOT NULL,
@@ -915,8 +647,8 @@ CREATE TABLE IF NOT EXISTS task_wakes (
915
647
  );
916
648
  CREATE INDEX IF NOT EXISTS idx_task_wakes_seq ON task_wakes(task_id, seq);
917
649
  `;
918
- /** Migration 17: immutable, Task-scoped ContextSnapshot records. */
919
- const MIGRATION_17_SQL = `
650
+ /** Immutable, Task-scoped ContextSnapshot records. */
651
+ const BASELINE_CONTEXT_SNAPSHOT_SQL = `
920
652
  CREATE TABLE IF NOT EXISTS context_snapshots (
921
653
  task_id TEXT NOT NULL,
922
654
  snapshot_id TEXT NOT NULL,
@@ -934,82 +666,23 @@ CREATE TABLE IF NOT EXISTS context_snapshots (
934
666
  CREATE UNIQUE INDEX IF NOT EXISTS idx_context_snapshots_scope_sequence
935
667
  ON context_snapshots(task_id, scope, COALESCE(scope_ref, ''), sequence);
936
668
  `;
937
- /**
938
- * Migration 18: align the current-Session hot projection with Session v4.
939
- *
940
- * Session existence now has one lifecycle: active or ended. Ended Sessions do
941
- * not belong in this bounded projection, so row presence itself means active;
942
- * the projection carries no independently writable lifecycle status. Host
943
- * cleanup is derived solely from whether the Session still names a Host
944
- * activation. Valid earlier live rows are projected into that contract;
945
- * terminal broken rows disappear with the rest of ended Session history.
946
- */
947
- const MIGRATION_18_SQL = `
948
- DROP INDEX idx_runtime_session_cleanup_required;
949
- ALTER TABLE runtime_session_candidates RENAME TO runtime_session_candidates_v11;
950
-
951
- CREATE TABLE runtime_session_candidates (
952
- scope TEXT NOT NULL CHECK (scope IN ('task','global')),
953
- task_id TEXT NOT NULL,
954
- role_name TEXT NOT NULL,
955
- agent_id TEXT NOT NULL,
956
- adapter_id TEXT NOT NULL,
957
- native_session_id TEXT NOT NULL,
958
- launch_id TEXT,
959
- session_updated_at TEXT NOT NULL,
960
- cleanup_required INTEGER NOT NULL CHECK (cleanup_required IN (0,1)),
961
- PRIMARY KEY (scope, task_id, role_name),
962
- CHECK (
963
- (scope = 'task' AND length(task_id) > 0)
964
- OR (scope = 'global' AND task_id = '')
965
- ),
966
- CHECK (
967
- cleanup_required = CASE WHEN launch_id IS NOT NULL THEN 1 ELSE 0 END
968
- )
969
- );
970
-
971
- INSERT INTO runtime_session_candidates (
972
- scope, task_id, role_name, agent_id, adapter_id, native_session_id,
973
- launch_id, session_updated_at, cleanup_required
974
- )
975
- SELECT
976
- scope, task_id, role_name, agent_id, adapter_id, native_session_id,
977
- launch_id, session_updated_at,
978
- CASE WHEN launch_id IS NOT NULL THEN 1 ELSE 0 END
979
- FROM runtime_session_candidates_v11
980
- WHERE status IN ('reserved','ready','running');
981
-
982
- DROP TABLE runtime_session_candidates_v11;
983
-
984
- CREATE INDEX idx_runtime_session_cleanup_required
985
- ON runtime_session_candidates(scope, task_id, role_name)
986
- WHERE cleanup_required = 1;
987
- `;
988
- /**
989
- * The single forward migration history for current SQLite Homes. New durable
990
- * layout, aggregate, and record changes belong here and must be expressible as
991
- * one atomic in-place transaction. The separate logical migration registry is
992
- * only the bridge from valid pre-SQLite Homes and is allowed to rebuild them.
993
- */
669
+ const MIGRATION_1_SQL = [
670
+ BASELINE_CORE_SQL,
671
+ BASELINE_JOB_AND_RELEASE_SQL,
672
+ BASELINE_JOB_CALLER_SQL,
673
+ BASELINE_TELEMETRY_AGGREGATE_SQL,
674
+ BASELINE_REVIEW_FINDING_SQL,
675
+ BASELINE_SESSION_OWNER_SQL,
676
+ BASELINE_RESOURCE_REGISTRY_SQL,
677
+ BASELINE_GATE_ARTIFACT_SQL,
678
+ BASELINE_RUNTIME_SESSION_SQL,
679
+ BASELINE_PUBLICATION_REFERENCE_SQL,
680
+ BASELINE_TASK_WAKE_SQL,
681
+ BASELINE_CONTEXT_SNAPSHOT_SQL
682
+ ].join("\n");
683
+ /** Future compatible releases append new migrations after this baseline. */
994
684
  const MIGRATIONS = [
995
- { version: 1, axis: "layout", sql: MIGRATION_1_SQL },
996
- { version: 2, axis: "record", recordKind: "durableJob+capability-grant+release-workflow", sql: MIGRATION_2_SQL },
997
- { version: 3, axis: "record", recordKind: "jobCallerKeyHash", sql: MIGRATION_3_SQL },
998
- { version: 4, axis: "record", recordKind: "durableJob", sql: MIGRATION_4_SQL },
999
- { version: 5, axis: "record", recordKind: "telemetryAggregate", sql: MIGRATION_5_SQL },
1000
- { version: 6, axis: "record", recordKind: "reviewFinding", sql: MIGRATION_6_SQL },
1001
- { version: 7, axis: "record", recordKind: "sessionOwner", sql: MIGRATION_7_SQL },
1002
- { version: 8, axis: "record", recordKind: "resource-registry", sql: MIGRATION_8_SQL },
1003
- { version: 9, axis: "record", recordKind: "gateArtifact", sql: MIGRATION_9_SQL },
1004
- { version: 10, axis: "layout", sql: MIGRATION_10_SQL },
1005
- { version: 11, axis: "layout", sql: MIGRATION_11_SQL },
1006
- { version: 12, axis: "layout", sql: MIGRATION_12_SQL },
1007
- { version: 13, axis: "layout", sql: MIGRATION_13_SQL },
1008
- { version: 14, axis: "record", recordKind: "workMailbox", sql: MIGRATION_14_SQL },
1009
- { version: 15, axis: "record", recordKind: "publicationReference", sql: MIGRATION_15_SQL },
1010
- { version: 16, axis: "record", recordKind: "taskWake", sql: MIGRATION_16_SQL },
1011
- { version: 17, axis: "record", recordKind: "contextSnapshot", sql: MIGRATION_17_SQL },
1012
- { version: 18, axis: "layout", sql: MIGRATION_18_SQL }
685
+ { version: 1, axis: "layout", sql: MIGRATION_1_SQL }
1013
686
  ];
1014
687
  /** Current hot-path indexes whose absence would invalidate a current Home. */
1015
688
  const REQUIRED_SCHEMA_INDEXES = [
@@ -1024,7 +697,7 @@ function checksum(sql) {
1024
697
  * A SQLite Home is only safe to open when its migration ledger proves exactly
1025
698
  * which schema definition was applied. The ledger is durable metadata, not a
1026
699
  * best-effort cache: a missing row, a changed checksum, or an unknown version
1027
- * must stop startup before any pending migration is run.
700
+ * must stop startup before any pending schema step is applied.
1028
701
  */
1029
702
  export class SqliteSchemaMigrationError extends Error {
1030
703
  constructor(detail, subject = "metadata") {
@@ -1127,82 +800,7 @@ function validateSchemaObjects(db) {
1127
800
  }
1128
801
  }
1129
802
  }
1130
- /** Validate migration 13's source invariant before creating its hot table. */
1131
- function validatePendingProjectionBackfillSources(db) {
1132
- const existing = db.prepare(`SELECT name
1133
- FROM sqlite_master
1134
- WHERE name IN (
1135
- 'pending_runtime_turn_completions',
1136
- 'idx_pending_runtime_turn_completions_due'
1137
- )
1138
- LIMIT 1`).get();
1139
- if (existing !== undefined) {
1140
- throw new SqliteSchemaMigrationError(`schema object '${existing.name}' exists before migration 13 is recorded`, "backfill");
1141
- }
1142
- const mismatch = db.prepare(`SELECT task_id, role_name
1143
- FROM role_session_sets
1144
- WHERE COALESCE(json_type(payload, '$.pendingTurnCompletion'), 'missing')
1145
- NOT IN ('null', 'object')
1146
- OR (
1147
- json_type(payload, '$.pendingTurnCompletion') = 'object'
1148
- AND (
1149
- json_extract(payload, '$.owner.scope') <> 'task'
1150
- OR json_extract(payload, '$.owner.taskId') IS NULL
1151
- OR json_extract(payload, '$.owner.taskId') <> task_id
1152
- OR json_extract(payload, '$.owner.roleName') IS NULL
1153
- OR json_extract(payload, '$.owner.roleName') <> role_name
1154
- OR json_extract(payload, '$.pendingTurnCompletion.taskId') IS NULL
1155
- OR json_extract(payload, '$.pendingTurnCompletion.taskId') <> task_id
1156
- OR json_extract(payload, '$.pendingTurnCompletion.roleName') IS NULL
1157
- OR json_extract(payload, '$.pendingTurnCompletion.roleName') <> role_name
1158
- OR json_extract(payload, '$.pendingTurnCompletion.schemaVersion') IS NOT 1
1159
- OR COALESCE(json_type(payload, '$.pendingTurnCompletion.agentId'), 'missing') <> 'text'
1160
- OR COALESCE(json_type(payload, '$.pendingTurnCompletion.nativeSessionId'), 'missing') <> 'text'
1161
- OR COALESCE(json_type(payload, '$.pendingTurnCompletion.turnId'), 'missing') <> 'text'
1162
- OR COALESCE(json_type(payload, '$.pendingTurnCompletion.runId'), 'missing') <> 'text'
1163
- OR COALESCE(json_type(payload, '$.pendingTurnCompletion.summary'), 'missing') <> 'text'
1164
- OR COALESCE(json_type(payload, '$.pendingTurnCompletion.observedAt'), 'missing') <> 'text'
1165
- OR COALESCE(json_type(payload, '$.pendingTurnCompletion.dueAt'), 'missing') <> 'text'
1166
- OR COALESCE(json_type(payload, '$.inFlight'), 'missing') <> 'object'
1167
- OR COALESCE(json_type(payload, '$.activeAgentId'), 'missing') <> 'text'
1168
- OR COALESCE(json_type(payload, '$.inFlight.agentId'), 'missing') <> 'text'
1169
- OR COALESCE(json_type(payload, '$.inFlight.runId'), 'missing') <> 'text'
1170
- OR json_extract(payload, '$.activeAgentId')
1171
- <> json_extract(payload, '$.pendingTurnCompletion.agentId')
1172
- OR json_extract(payload, '$.inFlight.agentId')
1173
- <> json_extract(payload, '$.pendingTurnCompletion.agentId')
1174
- OR json_extract(payload, '$.inFlight.runId')
1175
- <> json_extract(payload, '$.pendingTurnCompletion.runId')
1176
- OR COALESCE(json_type(payload, '$.inFlight.pushedAt'), 'missing') <> 'text'
1177
- OR COALESCE(
1178
- json_type(
1179
- payload,
1180
- '$.sessions.' || json_quote(
1181
- json_extract(payload, '$.pendingTurnCompletion.agentId')
1182
- ) || '.nativeSessionId'
1183
- ),
1184
- 'missing'
1185
- ) <> 'text'
1186
- OR json_extract(
1187
- payload,
1188
- '$.sessions.' || json_quote(
1189
- json_extract(payload, '$.pendingTurnCompletion.agentId')
1190
- ) || '.nativeSessionId'
1191
- ) <> json_extract(payload, '$.pendingTurnCompletion.nativeSessionId')
1192
- OR json_extract(payload, '$.pendingTurnCompletion.dueAt')
1193
- < json_extract(payload, '$.pendingTurnCompletion.observedAt')
1194
- )
1195
- )
1196
- LIMIT 1`).get();
1197
- if (mismatch !== undefined) {
1198
- throw new SqliteSchemaMigrationError(`pending Turn completion source is invalid for ${mismatch.task_id}/${mismatch.role_name}`, "backfill");
1199
- }
1200
- }
1201
- /**
1202
- * Inspect a database's checksummed migration prefix without changing it.
1203
- * Pending versions are a supported upgrade state; malformed, gapped, changed,
1204
- * or future ledger entries remain explicit integrity failures.
1205
- */
803
+ /** Inspect a recognized migration prefix without changing it. */
1206
804
  export function inspectSqliteSchemaMigrations(db) {
1207
805
  const ledgerWasCreated = ensureMigrationLedger(db, "validate");
1208
806
  const applied = validateAppliedMigrations(db, ledgerWasCreated);
@@ -1241,14 +839,11 @@ export function migrateSqliteSchema(db, options) {
1241
839
  // skipping a later schema/data step.
1242
840
  const applied = validateAppliedMigrations(db, ledgerWasCreated);
1243
841
  const pending = MIGRATIONS.filter((migration) => !applied.has(migration.version));
1244
- if (options.mode === "validate" && pending.length > 0) {
1245
- throw new SqliteSchemaMigrationError(`pending SQLite schema migration ${pending[0].version}; `
1246
- + "run the explicit storage upgrade before opening this database", "admission");
842
+ if (!ledgerWasCreated && pending.length > 0 && options.mode === "validate") {
843
+ throw new SqliteSchemaMigrationError(`SQLite schema ${applied.size} requires an explicit upgrade to ${SQLITE_SCHEMA_VERSION}`, "admission");
1247
844
  }
1248
845
  const newlyApplied = [];
1249
846
  for (const migration of pending) {
1250
- if (migration.version === 13)
1251
- validatePendingProjectionBackfillSources(db);
1252
847
  db.exec(migration.sql);
1253
848
  db.prepare(`INSERT INTO schema_migrations (version, axis, record_kind, applied_at, checksum)
1254
849
  VALUES (?, ?, ?, ?, ?)`).run(migration.version, migration.axis, migration.recordKind ?? null, new Date().toISOString(), checksum(migration.sql));
@@ -1288,8 +883,8 @@ export const SQLITE_SCHEMA_TABLES = [
1288
883
  "work_items",
1289
884
  "work_item_candidates",
1290
885
  "context_snapshots",
1291
- "agent_runs",
1292
- "active_runs",
886
+ "turns",
887
+ "active_turns",
1293
888
  "review_rounds",
1294
889
  "review_findings",
1295
890
  "change_sets",
@@ -1305,6 +900,7 @@ export const SQLITE_SCHEMA_TABLES = [
1305
900
  "capability_grants",
1306
901
  "release_workflows",
1307
902
  "publication_references",
903
+ "task_wakes",
1308
904
  "session_owners",
1309
905
  "runtime_session_candidates",
1310
906
  "resource_registry",