@zq-silk/yui 0.15.11 → 0.16.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 (280) hide show
  1. package/ARCHITECTURE.md +11 -6
  2. package/ARCHITECTURE.zh-CN.md +8 -4
  3. package/README.md +13 -5
  4. package/dist/agentRun/agentRun.js +3 -0
  5. package/dist/artifacts/artifactCommitLock.js +16 -55
  6. package/dist/artifacts/taskArtifactRepository.js +3 -3
  7. package/dist/cli/agentConfigurationPicker.js +1 -1
  8. package/dist/cli/commandCatalog.js +92 -48
  9. package/dist/cli/completion.js +18 -18
  10. package/dist/cli/completionWizard.js +1 -1
  11. package/dist/cli/dynamicCompletion.js +1 -1
  12. package/dist/cli/interactionPolicy.js +11 -3
  13. package/dist/cli/invocationAuthority.js +64 -0
  14. package/dist/cli/managedDiagnostics.js +2 -2
  15. package/dist/cli/parseRepeatable.js +35 -0
  16. package/dist/cli/updatePorts.js +26 -5
  17. package/dist/cli.js +1303 -1235
  18. package/dist/commands/agentCommands.js +4 -4
  19. package/dist/commands/capabilityCommands.js +1 -1
  20. package/dist/commands/configCommands.js +6 -57
  21. package/dist/commands/configOverview.js +2 -2
  22. package/dist/commands/durableJobCommands.js +12 -6
  23. package/dist/commands/executionAuditCommands.js +10 -0
  24. package/dist/commands/globalRoleCommands.js +78 -29
  25. package/dist/commands/grantCommands.js +0 -3
  26. package/dist/commands/jobCommands.js +1 -4
  27. package/dist/commands/operatorCommands.js +3 -3
  28. package/dist/commands/projectCommands.js +45 -25
  29. package/dist/commands/resourcesCommands.js +10 -40
  30. package/dist/commands/roleConfiguration.js +2 -6
  31. package/dist/commands/taskActivationCommands.js +4 -8
  32. package/dist/commands/taskAgentCapabilities.js +9 -0
  33. package/dist/commands/taskCommandSupport.js +155 -0
  34. package/dist/commands/taskCommandTypes.js +1 -0
  35. package/dist/commands/taskCommands.js +355 -732
  36. package/dist/commands/taskCompletionGate.js +1 -1
  37. package/dist/commands/taskExecutionCommands.js +1 -1
  38. package/dist/commands/taskFactCommands.js +325 -0
  39. package/dist/commands/taskInputCommands.js +12 -42
  40. package/dist/commands/taskIntegrationCommands.js +20 -58
  41. package/dist/commands/taskNextActionCommand.js +7 -6
  42. package/dist/commands/taskPublicationAdoptCommand.js +127 -0
  43. package/dist/commands/taskPublicationCommands.js +12 -3
  44. package/dist/commands/taskPublicationVerifyCommand.js +24 -40
  45. package/dist/commands/taskRemoteDeliveryCommand.js +5 -86
  46. package/dist/commands/taskUpstreamCommands.js +19 -8
  47. package/dist/commands/workflowCommands.js +6 -1
  48. package/dist/completion/completionInstaller.js +26 -26
  49. package/dist/completion/completionState.js +26 -26
  50. package/dist/completion/fileCompletionManager.js +6 -11
  51. package/dist/config/configCatalog.js +0 -2
  52. package/dist/config/timeZone.js +14 -0
  53. package/dist/config/yuiConfig.js +0 -29
  54. package/dist/context/runContextPack.js +5 -3
  55. package/dist/context/taskCatalog.js +185 -0
  56. package/dist/context/taskContext.js +152 -151
  57. package/dist/context/wakeRunReferences.js +11 -0
  58. package/dist/controller/agentCapabilities.js +58 -0
  59. package/dist/controller/agentRuntimeObserver.js +6 -11
  60. package/dist/controller/clientRuntime.js +22 -14
  61. package/dist/controller/controller.js +20 -24
  62. package/dist/controller/fileSchedulerStoreAdapter.js +191 -225
  63. package/dist/controller/globalInputDelivery.js +13 -0
  64. package/dist/controller/jobClient.js +4 -4
  65. package/dist/controller/jobControl.js +60 -25
  66. package/dist/controller/jobSupervisor.js +4 -4
  67. package/dist/controller/providerRetryAdmission.js +100 -0
  68. package/dist/controller/providerRetryDelivery.js +218 -0
  69. package/dist/controller/runtime.js +85 -43
  70. package/dist/controller/runtimeEventProcessor.js +0 -5
  71. package/dist/controller/sessionOwnerReconciliation.js +5 -0
  72. package/dist/controller/taskAgentError.js +35 -0
  73. package/dist/coordination/workMailboxQueue.js +2 -24
  74. package/dist/core/controllerClient.js +2 -2
  75. package/dist/core/fileLockOwner.js +74 -0
  76. package/dist/decision/decision.js +17 -0
  77. package/dist/doctor/doctor.js +18 -9
  78. package/dist/event/taskEvent.js +12 -0
  79. package/dist/execution/workItemExecutionProjection.js +2 -2
  80. package/dist/executor/agentCapabilityConfig.js +43 -0
  81. package/dist/executor/agentConfigurationCatalog.js +49 -11
  82. package/dist/executor/agentExecutor.js +5 -8
  83. package/dist/executor/fileRoleLaunchPlanner.js +11 -53
  84. package/dist/executor/taskAgentCapabilities.js +64 -0
  85. package/dist/integration/deliveryObligation.js +1 -69
  86. package/dist/integration/gitIntegrationService.js +79 -90
  87. package/dist/integration/integrationAttempt.js +3 -14
  88. package/dist/integration/integrationSourceApplication.js +4 -12
  89. package/dist/integration/manifestTags.js +2 -2
  90. package/dist/job/durableJob.js +4 -25
  91. package/dist/job/jobAssignmentScope.js +22 -0
  92. package/dist/job/jobOperation.js +16 -0
  93. package/dist/job/jobRunner.js +2 -1
  94. package/dist/job/stepDirectory.js +15 -0
  95. package/dist/kernel/builtinCapabilities.js +15 -11
  96. package/dist/kernel/kernelPorts.js +1 -17
  97. package/dist/lifecycle/exactRunTerminalization.js +5 -10
  98. package/dist/message/globalProviderRetry.js +15 -0
  99. package/dist/message/inputControlResolution.js +3 -4
  100. package/dist/message/message.js +17 -20
  101. package/dist/message/messageContinuation.js +1 -1
  102. package/dist/milestone/milestone.js +11 -0
  103. package/dist/observability/executionAudit.js +19 -0
  104. package/dist/observability/orchestrationMetrics.js +24 -1
  105. package/dist/observability/runtimeIdentity.js +1 -17
  106. package/dist/output/agentConfigurationPresentation.js +2 -0
  107. package/dist/output/timePresentation.js +1 -14
  108. package/dist/plugins/pluginService.js +12 -4
  109. package/dist/release/releaseWorkflowPorts.js +45 -96
  110. package/dist/release/runtimeRelease.js +9 -15
  111. package/dist/repository/gitWorkspace.js +371 -105
  112. package/dist/repository/projectMaintenanceLock.js +98 -82
  113. package/dist/repository/taskBaseFreshness.js +1 -1
  114. package/dist/repository/taskWorkspaceCoordinator.js +82 -144
  115. package/dist/repository/taskWorkspacePreparer.js +113 -47
  116. package/dist/repository/workspaceCleanupInspection.js +187 -0
  117. package/dist/resources/autoResourceGc.js +4 -77
  118. package/dist/resources/liveReferences.js +7 -54
  119. package/dist/resources/resourceDiscovery.js +5 -118
  120. package/dist/resources/resourceGc.js +233 -181
  121. package/dist/resources/resourceRegistrar.js +5 -4
  122. package/dist/resources/resourceRegistry.js +3 -37
  123. package/dist/resources/resourceRegistryStore.js +13 -36
  124. package/dist/resources/sqliteResourceRegistry.js +43 -26
  125. package/dist/review/deltaRecheck.js +1 -1
  126. package/dist/review/reviewAcceptance.js +1 -1
  127. package/dist/review/reviewDecision.js +1 -1
  128. package/dist/review/reviewRound.js +8 -7
  129. package/dist/review/taskFinalReviewContractResolution.js +1 -1
  130. package/dist/runtime/acpProtocol.js +4 -51
  131. package/dist/runtime/acpSession.js +10 -75
  132. package/dist/runtime/acpSessionConfiguration.js +1 -8
  133. package/dist/runtime/agentEndpoint.js +3 -2
  134. package/dist/runtime/agentError.js +5 -3
  135. package/dist/runtime/agentFailureContext.js +43 -0
  136. package/dist/runtime/agentHost.js +42 -25
  137. package/dist/runtime/builtinAgentErrorMappers.js +91 -0
  138. package/dist/runtime/codexAppServerRuntime.js +34 -3
  139. package/dist/runtime/codexInteractiveHost.js +2 -2
  140. package/dist/runtime/index.js +0 -1
  141. package/dist/runtime/managedCaller.js +21 -1
  142. package/dist/runtime/providerControl.js +5 -1
  143. package/dist/runtime/providerErrors.js +38 -0
  144. package/dist/runtime/providerRetry.js +198 -0
  145. package/dist/runtime/providerRuntimeIdentity.js +28 -2
  146. package/dist/runtime/runtimeObservation.js +0 -5
  147. package/dist/runtime/runtimeSessionCandidate.js +2 -3
  148. package/dist/runtime/sessionOwnerIdentity.js +1 -1
  149. package/dist/runtime/sessionTokenMetrics.js +15 -5
  150. package/dist/runtime/structuredProviderHost.js +10 -43
  151. package/dist/runtime/taskUsageMetrics.js +275 -0
  152. package/dist/runtime/tmuxAdapters.js +6 -8
  153. package/dist/scheduler/activeRoleRunDelivery.js +19 -7
  154. package/dist/scheduler/leaderWakeupProcessor.js +24 -6
  155. package/dist/scheduler/operatorEvent.js +12 -20
  156. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  157. package/dist/scheduler/ports.js +5 -8
  158. package/dist/scheduler/roleRunLiveness.js +4 -4
  159. package/dist/scheduler/roleRunStall.js +16 -23
  160. package/dist/scheduler/taskExecutionProjection.js +49 -36
  161. package/dist/scheduler/taskObservabilityProjection.js +6 -45
  162. package/dist/scheduler/taskWake.js +5 -10
  163. package/dist/scheduler/wakeReason.js +2 -0
  164. package/dist/scheduler/wakeupQueue.js +2 -4
  165. package/dist/setup/setupCommand.js +1 -1
  166. package/dist/storage/contextRecords.js +106 -0
  167. package/dist/storage/currentTaskStore.js +1 -1
  168. package/dist/storage/homeLayout.js +13 -17
  169. package/dist/storage/migrations/agentFailureContext.js +22 -0
  170. package/dist/storage/migrations/currentInputContract.js +86 -0
  171. package/dist/storage/migrations/currentRuntimeContract.js +228 -0
  172. package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
  173. package/dist/storage/migrations/integrationContinuation.js +5 -4
  174. package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
  175. package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
  176. package/dist/storage/migrations/verificationPlanV1.js +162 -0
  177. package/dist/storage/migrations/verificationPolicy.js +74 -0
  178. package/dist/storage/migrations/workItemHistory.js +46 -0
  179. package/dist/storage/persistenceWorker.js +12 -4
  180. package/dist/storage/recordValidation.js +87 -0
  181. package/dist/storage/sqliteSchema.js +173 -1
  182. package/dist/storage/sqliteStore.js +172 -161
  183. package/dist/storage/storageSchema.js +2 -14
  184. package/dist/storage/storageVersions.js +1 -1
  185. package/dist/storage/storeRpc.js +16 -7
  186. package/dist/storage/taskCatalog.js +123 -0
  187. package/dist/storage/taskStore.js +5 -7
  188. package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
  189. package/dist/task/archiveDiagnostics.js +1 -0
  190. package/dist/task/archivePreflight.js +124 -0
  191. package/dist/task/completionReadiness.js +5 -24
  192. package/dist/task/draftPlan.js +0 -53
  193. package/dist/task/nextAction.js +7 -13
  194. package/dist/task/publicationAdoption.js +56 -0
  195. package/dist/task/publicationReference.js +10 -0
  196. package/dist/task/remoteDelivery.js +31 -16
  197. package/dist/task/remoteDeliveryService.js +89 -0
  198. package/dist/task/taskActivation.js +2 -25
  199. package/dist/task/taskActivationService.js +0 -2
  200. package/dist/task/taskRecordReference.js +0 -1
  201. package/dist/task/taskSubmission.js +3 -14
  202. package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
  203. package/dist/telemetry/sqliteTelemetryStore.js +49 -49
  204. package/dist/telemetry/telemetryWiring.js +4 -3
  205. package/dist/tmux/tmuxManager.js +29 -20
  206. package/dist/verification/gateArtifact.js +15 -24
  207. package/dist/verification/gateArtifactStore.js +14 -7
  208. package/dist/verification/verificationGateService.js +29 -88
  209. package/dist/verification/verificationPlan.js +27 -72
  210. package/dist/web/assets/client/app.js +92 -17
  211. package/dist/web/assets/client/components.js +55 -13
  212. package/dist/web/assets/client/i18n.js +72 -4
  213. package/dist/web/assets/client/taskSurface.js +6 -5
  214. package/dist/web/assets/client/view.js +35 -7
  215. package/dist/web/assets/shell.js +6 -0
  216. package/dist/web/assets/styles/layout.js +7 -0
  217. package/dist/web/webServer.js +12 -3
  218. package/dist/web/webSnapshot.js +14 -85
  219. package/dist/web/webTaskSurface.js +17 -46
  220. package/dist/workItem/workItem.js +1 -12
  221. package/dist/workspace/cleanupInspection.js +63 -0
  222. package/dist/workspace/workItemChangeSetManager.js +110 -50
  223. package/docs/agent-result-consumption.md +4 -0
  224. package/docs/agent-result-consumption.zh-CN.md +3 -0
  225. package/docs/agent-runtime-drivers.md +7 -0
  226. package/docs/agent-runtime-drivers.zh-CN.md +5 -0
  227. package/docs/architecture/README.md +2 -0
  228. package/docs/architecture/README.zh-CN.md +3 -1
  229. package/docs/architecture/capabilities-and-resources.md +42 -5
  230. package/docs/architecture/capabilities-and-resources.zh-CN.md +33 -3
  231. package/docs/managed-turn-and-session-runtime.md +121 -1
  232. package/docs/managed-turn-and-session-runtime.zh-CN.md +96 -2
  233. package/docs/observability/README.md +62 -0
  234. package/docs/observability/README.zh-CN.md +47 -0
  235. package/docs/plugin-sdk.md +4 -2
  236. package/docs/project-refresh.md +77 -0
  237. package/docs/project-refresh.zh-CN.md +59 -0
  238. package/docs/provider-retry.md +70 -0
  239. package/docs/provider-runtime.md +3 -1
  240. package/docs/provider-runtime.zh-CN.md +4 -2
  241. package/docs/release-workflow.md +190 -7
  242. package/docs/release-workflow.zh-CN.md +139 -5
  243. package/docs/roles-and-configuration.md +29 -0
  244. package/docs/roles-and-configuration.zh-CN.md +21 -0
  245. package/docs/sqlite-control-plane-design.md +16 -3
  246. package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
  247. package/docs/task-dag-semantics.md +3 -3
  248. package/docs/task-dag-semantics.zh-CN.md +2 -2
  249. package/docs/task-delivery.md +223 -17
  250. package/docs/task-delivery.zh-CN.md +171 -14
  251. package/docs/task-discovery.md +101 -0
  252. package/docs/task-discovery.zh-CN.md +85 -0
  253. package/docs/testing/verification-levels.md +161 -9
  254. package/docs/testing/verification-levels.zh-CN.md +116 -9
  255. package/i18n/README.zh-CN.md +13 -7
  256. package/package.json +1 -1
  257. package/skills/yui-leader/SKILL.md +5 -0
  258. package/skills/yui-leader/references/execution.md +3 -2
  259. package/skills/yui-leader/references/integration.md +9 -5
  260. package/skills/yui-leader/references/planning.md +9 -3
  261. package/skills/yui-operator/SKILL.md +24 -8
  262. package/skills/yui-reviewer/SKILL.md +4 -0
  263. package/skills/yui-runtime/SKILL.md +17 -2
  264. package/skills/yui-runtime/references/publication.md +26 -4
  265. package/skills/yui-runtime/references/recovery.md +71 -0
  266. package/dist/agent/agentRegistry.js +0 -10
  267. package/dist/agentRun/runIdentity.js +0 -22
  268. package/dist/commands/deliveryGuardPreflight.js +0 -30
  269. package/dist/commands/taskIntegrationQueueCommands.js +0 -226
  270. package/dist/commands/taskOverviewCommand.js +0 -376
  271. package/dist/completion/completionWizard.js +0 -125
  272. package/dist/context/dispatchContext.js +0 -110
  273. package/dist/context/wakeNotification.js +0 -144
  274. package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
  275. package/dist/integration/integrationQueueEntry.js +0 -191
  276. package/dist/integration/integrationQueueService.js +0 -810
  277. package/dist/release/fakeReleasePorts.js +0 -55
  278. package/dist/runtime/sessionOwnerRegistry.js +0 -137
  279. package/dist/task/deliveryGuard.js +0 -226
  280. /package/dist/{commands/taskActor.js → task/taskAuthority.js} +0 -0
