@knpkv/herdr-work 0.5.1 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/store.js CHANGED
@@ -138,544 +138,436 @@ const legacyDecisionEquivalent = Schema.toEquivalence(LegacyDecisionRecord);
138
138
  const TransactionId = Schema.String.check(Schema.isNonEmpty(), Schema.isMaxLength(256), Schema.isPattern(/^(?:[^\uD800-\uDFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF])*$/));
139
139
  const storeError = (operation) => (cause) => new WorkStoreError({ cause, operation });
140
140
  const fromPrivateDatabaseError = (error) => new WorkStoreError({ cause: error.cause, operation: error.operation });
141
+ /**
142
+ * Upgrades pre-session Work tables in place. Runs inside `WorkStore.open`'s
143
+ * schema transaction, so a failure here or in a later schema step leaves the
144
+ * file as it was.
145
+ */
141
146
  const migrateLegacyAuthorityTables = (database) => {
142
- let transaction = false;
143
- try {
144
- database.exec("BEGIN IMMEDIATE");
145
- transaction = true;
146
- const columns = (table) => Schema.decodeUnknownSync(Schema.Array(Schema.Struct({ name: Schema.String })))(database.prepare(`PRAGMA table_info(${table})`).all()).map(({ name }) => name);
147
- const laneColumns = columns("work_lane_claims");
148
- const decisionColumns = columns("work_decision_handoffs");
149
- const tables = Schema.decodeUnknownSync(Schema.Array(Schema.Struct({ name: Schema.String })))(database.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all()).map(({ name }) => name);
150
- const coordinatorDispatchColumns = tables.includes("orchestrator_dispatches")
151
- ? columns("orchestrator_dispatches")
147
+ const columns = (table) => Schema.decodeUnknownSync(Schema.Array(Schema.Struct({ name: Schema.String })))(database.prepare(`PRAGMA table_info(${table})`).all()).map(({ name }) => name);
148
+ const laneColumns = columns("work_lane_claims");
149
+ const decisionColumns = columns("work_decision_handoffs");
150
+ const tables = Schema.decodeUnknownSync(Schema.Array(Schema.Struct({ name: Schema.String })))(database.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all()).map(({ name }) => name);
151
+ const coordinatorDispatchColumns = tables.includes("orchestrator_dispatches")
152
+ ? columns("orchestrator_dispatches")
153
+ : [];
154
+ const requireBindingCompanions = (binding, operation) => {
155
+ const laneInput = tables.includes("work_lane_operations")
156
+ ? database.prepare(`SELECT operation_id AS operationId, lane_id AS laneId, goal_id AS goalId,
157
+ phase, revision, record
158
+ FROM work_lane_operations WHERE operation_id = ?`).get(binding.lane.operationId)
159
+ : undefined;
160
+ const checkpointInput = tables.includes("work_goal_events")
161
+ ? database.prepare(`SELECT event_id AS eventId, goal_id AS goalId, occurred_at AS occurredAt, record
162
+ FROM work_goal_events WHERE event_id = ?`).get(binding.checkpoint.eventId)
163
+ : undefined;
164
+ const error = agentBindingReadbackError(binding, laneInput === undefined ? undefined : Schema.decodeUnknownSync(AgentBindingLaneOperationRow)(laneInput), checkpointInput === undefined
165
+ ? undefined
166
+ : Schema.decodeUnknownSync(AgentBindingGoalEventRow)(checkpointInput), operation);
167
+ if (error !== undefined)
168
+ throw error;
169
+ };
170
+ const coordinatorTablesPresent = {
171
+ dispatch: tables.includes("orchestrator_dispatches"),
172
+ event: tables.includes("orchestrator_events"),
173
+ metadata: tables.includes("orchestrator_dispatch_metadata")
174
+ };
175
+ const requireCoordinatorSchema = (operation) => {
176
+ const present = Object.values(coordinatorTablesPresent).filter(Boolean).length;
177
+ if (present !== 0 && present !== 3) {
178
+ throw new WorkStoreError({ cause: coordinatorTablesPresent, operation: `${operation}.schema` });
179
+ }
180
+ return present === 3;
181
+ };
182
+ requireCoordinatorSchema("open.migrate");
183
+ const readCoordinatorLifecycle = (dispatchRequestId) => ({
184
+ dispatchRows: Schema.decodeUnknownSync(Schema.Array(CoordinatorLifecycleDispatchRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId,
185
+ activity_idempotency_key AS activityIdempotencyKey, command,
186
+ accepted_at AS acceptedAt, status
187
+ FROM orchestrator_dispatches WHERE dispatch_request_id = ? LIMIT 2`).all(dispatchRequestId)),
188
+ eventRows: Schema.decodeUnknownSync(Schema.Array(CoordinatorLifecycleEventRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, sequence, type,
189
+ activity_idempotency_key AS activityIdempotencyKey,
190
+ occurred_at AS occurredAt, detail, result
191
+ FROM orchestrator_events WHERE dispatch_request_id = ?
192
+ ORDER BY sequence ASC LIMIT 5`).all(dispatchRequestId))
193
+ });
194
+ const routeStorageAuthority = (dispatchRequestId, operation) => {
195
+ if (!coordinatorDispatchColumns.includes("is_routed")) {
196
+ return { _tag: "legacy_without_routed_discriminator" };
197
+ }
198
+ const rows = Schema.decodeUnknownSync(Schema.Array(CoordinatorRouteDiscriminatorRow))(database.prepare(`SELECT is_routed AS isRouted FROM orchestrator_dispatches
199
+ WHERE dispatch_request_id = ? LIMIT 2`).all(dispatchRequestId));
200
+ const row = rows[0];
201
+ if (rows.length !== 1 || row === undefined) {
202
+ throw new WorkStoreError({ cause: { dispatchRequestId, rows }, operation });
203
+ }
204
+ return { _tag: "routed_discriminator", isRouted: row.isRouted };
205
+ };
206
+ const requireLinkedParentAuthority = (linkedRequestId, operation) => {
207
+ if (linkedRequestId === null)
208
+ return;
209
+ const metadataRows = Schema.decodeUnknownSync(Schema.Array(MetadataWorkLinkRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, route, work_link AS workLink
210
+ FROM orchestrator_dispatch_metadata WHERE dispatch_request_id = ? LIMIT 2`).all(linkedRequestId));
211
+ const metadata = metadataRows[0];
212
+ if (metadataRows.length !== 1 || metadata === undefined || metadata.workLink !== null) {
213
+ throw new WorkStoreError({ cause: { linkedRequestId, metadataRows }, operation });
214
+ }
215
+ const lifecycle = readCoordinatorLifecycle(linkedRequestId);
216
+ requireCoordinatorFailedLunaAuthority(lifecycle.dispatchRows, lifecycle.eventRows, metadata.route, routeStorageAuthority(linkedRequestId, operation), operation);
217
+ };
218
+ const requireMetadataAuthority = (dispatchRequestId, routeText, lineage, operation) => {
219
+ const commandRows = Schema.decodeUnknownSync(Schema.Array(CoordinatorCommandRow))(database.prepare(`SELECT activity_idempotency_key AS activityIdempotencyKey, command FROM orchestrator_dispatches
220
+ WHERE dispatch_request_id = ? LIMIT 2`).all(dispatchRequestId));
221
+ const commandRow = commandRows[0];
222
+ if (commandRows.length !== 1 || commandRow === undefined) {
223
+ throw new WorkStoreError({ cause: { commandRows, dispatchRequestId }, operation });
224
+ }
225
+ const route = requireCoordinatorRouteAuthority(commandRow.command, commandRow.activityIdempotencyKey, routeText, lineage, routeStorageAuthority(dispatchRequestId, operation), operation);
226
+ requireLinkedParentAuthority(route.linkedRequestId, `${operation}.parent`);
227
+ };
228
+ const requireLifecycleAuthority = (dispatchRequestId, expectedRunningAt, operation) => {
229
+ if (!requireCoordinatorSchema(operation))
230
+ return;
231
+ const lifecycle = readCoordinatorLifecycle(dispatchRequestId);
232
+ requireCoordinatorLifecycleAuthority(lifecycle.dispatchRows, lifecycle.eventRows, expectedRunningAt, operation);
233
+ };
234
+ const requireCurrentAgentBindingAuthority = (dispatchRequestId, handoff, operation) => {
235
+ const lifecycle = readCoordinatorLifecycle(dispatchRequestId);
236
+ const runningAt = coordinatorLifecycleRunningAt(lifecycle.dispatchRows, lifecycle.eventRows, `${operation}.lifecycle`);
237
+ const bindingRows = tables.includes("work_agent_bindings")
238
+ ? Schema.decodeUnknownSync(Schema.Array(AgentBindingRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, lane_id AS laneId,
239
+ expected_revision AS expectedRevision, revision, agent_id AS agentId, host, record
240
+ FROM work_agent_bindings WHERE dispatch_request_id = ? LIMIT 2`).all(dispatchRequestId))
152
241
  : [];
153
- const requireBindingCompanions = (binding, operation) => {
154
- const laneInput = tables.includes("work_lane_operations")
155
- ? database.prepare(`SELECT operation_id AS operationId, lane_id AS laneId, goal_id AS goalId,
242
+ if (runningAt === null && bindingRows.length === 0)
243
+ return;
244
+ const bindingRow = bindingRows[0];
245
+ if (runningAt === null || bindingRows.length !== 1 || bindingRow === undefined) {
246
+ throw new WorkStoreError({ cause: { bindingRows, lifecycle }, operation });
247
+ }
248
+ const decoded = decodeAgentBindingRow(bindingRow, { dispatchRequestId, laneId: handoff.laneId }, operation);
249
+ if (decoded._tag === "invalid")
250
+ throw decoded.error;
251
+ if (decoded.binding.request.expectedRevision !== handoff.expectedRevision ||
252
+ decoded.binding.lane.goalId !== handoff.goalId ||
253
+ decoded.binding.checkpoint.occurredAt !== runningAt) {
254
+ throw new WorkStoreError({ cause: { binding: decoded.binding, handoff, runningAt }, operation });
255
+ }
256
+ requireBindingCompanions(decoded.binding, operation);
257
+ const currentLaneRows = Schema.decodeUnknownSync(Schema.Array(LaneRow))(database.prepare(`SELECT lane_id AS laneId, goal_id AS goalId, operation_id AS operationId,
258
+ phase, revision, record
259
+ FROM work_lane_claims WHERE lane_id = ? LIMIT 2`).all(decoded.binding.lane.laneId));
260
+ const currentLaneRow = currentLaneRows[0];
261
+ if (currentLaneRows.length !== 1 || currentLaneRow === undefined) {
262
+ throw new WorkStoreError({
263
+ cause: { binding: decoded.binding, currentLaneRows },
264
+ operation: `${operation}.lane-revision`
265
+ });
266
+ }
267
+ let currentLane;
268
+ try {
269
+ currentLane = Schema.decodeUnknownSync(WorkLaneClaimed)(JSON.parse(currentLaneRow.record));
270
+ }
271
+ catch (cause) {
272
+ throw new WorkStoreError({ cause, operation: `${operation}.lane-revision` });
273
+ }
274
+ const currentLaneSchema = laneColumns.includes("goal_id");
275
+ const currentOperationRows = currentLaneSchema
276
+ ? Schema.decodeUnknownSync(Schema.Array(AgentBindingLaneOperationRow))(database.prepare(`SELECT operation_id AS operationId, lane_id AS laneId, goal_id AS goalId,
156
277
  phase, revision, record
157
- FROM work_lane_operations WHERE operation_id = ?`).get(binding.lane.operationId)
158
- : undefined;
159
- const checkpointInput = tables.includes("work_goal_events")
160
- ? database.prepare(`SELECT event_id AS eventId, goal_id AS goalId, occurred_at AS occurredAt, record
161
- FROM work_goal_events WHERE event_id = ?`).get(binding.checkpoint.eventId)
162
- : undefined;
163
- const error = agentBindingReadbackError(binding, laneInput === undefined ? undefined : Schema.decodeUnknownSync(AgentBindingLaneOperationRow)(laneInput), checkpointInput === undefined
164
- ? undefined
165
- : Schema.decodeUnknownSync(AgentBindingGoalEventRow)(checkpointInput), operation);
166
- if (error !== undefined)
167
- throw error;
168
- };
169
- const coordinatorTablesPresent = {
170
- dispatch: tables.includes("orchestrator_dispatches"),
171
- event: tables.includes("orchestrator_events"),
172
- metadata: tables.includes("orchestrator_dispatch_metadata")
173
- };
174
- const requireCoordinatorSchema = (operation) => {
175
- const present = Object.values(coordinatorTablesPresent).filter(Boolean).length;
176
- if (present !== 0 && present !== 3) {
177
- throw new WorkStoreError({ cause: coordinatorTablesPresent, operation: `${operation}.schema` });
178
- }
179
- return present === 3;
180
- };
181
- requireCoordinatorSchema("open.migrate");
182
- const readCoordinatorLifecycle = (dispatchRequestId) => ({
183
- dispatchRows: Schema.decodeUnknownSync(Schema.Array(CoordinatorLifecycleDispatchRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId,
184
- activity_idempotency_key AS activityIdempotencyKey, command,
185
- accepted_at AS acceptedAt, status
186
- FROM orchestrator_dispatches WHERE dispatch_request_id = ? LIMIT 2`).all(dispatchRequestId)),
187
- eventRows: Schema.decodeUnknownSync(Schema.Array(CoordinatorLifecycleEventRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, sequence, type,
188
- activity_idempotency_key AS activityIdempotencyKey,
189
- occurred_at AS occurredAt, detail, result
190
- FROM orchestrator_events WHERE dispatch_request_id = ?
191
- ORDER BY sequence ASC LIMIT 5`).all(dispatchRequestId))
278
+ FROM work_lane_operations WHERE operation_id = ? LIMIT 2`).all(currentLane.operationId))
279
+ : [];
280
+ const currentOperationError = currentLaneSchema
281
+ ? laneOperationReadbackError(currentLane, currentOperationRows.length === 1 ? currentOperationRows[0] : undefined, `${operation}.lane-revision`)
282
+ : undefined;
283
+ if (currentLane.laneId !== currentLaneRow.laneId ||
284
+ currentLane.goalId !== currentLaneRow.goalId ||
285
+ currentLane.operationId !== currentLaneRow.operationId ||
286
+ currentLane.phase !== currentLaneRow.phase ||
287
+ currentLane.revision !== currentLaneRow.revision ||
288
+ currentLane.laneId !== decoded.binding.lane.laneId ||
289
+ currentLane.goalId !== decoded.binding.lane.goalId ||
290
+ currentLane.revision < decoded.binding.lane.revision ||
291
+ (currentLaneSchema && currentLane.revision === decoded.binding.lane.revision &&
292
+ !Equal.equals(currentLane, decoded.binding.lane)) ||
293
+ currentOperationError !== undefined) {
294
+ throw new WorkStoreError({
295
+ cause: { binding: decoded.binding, currentLane, currentLaneRow, currentOperationError },
296
+ operation: `${operation}.lane-revision`
297
+ });
298
+ }
299
+ };
300
+ const dispatches = tables.includes("work_dispatch_handoffs")
301
+ ? Schema.decodeUnknownSync(Schema.Array(LegacyDispatchStoredRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, handoff_id AS handoffId,
302
+ lane_id AS laneId, occurred_at AS occurredAt, lineage, record
303
+ FROM work_dispatch_handoffs`).all())
304
+ : [];
305
+ const decisionIdentities = decisionColumns.length > 0
306
+ ? Schema.decodeUnknownSync(Schema.Array(DecisionIdentityRow))(database.prepare("SELECT handoff_id AS handoffId FROM work_decision_handoffs").all())
307
+ : [];
308
+ const orphanDispatch = dispatches.find(({ handoffId }) => !decisionIdentities.some((decision) => decision.handoffId === handoffId));
309
+ if (orphanDispatch !== undefined) {
310
+ throw new WorkStoreError({
311
+ cause: { decisionIdentities, dispatch: orphanDispatch },
312
+ operation: "open.migrate.dispatch-decision"
313
+ });
314
+ }
315
+ if (tables.includes("orchestrator_dispatch_metadata")) {
316
+ const linkedMetadata = Schema.decodeUnknownSync(Schema.Array(MetadataHandoffIdentityRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId,
317
+ CASE WHEN work_link IS NULL THEN 0 ELSE 1 END AS hasWorkLink,
318
+ CASE WHEN json_valid(work_link) AND json_type(work_link, '$.handoff.id') = 'text'
319
+ THEN json_extract(work_link, '$.handoff.id') END AS handoffId,
320
+ route
321
+ FROM orchestrator_dispatch_metadata AS metadata
322
+ WHERE work_link IS NOT NULL OR EXISTS (
323
+ SELECT 1 FROM orchestrator_dispatch_metadata AS linked
324
+ WHERE linked.dispatch_request_id = metadata.dispatch_request_id
325
+ AND linked.work_link IS NOT NULL
326
+ ) OR (json_valid(route) AND json_extract(route, '$.model') = 'gpt-5.6-sol')
327
+ LIMIT ${workDecisionMaxRecords + 1}`).all());
328
+ if (linkedMetadata.length > workDecisionMaxRecords) {
329
+ throw new WorkStoreError({ cause: linkedMetadata.length, operation: "open.migrate.metadata-capacity" });
330
+ }
331
+ const metadataCounts = new Map();
332
+ const dispatchCounts = new Map();
333
+ const decisionCounts = new Map();
334
+ for (const metadata of linkedMetadata) {
335
+ metadataCounts.set(metadata.dispatchRequestId, (metadataCounts.get(metadata.dispatchRequestId) ?? 0) + 1);
336
+ }
337
+ for (const dispatch of dispatches) {
338
+ const identity = JSON.stringify([dispatch.dispatchRequestId, dispatch.handoffId]);
339
+ dispatchCounts.set(identity, (dispatchCounts.get(identity) ?? 0) + 1);
340
+ }
341
+ for (const decision of decisionIdentities) {
342
+ decisionCounts.set(decision.handoffId, (decisionCounts.get(decision.handoffId) ?? 0) + 1);
343
+ }
344
+ const invalidMetadata = linkedMetadata.find((metadata) => {
345
+ if (metadata.route === null)
346
+ return true;
347
+ const requiresWorkLink = coordinatorRouteRequiresWorkLink(metadata.route, routeStorageAuthority(metadata.dispatchRequestId, "open.migrate.metadata-decision"), "open.migrate.metadata-decision");
348
+ if (!requiresWorkLink)
349
+ return metadata.hasWorkLink === 1;
350
+ const dispatchIdentity = JSON.stringify([metadata.dispatchRequestId, metadata.handoffId]);
351
+ return (metadata.handoffId === null || metadataCounts.get(metadata.dispatchRequestId) !== 1 ||
352
+ dispatchCounts.get(dispatchIdentity) !== 1 || decisionCounts.get(metadata.handoffId) !== 1);
192
353
  });
193
- const routeStorageAuthority = (dispatchRequestId, operation) => {
194
- if (!coordinatorDispatchColumns.includes("is_routed")) {
195
- return { _tag: "legacy_without_routed_discriminator" };
354
+ if (invalidMetadata !== undefined) {
355
+ throw new WorkStoreError({
356
+ cause: { decisionIdentities, dispatches, linkedMetadata, metadata: invalidMetadata },
357
+ operation: "open.migrate.metadata-decision"
358
+ });
359
+ }
360
+ }
361
+ const lanes = laneColumns.length > 0 && !laneColumns.includes("goal_id")
362
+ ? Schema.decodeUnknownSync(Schema.Array(LegacyLaneStoredRow))(database.prepare("SELECT lane_id AS laneId, revision, record FROM work_lane_claims").all()).map((row) => {
363
+ const legacy = Schema.decodeUnknownSync(LegacyLaneRecord)(JSON.parse(row.record));
364
+ if (legacy.laneId !== row.laneId || legacy.revision !== row.revision) {
365
+ throw new WorkStoreError({ cause: { legacy, row }, operation: "open.migrate.lane-identity" });
196
366
  }
197
- const rows = Schema.decodeUnknownSync(Schema.Array(CoordinatorRouteDiscriminatorRow))(database.prepare(`SELECT is_routed AS isRouted FROM orchestrator_dispatches
198
- WHERE dispatch_request_id = ? LIMIT 2`).all(dispatchRequestId));
199
- const row = rows[0];
200
- if (rows.length !== 1 || row === undefined) {
201
- throw new WorkStoreError({ cause: { dispatchRequestId, rows }, operation });
367
+ return Schema.decodeUnknownSync(WorkLaneClaimed)({
368
+ ...legacy,
369
+ goalId: legacy.laneId,
370
+ operationId: legacy.laneId
371
+ });
372
+ })
373
+ : [];
374
+ const decisions = decisionColumns.length > 0 && !decisionColumns.includes("session_id")
375
+ ? Schema.decodeUnknownSync(Schema.Array(LegacyDecisionStoredRow))(database.prepare("SELECT handoff_id AS handoffId, record FROM work_decision_handoffs").all()).map((row) => {
376
+ const legacy = Schema.decodeUnknownSync(LegacyDecisionRecord)(JSON.parse(row.record));
377
+ if (legacy.id !== row.handoffId) {
378
+ throw new WorkStoreError({ cause: { legacy, row }, operation: "open.migrate.handoff-identity" });
202
379
  }
203
- return { _tag: "routed_discriminator", isRouted: row.isRouted };
204
- };
205
- const requireLinkedParentAuthority = (linkedRequestId, operation) => {
206
- if (linkedRequestId === null)
207
- return;
208
- const metadataRows = Schema.decodeUnknownSync(Schema.Array(MetadataWorkLinkRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, route, work_link AS workLink
209
- FROM orchestrator_dispatch_metadata WHERE dispatch_request_id = ? LIMIT 2`).all(linkedRequestId));
210
- const metadata = metadataRows[0];
211
- if (metadataRows.length !== 1 || metadata === undefined || metadata.workLink !== null) {
212
- throw new WorkStoreError({ cause: { linkedRequestId, metadataRows }, operation });
380
+ const matchingDispatches = dispatches.filter(({ handoffId }) => handoffId === legacy.id);
381
+ const dispatch = matchingDispatches[0];
382
+ const lane = lanes.find(({ laneId }) => laneId === legacy.laneId);
383
+ if (lane === undefined) {
384
+ throw new WorkStoreError({ cause: legacy, operation: "open.migrate.handoff-lane" });
213
385
  }
214
- const lifecycle = readCoordinatorLifecycle(linkedRequestId);
215
- requireCoordinatorFailedLunaAuthority(lifecycle.dispatchRows, lifecycle.eventRows, metadata.route, routeStorageAuthority(linkedRequestId, operation), operation);
216
- };
217
- const requireMetadataAuthority = (dispatchRequestId, routeText, lineage, operation) => {
218
- const commandRows = Schema.decodeUnknownSync(Schema.Array(CoordinatorCommandRow))(database.prepare(`SELECT activity_idempotency_key AS activityIdempotencyKey, command FROM orchestrator_dispatches
219
- WHERE dispatch_request_id = ? LIMIT 2`).all(dispatchRequestId));
220
- const commandRow = commandRows[0];
221
- if (commandRows.length !== 1 || commandRow === undefined) {
222
- throw new WorkStoreError({ cause: { commandRows, dispatchRequestId }, operation });
386
+ const dispatchIds = dispatch === undefined
387
+ ? []
388
+ : Schema.decodeUnknownSync(WorkDispatchHandoff.fields.lineage)(JSON.parse(dispatch.lineage));
389
+ if (matchingDispatches.length !== 1 || dispatch === undefined) {
390
+ throw new WorkStoreError({
391
+ cause: { legacy, matchingDispatches },
392
+ operation: "open.migrate.legacy-dispatch-cardinality"
393
+ });
394
+ }
395
+ const dispatchHandoff = Schema.decodeUnknownSync(LegacyDecisionRecord)(JSON.parse(dispatch.record));
396
+ if (dispatch.laneId !== legacy.laneId || dispatch.occurredAt !== legacy.occurredAt ||
397
+ !legacyDecisionEquivalent(dispatchHandoff, legacy)) {
398
+ throw new WorkStoreError({
399
+ cause: { dispatch, dispatchHandoff, legacy },
400
+ operation: "open.migrate.legacy-dispatch-authority"
401
+ });
402
+ }
403
+ if (tables.includes("orchestrator_dispatch_metadata")) {
404
+ const metadataRows = Schema.decodeUnknownSync(Schema.Array(MetadataWorkLinkRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, route, work_link AS workLink
405
+ FROM orchestrator_dispatch_metadata WHERE dispatch_request_id = ? LIMIT 2`).all(dispatch.dispatchRequestId));
406
+ const metadata = metadataRows[0];
407
+ if (metadataRows.length !== 1 || metadata === undefined) {
408
+ throw new WorkStoreError({
409
+ cause: { dispatch, metadataRows },
410
+ operation: "open.migrate.legacy-metadata-authority"
411
+ });
412
+ }
413
+ if (metadata.workLink === null) {
414
+ throw new WorkStoreError({
415
+ cause: { dispatch, metadata },
416
+ operation: "open.migrate.legacy-metadata-authority"
417
+ });
418
+ }
419
+ const workLink = Schema.decodeUnknownSync(LegacyMetadataWorkLink)(JSON.parse(metadata.workLink));
420
+ requireMetadataAuthority(dispatch.dispatchRequestId, metadata.route, workLink.lineage, "open.migrate.legacy-metadata-authority");
421
+ if (!legacyDecisionEquivalent(workLink.handoff, legacy) ||
422
+ !workDispatchLineageEquivalent(workLink.lineage, dispatchIds)) {
423
+ throw new WorkStoreError({
424
+ cause: { legacy, workLink },
425
+ operation: "open.migrate.legacy-metadata-authority"
426
+ });
427
+ }
223
428
  }
224
- const route = requireCoordinatorRouteAuthority(commandRow.command, commandRow.activityIdempotencyKey, routeText, lineage, routeStorageAuthority(dispatchRequestId, operation), operation);
225
- requireLinkedParentAuthority(route.linkedRequestId, `${operation}.parent`);
226
- };
227
- const requireLifecycleAuthority = (dispatchRequestId, expectedRunningAt, operation) => {
228
- if (!requireCoordinatorSchema(operation))
229
- return;
230
- const lifecycle = readCoordinatorLifecycle(dispatchRequestId);
231
- requireCoordinatorLifecycleAuthority(lifecycle.dispatchRows, lifecycle.eventRows, expectedRunningAt, operation);
232
- };
233
- const requireCurrentAgentBindingAuthority = (dispatchRequestId, handoff, operation) => {
234
- const lifecycle = readCoordinatorLifecycle(dispatchRequestId);
235
- const runningAt = coordinatorLifecycleRunningAt(lifecycle.dispatchRows, lifecycle.eventRows, `${operation}.lifecycle`);
236
429
  const bindingRows = tables.includes("work_agent_bindings")
237
430
  ? Schema.decodeUnknownSync(Schema.Array(AgentBindingRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, lane_id AS laneId,
238
431
  expected_revision AS expectedRevision, revision, agent_id AS agentId, host, record
239
- FROM work_agent_bindings WHERE dispatch_request_id = ? LIMIT 2`).all(dispatchRequestId))
432
+ FROM work_agent_bindings
433
+ WHERE dispatch_request_id = ?`).all(dispatch.dispatchRequestId))
240
434
  : [];
241
- if (runningAt === null && bindingRows.length === 0)
242
- return;
243
435
  const bindingRow = bindingRows[0];
244
- if (runningAt === null || bindingRows.length !== 1 || bindingRow === undefined) {
245
- throw new WorkStoreError({ cause: { bindingRows, lifecycle }, operation });
246
- }
247
- const decoded = decodeAgentBindingRow(bindingRow, { dispatchRequestId, laneId: handoff.laneId }, operation);
248
- if (decoded._tag === "invalid")
249
- throw decoded.error;
250
- if (decoded.binding.request.expectedRevision !== handoff.expectedRevision ||
251
- decoded.binding.lane.goalId !== handoff.goalId ||
252
- decoded.binding.checkpoint.occurredAt !== runningAt) {
253
- throw new WorkStoreError({ cause: { binding: decoded.binding, handoff, runningAt }, operation });
254
- }
255
- requireBindingCompanions(decoded.binding, operation);
256
- const currentLaneRows = Schema.decodeUnknownSync(Schema.Array(LaneRow))(database.prepare(`SELECT lane_id AS laneId, goal_id AS goalId, operation_id AS operationId,
257
- phase, revision, record
258
- FROM work_lane_claims WHERE lane_id = ? LIMIT 2`).all(decoded.binding.lane.laneId));
259
- const currentLaneRow = currentLaneRows[0];
260
- if (currentLaneRows.length !== 1 || currentLaneRow === undefined) {
436
+ if (bindingRows.length !== 1 || bindingRow === undefined) {
261
437
  throw new WorkStoreError({
262
- cause: { binding: decoded.binding, currentLaneRows },
263
- operation: `${operation}.lane-revision`
438
+ cause: { bindingRows, dispatch, legacy },
439
+ operation: "open.migrate.legacy-handoff-revision"
264
440
  });
265
441
  }
266
- let currentLane;
267
- try {
268
- currentLane = Schema.decodeUnknownSync(WorkLaneClaimed)(JSON.parse(currentLaneRow.record));
269
- }
270
- catch (cause) {
271
- throw new WorkStoreError({ cause, operation: `${operation}.lane-revision` });
442
+ const bindingDecision = decodeAgentBindingRow(bindingRow, { dispatchRequestId: dispatch.dispatchRequestId, laneId: legacy.laneId }, "open.migrate.legacy-agent-binding");
443
+ if (bindingDecision._tag === "invalid")
444
+ throw bindingDecision.error;
445
+ if (bindingDecision.binding.lane.goalId !== legacy.goalId) {
446
+ throw new WorkStoreError({
447
+ cause: { binding: bindingDecision.binding, legacy },
448
+ operation: "open.migrate.legacy-agent-binding.goal"
449
+ });
272
450
  }
273
- const currentLaneSchema = laneColumns.includes("goal_id");
274
- const currentOperationRows = currentLaneSchema
275
- ? Schema.decodeUnknownSync(Schema.Array(AgentBindingLaneOperationRow))(database.prepare(`SELECT operation_id AS operationId, lane_id AS laneId, goal_id AS goalId,
276
- phase, revision, record
277
- FROM work_lane_operations WHERE operation_id = ? LIMIT 2`).all(currentLane.operationId))
278
- : [];
279
- const currentOperationError = currentLaneSchema
280
- ? laneOperationReadbackError(currentLane, currentOperationRows.length === 1 ? currentOperationRows[0] : undefined, `${operation}.lane-revision`)
281
- : undefined;
282
- if (currentLane.laneId !== currentLaneRow.laneId ||
283
- currentLane.goalId !== currentLaneRow.goalId ||
284
- currentLane.operationId !== currentLaneRow.operationId ||
285
- currentLane.phase !== currentLaneRow.phase ||
286
- currentLane.revision !== currentLaneRow.revision ||
287
- currentLane.laneId !== decoded.binding.lane.laneId ||
288
- currentLane.goalId !== decoded.binding.lane.goalId ||
289
- currentLane.revision < decoded.binding.lane.revision ||
290
- (currentLaneSchema && currentLane.revision === decoded.binding.lane.revision &&
291
- !Equal.equals(currentLane, decoded.binding.lane)) ||
292
- currentOperationError !== undefined) {
451
+ if (lane.revision < bindingDecision.binding.lane.revision) {
293
452
  throw new WorkStoreError({
294
- cause: { binding: decoded.binding, currentLane, currentLaneRow, currentOperationError },
295
- operation: `${operation}.lane-revision`
453
+ cause: { binding: bindingDecision.binding, lane, legacy },
454
+ operation: "open.migrate.legacy-handoff-lane-revision"
296
455
  });
297
456
  }
298
- };
299
- const dispatches = tables.includes("work_dispatch_handoffs")
300
- ? Schema.decodeUnknownSync(Schema.Array(LegacyDispatchStoredRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, handoff_id AS handoffId,
301
- lane_id AS laneId, occurred_at AS occurredAt, lineage, record
302
- FROM work_dispatch_handoffs`).all())
303
- : [];
304
- const decisionIdentities = decisionColumns.length > 0
305
- ? Schema.decodeUnknownSync(Schema.Array(DecisionIdentityRow))(database.prepare("SELECT handoff_id AS handoffId FROM work_decision_handoffs").all())
306
- : [];
307
- const orphanDispatch = dispatches.find(({ handoffId }) => !decisionIdentities.some((decision) => decision.handoffId === handoffId));
308
- if (orphanDispatch !== undefined) {
309
- throw new WorkStoreError({
310
- cause: { decisionIdentities, dispatch: orphanDispatch },
311
- operation: "open.migrate.dispatch-decision"
457
+ requireBindingCompanions(bindingDecision.binding, "open.migrate.legacy-agent-binding");
458
+ requireLifecycleAuthority(bindingDecision.binding.request.dispatchRequestId, bindingDecision.binding.checkpoint.occurredAt, "open.migrate.legacy-agent-binding.lifecycle");
459
+ return Schema.decodeUnknownSync(WorkDecisionHandoff)({
460
+ ...legacy,
461
+ contextDelta: legacy.summary,
462
+ expectedRevision: bindingDecision.binding.request.expectedRevision,
463
+ sessionId: legacy.id,
464
+ dispatchIds,
465
+ blockers: [],
466
+ evidenceRefs: [],
467
+ version: "herdr.work.decision.v2"
312
468
  });
313
- }
314
- if (tables.includes("orchestrator_dispatch_metadata")) {
315
- const linkedMetadata = Schema.decodeUnknownSync(Schema.Array(MetadataHandoffIdentityRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId,
316
- CASE WHEN work_link IS NULL THEN 0 ELSE 1 END AS hasWorkLink,
317
- CASE WHEN json_valid(work_link) AND json_type(work_link, '$.handoff.id') = 'text'
318
- THEN json_extract(work_link, '$.handoff.id') END AS handoffId,
319
- route
320
- FROM orchestrator_dispatch_metadata AS metadata
321
- WHERE work_link IS NOT NULL OR EXISTS (
322
- SELECT 1 FROM orchestrator_dispatch_metadata AS linked
323
- WHERE linked.dispatch_request_id = metadata.dispatch_request_id
324
- AND linked.work_link IS NOT NULL
325
- ) OR (json_valid(route) AND json_extract(route, '$.model') = 'gpt-5.6-sol')
326
- LIMIT ${workDecisionMaxRecords + 1}`).all());
327
- if (linkedMetadata.length > workDecisionMaxRecords) {
328
- throw new WorkStoreError({ cause: linkedMetadata.length, operation: "open.migrate.metadata-capacity" });
329
- }
330
- const metadataCounts = new Map();
331
- const dispatchCounts = new Map();
332
- const decisionCounts = new Map();
333
- for (const metadata of linkedMetadata) {
334
- metadataCounts.set(metadata.dispatchRequestId, (metadataCounts.get(metadata.dispatchRequestId) ?? 0) + 1);
335
- }
469
+ })
470
+ : [];
471
+ if (laneColumns.length > 0 && !laneColumns.includes("goal_id")) {
472
+ database.exec("ALTER TABLE work_lane_claims ADD COLUMN goal_id TEXT");
473
+ database.exec("ALTER TABLE work_lane_claims ADD COLUMN operation_id TEXT");
474
+ database.exec("ALTER TABLE work_lane_claims ADD COLUMN phase TEXT");
475
+ const update = database.prepare("UPDATE work_lane_claims SET goal_id = ?, operation_id = ?, phase = ?, record = ? WHERE lane_id = ?");
476
+ for (const lane of lanes)
477
+ update.run(lane.goalId, lane.operationId, lane.phase, JSON.stringify(lane), lane.laneId);
478
+ }
479
+ if (decisionColumns.length > 0 && !decisionColumns.includes("session_id")) {
480
+ database.exec("ALTER TABLE work_decision_handoffs ADD COLUMN session_id TEXT");
481
+ const update = database.prepare("UPDATE work_decision_handoffs SET session_id = ?, record = ? WHERE handoff_id = ?");
482
+ for (const decision of decisions)
483
+ update.run(decision.sessionId, JSON.stringify(decision), decision.id);
484
+ if (dispatches.length > 0) {
485
+ const updateDispatch = database.prepare("UPDATE work_dispatch_handoffs SET record = ? WHERE dispatch_request_id = ?");
486
+ const updateMetadata = tables.includes("orchestrator_dispatch_metadata")
487
+ ? database.prepare("UPDATE orchestrator_dispatch_metadata SET work_link = ? WHERE dispatch_request_id = ?")
488
+ : null;
336
489
  for (const dispatch of dispatches) {
337
- const identity = JSON.stringify([dispatch.dispatchRequestId, dispatch.handoffId]);
338
- dispatchCounts.set(identity, (dispatchCounts.get(identity) ?? 0) + 1);
339
- }
340
- for (const decision of decisionIdentities) {
341
- decisionCounts.set(decision.handoffId, (decisionCounts.get(decision.handoffId) ?? 0) + 1);
342
- }
343
- const invalidMetadata = linkedMetadata.find((metadata) => {
344
- if (metadata.route === null)
345
- return true;
346
- const requiresWorkLink = coordinatorRouteRequiresWorkLink(metadata.route, routeStorageAuthority(metadata.dispatchRequestId, "open.migrate.metadata-decision"), "open.migrate.metadata-decision");
347
- if (!requiresWorkLink)
348
- return metadata.hasWorkLink === 1;
349
- const dispatchIdentity = JSON.stringify([metadata.dispatchRequestId, metadata.handoffId]);
350
- return (metadata.handoffId === null || metadataCounts.get(metadata.dispatchRequestId) !== 1 ||
351
- dispatchCounts.get(dispatchIdentity) !== 1 || decisionCounts.get(metadata.handoffId) !== 1);
352
- });
353
- if (invalidMetadata !== undefined) {
354
- throw new WorkStoreError({
355
- cause: { decisionIdentities, dispatches, linkedMetadata, metadata: invalidMetadata },
356
- operation: "open.migrate.metadata-decision"
357
- });
490
+ const decision = decisions.find(({ id }) => id === dispatch.handoffId);
491
+ if (decision === undefined)
492
+ continue;
493
+ updateDispatch.run(JSON.stringify(decision), dispatch.dispatchRequestId);
494
+ updateMetadata?.run(JSON.stringify({ handoff: decision, lineage: decision.dispatchIds }), dispatch.dispatchRequestId);
358
495
  }
359
496
  }
360
- const lanes = laneColumns.length > 0 && !laneColumns.includes("goal_id")
361
- ? Schema.decodeUnknownSync(Schema.Array(LegacyLaneStoredRow))(database.prepare("SELECT lane_id AS laneId, revision, record FROM work_lane_claims").all()).map((row) => {
362
- const legacy = Schema.decodeUnknownSync(LegacyLaneRecord)(JSON.parse(row.record));
363
- if (legacy.laneId !== row.laneId || legacy.revision !== row.revision) {
364
- throw new WorkStoreError({ cause: { legacy, row }, operation: "open.migrate.lane-identity" });
365
- }
366
- return Schema.decodeUnknownSync(WorkLaneClaimed)({
367
- ...legacy,
368
- goalId: legacy.laneId,
369
- operationId: legacy.laneId
370
- });
371
- })
372
- : [];
373
- const decisions = decisionColumns.length > 0 && !decisionColumns.includes("session_id")
374
- ? Schema.decodeUnknownSync(Schema.Array(LegacyDecisionStoredRow))(database.prepare("SELECT handoff_id AS handoffId, record FROM work_decision_handoffs").all()).map((row) => {
375
- const legacy = Schema.decodeUnknownSync(LegacyDecisionRecord)(JSON.parse(row.record));
376
- if (legacy.id !== row.handoffId) {
377
- throw new WorkStoreError({ cause: { legacy, row }, operation: "open.migrate.handoff-identity" });
378
- }
379
- const matchingDispatches = dispatches.filter(({ handoffId }) => handoffId === legacy.id);
380
- const dispatch = matchingDispatches[0];
381
- const lane = lanes.find(({ laneId }) => laneId === legacy.laneId);
382
- if (lane === undefined) {
383
- throw new WorkStoreError({ cause: legacy, operation: "open.migrate.handoff-lane" });
384
- }
385
- const dispatchIds = dispatch === undefined
386
- ? []
387
- : Schema.decodeUnknownSync(WorkDispatchHandoff.fields.lineage)(JSON.parse(dispatch.lineage));
388
- if (matchingDispatches.length !== 1 || dispatch === undefined) {
389
- throw new WorkStoreError({
390
- cause: { legacy, matchingDispatches },
391
- operation: "open.migrate.legacy-dispatch-cardinality"
392
- });
393
- }
394
- const dispatchHandoff = Schema.decodeUnknownSync(LegacyDecisionRecord)(JSON.parse(dispatch.record));
395
- if (dispatch.laneId !== legacy.laneId || dispatch.occurredAt !== legacy.occurredAt ||
396
- !legacyDecisionEquivalent(dispatchHandoff, legacy)) {
397
- throw new WorkStoreError({
398
- cause: { dispatch, dispatchHandoff, legacy },
399
- operation: "open.migrate.legacy-dispatch-authority"
400
- });
401
- }
402
- if (tables.includes("orchestrator_dispatch_metadata")) {
403
- const metadataRows = Schema.decodeUnknownSync(Schema.Array(MetadataWorkLinkRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, route, work_link AS workLink
404
- FROM orchestrator_dispatch_metadata WHERE dispatch_request_id = ? LIMIT 2`).all(dispatch.dispatchRequestId));
405
- const metadata = metadataRows[0];
406
- if (metadataRows.length !== 1 || metadata === undefined) {
497
+ }
498
+ if (decisionColumns.length > 0) {
499
+ const storedDecisions = Schema.decodeUnknownSync(Schema.Array(DecisionRow))(database.prepare(`SELECT handoff_id AS handoffId, session_id AS sessionId, lane_id AS laneId,
500
+ occurred_at AS occurredAt, record
501
+ FROM work_decision_handoffs`).all());
502
+ const updateDecision = database.prepare("UPDATE work_decision_handoffs SET record = ? WHERE handoff_id = ?");
503
+ const updateDispatch = tables.includes("work_dispatch_handoffs")
504
+ ? database.prepare("UPDATE work_dispatch_handoffs SET record = ? WHERE handoff_id = ?")
505
+ : null;
506
+ const updateMetadata = tables.includes("orchestrator_dispatch_metadata")
507
+ ? database.prepare(`UPDATE orchestrator_dispatch_metadata SET work_link = ?
508
+ WHERE dispatch_request_id = ?`)
509
+ : null;
510
+ const previousDecisions = storedDecisions.flatMap((row) => {
511
+ const input = Schema.decodeUnknownSync(Schema.fromJsonString(Schema.Json))(row.record);
512
+ const previous = decodePreviousDecisionHandoff(input);
513
+ const handoffDispatches = tables.includes("work_dispatch_handoffs")
514
+ ? Schema.decodeUnknownSync(Schema.Array(LegacyDispatchStoredRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, handoff_id AS handoffId,
515
+ lane_id AS laneId, occurred_at AS occurredAt, lineage, record
516
+ FROM work_dispatch_handoffs WHERE handoff_id = ?`).all(row.handoffId))
517
+ : [];
518
+ switch (previous._tag) {
519
+ case "current": {
520
+ if (previous.value.id !== row.handoffId || previous.value.sessionId !== row.sessionId ||
521
+ previous.value.laneId !== row.laneId || previous.value.occurredAt !== row.occurredAt) {
407
522
  throw new WorkStoreError({
408
- cause: { dispatch, metadataRows },
409
- operation: "open.migrate.legacy-metadata-authority"
523
+ cause: { handoff: previous.value, row },
524
+ operation: "open.migrate.handoff-identity"
410
525
  });
411
526
  }
412
- if (metadata.workLink === null) {
527
+ if (handoffDispatches.length > 1) {
413
528
  throw new WorkStoreError({
414
- cause: { dispatch, metadata },
415
- operation: "open.migrate.legacy-metadata-authority"
529
+ cause: { handoffDispatches, row },
530
+ operation: "open.migrate.dispatch-cardinality"
416
531
  });
417
532
  }
418
- const workLink = Schema.decodeUnknownSync(LegacyMetadataWorkLink)(JSON.parse(metadata.workLink));
419
- requireMetadataAuthority(dispatch.dispatchRequestId, metadata.route, workLink.lineage, "open.migrate.legacy-metadata-authority");
420
- if (!legacyDecisionEquivalent(workLink.handoff, legacy) ||
421
- !workDispatchLineageEquivalent(workLink.lineage, dispatchIds)) {
533
+ const linkedMetadata = tables.includes("orchestrator_dispatch_metadata")
534
+ ? Schema.decodeUnknownSync(Schema.Array(MetadataWorkLinkRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, route, work_link AS workLink
535
+ FROM orchestrator_dispatch_metadata
536
+ WHERE work_link IS NOT NULL
537
+ AND CASE WHEN json_valid(work_link)
538
+ THEN json_extract(work_link, '$.handoff.id') END = ?`).all(row.handoffId))
539
+ : [];
540
+ if (tables.includes("orchestrator_dispatch_metadata") &&
541
+ (linkedMetadata.length !== handoffDispatches.length ||
542
+ linkedMetadata.some((metadata) => !handoffDispatches.some(({ dispatchRequestId }) => dispatchRequestId === metadata.dispatchRequestId)))) {
422
543
  throw new WorkStoreError({
423
- cause: { legacy, workLink },
424
- operation: "open.migrate.legacy-metadata-authority"
544
+ cause: { handoffDispatches, linkedMetadata, row },
545
+ operation: "open.migrate.metadata-authority"
425
546
  });
426
547
  }
427
- }
428
- const bindingRows = tables.includes("work_agent_bindings")
429
- ? Schema.decodeUnknownSync(Schema.Array(AgentBindingRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, lane_id AS laneId,
430
- expected_revision AS expectedRevision, revision, agent_id AS agentId, host, record
431
- FROM work_agent_bindings
432
- WHERE dispatch_request_id = ?`).all(dispatch.dispatchRequestId))
433
- : [];
434
- const bindingRow = bindingRows[0];
435
- if (bindingRows.length !== 1 || bindingRow === undefined) {
436
- throw new WorkStoreError({
437
- cause: { bindingRows, dispatch, legacy },
438
- operation: "open.migrate.legacy-handoff-revision"
439
- });
440
- }
441
- const bindingDecision = decodeAgentBindingRow(bindingRow, { dispatchRequestId: dispatch.dispatchRequestId, laneId: legacy.laneId }, "open.migrate.legacy-agent-binding");
442
- if (bindingDecision._tag === "invalid")
443
- throw bindingDecision.error;
444
- if (bindingDecision.binding.lane.goalId !== legacy.goalId) {
445
- throw new WorkStoreError({
446
- cause: { binding: bindingDecision.binding, legacy },
447
- operation: "open.migrate.legacy-agent-binding.goal"
448
- });
449
- }
450
- if (lane.revision < bindingDecision.binding.lane.revision) {
451
- throw new WorkStoreError({
452
- cause: { binding: bindingDecision.binding, lane, legacy },
453
- operation: "open.migrate.legacy-handoff-lane-revision"
454
- });
455
- }
456
- requireBindingCompanions(bindingDecision.binding, "open.migrate.legacy-agent-binding");
457
- requireLifecycleAuthority(bindingDecision.binding.request.dispatchRequestId, bindingDecision.binding.checkpoint.occurredAt, "open.migrate.legacy-agent-binding.lifecycle");
458
- return Schema.decodeUnknownSync(WorkDecisionHandoff)({
459
- ...legacy,
460
- contextDelta: legacy.summary,
461
- expectedRevision: bindingDecision.binding.request.expectedRevision,
462
- sessionId: legacy.id,
463
- dispatchIds,
464
- blockers: [],
465
- evidenceRefs: [],
466
- version: "herdr.work.decision.v2"
467
- });
468
- })
469
- : [];
470
- if (laneColumns.length > 0 && !laneColumns.includes("goal_id")) {
471
- database.exec("ALTER TABLE work_lane_claims ADD COLUMN goal_id TEXT");
472
- database.exec("ALTER TABLE work_lane_claims ADD COLUMN operation_id TEXT");
473
- database.exec("ALTER TABLE work_lane_claims ADD COLUMN phase TEXT");
474
- const update = database.prepare("UPDATE work_lane_claims SET goal_id = ?, operation_id = ?, phase = ?, record = ? WHERE lane_id = ?");
475
- for (const lane of lanes)
476
- update.run(lane.goalId, lane.operationId, lane.phase, JSON.stringify(lane), lane.laneId);
477
- }
478
- if (decisionColumns.length > 0 && !decisionColumns.includes("session_id")) {
479
- database.exec("ALTER TABLE work_decision_handoffs ADD COLUMN session_id TEXT");
480
- const update = database.prepare("UPDATE work_decision_handoffs SET session_id = ?, record = ? WHERE handoff_id = ?");
481
- for (const decision of decisions)
482
- update.run(decision.sessionId, JSON.stringify(decision), decision.id);
483
- if (dispatches.length > 0) {
484
- const updateDispatch = database.prepare("UPDATE work_dispatch_handoffs SET record = ? WHERE dispatch_request_id = ?");
485
- const updateMetadata = tables.includes("orchestrator_dispatch_metadata")
486
- ? database.prepare("UPDATE orchestrator_dispatch_metadata SET work_link = ? WHERE dispatch_request_id = ?")
487
- : null;
488
- for (const dispatch of dispatches) {
489
- const decision = decisions.find(({ id }) => id === dispatch.handoffId);
490
- if (decision === undefined)
491
- continue;
492
- updateDispatch.run(JSON.stringify(decision), dispatch.dispatchRequestId);
493
- updateMetadata?.run(JSON.stringify({ handoff: decision, lineage: decision.dispatchIds }), dispatch.dispatchRequestId);
494
- }
495
- }
496
- }
497
- if (decisionColumns.length > 0) {
498
- const storedDecisions = Schema.decodeUnknownSync(Schema.Array(DecisionRow))(database.prepare(`SELECT handoff_id AS handoffId, session_id AS sessionId, lane_id AS laneId,
499
- occurred_at AS occurredAt, record
500
- FROM work_decision_handoffs`).all());
501
- const updateDecision = database.prepare("UPDATE work_decision_handoffs SET record = ? WHERE handoff_id = ?");
502
- const updateDispatch = tables.includes("work_dispatch_handoffs")
503
- ? database.prepare("UPDATE work_dispatch_handoffs SET record = ? WHERE handoff_id = ?")
504
- : null;
505
- const updateMetadata = tables.includes("orchestrator_dispatch_metadata")
506
- ? database.prepare(`UPDATE orchestrator_dispatch_metadata SET work_link = ?
507
- WHERE dispatch_request_id = ?`)
508
- : null;
509
- const previousDecisions = storedDecisions.flatMap((row) => {
510
- const input = Schema.decodeUnknownSync(Schema.fromJsonString(Schema.Json))(row.record);
511
- const previous = decodePreviousDecisionHandoff(input);
512
- const handoffDispatches = tables.includes("work_dispatch_handoffs")
513
- ? Schema.decodeUnknownSync(Schema.Array(LegacyDispatchStoredRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, handoff_id AS handoffId,
514
- lane_id AS laneId, occurred_at AS occurredAt, lineage, record
515
- FROM work_dispatch_handoffs WHERE handoff_id = ?`).all(row.handoffId))
516
- : [];
517
- switch (previous._tag) {
518
- case "current": {
519
- if (previous.value.id !== row.handoffId || previous.value.sessionId !== row.sessionId ||
520
- previous.value.laneId !== row.laneId || previous.value.occurredAt !== row.occurredAt) {
521
- throw new WorkStoreError({
522
- cause: { handoff: previous.value, row },
523
- operation: "open.migrate.handoff-identity"
524
- });
525
- }
526
- if (handoffDispatches.length > 1) {
527
- throw new WorkStoreError({
528
- cause: { handoffDispatches, row },
529
- operation: "open.migrate.dispatch-cardinality"
530
- });
531
- }
532
- const linkedMetadata = tables.includes("orchestrator_dispatch_metadata")
533
- ? Schema.decodeUnknownSync(Schema.Array(MetadataWorkLinkRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, route, work_link AS workLink
534
- FROM orchestrator_dispatch_metadata
535
- WHERE work_link IS NOT NULL
536
- AND CASE WHEN json_valid(work_link)
537
- THEN json_extract(work_link, '$.handoff.id') END = ?`).all(row.handoffId))
538
- : [];
539
- if (tables.includes("orchestrator_dispatch_metadata") &&
540
- (linkedMetadata.length !== handoffDispatches.length ||
541
- linkedMetadata.some((metadata) => !handoffDispatches.some(({ dispatchRequestId }) => dispatchRequestId === metadata.dispatchRequestId)))) {
542
- throw new WorkStoreError({
543
- cause: { handoffDispatches, linkedMetadata, row },
544
- operation: "open.migrate.metadata-authority"
545
- });
546
- }
547
- for (const dispatch of handoffDispatches) {
548
- const dispatchAuthority = (() => {
549
- try {
550
- return {
551
- handoff: Schema.decodeUnknownSync(WorkDecisionHandoff)(JSON.parse(dispatch.record)),
552
- lineage: Schema.decodeUnknownSync(WorkDispatchHandoff.fields.lineage)(JSON.parse(dispatch.lineage))
553
- };
554
- }
555
- catch (cause) {
556
- throw new WorkStoreError({ cause, operation: "open.migrate.decode-dispatch-authority" });
557
- }
558
- })();
559
- if (dispatch.laneId !== previous.value.laneId ||
560
- dispatch.occurredAt !== previous.value.occurredAt ||
561
- !currentDecisionHandoffEquivalent(dispatchAuthority.handoff, previous.value) ||
562
- !workDispatchLineageContainedBy(dispatchAuthority.lineage, previous.value.dispatchIds)) {
563
- throw new WorkStoreError({
564
- cause: { dispatch, dispatchAuthority, handoff: previous.value },
565
- operation: "open.migrate.dispatch-authority"
566
- });
567
- }
568
- if (!tables.includes("orchestrator_dispatch_metadata"))
569
- continue;
570
- const metadata = linkedMetadata.find(({ dispatchRequestId }) => dispatchRequestId === dispatch.dispatchRequestId);
571
- if (metadata?.workLink === null || metadata?.workLink === undefined) {
572
- throw new WorkStoreError({
573
- cause: { dispatch, metadata },
574
- operation: "open.migrate.metadata-authority"
575
- });
548
+ for (const dispatch of handoffDispatches) {
549
+ const dispatchAuthority = (() => {
550
+ try {
551
+ return {
552
+ handoff: Schema.decodeUnknownSync(WorkDecisionHandoff)(JSON.parse(dispatch.record)),
553
+ lineage: Schema.decodeUnknownSync(WorkDispatchHandoff.fields.lineage)(JSON.parse(dispatch.lineage))
554
+ };
576
555
  }
577
- const workLink = (() => {
578
- try {
579
- return Schema.decodeUnknownSync(CurrentMetadataWorkLink)(JSON.parse(metadata.workLink));
580
- }
581
- catch (cause) {
582
- throw new WorkStoreError({ cause, operation: "open.migrate.decode-metadata-authority" });
583
- }
584
- })();
585
- requireMetadataAuthority(dispatch.dispatchRequestId, metadata.route, workLink.lineage, "open.migrate.metadata-authority");
586
- if (!currentDecisionHandoffEquivalent(workLink.handoff, previous.value) ||
587
- !workDispatchLineageEquivalent(workLink.lineage, dispatchAuthority.lineage)) {
588
- throw new WorkStoreError({
589
- cause: { handoff: previous.value, workLink },
590
- operation: "open.migrate.metadata-authority"
591
- });
556
+ catch (cause) {
557
+ throw new WorkStoreError({ cause, operation: "open.migrate.decode-dispatch-authority" });
592
558
  }
593
- requireCurrentAgentBindingAuthority(dispatch.dispatchRequestId, previous.value, "open.migrate.current-agent-binding");
559
+ })();
560
+ if (dispatch.laneId !== previous.value.laneId ||
561
+ dispatch.occurredAt !== previous.value.occurredAt ||
562
+ !currentDecisionHandoffEquivalent(dispatchAuthority.handoff, previous.value) ||
563
+ !workDispatchLineageContainedBy(dispatchAuthority.lineage, previous.value.dispatchIds)) {
564
+ throw new WorkStoreError({
565
+ cause: { dispatch, dispatchAuthority, handoff: previous.value },
566
+ operation: "open.migrate.dispatch-authority"
567
+ });
594
568
  }
595
- return [];
596
- }
597
- case "invalid":
598
- throw new WorkStoreError({
599
- cause: previous.cause,
600
- operation: "open.migrate.invalid-handoff"
601
- });
602
- case "previous":
603
- return [{ previous: previous.value, row }];
604
- }
605
- });
606
- for (const { previous, row } of previousDecisions) {
607
- const laneRevision = Schema.decodeUnknownSync(LaneRevisionRow)(database.prepare("SELECT revision FROM work_lane_claims WHERE lane_id = ?").get(row.laneId));
608
- const handoffDispatches = tables.includes("work_dispatch_handoffs")
609
- ? Schema.decodeUnknownSync(Schema.Array(LegacyDispatchStoredRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, handoff_id AS handoffId,
610
- lane_id AS laneId, occurred_at AS occurredAt, lineage, record
611
- FROM work_dispatch_handoffs WHERE handoff_id = ?`).all(row.handoffId))
612
- : [];
613
- if (handoffDispatches.length > 1) {
614
- throw new WorkStoreError({
615
- cause: { handoffDispatches, row },
616
- operation: "open.migrate.dispatch-cardinality"
617
- });
618
- }
619
- const linkedMetadata = tables.includes("orchestrator_dispatch_metadata")
620
- ? Schema.decodeUnknownSync(Schema.Array(MetadataWorkLinkRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, route, work_link AS workLink
621
- FROM orchestrator_dispatch_metadata
622
- WHERE work_link IS NOT NULL
623
- AND CASE WHEN json_valid(work_link)
624
- THEN json_extract(work_link, '$.handoff.id') END = ?`).all(row.handoffId))
625
- : [];
626
- if (tables.includes("orchestrator_dispatch_metadata") &&
627
- (linkedMetadata.length !== handoffDispatches.length ||
628
- linkedMetadata.some((metadata) => !handoffDispatches.some(({ dispatchRequestId }) => dispatchRequestId === metadata.dispatchRequestId)))) {
629
- throw new WorkStoreError({
630
- cause: { handoffDispatches, linkedMetadata, row },
631
- operation: "open.migrate.metadata-authority"
632
- });
633
- }
634
- const bindingRows = tables.includes("work_agent_bindings") && tables.includes("work_dispatch_handoffs")
635
- ? Schema.decodeUnknownSync(Schema.Array(AgentBindingRow))(database.prepare(`SELECT binding.dispatch_request_id AS dispatchRequestId, binding.lane_id AS laneId,
636
- binding.expected_revision AS expectedRevision, binding.revision,
637
- binding.agent_id AS agentId, binding.host, binding.record
638
- FROM work_agent_bindings binding
639
- JOIN work_dispatch_handoffs dispatch
640
- ON dispatch.dispatch_request_id = binding.dispatch_request_id
641
- WHERE dispatch.handoff_id = ?`).all(row.handoffId))
642
- : [];
643
- const bindingRow = bindingRows[0];
644
- const handoffDispatch = bindingRow === undefined
645
- ? undefined
646
- : handoffDispatches.find(({ dispatchRequestId }) => dispatchRequestId === bindingRow.dispatchRequestId);
647
- if (bindingRows.length !== 1 || bindingRow === undefined || handoffDispatch === undefined) {
648
- throw new WorkStoreError({ cause: { bindingRows, row }, operation: "open.migrate.handoff-revision" });
649
- }
650
- const bindingDecision = decodeAgentBindingRow(bindingRow, { dispatchRequestId: handoffDispatch.dispatchRequestId, laneId: previous.laneId }, "open.migrate.agent-binding");
651
- if (bindingDecision._tag === "invalid")
652
- throw bindingDecision.error;
653
- if (laneRevision.revision < bindingDecision.binding.lane.revision) {
654
- throw new WorkStoreError({
655
- cause: { binding: bindingDecision.binding, laneRevision, previous },
656
- operation: "open.migrate.handoff-lane-revision"
657
- });
658
- }
659
- if (bindingDecision.binding.lane.goalId !== previous.goalId) {
660
- throw new WorkStoreError({
661
- cause: { binding: bindingDecision.binding, previous },
662
- operation: "open.migrate.agent-binding.goal"
663
- });
664
- }
665
- requireBindingCompanions(bindingDecision.binding, "open.migrate.agent-binding");
666
- requireLifecycleAuthority(bindingDecision.binding.request.dispatchRequestId, bindingDecision.binding.checkpoint.occurredAt, "open.migrate.agent-binding.lifecycle");
667
- const verifiedDispatches = handoffDispatches.map((dispatch) => {
668
- const dispatchHandoff = Schema.decodeUnknownSync(PreviousWorkDecisionHandoff)(JSON.parse(dispatch.record));
669
- const lineage = Schema.decodeUnknownSync(WorkDispatchHandoff.fields.lineage)(JSON.parse(dispatch.lineage));
670
- if (dispatch.laneId !== previous.laneId || dispatch.occurredAt !== previous.occurredAt ||
671
- !previousDecisionHandoffEquivalent(dispatchHandoff, previous) ||
672
- !workDispatchLineageContainedBy(lineage, previous.dispatchIds)) {
673
- throw new WorkStoreError({
674
- cause: { dispatch, dispatchHandoff, previous },
675
- operation: "open.migrate.dispatch-authority"
676
- });
677
- }
678
- if (tables.includes("orchestrator_dispatch_metadata")) {
569
+ if (!tables.includes("orchestrator_dispatch_metadata"))
570
+ continue;
679
571
  const metadata = linkedMetadata.find(({ dispatchRequestId }) => dispatchRequestId === dispatch.dispatchRequestId);
680
572
  if (metadata?.workLink === null || metadata?.workLink === undefined) {
681
573
  throw new WorkStoreError({
@@ -683,116 +575,217 @@ const migrateLegacyAuthorityTables = (database) => {
683
575
  operation: "open.migrate.metadata-authority"
684
576
  });
685
577
  }
686
- const workLink = Schema.decodeUnknownSync(PreviousMetadataWorkLink)(JSON.parse(metadata.workLink));
578
+ const workLink = (() => {
579
+ try {
580
+ return Schema.decodeUnknownSync(CurrentMetadataWorkLink)(JSON.parse(metadata.workLink));
581
+ }
582
+ catch (cause) {
583
+ throw new WorkStoreError({ cause, operation: "open.migrate.decode-metadata-authority" });
584
+ }
585
+ })();
687
586
  requireMetadataAuthority(dispatch.dispatchRequestId, metadata.route, workLink.lineage, "open.migrate.metadata-authority");
688
- if (!previousDecisionHandoffEquivalent(workLink.handoff, previous) ||
689
- !workDispatchLineageEquivalent(workLink.lineage, lineage)) {
587
+ if (!currentDecisionHandoffEquivalent(workLink.handoff, previous.value) ||
588
+ !workDispatchLineageEquivalent(workLink.lineage, dispatchAuthority.lineage)) {
690
589
  throw new WorkStoreError({
691
- cause: { previous, workLink },
590
+ cause: { handoff: previous.value, workLink },
692
591
  operation: "open.migrate.metadata-authority"
693
592
  });
694
593
  }
594
+ requireCurrentAgentBindingAuthority(dispatch.dispatchRequestId, previous.value, "open.migrate.current-agent-binding");
695
595
  }
696
- return { dispatch, lineage };
697
- });
698
- const migrated = upgradePreviousDecisionHandoff(previous, bindingDecision.binding.request.expectedRevision);
699
- if (migrated.id !== row.handoffId || migrated.sessionId !== row.sessionId ||
700
- migrated.laneId !== row.laneId || migrated.occurredAt !== row.occurredAt) {
701
- throw new WorkStoreError({ cause: { migrated, row }, operation: "open.migrate.handoff-identity" });
702
- }
703
- const encoded = JSON.stringify(migrated);
704
- updateDecision.run(encoded, row.handoffId);
705
- updateDispatch?.run(encoded, row.handoffId);
706
- for (const { dispatch, lineage } of verifiedDispatches) {
707
- updateMetadata?.run(JSON.stringify({ handoff: migrated, lineage }), dispatch.dispatchRequestId);
596
+ return [];
708
597
  }
598
+ case "invalid":
599
+ throw new WorkStoreError({
600
+ cause: previous.cause,
601
+ operation: "open.migrate.invalid-handoff"
602
+ });
603
+ case "previous":
604
+ return [{ previous: previous.value, row }];
605
+ }
606
+ });
607
+ for (const { previous, row } of previousDecisions) {
608
+ const laneRevision = Schema.decodeUnknownSync(LaneRevisionRow)(database.prepare("SELECT revision FROM work_lane_claims WHERE lane_id = ?").get(row.laneId));
609
+ const handoffDispatches = tables.includes("work_dispatch_handoffs")
610
+ ? Schema.decodeUnknownSync(Schema.Array(LegacyDispatchStoredRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, handoff_id AS handoffId,
611
+ lane_id AS laneId, occurred_at AS occurredAt, lineage, record
612
+ FROM work_dispatch_handoffs WHERE handoff_id = ?`).all(row.handoffId))
613
+ : [];
614
+ if (handoffDispatches.length > 1) {
615
+ throw new WorkStoreError({
616
+ cause: { handoffDispatches, row },
617
+ operation: "open.migrate.dispatch-cardinality"
618
+ });
619
+ }
620
+ const linkedMetadata = tables.includes("orchestrator_dispatch_metadata")
621
+ ? Schema.decodeUnknownSync(Schema.Array(MetadataWorkLinkRow))(database.prepare(`SELECT dispatch_request_id AS dispatchRequestId, route, work_link AS workLink
622
+ FROM orchestrator_dispatch_metadata
623
+ WHERE work_link IS NOT NULL
624
+ AND CASE WHEN json_valid(work_link)
625
+ THEN json_extract(work_link, '$.handoff.id') END = ?`).all(row.handoffId))
626
+ : [];
627
+ if (tables.includes("orchestrator_dispatch_metadata") &&
628
+ (linkedMetadata.length !== handoffDispatches.length ||
629
+ linkedMetadata.some((metadata) => !handoffDispatches.some(({ dispatchRequestId }) => dispatchRequestId === metadata.dispatchRequestId)))) {
630
+ throw new WorkStoreError({
631
+ cause: { handoffDispatches, linkedMetadata, row },
632
+ operation: "open.migrate.metadata-authority"
633
+ });
634
+ }
635
+ const bindingRows = tables.includes("work_agent_bindings") && tables.includes("work_dispatch_handoffs")
636
+ ? Schema.decodeUnknownSync(Schema.Array(AgentBindingRow))(database.prepare(`SELECT binding.dispatch_request_id AS dispatchRequestId, binding.lane_id AS laneId,
637
+ binding.expected_revision AS expectedRevision, binding.revision,
638
+ binding.agent_id AS agentId, binding.host, binding.record
639
+ FROM work_agent_bindings binding
640
+ JOIN work_dispatch_handoffs dispatch
641
+ ON dispatch.dispatch_request_id = binding.dispatch_request_id
642
+ WHERE dispatch.handoff_id = ?`).all(row.handoffId))
643
+ : [];
644
+ const bindingRow = bindingRows[0];
645
+ const handoffDispatch = bindingRow === undefined
646
+ ? undefined
647
+ : handoffDispatches.find(({ dispatchRequestId }) => dispatchRequestId === bindingRow.dispatchRequestId);
648
+ if (bindingRows.length !== 1 || bindingRow === undefined || handoffDispatch === undefined) {
649
+ throw new WorkStoreError({ cause: { bindingRows, row }, operation: "open.migrate.handoff-revision" });
650
+ }
651
+ const bindingDecision = decodeAgentBindingRow(bindingRow, { dispatchRequestId: handoffDispatch.dispatchRequestId, laneId: previous.laneId }, "open.migrate.agent-binding");
652
+ if (bindingDecision._tag === "invalid")
653
+ throw bindingDecision.error;
654
+ if (laneRevision.revision < bindingDecision.binding.lane.revision) {
655
+ throw new WorkStoreError({
656
+ cause: { binding: bindingDecision.binding, laneRevision, previous },
657
+ operation: "open.migrate.handoff-lane-revision"
658
+ });
709
659
  }
710
- if (decisions.length > 0 || previousDecisions.length > 0) {
711
- const migratedLedger = Schema.decodeUnknownSync(LedgerBytesRow)(database.prepare(`SELECT COALESCE(SUM(
712
- length(CAST(handoff_id AS BLOB)) + length(CAST(record AS BLOB))
713
- ), 0) AS bytes
714
- FROM work_decision_handoffs`).get());
715
- if (migratedLedger.bytes > workDecisionMaxBytes) {
660
+ if (bindingDecision.binding.lane.goalId !== previous.goalId) {
661
+ throw new WorkStoreError({
662
+ cause: { binding: bindingDecision.binding, previous },
663
+ operation: "open.migrate.agent-binding.goal"
664
+ });
665
+ }
666
+ requireBindingCompanions(bindingDecision.binding, "open.migrate.agent-binding");
667
+ requireLifecycleAuthority(bindingDecision.binding.request.dispatchRequestId, bindingDecision.binding.checkpoint.occurredAt, "open.migrate.agent-binding.lifecycle");
668
+ const verifiedDispatches = handoffDispatches.map((dispatch) => {
669
+ const dispatchHandoff = Schema.decodeUnknownSync(PreviousWorkDecisionHandoff)(JSON.parse(dispatch.record));
670
+ const lineage = Schema.decodeUnknownSync(WorkDispatchHandoff.fields.lineage)(JSON.parse(dispatch.lineage));
671
+ if (dispatch.laneId !== previous.laneId || dispatch.occurredAt !== previous.occurredAt ||
672
+ !previousDecisionHandoffEquivalent(dispatchHandoff, previous) ||
673
+ !workDispatchLineageContainedBy(lineage, previous.dispatchIds)) {
716
674
  throw new WorkStoreError({
717
- cause: migratedLedger,
718
- operation: "open.migrate.handoff-capacity"
675
+ cause: { dispatch, dispatchHandoff, previous },
676
+ operation: "open.migrate.dispatch-authority"
719
677
  });
720
678
  }
721
- }
722
- }
723
- if (tables.includes("orchestrator_dispatch_metadata") && coordinatorDispatchColumns.includes("is_routed")) {
724
- const pageSize = 512;
725
- let cursor = null;
726
- while (true) {
727
- const input = cursor === null
728
- ? database.prepare(`SELECT CAST(metadata.rowid AS TEXT) AS rowId,
729
- metadata.dispatch_request_id AS dispatchRequestId,
730
- dispatch.activity_idempotency_key AS activityIdempotencyKey,
731
- dispatch.command, dispatch.is_routed AS isRouted,
732
- CASE WHEN metadata.work_link IS NULL THEN 0 ELSE 1 END AS hasWorkLink,
733
- metadata.route
734
- FROM orchestrator_dispatch_metadata AS metadata
735
- LEFT JOIN orchestrator_dispatches AS dispatch
736
- ON dispatch.dispatch_request_id = metadata.dispatch_request_id
737
- ORDER BY metadata.rowid ASC LIMIT ?`).all(pageSize)
738
- : database.prepare(`SELECT CAST(metadata.rowid AS TEXT) AS rowId,
739
- metadata.dispatch_request_id AS dispatchRequestId,
740
- dispatch.activity_idempotency_key AS activityIdempotencyKey,
741
- dispatch.command, dispatch.is_routed AS isRouted,
742
- CASE WHEN metadata.work_link IS NULL THEN 0 ELSE 1 END AS hasWorkLink,
743
- metadata.route
744
- FROM orchestrator_dispatch_metadata AS metadata
745
- LEFT JOIN orchestrator_dispatches AS dispatch
746
- ON dispatch.dispatch_request_id = metadata.dispatch_request_id
747
- WHERE metadata.rowid > CAST(? AS INTEGER)
748
- ORDER BY metadata.rowid ASC LIMIT ?`).all(cursor, pageSize);
749
- const rows = Schema.decodeUnknownSync(Schema.Array(CoordinatorMetadataRouteRow))(input);
750
- for (const row of rows) {
751
- try {
752
- const isLegacyUnroutedOrphan = row.activityIdempotencyKey === null && row.command === null &&
753
- row.isRouted === null && row.route === null && row.hasWorkLink === 0;
754
- if (isLegacyUnroutedOrphan)
755
- continue;
756
- if (row.activityIdempotencyKey === null || row.command === null ||
757
- row.route === null || row.isRouted === null) {
758
- throw new WorkStoreError({ cause: row, operation: "open.migrate.metadata-route" });
759
- }
760
- requireCoordinatorRouteBinding(row.command, row.activityIdempotencyKey, row.route, row.hasWorkLink === 1, { _tag: "routed_discriminator", isRouted: row.isRouted }, "open.migrate.metadata-route");
679
+ if (tables.includes("orchestrator_dispatch_metadata")) {
680
+ const metadata = linkedMetadata.find(({ dispatchRequestId }) => dispatchRequestId === dispatch.dispatchRequestId);
681
+ if (metadata?.workLink === null || metadata?.workLink === undefined) {
682
+ throw new WorkStoreError({
683
+ cause: { dispatch, metadata },
684
+ operation: "open.migrate.metadata-authority"
685
+ });
761
686
  }
762
- catch (cause) {
763
- if (Schema.is(WorkStoreError)(cause))
764
- throw cause;
765
- throw new WorkStoreError({ cause, operation: "open.migrate.metadata-route" });
687
+ const workLink = Schema.decodeUnknownSync(PreviousMetadataWorkLink)(JSON.parse(metadata.workLink));
688
+ requireMetadataAuthority(dispatch.dispatchRequestId, metadata.route, workLink.lineage, "open.migrate.metadata-authority");
689
+ if (!previousDecisionHandoffEquivalent(workLink.handoff, previous) ||
690
+ !workDispatchLineageEquivalent(workLink.lineage, lineage)) {
691
+ throw new WorkStoreError({
692
+ cause: { previous, workLink },
693
+ operation: "open.migrate.metadata-authority"
694
+ });
766
695
  }
767
696
  }
768
- const last = rows.at(-1);
769
- if (rows.length < pageSize || last === undefined)
770
- break;
771
- cursor = last.rowId;
697
+ return { dispatch, lineage };
698
+ });
699
+ const migrated = upgradePreviousDecisionHandoff(previous, bindingDecision.binding.request.expectedRevision);
700
+ if (migrated.id !== row.handoffId || migrated.sessionId !== row.sessionId ||
701
+ migrated.laneId !== row.laneId || migrated.occurredAt !== row.occurredAt) {
702
+ throw new WorkStoreError({ cause: { migrated, row }, operation: "open.migrate.handoff-identity" });
703
+ }
704
+ const encoded = JSON.stringify(migrated);
705
+ updateDecision.run(encoded, row.handoffId);
706
+ updateDispatch?.run(encoded, row.handoffId);
707
+ for (const { dispatch, lineage } of verifiedDispatches) {
708
+ updateMetadata?.run(JSON.stringify({ handoff: migrated, lineage }), dispatch.dispatchRequestId);
772
709
  }
773
- const invalidRoutedMetadata = Schema.decodeUnknownSync(Schema.Array(RoutedMetadataCardinalityRow))(database.prepare(`SELECT dispatch.dispatch_request_id AS dispatchRequestId,
774
- COUNT(metadata.dispatch_request_id) AS metadataCount
775
- FROM orchestrator_dispatches AS dispatch
776
- LEFT JOIN orchestrator_dispatch_metadata AS metadata
777
- ON metadata.dispatch_request_id = dispatch.dispatch_request_id
778
- WHERE dispatch.is_routed = 1
779
- GROUP BY dispatch.dispatch_request_id
780
- HAVING COUNT(metadata.dispatch_request_id) <> 1
781
- LIMIT 1`).all())[0];
782
- if (invalidRoutedMetadata !== undefined) {
710
+ }
711
+ if (decisions.length > 0 || previousDecisions.length > 0) {
712
+ const migratedLedger = Schema.decodeUnknownSync(LedgerBytesRow)(database.prepare(`SELECT COALESCE(SUM(
713
+ length(CAST(handoff_id AS BLOB)) + length(CAST(record AS BLOB))
714
+ ), 0) AS bytes
715
+ FROM work_decision_handoffs`).get());
716
+ if (migratedLedger.bytes > workDecisionMaxBytes) {
783
717
  throw new WorkStoreError({
784
- cause: invalidRoutedMetadata,
785
- operation: "open.migrate.routed-metadata"
718
+ cause: migratedLedger,
719
+ operation: "open.migrate.handoff-capacity"
786
720
  });
787
721
  }
788
722
  }
789
- database.exec("COMMIT");
790
- transaction = false;
791
723
  }
792
- catch (error) {
793
- if (transaction)
794
- database.exec("ROLLBACK");
795
- throw error;
724
+ if (tables.includes("orchestrator_dispatch_metadata") && coordinatorDispatchColumns.includes("is_routed")) {
725
+ const pageSize = 512;
726
+ let cursor = null;
727
+ while (true) {
728
+ const input = cursor === null
729
+ ? database.prepare(`SELECT CAST(metadata.rowid AS TEXT) AS rowId,
730
+ metadata.dispatch_request_id AS dispatchRequestId,
731
+ dispatch.activity_idempotency_key AS activityIdempotencyKey,
732
+ dispatch.command, dispatch.is_routed AS isRouted,
733
+ CASE WHEN metadata.work_link IS NULL THEN 0 ELSE 1 END AS hasWorkLink,
734
+ metadata.route
735
+ FROM orchestrator_dispatch_metadata AS metadata
736
+ LEFT JOIN orchestrator_dispatches AS dispatch
737
+ ON dispatch.dispatch_request_id = metadata.dispatch_request_id
738
+ ORDER BY metadata.rowid ASC LIMIT ?`).all(pageSize)
739
+ : database.prepare(`SELECT CAST(metadata.rowid AS TEXT) AS rowId,
740
+ metadata.dispatch_request_id AS dispatchRequestId,
741
+ dispatch.activity_idempotency_key AS activityIdempotencyKey,
742
+ dispatch.command, dispatch.is_routed AS isRouted,
743
+ CASE WHEN metadata.work_link IS NULL THEN 0 ELSE 1 END AS hasWorkLink,
744
+ metadata.route
745
+ FROM orchestrator_dispatch_metadata AS metadata
746
+ LEFT JOIN orchestrator_dispatches AS dispatch
747
+ ON dispatch.dispatch_request_id = metadata.dispatch_request_id
748
+ WHERE metadata.rowid > CAST(? AS INTEGER)
749
+ ORDER BY metadata.rowid ASC LIMIT ?`).all(cursor, pageSize);
750
+ const rows = Schema.decodeUnknownSync(Schema.Array(CoordinatorMetadataRouteRow))(input);
751
+ for (const row of rows) {
752
+ try {
753
+ const isLegacyUnroutedOrphan = row.activityIdempotencyKey === null && row.command === null &&
754
+ row.isRouted === null && row.route === null && row.hasWorkLink === 0;
755
+ if (isLegacyUnroutedOrphan)
756
+ continue;
757
+ if (row.activityIdempotencyKey === null || row.command === null ||
758
+ row.route === null || row.isRouted === null) {
759
+ throw new WorkStoreError({ cause: row, operation: "open.migrate.metadata-route" });
760
+ }
761
+ requireCoordinatorRouteBinding(row.command, row.activityIdempotencyKey, row.route, row.hasWorkLink === 1, { _tag: "routed_discriminator", isRouted: row.isRouted }, "open.migrate.metadata-route");
762
+ }
763
+ catch (cause) {
764
+ if (Schema.is(WorkStoreError)(cause))
765
+ throw cause;
766
+ throw new WorkStoreError({ cause, operation: "open.migrate.metadata-route" });
767
+ }
768
+ }
769
+ const last = rows.at(-1);
770
+ if (rows.length < pageSize || last === undefined)
771
+ break;
772
+ cursor = last.rowId;
773
+ }
774
+ const invalidRoutedMetadata = Schema.decodeUnknownSync(Schema.Array(RoutedMetadataCardinalityRow))(database.prepare(`SELECT dispatch.dispatch_request_id AS dispatchRequestId,
775
+ COUNT(metadata.dispatch_request_id) AS metadataCount
776
+ FROM orchestrator_dispatches AS dispatch
777
+ LEFT JOIN orchestrator_dispatch_metadata AS metadata
778
+ ON metadata.dispatch_request_id = dispatch.dispatch_request_id
779
+ WHERE dispatch.is_routed = 1
780
+ GROUP BY dispatch.dispatch_request_id
781
+ HAVING COUNT(metadata.dispatch_request_id) <> 1
782
+ LIMIT 1`).all())[0];
783
+ if (invalidRoutedMetadata !== undefined) {
784
+ throw new WorkStoreError({
785
+ cause: invalidRoutedMetadata,
786
+ operation: "open.migrate.routed-metadata"
787
+ });
788
+ }
796
789
  }
797
790
  };
798
791
  const readTransactionLedgerTotals = (database) => Schema.decodeUnknownSync(TransactionLedgerTotalsRow)(database.prepare(`SELECT transaction_count AS transactionCount, transaction_bytes AS transactionBytes
@@ -1209,9 +1202,14 @@ export class WorkStore {
1209
1202
  const opened = yield* openPrivateSqlite(path, {
1210
1203
  busyTimeoutMillis: workStoreBusyTimeoutMillis,
1211
1204
  initialize: (database) => {
1212
- migrateLegacyAuthorityTables(database);
1213
- const hadLaneOperationLedger = database.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'work_lane_operations'").get() !== undefined;
1214
- database.exec(`
1205
+ // One transaction: legacy migration and schema creation succeed or fail
1206
+ // together, and their statements share one commit instead of syncing
1207
+ // the file once each.
1208
+ database.exec("BEGIN IMMEDIATE");
1209
+ try {
1210
+ migrateLegacyAuthorityTables(database);
1211
+ const hadLaneOperationLedger = database.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'work_lane_operations'").get() !== undefined;
1212
+ database.exec(`
1215
1213
  CREATE TABLE IF NOT EXISTS work_goal_events (
1216
1214
  event_id TEXT PRIMARY KEY,
1217
1215
  goal_id TEXT NOT NULL,
@@ -1317,19 +1315,19 @@ export class WorkStore {
1317
1315
  CREATE INDEX IF NOT EXISTS work_decision_handoffs_session
1318
1316
  ON work_decision_handoffs (session_id);
1319
1317
  `);
1320
- const columns = Schema.decodeUnknownSync(Schema.Array(Schema.Struct({ name: Schema.String })))(database.prepare("PRAGMA table_info(work_goal_events)").all());
1321
- if (!columns.some(({ name }) => name === "transaction_id")) {
1322
- database.exec("ALTER TABLE work_goal_events ADD COLUMN transaction_id TEXT");
1323
- }
1324
- if (!hadLaneOperationLedger) {
1325
- database.exec(`
1318
+ const columns = Schema.decodeUnknownSync(Schema.Array(Schema.Struct({ name: Schema.String })))(database.prepare("PRAGMA table_info(work_goal_events)").all());
1319
+ if (!columns.some(({ name }) => name === "transaction_id")) {
1320
+ database.exec("ALTER TABLE work_goal_events ADD COLUMN transaction_id TEXT");
1321
+ }
1322
+ if (!hadLaneOperationLedger) {
1323
+ database.exec(`
1326
1324
  INSERT OR IGNORE INTO work_lane_operations
1327
1325
  (operation_id, lane_id, goal_id, phase, revision, record)
1328
1326
  SELECT operation_id, lane_id, goal_id, phase, revision, record
1329
1327
  FROM work_lane_claims
1330
1328
  `);
1331
- }
1332
- database.exec(`
1329
+ }
1330
+ database.exec(`
1333
1331
  INSERT OR IGNORE INTO work_lane_operation_totals
1334
1332
  (singleton, operation_count, operation_bytes)
1335
1333
  SELECT 1, COUNT(*), COALESCE(SUM(
@@ -1337,7 +1335,7 @@ export class WorkStore {
1337
1335
  ), 0)
1338
1336
  FROM work_lane_operations
1339
1337
  `);
1340
- database.exec(`
1338
+ database.exec(`
1341
1339
  INSERT OR IGNORE INTO work_goal_transaction_totals
1342
1340
  (singleton, transaction_count, transaction_bytes)
1343
1341
  SELECT 1, COUNT(*), COALESCE(SUM(
@@ -1345,7 +1343,7 @@ export class WorkStore {
1345
1343
  ), 0)
1346
1344
  FROM work_goal_transactions
1347
1345
  `);
1348
- database.exec(`
1346
+ database.exec(`
1349
1347
  INSERT OR REPLACE INTO work_decision_totals
1350
1348
  (singleton, decision_count, decision_bytes)
1351
1349
  SELECT 1, COUNT(*), COALESCE(SUM(
@@ -1353,6 +1351,15 @@ export class WorkStore {
1353
1351
  ), 0)
1354
1352
  FROM work_decision_handoffs
1355
1353
  `);
1354
+ database.exec("COMMIT");
1355
+ }
1356
+ catch (error) {
1357
+ // SQLite may already have rolled back (SQLITE_FULL, IOERR); a second
1358
+ // ROLLBACK would throw and replace the real error.
1359
+ if (database.isTransaction)
1360
+ database.exec("ROLLBACK");
1361
+ throw error;
1362
+ }
1356
1363
  }
1357
1364
  }).pipe(Effect.mapError(fromPrivateDatabaseError));
1358
1365
  return new WorkStore(path, opened, cryptoService);