@@ -28,45 +28,44 @@
28
28
  * `telemetry` scoped by its primary key — it never rewrites global
29
29
  * state and never touches another Task's rows (§4.4).
30
30
  *
31
- * The in-process store is phase 1 of §6. It does not re-run the heavy record
32
- * validators on write (the domain layer that constructs records already does,
33
- * and the design places record validation in the persistence worker, phase 2);
34
- * it performs the same cheap structural checks the file store relies on
35
- * (identity presence, taskId matching, referential lookups).
31
+ * The same current record validators apply to direct and worker-backed access,
32
+ * including writes, reads and explicit Home diagnostics. There is no weaker
33
+ * in-process contract or deferred validation phase.
36
34
  */
35
+ import Database from "better-sqlite3";
37
36
  import { existsSync, mkdirSync } from "node:fs";
38
37
  import { join } from "node:path";
39
38
  import { isDeepStrictEqual } from "node:util";
40
- import Database from "better-sqlite3";
41
- import { validatePluginValidation } from "../plugins/pluginPackage.js";
42
- import { validatePluginIntent, validatePluginIntentFailure } from "../plugins/pluginIntent.js";
43
- import { validateLocalResource, validateEnvironmentPreparation } from "../resources/projectResource.js";
44
- import { validateConfiguredAgent } from "../agent/agent.js";
39
+ import { runPurposeAdmitsTaskState } from "../agentRun/agentRun.js";
45
40
  import { validateTaskBrief } from "../brief/taskBrief.js";
46
- import { consumePendingBatch, mailboxTargetKey, validateWorkMailbox } from "../coordination/workMailbox.js";
47
41
  import { validateContextSnapshot } from "../context/contextSnapshot.js";
42
+ import { consumePendingBatch, mailboxTargetKey, validateWorkMailbox } from "../coordination/workMailbox.js";
43
+ import { validateDurableJob, validDurableJobTransition } from "../job/durableJob.js";
48
44
  import { validateGlobalRoleMessage } from "../message/message.js";
49
- import { runPurposeAdmitsTaskState } from "../agentRun/agentRun.js";
45
+ import { validatePluginIntent, validatePluginIntentFailure } from "../plugins/pluginIntent.js";
46
+ import { validatePluginValidation } from "../plugins/pluginPackage.js";
47
+ import { generateHomeIdentity, validateHomeIdentity } from "../repository/homeIdentity.js";
48
+ import { validateProject } from "../repository/project.js";
49
+ import { validateEnvironmentPreparation, validateLocalResource } from "../resources/projectResource.js";
50
+ import { validateReviewRound } from "../review/reviewRound.js";
50
51
  import { compareRuntimeSessionCandidates, projectRuntimeSessionCandidate } from "../runtime/runtimeSessionCandidate.js";
51
52
  import { sessionOwnerProcessKey } from "../runtime/sessionOwnerIdentity.js";
52
- import { validateReviewRound } from "../review/reviewRound.js";
53
- import { validateProject } from "../repository/project.js";
54
- import { generateHomeIdentity, validateHomeIdentity } from "../repository/homeIdentity.js";
55
- import { validateIntegrationQueueEntry } from "../integration/integrationQueueEntry.js";
56
- import { validDurableJobTransition, validateDurableJob } from "../job/durableJob.js";
57
- import { validateGlobalRole, validateTaskRole } from "../role/role.js";
58
53
  import { validateTaskWake } from "../scheduler/taskWake.js";
59
- import { validateTask } from "../task/task.js";
60
54
  import { pendingCompletionMessages } from "../task/completionReadiness.js";
61
- import { operationalTaskRecords, TASK_RECORD_RETIRED_EVENT } from "../task/taskRecordRetirement.js";
55
+ import { publicationExternalKey } from "../task/publicationReference.js";
56
+ import { validateTask } from "../task/task.js";
62
57
  import { TASK_RECORD_ID_PREFIXES } from "../task/taskRecordReference.js";
58
+ import { operationalTaskRecords, TASK_RECORD_RETIRED_EVENT } from "../task/taskRecordRetirement.js";
59
+ import { writeTelemetryBatch } from "../telemetry/sqliteTelemetryBatch.js";
60
+ import { STORED_RECORD_TABLES, validateStoredRecord } from "./recordValidation.js";
61
+ import { gateArtifactKey, validateGateArtifact } from "../verification/gateArtifact.js";
63
62
  import { validateWorkItem } from "../workItem/workItem.js";
64
63
  import { managedWorkspaceKey } from "../worktree/managedWorkspace.js";
65
- import { CURRENT_CONFIG_SCHEMA_VERSION, CURRENT_WORK_MAILBOX_SCHEMA_VERSION, executionLaneActiveRunKey, executionLaneActiveRunKeyParts, StorageConflictError, StorageCancelledError, StorageRecordError, storedCapabilityGrant, storedPublicationReference, storedReleaseWorkflow, isValidCapabilityGrantTransition, isValidReleaseWorkflowTransition, pendingWakeupProjection, validateYuiConfig } from "./taskStore.js";
66
- import { publicationExternalKey } from "../task/publicationReference.js";
67
- import { gateArtifactKey, validateGateArtifact } from "../verification/gateArtifact.js";
64
+ import { contextInputReferences, queryContextRecords } from "./contextRecords.js";
68
65
  import { inspectSqliteSchemaMigrations, migrateSqliteSchema, SqliteSchemaMigrationError, TELEMETRY_KEEP_PER_RUN, TELEMETRY_RUN_CAP } from "./sqliteSchema.js";
69
66
  import { StorageSchemaError } from "./storageSchema.js";
67
+ import { queryTaskCatalog } from "./taskCatalog.js";
68
+ import { CURRENT_CONFIG_SCHEMA_VERSION, CURRENT_WORK_MAILBOX_SCHEMA_VERSION, executionLaneActiveRunKey, executionLaneActiveRunKeyParts, isValidCapabilityGrantTransition, isValidReleaseWorkflowTransition, pendingWakeupProjection, StorageCancelledError, StorageConflictError, StorageRecordError, storedCapabilityGrant, storedPublicationReference, storedReleaseWorkflow, validateYuiConfig } from "./taskStore.js";
70
69
  /** Read the immutable Home identity without opening a writable Store connection. */
71
70
  export function readSqliteHomeIdentity(rootDir, databaseFilename = "yui.db") {
72
71
  const database = new Database(join(rootDir, databaseFilename), {
@@ -221,7 +220,75 @@ export class SqliteTaskStore {
221
220
  }
222
221
  #now() { return new Date().toISOString(); }
223
222
  #json(value) { return JSON.stringify(value); }
223
+ #recordJson(table, value) {
224
+ validateStoredRecord(table, value);
225
+ return this.#json(value);
226
+ }
227
+ /** Explicit full-Home diagnosis; ordinary reads validate only their records. */
228
+ validateCurrentRecords() {
229
+ this.getConfig();
230
+ for (const table of STORED_RECORD_TABLES) {
231
+ for (const row of this.#db.prepare(`SELECT payload FROM ${table}`).iterate()) {
232
+ this.#validateTaskPayload(table, row.payload);
233
+ }
234
+ }
235
+ for (const row of this.#db.prepare("SELECT brief FROM task_records WHERE brief IS NOT NULL").iterate()) {
236
+ validateTaskBrief(this.#parse(row.brief));
237
+ }
238
+ }
224
239
  #parse(text) { return JSON.parse(text); }
240
+ queryContextRecords(taskId, query) {
241
+ return queryContextRecords(this.#db, taskId, query);
242
+ }
243
+ contextInputReferences(taskId, scope) {
244
+ return contextInputReferences(this.#db, taskId, scope);
245
+ }
246
+ listActiveRuns(taskId) {
247
+ return this.#sortById(this.#listPayload("turns", "task_id = ? AND status = 'active'", [taskId]), run => run.id);
248
+ }
249
+ latestEventSequence(taskId) {
250
+ return this.#db.prepare("SELECT COALESCE(MAX(CAST(substr(event_id, 7) AS INTEGER)), 0) AS seq FROM events WHERE task_id = ?").get(taskId).seq;
251
+ }
252
+ listEventsByType(taskId, types) {
253
+ return this.#sortById(this.#listPayload("events", "task_id = ? AND type IN (SELECT value FROM json_each(?))", [taskId, JSON.stringify(types)]), event => event.id);
254
+ }
255
+ /** Delivery needs original workspace bases, never complete Run reports. */
256
+ listTaskRunWorkspaceBases(taskId) {
257
+ return this.#db.prepare(`SELECT json_object(
258
+ 'id', turn_id, 'createdAt', json_extract(payload, '$.createdAt'),
259
+ 'workspace', json_extract(payload, '$.workspace')) AS payload
260
+ FROM turns WHERE task_id = ? AND json_extract(payload, '$.workspace.owner.type') = 'task'`).all(taskId).map(row => JSON.parse(row.payload));
261
+ }
262
+ /** A pinned read snapshot never reserves the database's single writer slot.
263
+ * Nested reads share an existing transaction; standalone reads also prohibit
264
+ * accidental writes at SQLite's boundary.
265
+ */
266
+ readTransaction(execute) {
267
+ if (this.#inTransaction)
268
+ return execute(this);
269
+ const previous = this.#db.pragma("query_only", { simple: true });
270
+ this.#db.pragma("query_only = ON");
271
+ this.#db.exec("BEGIN");
272
+ this.#inTransaction = true;
273
+ try {
274
+ this.#prepareWrite();
275
+ const result = execute(this);
276
+ this.#commit();
277
+ return result;
278
+ }
279
+ catch (error) {
280
+ this.#rollback();
281
+ throw error;
282
+ }
283
+ finally {
284
+ this.#db.pragma(`query_only = ${previous === 1 ? "ON" : "OFF"}`);
285
+ }
286
+ }
287
+ /** Diagnostic worker write; deliberately outside Task revision/outbox state. */
288
+ flushTelemetry(entries, runCap) {
289
+ this.#prepareWrite();
290
+ writeTelemetryBatch(this.#db, entries, runCap);
291
+ }
225
292
  #begin() {
226
293
  this.#db.exec("BEGIN IMMEDIATE");
227
294
  this.#inTransaction = true;
@@ -535,22 +602,7 @@ export class SqliteTaskStore {
535
602
  }
536
603
  #validateTaskPayload(table, payload) {
537
604
  const record = this.#parse(payload);
538
- if (table === "task_records")
539
- validateTask(record);
540
- if (table === "work_items")
541
- validateWorkItem(record);
542
- // Agents and Roles carry the execution component, and their validators
543
- // refuse a record that lost it. Reading them unchecked defeated that: a
544
- // component-less row reached display and rendered `undefined` as if it
545
- // were a product, and reached launch as a binding nothing had vetted.
546
- if (table === "configured_agents")
547
- validateConfiguredAgent(record);
548
- if (table === "task_roles")
549
- validateTaskRole(record);
550
- if (table === "global_roles")
551
- validateGlobalRole(record);
552
- if (table === "global_role_messages")
553
- validateGlobalRoleMessage(record);
605
+ validateStoredRecord(table, record);
554
606
  return record;
555
607
  }
556
608
  #sortById(rows, idOf) {
@@ -582,12 +634,12 @@ export class SqliteTaskStore {
582
634
  saveConfiguredAgent(agent) {
583
635
  this.#mutate(() => {
584
636
  this.#db.prepare(`INSERT INTO configured_agents (id, payload, updated_at) VALUES (?, ?, ?)
585
- ON CONFLICT(id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(agent.id, this.#json(agent), this.#now());
637
+ ON CONFLICT(id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(agent.id, this.#recordJson("configured_agents", agent), this.#now());
586
638
  });
587
639
  }
588
640
  createConfiguredAgentIfAbsent(agent) {
589
641
  return this.#mutate(() => {
590
- const result = this.#db.prepare("INSERT OR IGNORE INTO configured_agents (id, payload, updated_at) VALUES (?, ?, ?)").run(agent.id, this.#json(agent), this.#now());
642
+ const result = this.#db.prepare("INSERT OR IGNORE INTO configured_agents (id, payload, updated_at) VALUES (?, ?, ?)").run(agent.id, this.#recordJson("configured_agents", agent), this.#now());
591
643
  return result.changes > 0 ? agent : null;
592
644
  });
593
645
  }
@@ -627,7 +679,7 @@ export class SqliteTaskStore {
627
679
  return;
628
680
  }
629
681
  this.#db.prepare("INSERT INTO plugin_validations (task_id, id, payload) VALUES (?, ?, ?)")
630
- .run(validation.taskId, validation.id, this.#json(validation));
682
+ .run(validation.taskId, validation.id, this.#recordJson("plugin_validations", validation));
631
683
  });
632
684
  }
633
685
  savePluginIntent(intent) {
@@ -644,7 +696,7 @@ export class SqliteTaskStore {
644
696
  }
645
697
  this.#db.prepare(`INSERT INTO plugin_intents (task_id, plugin_id, payload) VALUES (?, ?, ?)
646
698
  ON CONFLICT(task_id, plugin_id) DO UPDATE SET payload = excluded.payload`)
647
- .run(intent.taskId, intent.pluginId, this.#json(intent));
699
+ .run(intent.taskId, intent.pluginId, this.#recordJson("plugin_intents", intent));
648
700
  });
649
701
  }
650
702
  getPluginIntent(taskId, pluginId) {
@@ -662,7 +714,7 @@ export class SqliteTaskStore {
662
714
  return false;
663
715
  this.#mutate(() => {
664
716
  this.#db.prepare("UPDATE plugin_intents SET payload = ? WHERE task_id = ? AND plugin_id = ?")
665
- .run(this.#json({ ...current, lastFailure: failure }), taskId, pluginId);
717
+ .run(this.#recordJson("plugin_intents", { ...current, lastFailure: failure }), taskId, pluginId);
666
718
  });
667
719
  return true;
668
720
  });
@@ -681,7 +733,7 @@ export class SqliteTaskStore {
681
733
  return;
682
734
  }
683
735
  this.#db.prepare("INSERT INTO local_resources (id, canonical_identity, payload) VALUES (?, ?, ?)")
684
- .run(resource.id, `${resource.device}:${resource.inode}`, this.#json(resource));
736
+ .run(resource.id, `${resource.device}:${resource.inode}`, this.#recordJson("local_resources", resource));
685
737
  });
686
738
  }
687
739
  getLocalResource(resourceId) {
@@ -709,7 +761,7 @@ export class SqliteTaskStore {
709
761
  }
710
762
  this.#db.prepare(`INSERT INTO environment_preparations (task_id, id, payload) VALUES (?, ?, ?)
711
763
  ON CONFLICT(task_id, id) DO UPDATE SET payload = excluded.payload`)
712
- .run(preparation.taskId, preparation.id, this.#json(preparation));
764
+ .run(preparation.taskId, preparation.id, this.#recordJson("environment_preparations", preparation));
713
765
  });
714
766
  }
715
767
  getEnvironmentPreparation(taskId, preparationId) {
@@ -725,13 +777,13 @@ export class SqliteTaskStore {
725
777
  validateProject(project);
726
778
  this.#mutate(() => {
727
779
  this.#db.prepare(`INSERT INTO projects (id, name, path, payload, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)
728
- ON CONFLICT(id) DO UPDATE SET name = excluded.name, path = excluded.path, payload = excluded.payload, updated_at = excluded.updated_at`).run(project.id, project.name, project.path, this.#json(project), project.createdAt, project.updatedAt);
780
+ ON CONFLICT(id) DO UPDATE SET name = excluded.name, path = excluded.path, payload = excluded.payload, updated_at = excluded.updated_at`).run(project.id, project.name, project.path, this.#recordJson("projects", project), project.createdAt, project.updatedAt);
729
781
  });
730
782
  }
731
783
  createProjectIfAbsent(project) {
732
784
  validateProject(project);
733
785
  return this.#mutate(() => {
734
- const result = this.#db.prepare("INSERT OR IGNORE INTO projects (id, name, path, payload, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)").run(project.id, project.name, project.path, this.#json(project), project.createdAt, project.updatedAt);
786
+ const result = this.#db.prepare("INSERT OR IGNORE INTO projects (id, name, path, payload, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)").run(project.id, project.name, project.path, this.#recordJson("projects", project), project.createdAt, project.updatedAt);
735
787
  return result.changes > 0 ? project : null;
736
788
  });
737
789
  }
@@ -785,12 +837,12 @@ export class SqliteTaskStore {
785
837
  saveAgentProfile(profile) {
786
838
  this.#mutate(() => {
787
839
  this.#db.prepare(`INSERT INTO agent_profiles (id, payload, updated_at) VALUES (?, ?, ?)
788
- ON CONFLICT(id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(profile.id, this.#json(profile), this.#now());
840
+ ON CONFLICT(id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(profile.id, this.#recordJson("agent_profiles", profile), this.#now());
789
841
  });
790
842
  }
791
843
  createAgentProfileIfAbsent(profile) {
792
844
  return this.#mutate(() => {
793
- const result = this.#db.prepare("INSERT OR IGNORE INTO agent_profiles (id, payload, updated_at) VALUES (?, ?, ?)").run(profile.id, this.#json(profile), this.#now());
845
+ const result = this.#db.prepare("INSERT OR IGNORE INTO agent_profiles (id, payload, updated_at) VALUES (?, ?, ?)").run(profile.id, this.#recordJson("agent_profiles", profile), this.#now());
794
846
  return result.changes > 0 ? profile : null;
795
847
  });
796
848
  }
@@ -807,16 +859,16 @@ export class SqliteTaskStore {
807
859
  saveGlobalRole(role) {
808
860
  this.#mutate(() => {
809
861
  this.#db.prepare(`INSERT INTO global_roles (name, payload, updated_at) VALUES (?, ?, ?)
810
- ON CONFLICT(name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(role.name, this.#json(role), this.#now());
862
+ ON CONFLICT(name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(role.name, this.#recordJson("global_roles", role), this.#now());
811
863
  });
812
864
  }
813
865
  saveGlobalRoleWithSessionSet(role, sessions) {
814
866
  this.#mutate(() => {
815
867
  this.#db.prepare(`INSERT INTO global_roles (name, payload, updated_at) VALUES (?, ?, ?)
816
- ON CONFLICT(name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(role.name, this.#json(role), this.#now());
868
+ ON CONFLICT(name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(role.name, this.#recordJson("global_roles", role), this.#now());
817
869
  if (sessions !== null) {
818
870
  this.#db.prepare(`INSERT INTO global_role_session_sets (name, payload, updated_at) VALUES (?, ?, ?)
819
- ON CONFLICT(name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(role.name, this.#json(sessions), this.#now());
871
+ ON CONFLICT(name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(role.name, this.#recordJson("global_role_session_sets", sessions), this.#now());
820
872
  this.#saveRuntimeSessionCandidate(sessions);
821
873
  }
822
874
  else {
@@ -827,7 +879,7 @@ export class SqliteTaskStore {
827
879
  }
828
880
  createGlobalRoleIfAbsent(role) {
829
881
  return this.#mutate(() => {
830
- const result = this.#db.prepare("INSERT OR IGNORE INTO global_roles (name, payload, updated_at) VALUES (?, ?, ?)").run(role.name, this.#json(role), this.#now());
882
+ const result = this.#db.prepare("INSERT OR IGNORE INTO global_roles (name, payload, updated_at) VALUES (?, ?, ?)").run(role.name, this.#recordJson("global_roles", role), this.#now());
831
883
  return result.changes > 0 ? role : null;
832
884
  });
833
885
  }
@@ -854,7 +906,7 @@ export class SqliteTaskStore {
854
906
  saveGlobalRoleSessionSet(sessions) {
855
907
  this.#mutate(() => {
856
908
  this.#db.prepare(`INSERT INTO global_role_session_sets (name, payload, updated_at) VALUES (?, ?, ?)
857
- ON CONFLICT(name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(sessions.owner.roleName, this.#json(sessions), this.#now());
909
+ ON CONFLICT(name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(sessions.owner.roleName, this.#recordJson("global_role_session_sets", sessions), this.#now());
858
910
  this.#saveRuntimeSessionCandidate(sessions);
859
911
  });
860
912
  }
@@ -865,7 +917,7 @@ export class SqliteTaskStore {
865
917
  this.#requireGlobalRole(message.roleName);
866
918
  this.#mutate(() => {
867
919
  const seq = this.#globalMessageSequence(message.id);
868
- this.#db.prepare(`INSERT INTO global_role_messages (name, message_id, seq, payload, created_at) VALUES (?, ?, ?, ?, ?)`).run(message.roleName, message.id, seq, this.#json(message), message.createdAt);
920
+ this.#db.prepare(`INSERT INTO global_role_messages (name, message_id, seq, payload, created_at) VALUES (?, ?, ?, ?, ?)`).run(message.roleName, message.id, seq, this.#recordJson("global_role_messages", message), message.createdAt);
869
921
  });
870
922
  }
871
923
  listGlobalRoleMessages(roleName) {
@@ -876,7 +928,7 @@ export class SqliteTaskStore {
876
928
  this.#requireGlobalRole(message.roleName);
877
929
  this.#mutate(() => {
878
930
  const seq = this.#globalMessageSequence(message.id);
879
- const result = this.#db.prepare(`UPDATE global_role_messages SET seq = ?, payload = ? WHERE name = ? AND message_id = ?`).run(seq, this.#json(message), message.roleName, message.id);
931
+ const result = this.#db.prepare(`UPDATE global_role_messages SET seq = ?, payload = ? WHERE name = ? AND message_id = ?`).run(seq, this.#recordJson("global_role_messages", message), message.roleName, message.id);
880
932
  if (result.changes === 0) {
881
933
  throw new StorageRecordError(`Global message not found: ${message.roleName}/${message.id}`);
882
934
  }
@@ -914,13 +966,22 @@ export class SqliteTaskStore {
914
966
  ON CONFLICT(task_id) DO UPDATE SET status = excluded.status, lifecycle = excluded.lifecycle,
915
967
  is_active = excluded.is_active, updated_at = excluded.updated_at`).run(task.id, task.status, task.status, isActive, task.createdAt, task.updatedAt);
916
968
  this.#db.prepare(`INSERT INTO task_records (task_id, payload, updated_at) VALUES (?, ?, ?)
917
- ON CONFLICT(task_id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(task.id, this.#json(task), this.#now());
969
+ ON CONFLICT(task_id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(task.id, this.#recordJson("task_records", task), this.#now());
918
970
  });
919
971
  }
920
972
  listTasks() {
921
973
  const tasks = this.#listPayload("task_records", "1=1", []);
922
974
  return this.#sortById(tasks, (task) => task.id);
923
975
  }
976
+ queryTaskCatalog(query) {
977
+ return queryTaskCatalog(this.#db, query);
978
+ }
979
+ listTaskChoices() {
980
+ const rows = this.#db.prepare(`SELECT c.task_id AS id, c.status,
981
+ json_extract(r.payload, '$.title') AS title
982
+ FROM tasks_catalog c JOIN task_records r ON r.task_id = c.task_id`).all();
983
+ return this.#sortById(rows, task => task.id);
984
+ }
924
985
  getTask(id) {
925
986
  return this.#getPayload("task_records", "task_id = ?", [id]);
926
987
  }
@@ -950,7 +1011,6 @@ export class SqliteTaskStore {
950
1011
  integrationJobs: this.listDurableJobs(taskId)
951
1012
  .filter(job => job.owner.kind === "integration-attempt")
952
1013
  .map(job => ({ id: job.id, status: job.status })),
953
- integrationQueueEntries: this.#sortById(this.#listPayload("integration_queue", "task_id = ?", [taskId]), (entry) => entry.id),
954
1014
  reviewRounds: this.#sortById(this.#listPayload("review_rounds", "task_id = ?", [taskId]), (round) => round.id),
955
1015
  reviewConfig: this.getReviewConfig(),
956
1016
  openInputRequests: this.#sortById(this.#listPayload("input_requests", "task_id = ? AND status = 'open'", [taskId]), (request) => request.id),
@@ -970,7 +1030,6 @@ export class SqliteTaskStore {
970
1030
  pendingUserMessages: pendingCompletionMessages(this, taskId),
971
1031
  managedWorkspaces: this.#sortById(this.#listPayload("managed_workspaces", "task_id = ?", [taskId]), (workspace) => managedWorkspaceKey(workspace.owner)),
972
1032
  durableJobs: this.#sortById(this.#listPayload("durable_jobs", "task_id = ?", [taskId]), (job) => job.id),
973
- integrationQueueEntries: this.#sortById(this.#listPayload("integration_queue", "task_id = ?", [taskId]), (entry) => entry.id),
974
1033
  };
975
1034
  }
976
1035
  /** Task ids flagged active in the catalog projection (the global active index). */
@@ -1035,7 +1094,7 @@ export class SqliteTaskStore {
1035
1094
  this.#db.prepare(`INSERT INTO change_sets (task_id, change_set_id, project_id, head_sha, payload, created_at)
1036
1095
  VALUES (?, ?, ?, ?, ?, ?)
1037
1096
  ON CONFLICT(task_id, change_set_id) DO UPDATE SET project_id = excluded.project_id,
1038
- head_sha = excluded.head_sha, payload = excluded.payload`).run(taskId, changeSet.id, changeSet.projectId, changeSet.headCommit, this.#json(changeSet), changeSet.createdAt);
1097
+ head_sha = excluded.head_sha, payload = excluded.payload`).run(taskId, changeSet.id, changeSet.projectId, changeSet.headCommit, this.#recordJson("change_sets", changeSet), changeSet.createdAt);
1039
1098
  });
1040
1099
  }
1041
1100
  listChangeSets(taskId) {
@@ -1051,10 +1110,16 @@ export class SqliteTaskStore {
1051
1110
  throw new StorageRecordError(`Integration attempt belongs to another Task: ${attempt.taskId}`);
1052
1111
  this.#requireTask(taskId);
1053
1112
  this.#mutate(() => {
1113
+ if (typeof attempt.rerunChecks !== "boolean")
1114
+ throw new StorageRecordError("Integration rerunChecks must be explicit.");
1115
+ const existing = this.getIntegrationAttempt(taskId, attempt.id);
1116
+ if (existing !== null && existing.rerunChecks !== attempt.rerunChecks) {
1117
+ throw new StorageRecordError("Integration check execution intent is immutable; create a new attempt.");
1118
+ }
1054
1119
  this.#db.prepare(`INSERT INTO integration_attempts (task_id, integration_id, status, payload, updated_at)
1055
1120
  VALUES (?, ?, ?, ?, ?)
1056
1121
  ON CONFLICT(task_id, integration_id) DO UPDATE SET status = excluded.status,
1057
- payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, attempt.id, attempt.status, this.#json(attempt), this.#now());
1122
+ payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, attempt.id, attempt.status, this.#recordJson("integration_attempts", attempt), this.#now());
1058
1123
  });
1059
1124
  }
1060
1125
  listIntegrationAttempts(taskId) {
@@ -1063,46 +1128,6 @@ export class SqliteTaskStore {
1063
1128
  getIntegrationAttempt(taskId, integrationId) {
1064
1129
  return this.#getPayload("integration_attempts", "task_id = ? AND integration_id = ?", [taskId, integrationId]);
1065
1130
  }
1066
- // -- integration queue -----------------------------------------------------
1067
- nextIntegrationQueueEntryId(taskId) {
1068
- return this.#nextTaskRecordId(taskId, "integrationQueue");
1069
- }
1070
- saveIntegrationQueueEntry(taskId, entry) {
1071
- if (entry.taskId !== taskId) {
1072
- throw new StorageRecordError(`Integration queue entry belongs to another Task: ${entry.taskId}`);
1073
- }
1074
- validateIntegrationQueueEntry(entry);
1075
- this.#requireTask(taskId);
1076
- const changeSet = this.getChangeSet(taskId, entry.changeSetId);
1077
- if (changeSet === null) {
1078
- throw new StorageRecordError(`Integration queue ChangeSet not found: ${entry.changeSetId}`);
1079
- }
1080
- if (changeSet.projectId !== entry.projectId) {
1081
- throw new StorageRecordError(`Integration queue ChangeSet belongs to another Project: ${entry.changeSetId}`);
1082
- }
1083
- const existing = this.getIntegrationQueueEntry(taskId, entry.id);
1084
- if (existing !== null) {
1085
- if (Date.parse(entry.updatedAt) < Date.parse(existing.updatedAt)) {
1086
- throw new StorageRecordError(`Integration queue entry updatedAt cannot move backwards: ${entry.id}`);
1087
- }
1088
- if (!validIntegrationQueueTransition(existing, entry)) {
1089
- throw new StorageRecordError(`Integration queue entry transition is invalid: ${entry.id}`);
1090
- }
1091
- }
1092
- this.#mutate(() => {
1093
- this.#db.prepare(`INSERT INTO integration_queue (queue_id, task_id, project_id, change_set, status, payload, created_at, updated_at)
1094
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
1095
- ON CONFLICT(queue_id) DO UPDATE SET task_id = excluded.task_id,
1096
- project_id = excluded.project_id, change_set = excluded.change_set,
1097
- status = excluded.status, payload = excluded.payload, updated_at = excluded.updated_at`).run(entry.id, taskId, entry.projectId, entry.changeSetId, entry.status, this.#json(entry), entry.createdAt, entry.updatedAt);
1098
- });
1099
- }
1100
- listIntegrationQueueEntries(taskId) {
1101
- return this.#sortById(this.#listPayload("integration_queue", "task_id = ?", [taskId]), (entry) => entry.id);
1102
- }
1103
- getIntegrationQueueEntry(taskId, entryId) {
1104
- return this.#getPayload("integration_queue", "task_id = ? AND queue_id = ?", [taskId, entryId]);
1105
- }
1106
1131
  // -- durable jobs -----------------------------------------------------------
1107
1132
  nextDurableJobId(taskId) {
1108
1133
  return this.#nextTaskRecordId(taskId, "durableJob");
@@ -1127,7 +1152,7 @@ export class SqliteTaskStore {
1127
1152
  VALUES (?, ?, ?, ?, ?, ?, ?)
1128
1153
  ON CONFLICT(task_id, job_id) DO UPDATE SET idempotency_key = excluded.idempotency_key,
1129
1154
  status = excluded.status,
1130
- payload = excluded.payload, updated_at = excluded.updated_at`).run(job.id, taskId, job.idempotencyKey ?? null, job.status, this.#json(job), job.createdAt, job.updatedAt);
1155
+ payload = excluded.payload, updated_at = excluded.updated_at`).run(job.id, taskId, job.idempotencyKey ?? null, job.status, this.#recordJson("durable_jobs", job), job.createdAt, job.updatedAt);
1131
1156
  });
1132
1157
  }
1133
1158
  listDurableJobs(taskId) {
@@ -1159,12 +1184,12 @@ export class SqliteTaskStore {
1159
1184
  role_name = excluded.role_name, agent_id = excluded.agent_id,
1160
1185
  native_session_id = excluded.native_session_id,
1161
1186
  provider_root_pid = excluded.provider_root_pid,
1162
- payload = excluded.payload, recorded_at = excluded.recorded_at`).run(sessionOwnerProcessKey(identity), owner.scope, owner.scope === "task" ? owner.taskId : null, owner.roleName, identity.agentId, identity.nativeSessionId ?? null, identity.providerRoot.pid, this.#json(identity), identity.recordedAt);
1187
+ payload = excluded.payload, recorded_at = excluded.recorded_at`).run(sessionOwnerProcessKey(identity), owner.scope, owner.scope === "task" ? owner.taskId : null, owner.roleName, identity.agentId, identity.nativeSessionId ?? null, identity.providerRoot.pid, this.#recordJson("session_owners", identity), identity.recordedAt);
1163
1188
  });
1164
1189
  }
1165
1190
  getSessionOwner(processKey) {
1166
1191
  const row = this.#db.prepare("SELECT payload FROM session_owners WHERE process_key = ?").get(processKey);
1167
- return row === undefined ? null : this.#parse(row.payload);
1192
+ return row === undefined ? null : this.#validateTaskPayload("session_owners", row.payload);
1168
1193
  }
1169
1194
  listSessionOwners() {
1170
1195
  return this.#listPayload("session_owners", "1=1", []).sort((left, right) => left.recordedAt.localeCompare(right.recordedAt));
@@ -1185,7 +1210,7 @@ export class SqliteTaskStore {
1185
1210
  this.#requireTask(taskId);
1186
1211
  this.#mutate(() => {
1187
1212
  this.#db.prepare(`INSERT INTO task_roles (task_id, role_name, payload, updated_at) VALUES (?, ?, ?, ?)
1188
- ON CONFLICT(task_id, role_name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, role.name, this.#json(role), this.#now());
1213
+ ON CONFLICT(task_id, role_name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, role.name, this.#recordJson("task_roles", role), this.#now());
1189
1214
  });
1190
1215
  }
1191
1216
  listRoles(taskId) {
@@ -1198,9 +1223,9 @@ export class SqliteTaskStore {
1198
1223
  this.#requireTask(role.name ? role.taskId : sessions.owner.taskId);
1199
1224
  this.#mutate(() => {
1200
1225
  this.#db.prepare(`INSERT INTO task_roles (task_id, role_name, payload, updated_at) VALUES (?, ?, ?, ?)
1201
- ON CONFLICT(task_id, role_name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(sessions.owner.taskId, role.name, this.#json(role), this.#now());
1226
+ ON CONFLICT(task_id, role_name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(sessions.owner.taskId, role.name, this.#recordJson("task_roles", role), this.#now());
1202
1227
  this.#db.prepare(`INSERT INTO role_session_sets (task_id, role_name, payload, updated_at) VALUES (?, ?, ?, ?)
1203
- ON CONFLICT(task_id, role_name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(sessions.owner.taskId, sessions.owner.roleName, this.#json(sessions), this.#now());
1228
+ ON CONFLICT(task_id, role_name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(sessions.owner.taskId, sessions.owner.roleName, this.#recordJson("role_session_sets", sessions), this.#now());
1204
1229
  this.#saveRuntimeSessionCandidate(sessions);
1205
1230
  });
1206
1231
  }
@@ -1226,7 +1251,7 @@ export class SqliteTaskStore {
1226
1251
  this.#db.prepare(`INSERT INTO managed_workspaces (owner_kind, owner_id, task_id, path, payload, status, created_at, updated_at)
1227
1252
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)
1228
1253
  ON CONFLICT(owner_kind, owner_id) DO UPDATE SET task_id = excluded.task_id, path = excluded.path,
1229
- payload = excluded.payload, status = excluded.status, updated_at = excluded.updated_at`).run(workspace.owner.type, ownerId, taskId, workspace.root, this.#json(workspace), "active", workspace.createdAt, workspace.updatedAt);
1254
+ payload = excluded.payload, status = excluded.status, updated_at = excluded.updated_at`).run(workspace.owner.type, ownerId, taskId, workspace.root, this.#recordJson("managed_workspaces", workspace), "active", workspace.createdAt, workspace.updatedAt);
1230
1255
  });
1231
1256
  }
1232
1257
  listManagedWorkspaces(taskId) {
@@ -1267,6 +1292,13 @@ export class SqliteTaskStore {
1267
1292
  listRoleSessionSets(taskId) {
1268
1293
  return this.#sortById(this.#listPayload("role_session_sets", "task_id = ?", [taskId]), (set) => set.owner.roleName);
1269
1294
  }
1295
+ listProviderRetrySessions() {
1296
+ const pending = "json_extract(payload, '$.providerBinding.retry.status') IN ('waiting', 'in-flight')";
1297
+ return [
1298
+ ...this.#listPayload("role_session_sets", pending, []),
1299
+ ...this.#listPayload("global_role_session_sets", pending, [])
1300
+ ];
1301
+ }
1270
1302
  listRuntimeSessionCandidates(query = {}) {
1271
1303
  const taskIds = query.taskIds === undefined
1272
1304
  ? undefined
@@ -1373,7 +1405,7 @@ export class SqliteTaskStore {
1373
1405
  this.#requireTask(taskId);
1374
1406
  this.#mutate(() => {
1375
1407
  this.#db.prepare(`INSERT INTO role_session_sets (task_id, role_name, payload, updated_at) VALUES (?, ?, ?, ?)
1376
- ON CONFLICT(task_id, role_name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, sessions.owner.roleName, this.#json(sessions), this.#now());
1408
+ ON CONFLICT(task_id, role_name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, sessions.owner.roleName, this.#recordJson("role_session_sets", sessions), this.#now());
1377
1409
  this.#saveRuntimeSessionCandidate(sessions);
1378
1410
  });
1379
1411
  }
@@ -1424,7 +1456,7 @@ export class SqliteTaskStore {
1424
1456
  this.#mutate(() => {
1425
1457
  this.#db.prepare(`INSERT INTO work_items (task_id, work_item_id, status, payload, updated_at) VALUES (?, ?, ?, ?, ?)
1426
1458
  ON CONFLICT(task_id, work_item_id) DO UPDATE SET status = excluded.status,
1427
- payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, item.id, item.status, this.#json(item), this.#now());
1459
+ payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, item.id, item.status, this.#recordJson("work_items", item), this.#now());
1428
1460
  });
1429
1461
  }
1430
1462
  // -- capability grants ------------------------------------------------------
@@ -1449,7 +1481,7 @@ export class SqliteTaskStore {
1449
1481
  throw new StorageRecordError(`Capability grant cannot be overwritten: ${taskId}/${stored.id}`);
1450
1482
  }
1451
1483
  this.#db.prepare(`INSERT INTO capability_grants (task_id, grant_id, payload, updated_at) VALUES (?, ?, ?, ?)
1452
- ON CONFLICT(task_id, grant_id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, stored.id, this.#json(stored), this.#now());
1484
+ ON CONFLICT(task_id, grant_id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, stored.id, this.#recordJson("capability_grants", stored), this.#now());
1453
1485
  });
1454
1486
  }
1455
1487
  listCapabilityGrants(taskId) {
@@ -1472,7 +1504,7 @@ export class SqliteTaskStore {
1472
1504
  throw new StorageRecordError(`Release workflow cannot be overwritten: ${taskId}/${stored.id}`);
1473
1505
  }
1474
1506
  this.#db.prepare(`INSERT INTO release_workflows (task_id, workflow_id, payload, updated_at) VALUES (?, ?, ?, ?)
1475
- ON CONFLICT(task_id, workflow_id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, stored.id, this.#json(stored), this.#now());
1507
+ ON CONFLICT(task_id, workflow_id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, stored.id, this.#recordJson("release_workflows", stored), this.#now());
1476
1508
  });
1477
1509
  }
1478
1510
  listReleaseWorkflows(taskId) {
@@ -1519,7 +1551,7 @@ export class SqliteTaskStore {
1519
1551
  remote_commit, supersedes, payload, merged_at, created_at,
1520
1552
  title, source_branch, target_branch)
1521
1553
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1522
- ON CONFLICT(task_id, publication_id) DO UPDATE SET payload = excluded.payload`).run(taskId, stored.id, stored.projectId, stored.provider, stored.repository, stored.externalKind, stored.externalId, key, stored.state, stored.verification, stored.externalUrl ?? null, stored.localCommit ?? null, stored.remoteCommit ?? null, stored.supersedes ?? null, this.#json(stored), stored.mergedAt ?? null, this.#now(), stored.title ?? null, stored.sourceBranch ?? null, stored.targetBranch ?? null);
1554
+ ON CONFLICT(task_id, publication_id) DO UPDATE SET payload = excluded.payload`).run(taskId, stored.id, stored.projectId, stored.provider, stored.repository, stored.externalKind, stored.externalId, key, stored.state, stored.verification, stored.externalUrl ?? null, stored.localCommit ?? null, stored.remoteCommit ?? null, stored.supersedes ?? null, this.#recordJson("publication_references", stored), stored.mergedAt ?? null, this.#now(), stored.title ?? null, stored.sourceBranch ?? null, stored.targetBranch ?? null);
1523
1555
  });
1524
1556
  }
1525
1557
  listPublicationReferences(taskId) {
@@ -1536,7 +1568,7 @@ export class SqliteTaskStore {
1536
1568
  saveGateArtifact(artifact, logs) {
1537
1569
  validateGateArtifact(artifact);
1538
1570
  this.#mutate(() => {
1539
- const targetRef = artifact.boundary?.targetRef ?? null;
1571
+ const targetRef = artifact.boundary.targetRef;
1540
1572
  const completedAt = artifact.completedAt ?? null;
1541
1573
  this.#db.prepare(`INSERT INTO gate_artifacts
1542
1574
  (key, project_id, level, commit_sha, plan_digest, toolchain_digest,
@@ -1552,8 +1584,9 @@ export class SqliteTaskStore {
1552
1584
  status = excluded.status,
1553
1585
  outcome = excluded.outcome,
1554
1586
  payload = excluded.payload,
1587
+ created_at = excluded.created_at,
1555
1588
  completed_at = excluded.completed_at,
1556
- last_used_at = excluded.last_used_at`).run(artifact.key, artifact.projectId, artifact.level, artifact.commit, artifact.planDigest, artifact.toolchainDigest, targetRef, artifact.status, artifact.outcome, this.#json(artifact), artifact.createdAt, completedAt, artifact.lastUsedAt);
1589
+ last_used_at = excluded.last_used_at`).run(artifact.key, artifact.projectId, artifact.level, artifact.commit, artifact.planDigest, artifact.toolchainDigest, targetRef, artifact.status, artifact.outcome, this.#recordJson("gate_artifacts", artifact), artifact.createdAt, completedAt, artifact.lastUsedAt);
1557
1590
  // Replace all logs for this artifact in the same transaction.
1558
1591
  this.#db.prepare("DELETE FROM gate_artifact_logs WHERE artifact_key = ?").run(artifact.key);
1559
1592
  const insertLog = this.#db.prepare(`INSERT INTO gate_artifact_logs (artifact_key, step_name, log_content, log_digest, log_bytes)
@@ -1570,7 +1603,15 @@ export class SqliteTaskStore {
1570
1603
  touchGateArtifact(artifact) {
1571
1604
  validateGateArtifact(artifact);
1572
1605
  this.#mutate(() => {
1573
- const result = this.#db.prepare(`UPDATE gate_artifacts SET payload = ?, last_used_at = ? WHERE key = ?`).run(this.#json(artifact), artifact.lastUsedAt, artifact.key);
1606
+ const current = this.getGateArtifact(artifact.projectId, artifact.key);
1607
+ if (current === null || !isDeepStrictEqual(current, {
1608
+ ...artifact, reuseCount: current.reuseCount, lastUsedAt: current.lastUsedAt
1609
+ })) {
1610
+ throw new StorageConflictError("Gate artifact changed while consuming evidence; retry the exact lookup.");
1611
+ }
1612
+ const updated = { ...current, reuseCount: Math.max(current.reuseCount, artifact.reuseCount),
1613
+ lastUsedAt: Date.parse(artifact.lastUsedAt) > Date.parse(current.lastUsedAt) ? artifact.lastUsedAt : current.lastUsedAt };
1614
+ const result = this.#db.prepare(`UPDATE gate_artifacts SET payload = ?, last_used_at = ? WHERE key = ?`).run(this.#recordJson("gate_artifacts", updated), updated.lastUsedAt, artifact.key);
1574
1615
  if (result.changes === 0) {
1575
1616
  throw new StorageRecordError(`Gate artifact not found for touch: ${artifact.key}`);
1576
1617
  }
@@ -1585,18 +1626,8 @@ export class SqliteTaskStore {
1585
1626
  }
1586
1627
  findL2GateArtifactsForCommit(query) {
1587
1628
  const rows = this.#listPayload("gate_artifacts", `project_id = ? AND commit_sha = ? AND level = 'L2'
1588
- AND plan_digest = ? AND toolchain_digest = ? AND target_ref = ?
1589
- AND status = 'complete' AND outcome = 'succeeded'`, [query.projectId, query.commit, query.planDigest, query.toolchainDigest, query.targetRef]);
1590
- const valid = [];
1591
- for (const row of rows) {
1592
- try {
1593
- valid.push(validateGateArtifact(row));
1594
- }
1595
- catch {
1596
- // Skip corrupt rows; a direct lookup fails closed.
1597
- }
1598
- }
1599
- return valid;
1629
+ AND plan_digest = ? AND toolchain_digest = ? AND target_ref = ?`, [query.projectId, query.commit, query.planDigest, query.toolchainDigest, query.targetRef]);
1630
+ return rows.map(validateGateArtifact);
1600
1631
  }
1601
1632
  getGateArtifactLogs(artifactKey) {
1602
1633
  const rows = this.#db.prepare("SELECT step_name, log_content FROM gate_artifact_logs WHERE artifact_key = ?").all(artifactKey);
@@ -1667,7 +1698,7 @@ export class SqliteTaskStore {
1667
1698
  this.#mutate(() => {
1668
1699
  this.#db.prepare(`INSERT INTO context_snapshots
1669
1700
  (task_id, snapshot_id, scope, scope_ref, sequence, digest, payload, frozen_at)
1670
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(stored.taskId, stored.id, stored.scope, stored.scopeRef ?? null, stored.sequence, stored.digest, this.#json(stored), stored.frozenAt);
1701
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(stored.taskId, stored.id, stored.scope, stored.scopeRef ?? null, stored.sequence, stored.digest, this.#recordJson("context_snapshots", stored), stored.frozenAt);
1671
1702
  });
1672
1703
  }
1673
1704
  // -- turns -----------------------------------------------------------------
@@ -1698,7 +1729,7 @@ export class SqliteTaskStore {
1698
1729
  this.#mutate(() => {
1699
1730
  this.#db.prepare(`INSERT INTO turns (task_id, turn_id, role_name, status, payload, updated_at) VALUES (?, ?, ?, ?, ?, ?)
1700
1731
  ON CONFLICT(task_id, turn_id) DO UPDATE SET role_name = excluded.role_name, status = excluded.status,
1701
- payload = excluded.payload, updated_at = excluded.updated_at`).run(run.taskId, run.id, run.roleName, run.status, this.#json(run), this.#now());
1732
+ payload = excluded.payload, updated_at = excluded.updated_at`).run(run.taskId, run.id, run.roleName, run.status, this.#recordJson("turns", run), this.#now());
1702
1733
  });
1703
1734
  }
1704
1735
  // -- review rounds ----------------------------------------------------------
@@ -1717,7 +1748,7 @@ export class SqliteTaskStore {
1717
1748
  this.#mutate(() => {
1718
1749
  this.#db.prepare(`INSERT INTO review_rounds (task_id, review_round_id, status, payload, updated_at) VALUES (?, ?, ?, ?, ?)
1719
1750
  ON CONFLICT(task_id, review_round_id) DO UPDATE SET status = excluded.status,
1720
- payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, round.id, round.status, this.#json(round), this.#now());
1751
+ payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, round.id, round.status, this.#recordJson("review_rounds", round), this.#now());
1721
1752
  });
1722
1753
  }
1723
1754
  // -- active turns ----------------------------------------------------------
@@ -1905,7 +1936,7 @@ export class SqliteTaskStore {
1905
1936
  this.#requireTask(taskId);
1906
1937
  this.#mutate(() => {
1907
1938
  const seq = this.#idSequence(message.id, "message");
1908
- this.#db.prepare(`INSERT INTO messages (task_id, message_id, seq, payload, created_at) VALUES (?, ?, ?, ?, ?)`).run(taskId, message.id, seq, this.#json(message), message.createdAt);
1939
+ this.#db.prepare(`INSERT INTO messages (task_id, message_id, seq, payload, created_at) VALUES (?, ?, ?, ?, ?)`).run(taskId, message.id, seq, this.#recordJson("messages", message), message.createdAt);
1909
1940
  this.#observeHighWater(taskId, "message", seq);
1910
1941
  });
1911
1942
  }
@@ -1915,7 +1946,7 @@ export class SqliteTaskStore {
1915
1946
  }
1916
1947
  this.#requireTask(taskId);
1917
1948
  this.#mutate(() => {
1918
- const result = this.#db.prepare(`UPDATE messages SET payload = ? WHERE task_id = ? AND message_id = ?`).run(this.#json(message), taskId, message.id);
1949
+ const result = this.#db.prepare(`UPDATE messages SET payload = ? WHERE task_id = ? AND message_id = ?`).run(this.#recordJson("messages", message), taskId, message.id);
1919
1950
  if (result.changes !== 1) {
1920
1951
  throw new StorageRecordError(`Message does not exist: ${taskId}/${message.id}`);
1921
1952
  }
@@ -1932,7 +1963,7 @@ export class SqliteTaskStore {
1932
1963
  this.#requireTask(taskId);
1933
1964
  this.#mutate(() => {
1934
1965
  this.#db.prepare(`INSERT INTO input_requests (task_id, input_id, status, payload, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)
1935
- ON CONFLICT(task_id, input_id) DO UPDATE SET status = excluded.status, payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, request.id, request.status, this.#json(request), request.createdAt, this.#now());
1966
+ ON CONFLICT(task_id, input_id) DO UPDATE SET status = excluded.status, payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, request.id, request.status, this.#recordJson("input_requests", request), request.createdAt, this.#now());
1936
1967
  });
1937
1968
  }
1938
1969
  getInputRequest(taskId, requestId) {
@@ -1963,7 +1994,7 @@ export class SqliteTaskStore {
1963
1994
  this.#requireTask(taskId);
1964
1995
  this.#mutate(() => {
1965
1996
  this.#db.prepare(`INSERT INTO decisions (task_id, decision_id, payload, created_at) VALUES (?, ?, ?, ?)
1966
- ON CONFLICT(task_id, decision_id) DO UPDATE SET payload = excluded.payload`).run(taskId, decision.id, this.#json(decision), decision.createdAt);
1997
+ ON CONFLICT(task_id, decision_id) DO UPDATE SET payload = excluded.payload`).run(taskId, decision.id, this.#recordJson("decisions", decision), decision.createdAt);
1967
1998
  });
1968
1999
  }
1969
2000
  listDecisions(taskId) {
@@ -1980,7 +2011,7 @@ export class SqliteTaskStore {
1980
2011
  this.#requireTask(taskId);
1981
2012
  this.#mutate(() => {
1982
2013
  this.#db.prepare(`INSERT INTO milestones (task_id, milestone_id, payload, created_at) VALUES (?, ?, ?, ?)
1983
- ON CONFLICT(task_id, milestone_id) DO UPDATE SET payload = excluded.payload`).run(taskId, milestone.id, this.#json(milestone), milestone.createdAt);
2014
+ ON CONFLICT(task_id, milestone_id) DO UPDATE SET payload = excluded.payload`).run(taskId, milestone.id, this.#recordJson("milestones", milestone), milestone.createdAt);
1984
2015
  });
1985
2016
  }
1986
2017
  listMilestones(taskId) {
@@ -1998,7 +2029,7 @@ export class SqliteTaskStore {
1998
2029
  this.#mutate(() => {
1999
2030
  const seq = this.#idSequence(event.id, "event");
2000
2031
  // Events are terminal/semantic: retained individually, never pruned (§9).
2001
- this.#db.prepare(`INSERT INTO events (task_id, event_id, type, occurred_at, payload) VALUES (?, ?, ?, ?, ?)`).run(taskId, event.id, event.type, event.createdAt, this.#json(event));
2032
+ this.#db.prepare(`INSERT INTO events (task_id, event_id, type, occurred_at, payload) VALUES (?, ?, ?, ?, ?)`).run(taskId, event.id, event.type, event.createdAt, this.#recordJson("events", event));
2002
2033
  this.#observeHighWater(taskId, "event", seq);
2003
2034
  });
2004
2035
  }
@@ -2029,14 +2060,13 @@ export class SqliteTaskStore {
2029
2060
  this.#mutate(() => {
2030
2061
  const seq = this.#idSequence(wake.id, "taskWake");
2031
2062
  this.#db.prepare(`INSERT INTO task_wakes
2032
- (task_id, wake_id, seq, status, turn_id, from_cursor, to_cursor, reasons, payload, created_at, consumed_at)
2033
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
2063
+ (task_id, wake_id, seq, status, from_cursor, to_cursor, reasons, payload, created_at, consumed_at)
2064
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
2034
2065
  + ` ON CONFLICT(task_id, wake_id) DO UPDATE SET
2035
2066
  status = excluded.status,
2036
- turn_id = excluded.turn_id,
2037
2067
  reasons = excluded.reasons,
2038
2068
  payload = excluded.payload,
2039
- consumed_at = excluded.consumed_at`).run(taskId, wake.id, wake.seq, wake.status, wake.runId ?? null, wake.fromCursor, wake.toCursor, this.#json([...wake.reasons]), this.#json(wake), wake.createdAt, wake.consumedAt ?? null);
2069
+ consumed_at = excluded.consumed_at`).run(taskId, wake.id, wake.seq, wake.status, wake.fromCursor, wake.toCursor, this.#json([...wake.reasons]), this.#recordJson("task_wakes", wake), wake.createdAt, wake.consumedAt ?? null);
2040
2070
  this.#observeHighWater(taskId, "taskWake", seq);
2041
2071
  });
2042
2072
  }
@@ -2298,25 +2328,6 @@ export class SqliteTaskStore {
2298
2328
  });
2299
2329
  }
2300
2330
  }
2301
- function validIntegrationQueueTransition(before, after) {
2302
- if (before.id !== after.id
2303
- || before.taskId !== after.taskId
2304
- || before.projectId !== after.projectId
2305
- || before.changeSetId !== after.changeSetId
2306
- || before.targetRef !== after.targetRef
2307
- || !isDeepStrictEqual(before.checkCommands, after.checkCommands)
2308
- || !isDeepStrictEqual(before.evidenceRefs, after.evidenceRefs)
2309
- || before.createdAt !== after.createdAt)
2310
- return false;
2311
- const allowed = {
2312
- queued: ["queued", "running", "superseded"],
2313
- running: ["running", "conflicted", "committed"],
2314
- conflicted: ["conflicted", "running", "committed", "queued", "superseded"],
2315
- committed: ["committed"],
2316
- superseded: ["superseded"]
2317
- };
2318
- return allowed[before.status].includes(after.status);
2319
- }
2320
2331
  /** The product storage backend is one current SQLite contract. */
2321
2332
  export function resolveTaskStoreBackendForHome(_home, _env = process.env) {
2322
2333
  return "sqlite";