@yielded/agent-storage-cloudflare 0.1.0-beta.167 → 0.1.0-beta.169

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 (71) hide show
  1. package/dist/DoMemoryStore.mjs +1 -1
  2. package/dist/DoMessageDeliveryStore.d.mts +1 -1
  3. package/dist/DoMessageDeliveryStore.mjs +3 -2
  4. package/dist/DoMessageDeliveryStore.mjs.map +1 -1
  5. package/dist/DoScheduleStore.mjs +16 -37
  6. package/dist/DoScheduleStore.mjs.map +1 -1
  7. package/dist/DoStorageConfig.d.mts +2 -10
  8. package/dist/DoStorageConfig.mjs +2 -11
  9. package/dist/DoStorageConfig.mjs.map +1 -1
  10. package/dist/{DoStorageError-CGuAfSwF.d.mts → DoStorageError-CxjMnzUA.d.mts} +7 -18
  11. package/dist/DoStorageError.d.mts +2 -2
  12. package/dist/DoStorageError.mjs +14 -30
  13. package/dist/DoStorageError.mjs.map +1 -1
  14. package/dist/DoStorageFailpoint.d.mts +1 -1
  15. package/dist/DoStorageFailpointTesting.d.mts +1 -1
  16. package/dist/{DoStorageVersion-iNSyi3cJ.d.mts → DoStorageVersion-DTFoLglB.d.mts} +3 -4
  17. package/dist/DoStorageVersion.d.mts +1 -1
  18. package/dist/DoStorageVersion.mjs +2 -2
  19. package/dist/DoStorageVersion.mjs.map +1 -1
  20. package/dist/DoSubmissionLedger.d.mts +7 -5
  21. package/dist/DoSubmissionLedger.mjs +378 -528
  22. package/dist/DoSubmissionLedger.mjs.map +1 -1
  23. package/dist/DoSubscriptionStore.mjs +5 -24
  24. package/dist/DoSubscriptionStore.mjs.map +1 -1
  25. package/dist/{DoThreadStore-D_83OjCH.d.mts → DoThreadStore-Bd4fTns3.d.mts} +17 -18
  26. package/dist/DoThreadStore-CwBvTLoq.mjs +468 -0
  27. package/dist/DoThreadStore-CwBvTLoq.mjs.map +1 -0
  28. package/dist/DoThreadStore.d.mts +2 -2
  29. package/dist/DoThreadStore.mjs +4 -529
  30. package/dist/MemoryProtocol.mjs +3 -3
  31. package/dist/MemoryProtocol.mjs.map +1 -1
  32. package/dist/PortProtocol.d.mts +11727 -3410
  33. package/dist/PortProtocol.mjs +103 -15
  34. package/dist/PortProtocol.mjs.map +1 -1
  35. package/dist/PortRouting.d.mts +14 -9
  36. package/dist/PortRouting.mjs +155 -29
  37. package/dist/PortRouting.mjs.map +1 -1
  38. package/dist/do-journal-Cawnsetj.mjs +761 -0
  39. package/dist/do-journal-Cawnsetj.mjs.map +1 -0
  40. package/dist/index.d.mts +3 -3
  41. package/dist/index.mjs +1 -1
  42. package/dist/migrations-D_EP3KYf.mjs +148 -0
  43. package/dist/migrations-D_EP3KYf.mjs.map +1 -0
  44. package/dist/{owned-state-DHm2aRXx.mjs → owned-state-BHzUi0eb.mjs} +50 -19
  45. package/dist/owned-state-BHzUi0eb.mjs.map +1 -0
  46. package/package.json +1 -1
  47. package/src/DoMessageDeliveryStore.ts +2 -1
  48. package/src/DoScheduleStore.ts +19 -59
  49. package/src/DoStorageConfig.ts +2 -11
  50. package/src/DoStorageError.ts +3 -26
  51. package/src/DoStorageVersion.ts +1 -1
  52. package/src/DoSubmissionLedger.ts +1806 -2030
  53. package/src/DoSubscriptionStore.ts +5 -47
  54. package/src/DoThreadStore.ts +276 -622
  55. package/src/MemoryProtocol.ts +3 -3
  56. package/src/PortProtocol.ts +198 -12
  57. package/src/PortRouting.ts +527 -108
  58. package/src/internal/canonical-append.ts +15 -0
  59. package/src/internal/do-journal.ts +548 -1440
  60. package/src/internal/migrations.ts +184 -287
  61. package/src/internal/owned-state.ts +81 -30
  62. package/src/internal/prompt-checkpoint.ts +158 -0
  63. package/src/internal/storage-span.ts +2 -5
  64. package/dist/DoThreadStore.mjs.map +0 -1
  65. package/dist/do-journal-DUhjZkOf.mjs +0 -1352
  66. package/dist/do-journal-DUhjZkOf.mjs.map +0 -1
  67. package/dist/migrations-DtKz_Gxr.mjs +0 -306
  68. package/dist/migrations-DtKz_Gxr.mjs.map +0 -1
  69. package/dist/owned-state-DHm2aRXx.mjs.map +0 -1
  70. package/src/internal/message-delivery-schema.ts +0 -24
  71. package/src/internal/recovery-checkpoint-schema.ts +0 -17
@@ -2,19 +2,24 @@ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import { DoStorageConfig } from "./DoStorageConfig.mjs";
3
3
  import { DoStorageFailpoint } from "./DoStorageFailpoint.mjs";
4
4
  import { DoLedgerError, DoStorageCorruptionError, DoStorageError } from "./DoStorageError.mjs";
5
- import { n as ownedRows } from "./owned-state-DHm2aRXx.mjs";
6
- import { a as initializeDoJournal, i as decodeRows, s as withStorageSpan } from "./do-journal-DUhjZkOf.mjs";
7
- import { storageConfigLayer, storageFailpointLayer } from "./DoThreadStore.mjs";
5
+ import { n as ownedRows } from "./owned-state-BHzUi0eb.mjs";
6
+ import { i as initializeDoJournal, r as decodeRows } from "./do-journal-Cawnsetj.mjs";
7
+ import { l as prepareCanonicalAppend, o as storageConfigLayer, s as storageFailpointLayer } from "./DoThreadStore-CwBvTLoq.mjs";
8
+ import { BrowserCrypto } from "@effect/platform-browser";
8
9
  import { Cause, Clock, Context, Crypto, DateTime, Effect, Layer, Option, Schema, Stream } from "effect";
9
10
  import * as SqlClientService from "effect/sql/SqlClient";
10
- import { SqliteClient } from "@effect/sql-sqlite-do";
11
+ import { definitionJson, makeCanonicalStorage, recordBatchId, runKey } from "@yielded/agent-storage-sql/sql-canonical-storage";
11
12
  import { SqlStorageProgress } from "@yielded/agent-storage-sql/sql-storage-progress";
12
- import { EMPTY_TAIL_DIGEST } from "@yielded/agent/digest";
13
- import { ApprovalDecision, CanonicalSequence, DefinitionDigests, Digest, PersistedJson, ProducerEpoch, RecordEnvelope, SettlementOutcome, WorkerAdmission } from "@yielded/agent/records";
14
- import { ThreadStoreDiagnostic } from "@yielded/agent/thread-store";
15
- import { BrowserCrypto } from "@effect/platform-browser";
13
+ import { canonicalRecordJson } from "@yielded/agent-storage-sql/sql-thread-archive-range";
14
+ import { ApprovalDecision, CanonicalSequence, DefinitionDigests, Digest, PersistedJson, RecordEnvelope, SettlementOutcome, WorkerAdmission } from "@yielded/agent/records";
15
+ import { AppendConflict, ThreadNotMaterialized, ThreadStoreDiagnostic, ThreadStoreError, makeTransferFactCheck } from "@yielded/agent/thread-store";
16
+ import { SqliteClient } from "@effect/sql-sqlite-do";
17
+ import { StorageOwner } from "@yielded/agent/run-storage";
18
+ import { decodeAbortFact, decodeAdmissionFact, decodeApprovalFact, decodeResolutionFact } from "@yielded/agent-storage-sql/sql-admission-facts";
16
19
  import { InputMessage } from "@yielded/agent/messaging";
17
- import { AbortCommand, AbortIntent, AbortIntentRequest, AdmissionAdmitted, AdmissionConflict, AdmissionFence, AdmissionGroup, AdmissionNotAdmitted, AdmissionPolicyError, AdmissionRequest, AdmissionResult, ApprovalConflict, ApprovalDecisionCommand, ApprovalDecisionIntent, AttachChildToReservationRequest, BeginChildBudgetReleaseRequest, ChildAttachmentSnapshot, ChildBudgetReservationRequest, ChildBudgetReservationSnapshot, ChildReservationConflict, ChildReservationStatus, ChildSettledNotification, Claim, ClaimJoiningRequest, ClaimRequest, InputAppliedMarker, JoinSnapshot, JoinedToHost, JoiningClaim, LedgerCapabilities, LedgerError, MarkInputAppliedRequest, MarkJoinedRequest, MarkReadyRequest, MarkUnknownRequest, OwnershipLost, OwnershipRenewal, OwnershipSnapshot, ParentLinkage, QueueSequence, RecoverySnapshot, RecoverySnapshotRequest, ReleaseChildBudgetRequest, ReleaseOwnershipRequest, RenewOwnershipRequest, ReservedChildBudget, ReservedSettlement, RevertJoiningRequest, Settlement, SettlementConflict, SettlementFinalization, SettlementReservation, SettlementReservationSnapshot, SubmissionAdmissionFence, SubmissionLedger, SubmissionLookup, SubmissionLookupByKey, SubmissionSnapshot, SubmissionState, SubmissionWorkItem, SuspendRequest, SuspensionReason, SuspensionSnapshot, UnknownResolution, UnknownResolutionCommand, UnknownResolutionConflict, UnknownResolutionIntent, WorkerLedgerState, WorkerStopCommand, settlementFailureFromRecord, submissionAbortRecordId, workerTerminalFromRecord } from "@yielded/agent/submission-ledger";
20
+ import { runIdForSubmission } from "@yielded/agent/run-journal";
21
+ import { SettlementPublicationResult, SettlementPublisher, validateCanonicalSettlement, validateJoinedSettlement, validatePublication } from "@yielded/agent/settlement-publisher";
22
+ import { AbortCommand, AbortIntent, AbortIntentRequest, AdmissionAdmitted, AdmissionConflict, AdmissionFence, AdmissionGroup, AdmissionNotAdmitted, AdmissionPolicyError, AdmissionRequest, AdmissionResult, ApprovalConflict, ApprovalDecisionCommand, ApprovalDecisionIntent, AttachChildToReservationRequest, BeginChildBudgetReleaseRequest, ChildAttachmentSnapshot, ChildBudgetReservationRequest, ChildBudgetReservationSnapshot, ChildReservationConflict, ChildReservationStatus, ChildSettledNotification, Claim, ClaimJoiningRequest, ClaimRequest, FundingOwner, FundingOwnerRequest, InputAppliedMarker, JoinSnapshot, JoinedToHost, JoiningClaim, LedgerCapabilities, LedgerError, MarkInputAppliedRequest, MarkJoinedRequest, MarkReadyRequest, MarkUnknownRequest, OwnershipLost, ParentLinkage, QueueSequence, RecoverySnapshot, RecoverySnapshotRequest, ReleaseChildBudgetRequest, ReservedChildBudget, RevertJoiningRequest, Settlement, SettlementConflict, SettlementFinalization, SubmissionAdmissionFence, SubmissionLedger, SubmissionLookup, SubmissionLookupByKey, SubmissionSnapshot, SubmissionState, SubmissionWorkItem, SuspendRequest, SuspensionReason, SuspensionSnapshot, UnknownResolution, UnknownResolutionCommand, UnknownResolutionConflict, UnknownResolutionIntent, UnknownResolutionKind, WorkerLedgerState, WorkerStopCommand, approvalDecisionsCover, settlementFailureFromRecord, submissionAbortRecordId, submissionSettlementBatchId, submissionSettlementRecordId, unknownResolutionKind, unknownResolutionMatchesResult, workerTerminalFromRecord } from "@yielded/agent/submission-ledger";
18
23
  import { AssignmentTerminal } from "@yielded/agent/worker";
19
24
  //#region src/DoSubmissionLedger.ts
20
25
  var DoSubmissionLedger_exports = /* @__PURE__ */ __exportAll({
@@ -29,7 +34,7 @@ const BoundedStoredText = Schema.String.check(Schema.isMaxLength(2e6));
29
34
  const BoundedIdentifier = Schema.NonEmptyString.check(Schema.isMaxLength(1024));
30
35
  const BoundedTimestamp = Schema.NonEmptyString.check(Schema.isMaxLength(128));
31
36
  const SCAN_PAGE_SIZE = 256;
32
- const EPOCH_ZERO = Schema.decodeSync(ProducerEpoch)(0);
37
+ const admissionFitsTransfer = makeTransferFactCheck(AdmissionRequest);
33
38
  const RESUME_IMMEDIATELY = "resume-immediately";
34
39
  const SUSPENDED = "suspended";
35
40
  const NOT_WAITING = "not-waiting";
@@ -50,6 +55,8 @@ var SubmissionRow = class extends Schema.Class("SubmissionRow")({
50
55
  receipt_id: BoundedIdentifier,
51
56
  state: SubmissionState,
52
57
  settled_outcome: Schema.NullOr(SettlementOutcome),
58
+ settled_record_id: Schema.NullOr(BoundedIdentifier),
59
+ finalized_at: Schema.NullOr(BoundedTimestamp),
53
60
  created_at: BoundedTimestamp,
54
61
  ready_at: Schema.NullOr(BoundedTimestamp),
55
62
  input_applied_record_id: Schema.NullOr(BoundedIdentifier),
@@ -106,29 +113,12 @@ var ApprovalDecisionRow = class extends Schema.Class("ApprovalDecisionRow")({
106
113
  var UnknownResolutionRow = class extends Schema.Class("UnknownResolutionRow")({
107
114
  submission_id: BoundedIdentifier,
108
115
  tool_call_id: BoundedIdentifier,
116
+ resolution_kind: UnknownResolutionKind,
109
117
  author: BoundedIdentifier,
110
118
  reason: BoundedStoredText,
111
119
  resolution_json: BoundedStoredText,
112
120
  resolved_at: BoundedTimestamp
113
121
  }) {};
114
- var OwnershipRow = class extends Schema.Class("OwnershipRow")({
115
- submission_id: BoundedIdentifier,
116
- attempt_id: BoundedIdentifier,
117
- ownership_token: BoundedIdentifier,
118
- producer_epoch: ProducerEpoch,
119
- owner_producer_id: BoundedIdentifier,
120
- lease_expires_at: BoundedTimestamp
121
- }) {};
122
- var ReservationRow = class extends Schema.Class("ReservationRow")({
123
- submission_id: BoundedIdentifier,
124
- settlement_id: BoundedIdentifier,
125
- outcome: SettlementOutcome,
126
- record_id: BoundedIdentifier,
127
- record_json: BoundedStoredText,
128
- record_digest: Digest,
129
- reserved_at: BoundedTimestamp,
130
- finalized_at: Schema.NullOr(BoundedTimestamp)
131
- }) {};
132
122
  var AbortIntentRow = class extends Schema.Class("AbortIntentRow")({
133
123
  submission_id: BoundedIdentifier,
134
124
  author: BoundedIdentifier,
@@ -148,13 +138,14 @@ const SUBMISSION_COLUMNS = `
148
138
  principal,
149
139
  idempotency_key,
150
140
  agent_id,
151
- agent_digests_json,
152
141
  deployment_id,
153
142
  input_json,
154
143
  input_digest,
155
144
  receipt_id,
156
145
  state,
157
146
  settled_outcome,
147
+ settled_record_id,
148
+ finalized_at,
158
149
  created_at,
159
150
  ready_at,
160
151
  input_applied_record_id,
@@ -189,7 +180,6 @@ const ToolCallIdSchema = ApprovalDecisionCommand.fields.toolCallId;
189
180
  const ToolCallIdList = Schema.Array(ToolCallIdSchema);
190
181
  const encodePersistedJsonText = Schema.encodeEffect(Schema.fromJsonString(PersistedJson));
191
182
  const encodeDefinitionDigestsText = Schema.encodeEffect(Schema.fromJsonString(DefinitionDigests));
192
- const encodeRecordEnvelopeText = Schema.encodeEffect(Schema.fromJsonString(RecordEnvelope));
193
183
  const decodeRecordEnvelopeText = Schema.decodeEffect(Schema.fromJsonString(RecordEnvelope));
194
184
  const encodeSuspensionReasonText = Schema.encodeEffect(Schema.fromJsonString(SuspensionReason));
195
185
  const encodeUnknownResolutionText = Schema.encodeEffect(Schema.fromJsonString(UnknownResolution));
@@ -198,12 +188,10 @@ const decodeToolCallIdsText = Schema.decodeEffect(Schema.fromJsonString(ToolCall
198
188
  const parseStoredJsonText = Schema.decodeEffect(Schema.fromJsonString(Schema.Json));
199
189
  const decodeAdmissionResult = Schema.decodeUnknownEffect(AdmissionResult);
200
190
  const decodeClaim = Schema.decodeUnknownEffect(Claim);
201
- const decodeOwnershipRenewal = Schema.decodeUnknownEffect(OwnershipRenewal);
202
191
  const decodeSettlement = Schema.decodeUnknownEffect(Settlement);
203
192
  const decodeAbortIntent = Schema.decodeUnknownEffect(AbortIntent);
204
- const decodeOwnershipSnapshot = Schema.decodeUnknownEffect(OwnershipSnapshot);
205
193
  const decodeInputAppliedMarker = Schema.decodeUnknownEffect(InputAppliedMarker);
206
- const decodeSubmissionSnapshotUnknown = Schema.decodeUnknownEffect(SubmissionSnapshot);
194
+ const decodeSubmissionSnapshotType = (input) => Schema.decodeEffect(Schema.toType(Schema.Struct(SubmissionSnapshot.fields)))(input).pipe(Effect.map((fields) => SubmissionSnapshot.make(fields)));
207
195
  const decodeSubmissionId = Schema.decodeUnknownEffect(SubmissionSnapshot.fields.submissionId);
208
196
  const decodeQueueSequence = Schema.decodeUnknownEffect(QueueSequence);
209
197
  const decodeUtcInstant = Schema.decodeUnknownEffect(Schema.DateTimeUtcFromString);
@@ -243,27 +231,32 @@ const submissionsRows = ownedRows(SubmissionRow, "effect_agent_submissions", (ro
243
231
  "thread_id",
244
232
  "principal",
245
233
  "idempotency_key"
246
- ]]);
247
- const ownershipRows = ownedRows(OwnershipRow, "effect_agent_submission_ownership", (row) => row.submission_id, "submission_id");
248
- const reservationsRows = ownedRows(ReservationRow, "effect_agent_settlement_reservations", (row) => row.submission_id, "submission_id");
234
+ ]], (sql) => sql`*, ${definitionJson(sql)} AS agent_digests_json`);
249
235
  const abortsRows = ownedRows(AbortIntentRow, "effect_agent_abort_intents", (row) => row.submission_id, "submission_id");
250
236
  const approvalsRows = ownedRows(ApprovalDecisionRow, "effect_agent_approval_decisions", (row) => JSON.stringify([row.submission_id, row.tool_call_id]));
251
- const resolutionsRows = ownedRows(UnknownResolutionRow, "effect_agent_unknown_resolutions", (row) => JSON.stringify([row.submission_id, row.tool_call_id]));
237
+ const resolutionsRows = ownedRows(UnknownResolutionRow, "effect_agent_unknown_resolutions", (row) => JSON.stringify([
238
+ row.submission_id,
239
+ row.tool_call_id,
240
+ row.resolution_kind
241
+ ]));
252
242
  const childSettlementsRows = ownedRows(ChildSettlementMarkerRow, "effect_agent_child_settlements", (row) => JSON.stringify([row.parent_submission_id, row.child_submission_id]));
253
243
  const childReservationsRows = ownedRows(ChildReservationRow, "effect_agent_child_reservations", (row) => row.reservation_id, "reservation_id");
254
244
  const workerStopsRows = ownedRows(WorkerStopRow, "effect_agent_worker_stops", (row) => row.thread_id, "thread_id");
255
245
  const submissionWorkRows = ownedRows(SubmissionWorkRow, "effect_agent_submissions", (row) => row.submission_id);
256
- const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
246
+ const makeServices = Effect.fnUntraced(function* () {
257
247
  const progress = yield* SqlStorageProgress;
258
248
  const recordProgress = (kind, operation) => progress.committed(kind).pipe(Effect.catchCause((cause) => Effect.failCause(Cause.map(cause, internalFailure(operation)))));
259
249
  const config = yield* DoStorageConfig;
260
250
  const failpoint = yield* DoStorageFailpoint;
261
251
  const sql = yield* SqlClientService.SqlClient;
262
252
  const crypto = yield* Crypto.Crypto;
253
+ const physical = yield* makeCanonicalStorage();
254
+ const submissionColumns = sql`${sql.literal(SUBMISSION_COLUMNS)}, ${definitionJson(sql)} AS agent_digests_json`;
263
255
  const admissionFence = yield* SubmissionAdmissionFence;
264
256
  const journal = yield* initializeDoJournal(sql, failpoint.hit, config.maxStoredValueBytes);
265
257
  const lifecycle = journal.lifecycle;
266
258
  const state = journal.state;
259
+ const owner = state.owner;
267
260
  const submissionViews = submissionsRows(state, sql);
268
261
  const workRows = submissionWorkRows(state, sql);
269
262
  const rows = {
@@ -273,8 +266,6 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
273
266
  seed: submissionViews.seed,
274
267
  write: (effect) => submissionViews.write(effect).pipe(Effect.tap((changed) => workRows.write(Effect.succeed(changed))))
275
268
  },
276
- ownership: ownershipRows(state, sql),
277
- reservations: reservationsRows(state, sql),
278
269
  aborts: abortsRows(state, sql),
279
270
  approvals: approvalsRows(state, sql),
280
271
  resolutions: resolutionsRows(state, sql),
@@ -284,20 +275,12 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
284
275
  workerStops: workerStopsRows(state, sql)
285
276
  };
286
277
  const cached = (effect, operation) => effect.pipe(Effect.mapError(internalFailure(operation)));
287
- const laneWork = Effect.fnUntraced(function* (threadId, operation) {
288
- const work = yield* cached(workRows.matching(JSON.stringify(["lane", threadId]), (row) => row.thread_id === threadId, sql`SELECT submission_id, thread_id, queue_sequence, principal, idempotency_key,
289
- deployment_id, receipt_id, state FROM effect_agent_submissions WHERE thread_id = ${threadId} LIMIT 129`, 128), operation);
290
- return work.length > 128 ? void 0 : [...work].sort((a, b) => a.queue_sequence - b.queue_sequence);
291
- });
292
278
  const byKey = Effect.fnUntraced(function* (request, operation) {
293
- const lane = yield* laneWork(request.threadId, operation);
294
- if (lane === void 0) return yield* cached(rows.submissions.byFields([
279
+ return yield* cached(rows.submissions.byFields([
295
280
  ["thread_id", request.threadId],
296
281
  ["principal", request.principal],
297
282
  ["idempotency_key", request.idempotencyKey]
298
283
  ]), operation);
299
- const matches = lane.filter((row) => row.principal === request.principal && row.idempotency_key === request.idempotencyKey);
300
- return yield* Effect.forEach(matches, (row) => requireSubmission(operation, row.submission_id));
301
284
  }, state.read);
302
285
  const retainLifecycle = (submission, fact) => lifecycle === void 0 ? Effect.void : Effect.gen(function* () {
303
286
  const ownerThreadId = yield* Schema.decodeEffect(SubmissionSnapshot.fields.threadId)(submission.thread_id).pipe(Effect.mapError(internalFailure("lifecycle owner")));
@@ -331,18 +314,17 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
331
314
  * failures surface as LedgerError carrying the typed `DoStorageError` as cause.
332
315
  */
333
316
  const inWriteTransaction = (operation, effect, expected) => journal.withWriteTransaction(operation, expected)(effect).pipe(Effect.mapError((error) => isDoStorageError(error) ? internalFailure(operation)(error) : error));
334
- const mintUuid = (operation) => crypto.randomUUIDv7.pipe(Effect.mapError((error) => internalFailure(operation)(error)));
317
+ const mintUuid = (operation) => crypto.randomUUIDv7().pipe(Effect.mapError((error) => internalFailure(operation)(error)));
335
318
  const currentInstant = Effect.map(Clock.currentTimeMillis, (millis) => ({
336
319
  millis,
337
320
  iso: new Date(millis).toISOString()
338
321
  }));
339
- const timestampMillis = (operation, rowKey) => (timestamp) => decodeUtcInstant(timestamp).pipe(Effect.map(DateTime.toEpochMillis), Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submission_ownership", rowKey, error.message)));
340
322
  const decodeSubmissionRows = (operation, rowKey, rows) => decodeRows(Schema.Array(SubmissionRow), "effect_agent_submissions", rowKey, rows).pipe(Effect.mapError(internalFailure(operation)));
341
- const readSubmission = Effect.fn("DoSubmissionLedger.readSubmission")(function* (operation, submissionId) {
323
+ const readSubmission = Effect.fnUntraced(function* (operation, submissionId) {
342
324
  const found = yield* cached(rows.submissions.by("submission_id", submissionId), operation);
343
325
  return Option.fromUndefinedOr(found[0]);
344
326
  });
345
- const requireSubmission = Effect.fn("DoSubmissionLedger.requireSubmission")(function* (operation, submissionId) {
327
+ const requireSubmission = Effect.fnUntraced(function* (operation, submissionId) {
346
328
  const submission = yield* readSubmission(operation, submissionId);
347
329
  if (Option.isNone(submission)) return yield* LedgerError.make({
348
330
  operation,
@@ -350,33 +332,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
350
332
  });
351
333
  return submission.value;
352
334
  });
353
- const readOwnership = Effect.fn("DoSubmissionLedger.readOwnership")(function* (operation, submissionId) {
354
- const found = yield* cached(rows.ownership.by("submission_id", submissionId), operation);
355
- return Option.fromUndefinedOr(found[0]);
356
- });
357
- const threadEpoch = Effect.fn("DoSubmissionLedger.threadEpoch")(function* (operation, threadId) {
358
- const threads = yield* journal.getThread(threadId).pipe(Effect.mapError(internalFailure(operation)));
359
- return threads.length === 0 ? EPOCH_ZERO : threads[0].producer_epoch;
335
+ const requireOwner = Effect.fnUntraced(function* (operation, submission) {
336
+ if (owner.hasOwner(submission.thread_id, submission.submission_id)) return;
337
+ const submissionId = yield* decodeSubmissionId(submission.submission_id).pipe(Effect.mapError(internalFailure(operation)));
338
+ return yield* OwnershipLost.make({ submissionId });
360
339
  });
361
- /**
362
- * Verify inside the surrounding write transaction that the presented token still owns the
363
- * Submission's lane; a superseded or missing token fails with OwnershipLost carrying the
364
- * Thread's current producer epoch (DUR-006).
365
- */
366
- const requireOwnership = Effect.fnUntraced(function* (operation, submission, ownershipToken) {
367
- const ownership = yield* readOwnership(operation, submission.submission_id);
368
- const actualEpoch = yield* threadEpoch(operation, submission.thread_id);
369
- if (Option.isNone(ownership) || ownership.value.ownership_token !== ownershipToken || ownership.value.producer_epoch !== actualEpoch) {
370
- const submissionId = yield* Schema.decodeEffect(SubmissionSnapshot.fields.submissionId)(submission.submission_id).pipe(Effect.mapError(internalFailure(operation)));
371
- return yield* OwnershipLost.make({
372
- submissionId,
373
- actualEpoch
374
- });
375
- }
376
- return ownership.value;
377
- }, withStorageSpan("DoSubmissionLedger.requireOwnership", (error) => error._tag === "OwnershipLost"));
378
340
  const submissions = /* @__PURE__ */ new WeakMap();
379
- const decodeSubmissionSnapshot = Effect.fn("DoSubmissionLedger.decodeSubmissionSnapshot")(function* (operation, row) {
341
+ const decodeSubmissionSnapshot = Effect.fnUntraced(function* (operation, row) {
380
342
  const existing = submissions.get(row);
381
343
  if (existing !== void 0) return existing;
382
344
  const decodeFailure = (error) => internalFailure(operation)(DoStorageCorruptionError.make({
@@ -390,42 +352,39 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
390
352
  issueTag: error.issue._tag
391
353
  })
392
354
  }));
393
- const agentDigests = yield* parseStoredJsonText(row.agent_digests_json).pipe(Effect.mapError(decodeFailure));
394
- const inputPayload = yield* parseStoredJsonText(row.input_json).pipe(Effect.mapError(decodeFailure));
395
355
  if (row.parent_submission_id === null !== (row.parent_tool_call_id === null)) return yield* corruptionFailure(operation, "effect_agent_submissions", row.submission_id, "A parent linkage must record both the parent Submission and the parent Tool Call.");
396
- const snapshot = yield* decodeSubmissionSnapshotUnknown({
397
- submissionId: row.submission_id,
398
- threadId: row.thread_id,
399
- queueSequence: row.queue_sequence,
400
- principal: row.principal,
401
- idempotencyKey: row.idempotency_key,
402
- agentId: row.agent_id,
403
- agentDigests,
404
- deploymentId: row.deployment_id,
405
- inputPayload,
406
- inputDigest: row.input_digest,
407
- receiptId: row.receipt_id,
356
+ const admission = yield* decodeAdmissionFact(row).pipe(Effect.mapError(decodeFailure));
357
+ const readyAt = row.ready_at === null ? void 0 : yield* decodeUtcInstant(row.ready_at).pipe(Effect.mapError(decodeFailure));
358
+ const snapshot = yield* decodeSubmissionSnapshotType({
359
+ ...admission,
408
360
  state: row.state,
409
- createdAt: row.created_at,
410
- ...row.admission_group === null ? {} : { admissionGroup: row.admission_group },
411
- ...row.worker_admission_json === null ? {} : { workerAdmission: yield* parseStoredJsonText(row.worker_admission_json).pipe(Effect.mapError(decodeFailure)) },
412
- ...row.message_admission_json === null ? {} : { messageAdmission: yield* parseStoredJsonText(row.message_admission_json).pipe(Effect.mapError(decodeFailure)) },
413
- ...row.admission_fence_json === null ? {} : { admissionFence: yield* parseStoredJsonText(row.admission_fence_json).pipe(Effect.mapError(decodeFailure)) },
414
361
  ...row.settled_outcome === null ? {} : { settledOutcome: row.settled_outcome },
415
- ...row.ready_at === null ? {} : { readyAt: row.ready_at },
416
- ...row.parent_submission_id === null || row.parent_tool_call_id === null ? {} : { parentLinkage: {
417
- parentSubmissionId: row.parent_submission_id,
418
- parentToolCallId: row.parent_tool_call_id
419
- } }
362
+ ...readyAt === void 0 ? {} : { readyAt }
420
363
  }).pipe(Effect.mapError(decodeFailure));
421
364
  submissions.set(row, snapshot);
422
365
  return snapshot;
423
366
  });
424
- const readReservation = Effect.fn("DoSubmissionLedger.readReservation")(function* (operation, submissionId) {
425
- const found = yield* cached(rows.reservations.by("submission_id", submissionId), operation);
426
- return Option.fromUndefinedOr(found[0]);
367
+ const readCanonicalSettlement = Effect.fnUntraced(function* (operation, submission) {
368
+ const expected = yield* decodeSubmissionSnapshot(operation, submission);
369
+ const recordId = submissionSettlementRecordId(expected.submissionId);
370
+ const found = yield* sql`SELECT ${recordBatchId(sql)} AS batch_id, record_id, ${canonicalRecordJson(sql)} AS record_json
371
+ FROM effect_agent_canonical_records WHERE thread_id = ${submission.thread_id}
372
+ AND record_id = ${JSON.stringify(recordId)}`.pipe(Effect.mapError(sqlFailure(operation)));
373
+ const decoded = yield* decodeRows(Schema.Array(Schema.Struct({
374
+ batch_id: Schema.fromJsonString(BoundedIdentifier),
375
+ record_id: Schema.fromJsonString(BoundedIdentifier),
376
+ record_json: BoundedStoredText
377
+ })), "effect_agent_canonical_records", recordId, found).pipe(Effect.mapError(internalFailure(operation)));
378
+ if (decoded.length === 0) return void 0;
379
+ const row = decoded[0];
380
+ if (decoded.length !== 1 || row.batch_id !== submissionSettlementBatchId(expected.submissionId) || row.record_id !== recordId) return yield* corruptionFailure(operation, "effect_agent_canonical_records", recordId, "Canonical settlement identity is inconsistent.");
381
+ const record = yield* decodeRecordEnvelopeText(row.record_json).pipe(Effect.mapError(internalFailure(operation)));
382
+ return {
383
+ record,
384
+ settlement: yield* validateCanonicalSettlement(record, expected)
385
+ };
427
386
  });
428
- const readAbortIntent = Effect.fn("DoSubmissionLedger.readAbortIntent")(function* (operation, submissionId) {
387
+ const readAbortIntent = Effect.fnUntraced(function* (operation, submissionId) {
429
388
  const found = yield* cached(rows.aborts.by("submission_id", submissionId), operation);
430
389
  return Option.fromUndefinedOr(found[0]);
431
390
  });
@@ -434,7 +393,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
434
393
  * Submission (the DC realization of the port's "cross-store adapters record a durable
435
394
  * notification marker" contract).
436
395
  */
437
- const readChildSettlementMarkers = Effect.fn("DoSubmissionLedger.readChildSettlementMarkers")(function* (operation, parentSubmissionId) {
396
+ const readChildSettlementMarkers = Effect.fnUntraced(function* (operation, parentSubmissionId) {
438
397
  return (yield* cached(rows.childSettlements.by("parent_submission_id", parentSubmissionId), operation)).toSorted((a, b) => a.child_submission_id < b.child_submission_id ? -1 : a.child_submission_id > b.child_submission_id ? 1 : 0);
439
398
  });
440
399
  /**
@@ -443,13 +402,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
443
402
  * cross-store notification marker was recorded for it (the child's row lives in another
444
403
  * Durable Object and its owner reported the settlement through `recordChildSettled`).
445
404
  */
446
- const childProvablySettled = Effect.fn("DoSubmissionLedger.childProvablySettled")(function* (operation, markerChildren, childSubmissionId) {
405
+ const childProvablySettled = Effect.fnUntraced(function* (operation, markerChildren, childSubmissionId) {
447
406
  if (markerChildren.has(childSubmissionId)) return true;
448
407
  const childRow = yield* readSubmission(operation, childSubmissionId);
449
- return Option.isSome(childRow) && childRow.value.state === "settled";
408
+ return Option.isSome(childRow) && (yield* readCanonicalSettlement(operation, childRow.value)) !== void 0;
450
409
  });
451
410
  const decodeChildReservationRows = (operation, rowKey, rows) => decodeRows(Schema.Array(ChildReservationRow), "effect_agent_child_reservations", rowKey, rows).pipe(Effect.mapError(internalFailure(operation)));
452
- const readChildReservation = Effect.fn("DoSubmissionLedger.readChildReservation")(function* (operation, reservationId) {
411
+ const readChildReservation = Effect.fnUntraced(function* (operation, reservationId) {
453
412
  const rows = yield* sql`
454
413
  SELECT ${sql.literal(CHILD_RESERVATION_COLUMNS)}
455
414
  FROM effect_agent_child_reservations
@@ -459,7 +418,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
459
418
  if (decoded.length > 1) return yield* corruptionFailure(operation, "effect_agent_child_reservations", reservationId, "A child reservation primary key returned more than one row.");
460
419
  return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
461
420
  });
462
- const readChildReservationForCall = Effect.fn("DoSubmissionLedger.readChildReservationForCall")(function* (operation, parentSubmissionId, parentToolCallId) {
421
+ const readChildReservationForCall = Effect.fnUntraced(function* (operation, parentSubmissionId, parentToolCallId) {
463
422
  const rows = yield* sql`
464
423
  SELECT ${sql.literal(CHILD_RESERVATION_COLUMNS)}
465
424
  FROM effect_agent_child_reservations
@@ -470,7 +429,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
470
429
  if (decoded.length > 1) return yield* corruptionFailure(operation, "effect_agent_child_reservations", `${parentSubmissionId}/${parentToolCallId}`, "A parent Tool Call returned more than one child reservation.");
471
430
  return decoded.length === 0 ? Option.none() : Option.some(decoded[0]);
472
431
  });
473
- const childReservationSnapshotFromRow = Effect.fn("DoSubmissionLedger.childReservationSnapshotFromRow")(function* (operation, row) {
432
+ const childReservationSnapshotFromRow = Effect.fnUntraced(function* (operation, row) {
474
433
  const rowFailure = (error) => corruptionFailure(operation, "effect_agent_child_reservations", row.reservation_id, error.message);
475
434
  const allocation = yield* parseStoredJsonText(row.allocation_json).pipe(Effect.mapError(rowFailure));
476
435
  const accounting = row.accounting_json === null ? void 0 : yield* parseStoredJsonText(row.accounting_json).pipe(Effect.mapError(rowFailure));
@@ -488,35 +447,18 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
488
447
  ...row.released_at === null ? {} : { releasedAt: row.released_at }
489
448
  }).pipe(Effect.mapError(rowFailure));
490
449
  });
491
- const readApprovalDecisions = Effect.fn("DoSubmissionLedger.readApprovalDecisions")(function* (operation, submissionId) {
450
+ const readApprovalDecisions = Effect.fnUntraced(function* (operation, submissionId) {
492
451
  return (yield* cached(rows.approvals.by("submission_id", submissionId), operation)).toSorted((a, b) => a.tool_call_id < b.tool_call_id ? -1 : a.tool_call_id > b.tool_call_id ? 1 : 0);
493
452
  });
494
- const approvalIntentFromRow = Effect.fn("DoSubmissionLedger.approvalIntentFromRow")(function* (operation, row) {
495
- return yield* decodeApprovalDecisionIntent({
496
- submissionId: row.submission_id,
497
- toolCallId: row.tool_call_id,
498
- decision: row.decision,
499
- resolver: row.resolver,
500
- reason: row.reason,
501
- decidedAt: row.decided_at
502
- }).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_approval_decisions", `${row.submission_id}/${row.tool_call_id}`, error.message)));
503
- });
504
- const readUnknownResolutions = Effect.fn("DoSubmissionLedger.readUnknownResolutions")(function* (operation, submissionId) {
453
+ const approvalIntentFromRow = (operation, row) => decodeApprovalFact(row).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_approval_decisions", `${row.submission_id}/${row.tool_call_id}`, error.message)));
454
+ const readUnknownResolutions = Effect.fnUntraced(function* (operation, submissionId) {
505
455
  return (yield* cached(rows.resolutions.by("submission_id", submissionId), operation)).toSorted((a, b) => a.tool_call_id < b.tool_call_id ? -1 : a.tool_call_id > b.tool_call_id ? 1 : 0);
506
456
  });
507
- const unknownResolutionIntentFromRow = Effect.fn("DoSubmissionLedger.unknownResolutionIntentFromRow")(function* (operation, row) {
508
- const resolution = yield* parseStoredJsonText(row.resolution_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_unknown_resolutions", `${row.submission_id}/${row.tool_call_id}`, error.message)));
509
- return yield* decodeUnknownResolutionIntent({
510
- submissionId: row.submission_id,
511
- toolCallId: row.tool_call_id,
512
- author: row.author,
513
- reason: row.reason,
514
- resolution,
515
- resolvedAt: row.resolved_at
516
- }).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_unknown_resolutions", `${row.submission_id}/${row.tool_call_id}`, error.message)));
457
+ const unknownResolutionIntentFromRow = Effect.fnUntraced(function* (operation, row) {
458
+ return yield* decodeResolutionFact(row).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_unknown_resolutions", `${row.submission_id}/${row.tool_call_id}`, error.message)));
517
459
  });
518
460
  /** The Submission's marked-unknown open Tool Call identities, empty when never marked. */
519
- const storedUnknownToolCallIds = Effect.fn("DoSubmissionLedger.storedUnknownToolCallIds")(function* (operation, submission) {
461
+ const storedUnknownToolCallIds = Effect.fnUntraced(function* (operation, submission) {
520
462
  if (submission.unknown_tool_call_ids_json === null) return [];
521
463
  return yield* decodeToolCallIdsText(submission.unknown_tool_call_ids_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submissions", submission.submission_id, error.message)));
522
464
  });
@@ -525,22 +467,20 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
525
467
  * derived from the shared canonical-records table using the deterministic abort record
526
468
  * identity, never from a cached ledger marker.
527
469
  */
528
- const canonicalAbortRecordId = Effect.fn("DoSubmissionLedger.canonicalAbortRecordId")(function* (operation, threadId, submissionId) {
470
+ const canonicalAbortRecordId = Effect.fnUntraced(function* (operation, threadId, submissionId) {
529
471
  const recordId = submissionAbortRecordId(submissionId);
530
472
  return (yield* journal.hasRecord(threadId, recordId).pipe(Effect.mapError(internalFailure(operation)))) ? recordId : void 0;
531
473
  });
532
- const abortIntentFromRow = Effect.fn("DoSubmissionLedger.abortIntentFromRow")(function* (operation, submission, submissionId, row) {
474
+ const abortIntentFromRow = Effect.fnUntraced(function* (operation, submission, submissionId, row) {
533
475
  const canonicalRecordId = yield* canonicalAbortRecordId(operation, submission.thread_id, submissionId);
534
- return yield* decodeAbortIntent({
535
- submissionId: row.submission_id,
536
- author: row.author,
537
- reason: row.reason,
538
- requestedAt: row.requested_at,
476
+ const fact = yield* decodeAbortFact(row).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_abort_intents", row.submission_id, error.message)));
477
+ return AbortIntent.make({
478
+ ...fact,
539
479
  ...canonicalRecordId === void 0 ? {} : { canonicalRecordId }
540
- }).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_abort_intents", row.submission_id, error.message)));
480
+ });
541
481
  });
542
482
  const capabilities = Effect.succeed(LedgerCapabilities.make({ durability: "durable-cloudflare" }));
543
- const admit = Effect.fn("DoSubmissionLedger.admit")(function* (request) {
483
+ const admit = Effect.fnUntraced(function* (request) {
544
484
  const operation = "ledger admit";
545
485
  const validated = yield* Schema.decodeEffect(Schema.toType(AdmissionRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
546
486
  const inputJson = yield* encodePersistedJsonText(validated.inputPayload).pipe(Effect.mapError(internalFailure(operation)));
@@ -595,15 +535,18 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
595
535
  replayed: true
596
536
  }).pipe(Effect.mapError(internalFailure(operation)));
597
537
  }
538
+ if (!admissionFitsTransfer(validated.threadId, validated)) return yield* LedgerError.make({
539
+ operation,
540
+ message: "Admission exceeds the complete transfer page byte bound"
541
+ });
598
542
  if ((yield* cached(rows.workerStops.by("thread_id", validated.threadId), operation)).length > 0) return yield* AdmissionPolicyError.make({
599
543
  reason: "refused",
600
544
  code: "worker-stopped"
601
545
  });
602
- const lane = yield* laneWork(validated.threadId, operation);
603
- const firstRows = lane === void 0 ? yield* sql`
546
+ const firstRows = yield* sql`
604
547
  SELECT worker_admission_json FROM effect_agent_submissions
605
548
  WHERE thread_id=${validated.threadId} ORDER BY queue_sequence LIMIT 1
606
- `.pipe(Effect.mapError(sqlFailure(operation))) : lane[0] === void 0 ? [] : [yield* requireSubmission(operation, lane[0].submission_id)];
549
+ `.pipe(Effect.mapError(sqlFailure(operation)));
607
550
  const first = yield* Schema.decodeUnknownEffect(Schema.Array(Schema.Struct({ worker_admission_json: Schema.NullOr(BoundedStoredText) })))(firstRows).pipe(Effect.mapError(internalFailure(operation)));
608
551
  if (first[0] !== void 0) {
609
552
  const previous = first[0].worker_admission_json === null ? void 0 : yield* Schema.decodeEffect(Schema.fromJsonString(WorkerAdmission))(first[0].worker_admission_json).pipe(Effect.mapError(internalFailure(operation)));
@@ -622,11 +565,11 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
622
565
  code: "admission-group"
623
566
  });
624
567
  }
625
- const maxRows = lane === void 0 ? yield* sql`
568
+ const maxRows = yield* sql`
626
569
  SELECT COALESCE(MAX(queue_sequence), 0) AS max_queue_sequence
627
570
  FROM effect_agent_submissions
628
571
  WHERE thread_id = ${validated.threadId}
629
- `.pipe(Effect.mapError(sqlFailure(operation))) : [{ max_queue_sequence: lane.at(-1)?.queue_sequence ?? 0 }];
572
+ `.pipe(Effect.mapError(sqlFailure(operation)));
630
573
  const decodedMax = yield* decodeRows(Schema.Array(MaxQueueSequenceRow), "effect_agent_submissions", validated.threadId, maxRows).pipe(Effect.mapError(internalFailure(operation)));
631
574
  const queueSequence = yield* decodeQueueSequence((decodedMax[0]?.max_queue_sequence ?? 0) + 1).pipe(Effect.mapError(internalFailure(operation)));
632
575
  const now = yield* currentInstant;
@@ -638,7 +581,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
638
581
  principal,
639
582
  idempotency_key,
640
583
  agent_id,
641
- agent_digests_json,
584
+ definition_key,
642
585
  deployment_id,
643
586
  input_json,
644
587
  input_digest,
@@ -658,7 +601,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
658
601
  ${validated.principal},
659
602
  ${validated.idempotencyKey},
660
603
  ${validated.agentId},
661
- ${agentDigestsJson},
604
+ ${yield* physical.definition(agentDigestsJson).pipe(Effect.mapError(internalFailure(operation)))},
662
605
  ${validated.deploymentId},
663
606
  ${inputJson},
664
607
  ${validated.inputDigest},
@@ -672,10 +615,11 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
672
615
  ${workerAdmissionJson},
673
616
  ${messageAdmissionJson}
674
617
  )
675
- RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
618
+ RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
619
+ if (queueSequence === 1) {
620
+ if ((yield* journal.getThread(validated.threadId).pipe(Effect.mapError(internalFailure(operation)))).length === 0) yield* journal.work.initialize(validated.threadId).pipe(Effect.mapError(internalFailure(operation)));
621
+ }
676
622
  rows.aborts.seed("submission_id", mintedSubmissionId, []);
677
- rows.ownership.seed("submission_id", mintedSubmissionId, []);
678
- rows.reservations.seed("submission_id", mintedSubmissionId, []);
679
623
  rows.submissions.seed("joined_host_submission_id", mintedSubmissionId, []);
680
624
  rows.approvals.seed("submission_id", mintedSubmissionId, []);
681
625
  rows.resolutions.seed("submission_id", mintedSubmissionId, []);
@@ -693,7 +637,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
693
637
  yield* hitFailpoint("ledger:admit:after", operation);
694
638
  return result;
695
639
  });
696
- const markReady = Effect.fn("DoSubmissionLedger.markReady")(function* (request) {
640
+ const markReady = Effect.fnUntraced(function* (request) {
697
641
  const operation = "ledger mark ready";
698
642
  const validated = yield* Schema.decodeEffect(Schema.toType(MarkReadyRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
699
643
  yield* hitFailpoint("ledger:mark-ready:before", operation);
@@ -705,7 +649,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
705
649
  UPDATE effect_agent_submissions
706
650
  SET state = 'ready', ready_at = ${now.iso}
707
651
  WHERE submission_id = ${validated.submissionId}
708
- RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
652
+ RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
709
653
  yield* recordProgress("submission", operation);
710
654
  yield* retainLifecycle(submission, {
711
655
  _tag: "SubmissionReady",
@@ -714,7 +658,29 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
714
658
  }));
715
659
  yield* hitFailpoint("ledger:mark-ready:after", operation);
716
660
  });
717
- const lookup = Effect.fn("DoSubmissionLedger.lookup")(function* (request) {
661
+ const resolveFundingOwner = Effect.fnUntraced(function* (input) {
662
+ const operation = "resolve funding owner";
663
+ const request = yield* Schema.decodeEffect(FundingOwnerRequest)(input).pipe(Effect.mapError(internalFailure(operation)));
664
+ const selected = yield* requireSubmission(operation, request.submissionId);
665
+ if (selected.thread_id !== request.threadId) return yield* LedgerError.make({
666
+ operation,
667
+ message: "Selected funding input belongs to another Thread."
668
+ });
669
+ if (selected.state === "joining") return yield* LedgerError.make({
670
+ operation,
671
+ message: "Selected funding input is still joining; retry after durable binding."
672
+ });
673
+ const hostId = selected.joined_host_submission_id;
674
+ if (hostId === null && selected.state === "joined") return yield* corruptionFailure(operation, "effect_agent_submissions", selected.submission_id, "Joined input has no stable host identity.");
675
+ if (hostId !== null && selected.state !== "joined" && selected.state !== "settled") return yield* corruptionFailure(operation, "effect_agent_submissions", selected.submission_id, "Host linkage is inconsistent with selected input state.");
676
+ const owner = hostId === null ? selected : yield* requireSubmission(operation, hostId);
677
+ if (owner.thread_id !== request.threadId || hostId !== null && (owner.submission_id === selected.submission_id || owner.joined_host_submission_id !== null || owner.state === "joining" || owner.state === "joined" || owner.queue_sequence >= selected.queue_sequence)) return yield* corruptionFailure(operation, "effect_agent_submissions", selected.submission_id, "Funding host is not a stable earlier input in this Thread.");
678
+ return yield* Schema.decodeEffect(Schema.toType(FundingOwner))({
679
+ selected: yield* decodeSubmissionSnapshot(operation, selected),
680
+ owner: yield* decodeSubmissionSnapshot(operation, owner)
681
+ }).pipe(Effect.mapError(internalFailure(operation)));
682
+ }, state.read);
683
+ const lookup = Effect.fnUntraced(function* (request) {
718
684
  const operation = "ledger lookup";
719
685
  const validated = yield* Schema.decodeEffect(Schema.toType(SubmissionLookup))(request).pipe(Effect.mapError(internalFailure(operation)));
720
686
  if (validated._tag === "SubmissionLookupById") {
@@ -728,7 +694,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
728
694
  if (decoded.length === 0) return Option.none();
729
695
  return Option.some(yield* decodeSubmissionSnapshot(operation, decoded[0]));
730
696
  });
731
- const resolveAdmission = Effect.fn("DoSubmissionLedger.resolveAdmission")(function* (request) {
697
+ const resolveAdmission = Effect.fnUntraced(function* (request) {
732
698
  const operation = "ledger resolve admission";
733
699
  const validated = yield* Schema.decodeEffect(Schema.toType(SubmissionLookupByKey))(request).pipe(Effect.mapError(internalFailure(operation)));
734
700
  const foundRows = yield* byKey(validated, operation);
@@ -737,30 +703,15 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
737
703
  if (decoded.length === 0) return AdmissionNotAdmitted.make();
738
704
  return AdmissionAdmitted.make({ submission: yield* decodeSubmissionSnapshot(operation, decoded[0]) });
739
705
  });
740
- const claim = Effect.fn("DoSubmissionLedger.claim")(function* (request) {
741
- const operation = "ledger claim";
706
+ const selectHead = Effect.fn("DoSubmissionLedger.selectHead")(function* (request) {
707
+ const operation = "ledger select head";
742
708
  const validated = yield* Schema.decodeEffect(Schema.toType(ClaimRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
743
709
  const attemptId = `attempt-${yield* mintUuid(operation)}`;
744
- const ownershipToken = `owner-${yield* mintUuid(operation)}`;
745
710
  yield* hitFailpoint("ledger:claim:before", operation);
746
- const claimed = yield* inWriteTransaction(operation, Effect.gen(function* () {
747
- const now = yield* currentInstant;
748
- const lane = yield* laneWork(validated.threadId, operation);
749
- const ownershipRows = lane === void 0 ? yield* sql`
750
- SELECT ownership.*
751
- FROM effect_agent_submission_ownership AS ownership
752
- JOIN effect_agent_submissions AS submission
753
- ON submission.submission_id = ownership.submission_id
754
- WHERE submission.thread_id = ${validated.threadId}
755
- ORDER BY ownership.lease_expires_at DESC
756
- LIMIT 1
757
- `.pipe(Effect.mapError(sqlFailure(operation))) : (yield* Effect.forEach(lane.filter((item) => item.state !== "settled"), (item) => cached(rows.ownership.by("submission_id", item.submission_id), operation))).flat().sort((a, b) => b.lease_expires_at.localeCompare(a.lease_expires_at)).slice(0, 1);
758
- const ownership = yield* decodeRows(Schema.Array(OwnershipRow), "effect_agent_submission_ownership", validated.threadId, ownershipRows).pipe(Effect.mapError(internalFailure(operation)));
759
- if (ownership.length > 0) {
760
- if ((yield* timestampMillis(operation, ownership[0].submission_id)(ownership[0].lease_expires_at)) > now.millis) return Option.none();
761
- }
762
- const headRows = lane === void 0 ? yield* sql`
763
- SELECT ${sql.literal(SUBMISSION_COLUMNS)}
711
+ const claimed = yield* owner.withMutation(Effect.gen(function* () {
712
+ if (owner.hasOwner(validated.threadId)) return Option.none();
713
+ const headRows = yield* sql`
714
+ SELECT ${submissionColumns}
764
715
  FROM effect_agent_submissions
765
716
  WHERE thread_id = ${validated.threadId}
766
717
  AND state <> 'settled'
@@ -772,16 +723,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
772
723
  )
773
724
  ORDER BY queue_sequence ASC
774
725
  LIMIT ${validated.handoff === void 0 ? 1 : validated.handoff.deferredSubmissionIds.length + 1}
775
- `.pipe(Effect.mapError(sqlFailure(operation))) : yield* Effect.gen(function* () {
776
- const heads = [];
777
- for (const item of lane) {
778
- if (item.state === "settled") continue;
779
- if (item.state === "unknown" && Option.isNone(yield* readAbortIntent(operation, item.submission_id))) continue;
780
- heads.push(yield* requireSubmission(operation, item.submission_id));
781
- if (heads.length >= (validated.handoff === void 0 ? 1 : validated.handoff.deferredSubmissionIds.length + 1)) break;
782
- }
783
- return heads;
784
- });
726
+ `.pipe(Effect.mapError(sqlFailure(operation)));
785
727
  const heads = yield* decodeSubmissionRows(operation, validated.threadId, headRows);
786
728
  if (heads.length === 0) return Option.none();
787
729
  let head = heads[0];
@@ -794,146 +736,26 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
794
736
  }
795
737
  if (!handoff.deferredSubmissionIds.some((id) => id === candidate.submission_id) || candidate.state !== "input-applied" || Option.isSome(yield* readAbortIntent(operation, candidate.submission_id))) return Option.none();
796
738
  }
797
- if (head.submission_id !== handoff.submissionId || head.state !== "ready" && head.state !== "running" && head.state !== "input-applied") return Option.none();
739
+ if (head.submission_id !== handoff.submissionId || head.state !== "ready" && head.state !== "input-applied") return Option.none();
798
740
  }
799
741
  if (head.state === "joining" || head.state === "joined" || head.state === "suspended" || head.state === "unknown" && Option.isNone(yield* readAbortIntent(operation, head.submission_id))) return Option.none();
800
- const threads = yield* journal.getThread(head.thread_id).pipe(Effect.mapError(internalFailure(operation)));
801
- if (validated.handoff !== void 0 && threads[0]?.producer_epoch !== validated.handoff.producerEpoch) return Option.none();
802
- let producerEpoch;
803
- if (threads.length === 0) {
804
- producerEpoch = 1;
805
- yield* sql`
806
- INSERT INTO effect_agent_threads (
807
- thread_id,
808
- created_at,
809
- tail_sequence,
810
- tail_digest,
811
- producer_epoch
812
- ) VALUES (
813
- ${head.thread_id},
814
- ${now.iso},
815
- 0,
816
- ${EMPTY_TAIL_DIGEST},
817
- ${producerEpoch}
818
- )
819
- RETURNING *`.pipe(rows.threads.write, Effect.mapError(internalFailure(operation)));
820
- } else {
821
- producerEpoch = threads[0].producer_epoch + 1;
822
- yield* sql`
823
- UPDATE effect_agent_threads
824
- SET producer_epoch = ${producerEpoch}
825
- WHERE thread_id = ${head.thread_id}
826
- RETURNING *`.pipe(rows.threads.write, Effect.mapError(internalFailure(operation)));
827
- }
828
- const leaseExpiresAt = new Date(now.millis + config.ownershipLeaseDuration).toISOString();
829
- yield* sql`
830
- INSERT INTO effect_agent_submission_ownership (
831
- submission_id,
832
- attempt_id,
833
- ownership_token,
834
- producer_epoch,
835
- owner_producer_id,
836
- lease_expires_at
837
- ) VALUES (
838
- ${head.submission_id},
839
- ${attemptId},
840
- ${ownershipToken},
841
- ${producerEpoch},
842
- ${validated.producerId},
843
- ${leaseExpiresAt}
844
- )
845
- ON CONFLICT (submission_id) DO UPDATE SET
846
- attempt_id = excluded.attempt_id,
847
- ownership_token = excluded.ownership_token,
848
- producer_epoch = excluded.producer_epoch,
849
- owner_producer_id = excluded.owner_producer_id,
850
- lease_expires_at = excluded.lease_expires_at
851
- RETURNING *`.pipe(rows.ownership.write, Effect.mapError(internalFailure(operation)));
852
- yield* sql`
853
- INSERT INTO effect_agent_attempts (
854
- attempt_id,
855
- submission_id,
856
- thread_id,
857
- owner_producer_id,
858
- producer_epoch,
859
- claimed_at
860
- ) VALUES (
861
- ${attemptId},
862
- ${head.submission_id},
863
- ${head.thread_id},
864
- ${validated.producerId},
865
- ${producerEpoch},
866
- ${now.iso}
867
- )
868
- `.pipe(Effect.mapError(sqlFailure(operation)));
869
- if (head.state === "ready") yield* sql`
870
- UPDATE effect_agent_submissions
871
- SET state = 'running'
872
- WHERE submission_id = ${head.submission_id}
873
- RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
874
742
  const inputPayload = yield* parseStoredJsonText(head.input_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submissions", head.submission_id, error.message)));
875
743
  return Option.some(yield* decodeClaim({
876
744
  submissionId: head.submission_id,
877
745
  attemptId,
878
- ownershipToken,
879
- producerEpoch,
880
- leaseExpiresAt,
881
746
  inputPayload
882
747
  }).pipe(Effect.mapError(internalFailure(operation))));
883
748
  }));
884
749
  yield* hitFailpoint("ledger:claim:after", operation);
885
750
  return claimed;
886
751
  });
887
- const renewOwnership = Effect.fn("DoSubmissionLedger.renewOwnership")(function* (request) {
888
- const operation = "ledger renew ownership";
889
- const validated = yield* Schema.decodeEffect(Schema.toType(RenewOwnershipRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
890
- yield* hitFailpoint("ledger:renew:before", operation);
891
- const renewal = yield* inWriteTransaction(operation, Effect.gen(function* () {
892
- const submission = yield* requireSubmission(operation, validated.submissionId);
893
- yield* requireOwnership(operation, submission, validated.ownershipToken);
894
- const now = yield* currentInstant;
895
- const leaseExpiresAt = new Date(now.millis + config.ownershipLeaseDuration).toISOString();
896
- yield* sql`
897
- UPDATE effect_agent_submission_ownership
898
- SET lease_expires_at = ${leaseExpiresAt}
899
- WHERE submission_id = ${validated.submissionId}
900
- RETURNING *`.pipe(rows.ownership.write, Effect.mapError(internalFailure(operation)));
901
- return yield* decodeOwnershipRenewal({
902
- ownershipToken: validated.ownershipToken,
903
- leaseExpiresAt
904
- }).pipe(Effect.mapError(internalFailure(operation)));
905
- }));
906
- yield* hitFailpoint("ledger:renew:after", operation);
907
- return renewal;
908
- });
909
- const releaseOwnership = Effect.fnUntraced(function* (request) {
910
- const operation = "ledger release ownership";
911
- const validated = yield* Schema.decodeEffect(Schema.toType(ReleaseOwnershipRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
912
- yield* hitFailpoint("ledger:release:before", operation);
913
- const current = yield* requireSubmission(operation, validated.submissionId);
914
- yield* requireOwnership(operation, current, validated.ownershipToken);
915
- yield* inWriteTransaction(operation, Effect.gen(function* () {
916
- const submission = yield* requireSubmission(operation, validated.submissionId);
917
- yield* requireOwnership(operation, submission, validated.ownershipToken);
918
- yield* sql`
919
- DELETE FROM effect_agent_submission_ownership
920
- WHERE submission_id = ${validated.submissionId}
921
- RETURNING *`.pipe(rows.ownership.remove, Effect.mapError(internalFailure(operation)));
922
- if (submission.state === "running") yield* sql`
923
- UPDATE effect_agent_submissions
924
- SET state = 'ready'
925
- WHERE submission_id = ${validated.submissionId}
926
- RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
927
- }), (error) => error._tag === "OwnershipLost");
928
- yield* hitFailpoint("ledger:release:after", operation);
929
- }, withStorageSpan("DoSubmissionLedger.releaseOwnership", (error) => error._tag === "OwnershipLost"));
930
- const markInputApplied = Effect.fn("DoSubmissionLedger.markInputApplied")(function* (request) {
752
+ const markInputApplied = Effect.fnUntraced(function* (request) {
931
753
  const operation = "ledger mark input applied";
932
754
  const validated = yield* Schema.decodeEffect(Schema.toType(MarkInputAppliedRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
933
755
  yield* hitFailpoint("ledger:mark-input-applied:before", operation);
934
756
  yield* inWriteTransaction(operation, Effect.gen(function* () {
935
757
  const submission = yield* requireSubmission(operation, validated.submissionId);
936
- yield* requireOwnership(operation, submission, validated.ownershipToken);
758
+ yield* requireOwner(operation, submission);
937
759
  if (submission.input_applied_record_id !== null) {
938
760
  if (submission.input_applied_record_id === validated.recordId && submission.input_applied_sequence === validated.sequence) return;
939
761
  return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "A different canonical input marker is already recorded for this Submission.");
@@ -944,122 +766,132 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
944
766
  input_applied_record_id = ${validated.recordId},
945
767
  input_applied_sequence = ${validated.sequence},
946
768
  state = CASE
947
- WHEN state IN ('admitted', 'ready', 'running') THEN 'input-applied'
769
+ WHEN state IN ('admitted', 'ready') THEN 'input-applied'
948
770
  ELSE state
949
771
  END
950
772
  WHERE submission_id = ${validated.submissionId}
951
- RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
773
+ RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
952
774
  }));
953
775
  yield* hitFailpoint("ledger:mark-input-applied:after", operation);
954
776
  });
955
- const reserveSettlement = Effect.fn("DoSubmissionLedger.reserveSettlement")(function* (request) {
956
- const operation = "ledger reserve settlement";
957
- const validated = yield* Schema.decodeEffect(Schema.toType(SettlementReservation))(request).pipe(Effect.mapError(internalFailure(operation)));
958
- const recordJson = yield* encodeRecordEnvelopeText(validated.record).pipe(Effect.mapError(internalFailure(operation)));
959
- yield* journal.checkValueBound(operation, recordJson).pipe(Effect.mapError(internalFailure(operation)));
960
- yield* hitFailpoint("ledger:reserve-settlement:before", operation);
961
- const reserved = yield* inWriteTransaction(operation, Effect.gen(function* () {
962
- const existing = yield* readReservation(operation, validated.submissionId);
963
- if (Option.isSome(existing)) {
964
- if (!(existing.value.settlement_id === validated.settlementId && existing.value.outcome === validated.outcome && existing.value.record_digest === validated.recordDigest && existing.value.record_json === recordJson)) return yield* SettlementConflict.make({
965
- submissionId: validated.submissionId,
966
- existingOutcome: existing.value.outcome
777
+ const publish = Effect.fn("DoSubmissionLedger.publishSettlement")(function* (input) {
778
+ const operation = "publish settlement";
779
+ const { request, record, settlement } = yield* validatePublication(input);
780
+ const prepared = yield* prepareCanonicalAppend(request.append).pipe(Effect.provideService(Crypto.Crypto, crypto));
781
+ const mapAppendError = (error) => ThreadStoreError.make({
782
+ operation,
783
+ message: error.message,
784
+ cause: error
785
+ });
786
+ yield* journal.prepareAppend(prepared.raw).pipe(Effect.mapError(mapAppendError));
787
+ return yield* owner.withProgress(request.append.threadId, owner.withMutation(Effect.gen(function* () {
788
+ yield* hitFailpoint("append:before", operation);
789
+ const result = yield* journal.withWriteTransaction(operation)(Effect.gen(function* () {
790
+ const submission = yield* requireSubmission(operation, request.submissionId);
791
+ if (submission.thread_id !== prepared.request.threadId) return yield* LedgerError.make({
792
+ operation,
793
+ message: "Settlement publication belongs to another Thread."
967
794
  });
968
- const record = yield* decodeRecordEnvelopeText(existing.value.record_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_settlement_reservations", validated.submissionId, error.message)));
969
- return ReservedSettlement.make({
970
- submissionId: validated.submissionId,
971
- settlementId: validated.settlementId,
972
- outcome: validated.outcome,
795
+ const snapshot = yield* decodeSubmissionSnapshot(operation, submission);
796
+ yield* validateCanonicalSettlement(record, snapshot);
797
+ const existing = yield* readCanonicalSettlement(operation, submission);
798
+ const thread = (yield* journal.getThread(prepared.request.threadId))[0];
799
+ if (thread === void 0) return yield* ThreadNotMaterialized.make({ threadId: prepared.request.threadId });
800
+ switch (request.authority._tag) {
801
+ case "Owned":
802
+ yield* requireOwner(operation, submission);
803
+ yield* validateCanonicalSettlement(record, {
804
+ submissionId: request.submissionId,
805
+ receiptId: snapshot.receiptId,
806
+ ...settlement.outcome === "aborted" && settlement.runId === void 0 ? {} : { runId: runIdForSubmission(request.submissionId) }
807
+ });
808
+ break;
809
+ case "Joined": {
810
+ if (submission.state !== "joined" && !(submission.state === "settled" && existing !== void 0) || submission.joined_host_submission_id !== request.authority.hostSubmissionId) return yield* LedgerError.make({
811
+ operation,
812
+ message: "Submission is not joined to the named host."
813
+ });
814
+ const host = yield* requireSubmission(operation, request.authority.hostSubmissionId);
815
+ if (host.thread_id !== submission.thread_id) return yield* LedgerError.make({
816
+ operation,
817
+ message: "Joined host belongs to another Thread."
818
+ });
819
+ const canonicalHost = yield* readCanonicalSettlement(operation, host);
820
+ if (canonicalHost === void 0) return yield* LedgerError.make({
821
+ operation,
822
+ message: "Joined host has no canonical settlement."
823
+ });
824
+ yield* validateJoinedSettlement(settlement, canonicalHost.settlement);
825
+ break;
826
+ }
827
+ case "QueuedAbort":
828
+ if (submission.state !== "ready" && !(submission.state === "settled" && existing !== void 0) || settlement.outcome !== "aborted" || Option.isNone(yield* readAbortIntent(operation, request.submissionId)) || owner.hasOwner(submission.thread_id, request.submissionId)) return yield* LedgerError.make({
829
+ operation,
830
+ message: "Queued abort is no longer authorized."
831
+ });
832
+ yield* validateCanonicalSettlement(record, {
833
+ submissionId: request.submissionId,
834
+ receiptId: snapshot.receiptId,
835
+ runId: void 0
836
+ });
837
+ }
838
+ if (existing !== void 0) {
839
+ yield* journal.checkCommitIntent(prepared.raw);
840
+ return SettlementPublicationResult.make({
841
+ record: existing.record,
842
+ tailSequence: thread.tail_sequence,
843
+ replayed: true
844
+ });
845
+ }
846
+ if (submission.state === "settled") return yield* LedgerError.make({
847
+ operation,
848
+ message: "Finalized Submission has no canonical settlement."
849
+ });
850
+ const appended = yield* journal.appendPrepared(prepared.raw);
851
+ return SettlementPublicationResult.make({
973
852
  record,
974
- recordDigest: validated.recordDigest,
975
- replayed: true
853
+ tailSequence: appended.lastSequence,
854
+ replayed: appended.replayed
976
855
  });
977
- }
978
- const submission = yield* requireSubmission(operation, validated.submissionId);
979
- if (submission.state === "settled") {
980
- if (submission.settled_outcome === null) return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "A settled Submission carries no terminal outcome.");
981
- return yield* SettlementConflict.make({
982
- submissionId: validated.submissionId,
983
- existingOutcome: submission.settled_outcome
856
+ })).pipe(Effect.mapError((error) => {
857
+ if (error._tag === "DoAppendConflict") return AppendConflict.make({
858
+ threadId: prepared.request.threadId,
859
+ batchId: prepared.request.batch.batchId,
860
+ reason: error.reason,
861
+ ...error.actualTailSequence !== void 0 ? { actualTailSequence: error.actualTailSequence } : {}
984
862
  });
985
- }
986
- if (!(submission.state === "joined" && submission.joined_host_submission_id !== null)) {
987
- let queuedAbortSettlement = false;
988
- if (validated.outcome === "aborted" && (submission.state === "ready" || submission.state === "terminalizing")) {
989
- const abortIntent = yield* readAbortIntent(operation, validated.submissionId);
990
- if (Option.isSome(abortIntent)) {
991
- const ownership = yield* readOwnership(operation, validated.submissionId);
992
- queuedAbortSettlement = Option.isNone(ownership);
993
- }
994
- }
995
- if (!queuedAbortSettlement) yield* requireOwnership(operation, submission, validated.ownershipToken);
996
- }
997
- const now = yield* currentInstant;
998
- yield* sql`
999
- INSERT INTO effect_agent_settlement_reservations (
1000
- submission_id,
1001
- settlement_id,
1002
- outcome,
1003
- record_id,
1004
- record_json,
1005
- record_digest,
1006
- reserved_at
1007
- ) VALUES (
1008
- ${validated.submissionId},
1009
- ${validated.settlementId},
1010
- ${validated.outcome},
1011
- ${validated.record.recordId},
1012
- ${recordJson},
1013
- ${validated.recordDigest},
1014
- ${now.iso}
1015
- )
1016
- RETURNING *`.pipe(rows.reservations.write, Effect.mapError(internalFailure(operation)));
1017
- yield* sql`
1018
- UPDATE effect_agent_submissions
1019
- SET state = 'terminalizing'
1020
- WHERE submission_id = ${validated.submissionId}
1021
- RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1022
- return ReservedSettlement.make({
1023
- submissionId: validated.submissionId,
1024
- settlementId: validated.settlementId,
1025
- outcome: validated.outcome,
1026
- record: validated.record,
1027
- recordDigest: validated.recordDigest,
1028
- replayed: false
1029
- });
1030
- }));
1031
- yield* hitFailpoint("ledger:reserve-settlement:after", operation);
1032
- return reserved;
863
+ return isDoStorageError(error) || error._tag === "DoStorageCorruptionError" || error._tag === "DoStorageFailpointError" || error._tag === "DoValueBoundExceeded" ? mapAppendError(error) : error;
864
+ }));
865
+ yield* hitFailpoint("append:after", operation);
866
+ return result;
867
+ })));
1033
868
  });
1034
- const validateFinalization = Effect.fn("DoSubmissionLedger.validateFinalization")(function* (validated, reservation) {
869
+ const validateFinalization = Effect.fnUntraced(function* (validated, submission) {
1035
870
  const operation = "ledger finalize settlement";
1036
- if (Option.isNone(reservation)) return yield* LedgerError.make({
871
+ const canonical = yield* readCanonicalSettlement(operation, submission);
872
+ if (canonical === void 0) return yield* LedgerError.make({
1037
873
  operation,
1038
- message: `No settlement reservation exists for submission ${validated.submissionId}.`
874
+ message: `No canonical settlement exists for submission ${validated.submissionId}.`
1039
875
  });
1040
- if (reservation.value.settlement_id !== validated.settlementId) return yield* SettlementConflict.make({
876
+ if (canonical.settlement.settlementId !== validated.settlementId) return yield* SettlementConflict.make({
1041
877
  submissionId: validated.submissionId,
1042
- existingOutcome: reservation.value.outcome
878
+ existingOutcome: canonical.settlement.outcome
1043
879
  });
1044
- const reservationRecord = yield* decodeRecordEnvelopeText(reservation.value.record_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_settlement_reservations", validated.submissionId, error.message)));
1045
- const settlementFailure = settlementFailureFromRecord(reservationRecord);
1046
- if (reservation.value.outcome === "failed" !== (settlementFailure !== void 0)) return yield* corruptionFailure(operation, "effect_agent_settlement_reservations", validated.submissionId, "The reserved outcome and canonical failure diagnostic disagree.");
1047
880
  return {
1048
- reservation: reservation.value,
1049
- reservationRecord,
1050
- settlementFailure
881
+ ...canonical,
882
+ settlementFailure: settlementFailureFromRecord(canonical.record)
1051
883
  };
1052
884
  });
1053
- const replayFinalization = Effect.fn("DoSubmissionLedger.replayFinalization")(function* (validated, submission, { reservation, settlementFailure }) {
885
+ const replayFinalization = Effect.fnUntraced(function* (validated, submission, { record, settlement, settlementFailure }) {
1054
886
  const operation = "ledger finalize settlement";
1055
- if (reservation.finalized_at === null) return yield* corruptionFailure(operation, "effect_agent_settlement_reservations", validated.submissionId, "A settled Submission's reservation carries no finalization timestamp.");
887
+ if (submission.finalized_at === null || submission.settled_record_id !== record.recordId || submission.settled_outcome !== settlement.outcome) return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "Settled projection disagrees with its canonical settlement.");
1056
888
  return yield* decodeSettlement({
1057
889
  submissionId: validated.submissionId,
1058
890
  settlementId: validated.settlementId,
1059
891
  receiptId: submission.receipt_id,
1060
- outcome: reservation.outcome,
892
+ outcome: settlement.outcome,
1061
893
  ...settlementFailure === void 0 ? {} : { failure: settlementFailure },
1062
- settledAt: reservation.finalized_at
894
+ settledAt: submission.finalized_at
1063
895
  }).pipe(Effect.mapError(internalFailure(operation)));
1064
896
  });
1065
897
  const finalizeSettlement = Effect.fn("DoSubmissionLedger.finalizeSettlement")(function* (request) {
@@ -1069,20 +901,27 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1069
901
  const replay = yield* state.read(Effect.gen(function* () {
1070
902
  const submission = yield* readSubmission(operation, validated.submissionId);
1071
903
  if (Option.isNone(submission) || submission.value.state !== "settled") return Option.none();
1072
- const finalization = yield* validateFinalization(validated, yield* readReservation(operation, validated.submissionId));
1073
- return Option.some(yield* replayFinalization(validated, submission.value, finalization));
904
+ const finalization = yield* validateFinalization(validated, submission.value);
905
+ return Option.some({
906
+ settlement: yield* replayFinalization(validated, submission.value, finalization),
907
+ threadId: submission.value.thread_id
908
+ });
1074
909
  }));
1075
910
  if (Option.isSome(replay)) {
911
+ owner.closeSubmission(replay.value.threadId, validated.submissionId);
1076
912
  yield* hitFailpoint("ledger:finalize-settlement:after", operation);
1077
- return replay.value;
913
+ return replay.value.settlement;
1078
914
  }
1079
915
  const settlement = yield* inWriteTransaction(operation, Effect.gen(function* () {
1080
- const state = yield* validateFinalization(validated, yield* readReservation(operation, validated.submissionId));
1081
- const { reservation, reservationRecord, settlementFailure } = state;
1082
916
  const submission = yield* requireSubmission(operation, validated.submissionId);
1083
- if (submission.state === "settled") return yield* replayFinalization(validated, submission, state);
917
+ const state = yield* validateFinalization(validated, submission);
918
+ const { settlement: canonicalSettlement, record: canonicalRecord, settlementFailure } = state;
919
+ if (submission.state === "settled") return {
920
+ settlement: yield* replayFinalization(validated, submission, state),
921
+ threadId: submission.thread_id
922
+ };
1084
923
  const now = yield* currentInstant;
1085
- const terminal = submission.worker_admission_json === null ? void 0 : workerTerminalFromRecord(yield* decodeSubmissionSnapshot(operation, submission), reservationRecord);
924
+ const terminal = submission.worker_admission_json === null ? void 0 : workerTerminalFromRecord(yield* decodeSubmissionSnapshot(operation, submission), canonicalRecord);
1086
925
  let sealedTerminal;
1087
926
  if (terminal !== void 0) {
1088
927
  if ((terminal === "completed" ? yield* sql`SELECT submission_id FROM effect_agent_submissions
@@ -1103,37 +942,33 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1103
942
  }
1104
943
  yield* sql`
1105
944
  UPDATE effect_agent_submissions
1106
- SET state = 'settled', settled_outcome = ${reservation.outcome}
945
+ SET state = 'settled', settled_outcome = ${canonicalSettlement.outcome},
946
+ settled_record_id = ${canonicalRecord.recordId}, finalized_at = ${now.iso}
1107
947
  WHERE submission_id = ${validated.submissionId}
1108
- RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1109
- yield* sql`
1110
- UPDATE effect_agent_settlement_reservations
1111
- SET finalized_at = ${now.iso}
1112
- WHERE submission_id = ${validated.submissionId}
1113
- RETURNING *`.pipe(rows.reservations.write, Effect.mapError(internalFailure(operation)));
1114
- yield* sql`
1115
- DELETE FROM effect_agent_submission_ownership
1116
- WHERE submission_id = ${validated.submissionId}
1117
- RETURNING *`.pipe(rows.ownership.remove, Effect.mapError(internalFailure(operation)));
948
+ RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1118
949
  yield* recordProgress("submission", operation);
1119
950
  if (sealedTerminal !== void 0) yield* retainWorkerSeal(submission.thread_id, sealedTerminal);
1120
- return yield* decodeSettlement({
1121
- submissionId: validated.submissionId,
1122
- settlementId: validated.settlementId,
1123
- receiptId: submission.receipt_id,
1124
- outcome: reservation.outcome,
1125
- ...settlementFailure === void 0 ? {} : { failure: settlementFailure },
1126
- settledAt: now.iso
1127
- }).pipe(Effect.mapError(internalFailure(operation)));
951
+ return {
952
+ threadId: submission.thread_id,
953
+ settlement: yield* decodeSettlement({
954
+ submissionId: validated.submissionId,
955
+ settlementId: validated.settlementId,
956
+ receiptId: submission.receipt_id,
957
+ outcome: canonicalSettlement.outcome,
958
+ ...settlementFailure === void 0 ? {} : { failure: settlementFailure },
959
+ settledAt: now.iso
960
+ }).pipe(Effect.mapError(internalFailure(operation)))
961
+ };
1128
962
  }));
963
+ owner.closeSubmission(settlement.threadId, validated.submissionId);
1129
964
  yield* hitFailpoint("ledger:finalize-settlement:after", operation);
1130
- return settlement;
965
+ return settlement.settlement;
1131
966
  });
1132
- const inspectWorker = Effect.fn("DoSubmissionLedger.inspectWorker")(function* (threadId) {
967
+ const inspectWorker = Effect.fnUntraced(function* (threadId) {
1133
968
  const operation = "inspect worker";
1134
969
  return yield* state.transaction(Effect.gen(function* () {
1135
970
  const read = Effect.fnUntraced(function* (active) {
1136
- const rows = yield* sql`SELECT ${sql.literal(SUBMISSION_COLUMNS)} FROM effect_agent_submissions
971
+ const rows = yield* sql`SELECT ${submissionColumns} FROM effect_agent_submissions
1137
972
  WHERE thread_id = ${threadId} ${active ? sql`AND state <> 'settled'` : sql``}
1138
973
  ORDER BY queue_sequence ${active ? sql`ASC` : sql`DESC`} LIMIT 1`.pipe(Effect.mapError(sqlFailure(operation)));
1139
974
  const decoded = yield* decodeSubmissionRows(operation, threadId, rows);
@@ -1150,7 +985,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1150
985
  }).pipe(Effect.mapError(internalFailure(operation)));
1151
986
  })).pipe(Effect.provideService(SqlClientService.SqlClient, sql), Effect.catchTag("SqlError", (cause) => sqlFailure(operation)(cause)));
1152
987
  });
1153
- const stopWorker = Effect.fn("DoSubmissionLedger.stopWorker")(function* (request) {
988
+ const stopWorker = Effect.fnUntraced(function* (request) {
1154
989
  const operation = "ledger stop worker";
1155
990
  const validated = yield* Schema.decodeEffect(WorkerStopCommand)(request).pipe(Effect.mapError(internalFailure(operation)));
1156
991
  return yield* inWriteTransaction(operation, Effect.gen(function* () {
@@ -1159,15 +994,12 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1159
994
  const aborted = yield* sql`INSERT OR IGNORE INTO effect_agent_abort_intents (submission_id, author, reason, requested_at)
1160
995
  SELECT submission_id, ${validated.author}, 'Worker owner stopped the worker', ${now.iso}
1161
996
  FROM effect_agent_submissions WHERE thread_id = ${validated.threadId} AND state <> 'settled' RETURNING *`.pipe(rows.aborts.write, Effect.mapError(internalFailure(operation)));
1162
- const owners = yield* sql`SELECT o.submission_id FROM effect_agent_submission_ownership o
1163
- JOIN effect_agent_submissions s ON s.submission_id = o.submission_id
1164
- WHERE s.thread_id = ${validated.threadId} AND s.state <> 'settled'`.pipe(Effect.mapError(sqlFailure(operation)));
1165
997
  if (sealed.length > 0 || aborted.length > 0) yield* recordProgress("control", operation);
1166
998
  if (sealed.length > 0) yield* retainWorkerSeal(validated.threadId, null);
1167
- return owners.length;
999
+ return owner.hasOwner(validated.threadId) ? 1 : 0;
1168
1000
  }));
1169
1001
  });
1170
- const requestAbort = Effect.fn("DoSubmissionLedger.requestAbort")(function* (request) {
1002
+ const requestAbort = Effect.fnUntraced(function* (request) {
1171
1003
  const operation = "ledger request abort";
1172
1004
  const validated = yield* Schema.decodeEffect(Schema.toType(AbortCommand))(request).pipe(Effect.mapError(internalFailure(operation)));
1173
1005
  yield* hitFailpoint("ledger:request-abort:before", operation);
@@ -1222,7 +1054,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1222
1054
  yield* hitFailpoint("ledger:request-abort:after", operation);
1223
1055
  return intent;
1224
1056
  });
1225
- const claimJoining = Effect.fn("DoSubmissionLedger.claimJoining")(function* (request) {
1057
+ const claimJoining = Effect.fnUntraced(function* (request) {
1226
1058
  const operation = "ledger claim joining";
1227
1059
  const validated = yield* Schema.decodeEffect(Schema.toType(ClaimJoiningRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1228
1060
  yield* hitFailpoint("ledger:claim-joining:before", operation);
@@ -1232,16 +1064,15 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1232
1064
  operation,
1233
1065
  message: `Host submission ${validated.hostSubmissionId} does not belong to thread ${validated.threadId}.`
1234
1066
  });
1235
- yield* requireOwnership(operation, host, validated.ownershipToken);
1067
+ yield* requireOwner(operation, host);
1236
1068
  if ((yield* cached(rows.workerStops.by("thread_id", validated.threadId), operation)).length > 0) return [];
1237
- const lane = yield* laneWork(validated.threadId, operation);
1238
- const laterRows = lane === void 0 ? yield* sql`
1239
- SELECT ${sql.literal(SUBMISSION_COLUMNS)}
1069
+ const laterRows = yield* sql`
1070
+ SELECT ${submissionColumns}
1240
1071
  FROM effect_agent_submissions
1241
1072
  WHERE thread_id = ${validated.threadId}
1242
1073
  AND queue_sequence > ${host.queue_sequence}
1243
1074
  ORDER BY queue_sequence ASC
1244
- `.pipe(Effect.mapError(sqlFailure(operation))) : yield* Effect.forEach(lane.filter((item) => item.queue_sequence > host.queue_sequence), (item) => requireSubmission(operation, item.submission_id));
1075
+ `.pipe(Effect.mapError(sqlFailure(operation)));
1245
1076
  const later = yield* decodeSubmissionRows(operation, validated.threadId, laterRows);
1246
1077
  const claimed = [];
1247
1078
  for (const row of later) {
@@ -1249,11 +1080,14 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1249
1080
  if ((row.state === "joining" || row.state === "joined") && row.joined_host_submission_id === validated.hostSubmissionId) continue;
1250
1081
  if (row.state === "settled" && row.settled_outcome === "aborted") continue;
1251
1082
  if (row.state !== "ready") break;
1083
+ const canonical = yield* readCanonicalSettlement(operation, row);
1084
+ if (canonical?.settlement.outcome === "aborted") continue;
1085
+ if (canonical !== void 0) break;
1252
1086
  yield* sql`
1253
1087
  UPDATE effect_agent_submissions
1254
1088
  SET state = 'joining', joined_host_submission_id = ${validated.hostSubmissionId}
1255
1089
  WHERE submission_id = ${row.submission_id}
1256
- RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1090
+ RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1257
1091
  const inputPayload = yield* parseStoredJsonText(row.input_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submissions", row.submission_id, error.message)));
1258
1092
  claimed.push(yield* decodeJoiningClaim({
1259
1093
  submissionId: row.submission_id,
@@ -1266,7 +1100,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1266
1100
  yield* hitFailpoint("ledger:claim-joining:after", operation);
1267
1101
  return claims;
1268
1102
  });
1269
- const markJoined = Effect.fn("DoSubmissionLedger.markJoined")(function* (request) {
1103
+ const markJoined = Effect.fnUntraced(function* (request) {
1270
1104
  const operation = "ledger mark joined";
1271
1105
  const validated = yield* Schema.decodeEffect(Schema.toType(MarkJoinedRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1272
1106
  yield* hitFailpoint("ledger:mark-joined:before", operation);
@@ -1277,7 +1111,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1277
1111
  message: `Submission ${validated.submissionId} was never claimed for joining.`
1278
1112
  });
1279
1113
  const host = yield* requireSubmission(operation, submission.joined_host_submission_id);
1280
- yield* requireOwnership(operation, host, validated.ownershipToken);
1114
+ yield* requireOwner(operation, host);
1281
1115
  if (submission.input_applied_record_id !== null) {
1282
1116
  if (submission.input_applied_record_id === validated.recordId && submission.input_applied_sequence === validated.sequence) return;
1283
1117
  return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "A different join marker is already recorded for this Submission.");
@@ -1293,25 +1127,37 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1293
1127
  input_applied_sequence = ${validated.sequence},
1294
1128
  state = 'joined'
1295
1129
  WHERE submission_id = ${validated.submissionId}
1296
- RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1130
+ RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1297
1131
  }));
1298
1132
  yield* hitFailpoint("ledger:mark-joined:after", operation);
1299
1133
  });
1300
- const revertJoining = Effect.fn("DoSubmissionLedger.revertJoining")(function* (request) {
1134
+ const revertJoining = Effect.fnUntraced(function* (request) {
1301
1135
  const operation = "ledger revert joining";
1302
1136
  const validated = yield* Schema.decodeEffect(Schema.toType(RevertJoiningRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1303
1137
  yield* hitFailpoint("ledger:revert-joining:before", operation);
1304
1138
  yield* inWriteTransaction(operation, Effect.gen(function* () {
1305
- if ((yield* requireSubmission(operation, validated.submissionId)).state !== "joining") return;
1139
+ const submission = yield* requireSubmission(operation, validated.submissionId);
1140
+ if (submission.state !== "joining") return;
1141
+ const guard = validated.guard;
1142
+ if (guard !== void 0) {
1143
+ if (submission.joined_host_submission_id !== guard.hostSubmissionId) return;
1144
+ const host = yield* requireSubmission(operation, guard.hostSubmissionId);
1145
+ if (host.thread_id !== submission.thread_id) return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "The joined host belongs to another Thread.");
1146
+ if (host.state !== "settled") yield* requireOwner(operation, host).pipe(Effect.mapError((cause) => cause._tag === "OwnershipLost" ? LedgerError.make({
1147
+ operation,
1148
+ message: "Joining recovery no longer owns the linked host.",
1149
+ cause
1150
+ }) : cause));
1151
+ }
1306
1152
  yield* sql`
1307
1153
  UPDATE effect_agent_submissions
1308
1154
  SET state = 'ready', joined_host_submission_id = NULL
1309
1155
  WHERE submission_id = ${validated.submissionId}
1310
- RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1156
+ RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1311
1157
  }));
1312
1158
  yield* hitFailpoint("ledger:revert-joining:after", operation);
1313
1159
  });
1314
- const suspend = Effect.fn("DoSubmissionLedger.suspend")(function* (request) {
1160
+ const suspend = Effect.fnUntraced(function* (request) {
1315
1161
  const operation = "ledger suspend";
1316
1162
  const validated = yield* Schema.decodeEffect(Schema.toType(SuspendRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1317
1163
  const reasonJson = yield* encodeSuspensionReasonText(validated.reason).pipe(Effect.mapError(internalFailure(operation)));
@@ -1325,16 +1171,19 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1325
1171
  existingOutcome: submission.settled_outcome
1326
1172
  });
1327
1173
  }
1328
- const reservation = yield* readReservation(operation, validated.submissionId);
1329
- if (Option.isSome(reservation)) return yield* SettlementConflict.make({
1174
+ const canonical = yield* readCanonicalSettlement(operation, submission);
1175
+ if (canonical !== void 0) return yield* SettlementConflict.make({
1330
1176
  submissionId: validated.submissionId,
1331
- existingOutcome: reservation.value.outcome
1177
+ existingOutcome: canonical.settlement.outcome
1332
1178
  });
1333
- yield* requireOwnership(operation, submission, validated.ownershipToken);
1179
+ yield* requireOwner(operation, submission);
1334
1180
  if (validated.reason._tag === "ApprovalPending") {
1335
1181
  const decisions = yield* readApprovalDecisions(operation, validated.submissionId);
1336
- const decided = new Set(decisions.map((row) => row.tool_call_id));
1337
- if (validated.reason.toolCallIds.every((toolCallId) => decided.has(toolCallId))) return RESUME_IMMEDIATELY;
1182
+ const decided = new Map(decisions.map((row) => [row.tool_call_id, row.decision]));
1183
+ if (approvalDecisionsCover(validated.reason.toolCallIds, (toolCallId) => decided.get(toolCallId))) return {
1184
+ outcome: RESUME_IMMEDIATELY,
1185
+ threadId: submission.thread_id
1186
+ };
1338
1187
  } else {
1339
1188
  const markers = yield* readChildSettlementMarkers(operation, validated.submissionId);
1340
1189
  const markerChildren = new Set(markers.map((row) => row.child_submission_id));
@@ -1343,7 +1192,10 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1343
1192
  allSettled = false;
1344
1193
  break;
1345
1194
  }
1346
- if (allSettled) return RESUME_IMMEDIATELY;
1195
+ if (allSettled) return {
1196
+ outcome: RESUME_IMMEDIATELY,
1197
+ threadId: submission.thread_id
1198
+ };
1347
1199
  }
1348
1200
  const now = yield* currentInstant;
1349
1201
  yield* sql`
@@ -1353,34 +1205,34 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1353
1205
  suspended_reason_json = ${reasonJson},
1354
1206
  suspended_at = ${now.iso}
1355
1207
  WHERE submission_id = ${validated.submissionId}
1356
- RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1357
- yield* sql`
1358
- DELETE FROM effect_agent_submission_ownership
1359
- WHERE submission_id = ${validated.submissionId}
1360
- RETURNING *`.pipe(rows.ownership.remove, Effect.mapError(internalFailure(operation)));
1208
+ RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1361
1209
  yield* retainLifecycle(submission, {
1362
1210
  _tag: "SubmissionSuspended",
1363
1211
  submissionId: validated.submissionId,
1364
1212
  reason: validated.reason
1365
1213
  });
1366
- return SUSPENDED;
1214
+ return {
1215
+ outcome: SUSPENDED,
1216
+ threadId: submission.thread_id
1217
+ };
1367
1218
  }));
1219
+ if (outcome.outcome === SUSPENDED) owner.closeSubmission(outcome.threadId, validated.submissionId);
1368
1220
  yield* hitFailpoint("ledger:suspend:after", operation);
1369
- return outcome;
1221
+ return outcome.outcome;
1370
1222
  });
1371
1223
  /**
1372
- * Once every pending call of a recorded ApprovalPending suspension has a decision intent,
1224
+ * Once one pending call is denied or every pending call has a decision intent,
1373
1225
  * the lane wakes: suspended → input-applied, suspension cleared (plan §2.6). A
1374
1226
  * WaitingForChild suspension wakes only through recordChildSettled. Runs inside the caller's
1375
1227
  * write transaction.
1376
1228
  */
1377
- const wakeSuspendedIfCovered = Effect.fn("DoSubmissionLedger.wakeSuspendedIfCovered")(function* (operation, submission) {
1229
+ const wakeSuspendedIfCovered = Effect.fnUntraced(function* (operation, submission) {
1378
1230
  if (submission.state !== "suspended" || submission.suspended_reason_json === null) return;
1379
1231
  const reason = yield* Schema.decodeEffect(Schema.fromJsonString(SuspensionReason))(submission.suspended_reason_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_submissions", submission.submission_id, error.message)));
1380
1232
  if (reason._tag !== "ApprovalPending") return;
1381
1233
  const decisions = yield* readApprovalDecisions(operation, submission.submission_id);
1382
- const decided = new Set(decisions.map((row) => row.tool_call_id));
1383
- if (!reason.toolCallIds.every((toolCallId) => decided.has(toolCallId))) return;
1234
+ const decided = new Map(decisions.map((row) => [row.tool_call_id, row.decision]));
1235
+ if (!approvalDecisionsCover(reason.toolCallIds, (toolCallId) => decided.get(toolCallId))) return;
1384
1236
  yield* sql`
1385
1237
  UPDATE effect_agent_submissions
1386
1238
  SET
@@ -1388,13 +1240,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1388
1240
  suspended_reason_json = NULL,
1389
1241
  suspended_at = NULL
1390
1242
  WHERE submission_id = ${submission.submission_id}
1391
- RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1243
+ RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1392
1244
  yield* retainLifecycle(submission, {
1393
1245
  _tag: "SubmissionResumed",
1394
1246
  submissionId: yield* decodeSubmissionId(submission.submission_id).pipe(Effect.mapError(internalFailure(operation)))
1395
1247
  });
1396
1248
  });
1397
- const recordApprovalDecision = Effect.fn("DoSubmissionLedger.recordApprovalDecision")(function* (command) {
1249
+ const recordApprovalDecision = Effect.fnUntraced(function* (command) {
1398
1250
  const operation = "ledger record approval decision";
1399
1251
  const validated = yield* Schema.decodeEffect(Schema.toType(ApprovalDecisionCommand))(command).pipe(Effect.mapError(internalFailure(operation)));
1400
1252
  yield* hitFailpoint("ledger:approval-decision:before", operation);
@@ -1448,7 +1300,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1448
1300
  yield* hitFailpoint("ledger:approval-decision:after", operation);
1449
1301
  return intent;
1450
1302
  });
1451
- const markUnknown = Effect.fn("DoSubmissionLedger.markUnknown")(function* (request) {
1303
+ const markUnknown = Effect.fnUntraced(function* (request) {
1452
1304
  const operation = "ledger mark unknown";
1453
1305
  const validated = yield* Schema.decodeEffect(Schema.toType(MarkUnknownRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1454
1306
  yield* hitFailpoint("ledger:mark-unknown:before", operation);
@@ -1461,10 +1313,10 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1461
1313
  existingOutcome: submission.settled_outcome
1462
1314
  });
1463
1315
  }
1464
- const reservation = yield* readReservation(operation, validated.submissionId);
1465
- if (Option.isSome(reservation)) return yield* SettlementConflict.make({
1316
+ const canonical = yield* readCanonicalSettlement(operation, submission);
1317
+ if (canonical !== void 0) return yield* SettlementConflict.make({
1466
1318
  submissionId: validated.submissionId,
1467
- existingOutcome: reservation.value.outcome
1319
+ existingOutcome: canonical.settlement.outcome
1468
1320
  });
1469
1321
  const existingIds = yield* storedUnknownToolCallIds(operation, submission);
1470
1322
  const known = new Set(existingIds);
@@ -1477,7 +1329,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1477
1329
  unknown_reason = ${submission.unknown_reason ?? validated.reason},
1478
1330
  unknown_tool_call_ids_json = ${idsJson}
1479
1331
  WHERE submission_id = ${validated.submissionId}
1480
- RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1332
+ RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1481
1333
  if (submission.state !== "unknown") yield* retainLifecycle(submission, {
1482
1334
  _tag: "SubmissionUnknown",
1483
1335
  submissionId: validated.submissionId
@@ -1485,7 +1337,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1485
1337
  }));
1486
1338
  yield* hitFailpoint("ledger:mark-unknown:after", operation);
1487
1339
  });
1488
- const recordUnknownResolution = Effect.fn("DoSubmissionLedger.recordUnknownResolution")(function* (command) {
1340
+ const recordUnknownResolution = Effect.fnUntraced(function* (command) {
1489
1341
  const operation = "ledger record unknown resolution";
1490
1342
  const validated = yield* Schema.decodeEffect(Schema.toType(UnknownResolutionCommand))(command).pipe(Effect.mapError(internalFailure(operation)));
1491
1343
  const resolutionJson = yield* encodeUnknownResolutionText(validated.resolution).pipe(Effect.mapError(internalFailure(operation)));
@@ -1494,17 +1346,37 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1494
1346
  const submission = yield* requireSubmission(operation, validated.submissionId);
1495
1347
  if (submission.state === "settled") {
1496
1348
  if (submission.settled_outcome === null) return yield* corruptionFailure(operation, "effect_agent_submissions", validated.submissionId, "A settled Submission carries no terminal outcome.");
1497
- return yield* SettlementConflict.make({
1349
+ if (validated.resolution._tag !== "CompletedWithResult" && validated.resolution._tag !== "NeverHappened") return yield* SettlementConflict.make({
1498
1350
  submissionId: validated.submissionId,
1499
1351
  existingOutcome: submission.settled_outcome
1500
1352
  });
1501
1353
  }
1502
- const existing = (yield* readUnknownResolutions(operation, validated.submissionId)).find((row) => row.tool_call_id === validated.toolCallId);
1354
+ const resolutions = yield* readUnknownResolutions(operation, validated.submissionId);
1355
+ const kind = unknownResolutionKind(validated.resolution);
1356
+ const existing = resolutions.find((row) => row.tool_call_id === validated.toolCallId && row.resolution_kind === kind);
1503
1357
  const existingIntent = existing === void 0 ? void 0 : yield* unknownResolutionIntentFromRow(operation, existing);
1504
- if (existingIntent !== void 0 && !equivalentUnknownResolution(existingIntent.resolution, validated.resolution)) return yield* UnknownResolutionConflict.make({
1358
+ if (existingIntent !== void 0 && !equivalentUnknownResolution(existingIntent.resolution, validated.resolution) || existing === void 0 && kind === "execution" && resolutions.some((row) => row.tool_call_id === validated.toolCallId && row.resolution_kind === "factual")) return yield* UnknownResolutionConflict.make({
1505
1359
  submissionId: validated.submissionId,
1506
1360
  toolCallId: validated.toolCallId
1507
1361
  });
1362
+ if (existingIntent === void 0 && kind === "factual") {
1363
+ const runId = runIdForSubmission(validated.submissionId);
1364
+ const found = yield* sql`
1365
+ SELECT ${canonicalRecordJson(sql)} AS record_json FROM effect_agent_canonical_records
1366
+ WHERE thread_id = ${submission.thread_id}
1367
+ AND record_tag = 27
1368
+ AND run_id = ${runKey(sql, sql`${submission.thread_id}`, sql`${JSON.stringify(runId)}`)}
1369
+ AND tool_call_id = ${JSON.stringify(validated.toolCallId)}
1370
+ LIMIT 2
1371
+ `.pipe(Effect.mapError(sqlFailure(operation)));
1372
+ const outcomes = yield* decodeRows(Schema.Array(Schema.Struct({ record_json: BoundedStoredText })), "effect_agent_canonical_records", validated.toolCallId, found).pipe(Effect.mapError(internalFailure(operation)));
1373
+ const result = outcomes[0] === void 0 ? void 0 : (yield* decodeRecordEnvelopeText(outcomes[0].record_json).pipe(Effect.mapError(internalFailure(operation)))).payload;
1374
+ if (outcomes.length > 1 || result !== void 0 && (result._tag !== "ToolCallSettled" || result.runId !== runId || result.toolCallId !== validated.toolCallId)) return yield* corruptionFailure(operation, "effect_agent_canonical_records", validated.toolCallId, "Canonical Tool result identity is inconsistent.");
1375
+ if (result?._tag === "ToolCallSettled" && !unknownResolutionMatchesResult(validated.resolution, result)) return yield* UnknownResolutionConflict.make({
1376
+ submissionId: validated.submissionId,
1377
+ toolCallId: validated.toolCallId
1378
+ });
1379
+ }
1508
1380
  let resolved;
1509
1381
  if (existingIntent !== void 0) resolved = existingIntent;
1510
1382
  else {
@@ -1513,6 +1385,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1513
1385
  INSERT INTO effect_agent_unknown_resolutions (
1514
1386
  submission_id,
1515
1387
  tool_call_id,
1388
+ resolution_kind,
1516
1389
  author,
1517
1390
  reason,
1518
1391
  resolution_json,
@@ -1520,6 +1393,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1520
1393
  ) VALUES (
1521
1394
  ${validated.submissionId},
1522
1395
  ${validated.toolCallId},
1396
+ ${kind},
1523
1397
  ${validated.author},
1524
1398
  ${validated.reason},
1525
1399
  ${resolutionJson},
@@ -1549,7 +1423,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1549
1423
  unknown_reason = NULL,
1550
1424
  unknown_tool_call_ids_json = NULL
1551
1425
  WHERE submission_id = ${validated.submissionId}
1552
- RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1426
+ RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1553
1427
  yield* retainLifecycle(submission, {
1554
1428
  _tag: "SubmissionResumed",
1555
1429
  submissionId: validated.submissionId
@@ -1561,15 +1435,15 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1561
1435
  yield* hitFailpoint("ledger:unknown-resolution:after", operation);
1562
1436
  return intent;
1563
1437
  });
1564
- const recordChildSettled = Effect.fn("DoSubmissionLedger.recordChildSettled")(function* (request) {
1438
+ const recordChildSettled = Effect.fnUntraced(function* (request) {
1565
1439
  const operation = "ledger record child settled";
1566
1440
  const validated = yield* Schema.decodeEffect(Schema.toType(ChildSettledNotification))(request).pipe(Effect.mapError(internalFailure(operation)));
1567
1441
  yield* hitFailpoint("ledger:child-settled:before", operation);
1568
1442
  const outcome = yield* inWriteTransaction(operation, Effect.gen(function* () {
1569
1443
  const parent = yield* requireSubmission(operation, validated.parentSubmissionId);
1570
1444
  const child = yield* readSubmission(operation, validated.childSubmissionId);
1571
- const childReservation = yield* readReservation(operation, validated.childSubmissionId);
1572
- if (Option.isSome(child) && child.value.state !== "settled" && !(child.value.state === "terminalizing" && Option.isSome(childReservation))) return yield* LedgerError.make({
1445
+ const childCanonical = Option.isSome(child) ? yield* readCanonicalSettlement(operation, child.value) : void 0;
1446
+ if (Option.isSome(child) && childCanonical === void 0) return yield* LedgerError.make({
1573
1447
  operation,
1574
1448
  message: `Child submission ${validated.childSubmissionId} has no recorded settlement.`
1575
1449
  });
@@ -1583,7 +1457,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1583
1457
  ) VALUES (
1584
1458
  ${validated.parentSubmissionId},
1585
1459
  ${validated.childSubmissionId},
1586
- ${Option.isSome(child) && child.value.state === "settled" ? child.value.settled_outcome : Option.isSome(childReservation) ? childReservation.value.outcome : null},
1460
+ ${childCanonical?.settlement.outcome ?? null},
1587
1461
  ${now.iso}
1588
1462
  )
1589
1463
  ON CONFLICT (parent_submission_id, child_submission_id) DO NOTHING
@@ -1602,7 +1476,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1602
1476
  suspended_reason_json = NULL,
1603
1477
  suspended_at = NULL
1604
1478
  WHERE submission_id = ${validated.parentSubmissionId}
1605
- RETURNING *`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1479
+ RETURNING *, ${definitionJson(sql)} AS agent_digests_json`.pipe(rows.submissions.write, Effect.mapError(internalFailure(operation)));
1606
1480
  yield* retainLifecycle(parent, {
1607
1481
  _tag: "SubmissionResumed",
1608
1482
  submissionId: validated.parentSubmissionId
@@ -1612,7 +1486,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1612
1486
  yield* hitFailpoint("ledger:child-settled:after", operation);
1613
1487
  return outcome;
1614
1488
  });
1615
- const reserveChildBudget = Effect.fn("DoSubmissionLedger.reserveChildBudget")(function* (request) {
1489
+ const reserveChildBudget = Effect.fnUntraced(function* (request) {
1616
1490
  const operation = "ledger reserve child budget";
1617
1491
  const validated = yield* Schema.decodeEffect(Schema.toType(ChildBudgetReservationRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1618
1492
  const allocationJson = yield* encodePersistedJsonText(validated.allocation).pipe(Effect.mapError(internalFailure(operation)));
@@ -1638,7 +1512,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1638
1512
  message: `Parent Tool Call ${validated.parentToolCallId} already owns reservation ${collision.value.reservation_id}.`
1639
1513
  });
1640
1514
  const parent = yield* requireSubmission(operation, validated.parentSubmissionId);
1641
- yield* requireOwnership(operation, parent, validated.ownershipToken);
1515
+ yield* requireOwner(operation, parent);
1642
1516
  const now = yield* currentInstant;
1643
1517
  yield* sql`
1644
1518
  INSERT INTO effect_agent_child_reservations (
@@ -1669,7 +1543,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1669
1543
  yield* hitFailpoint("ledger:child-reservation:after", operation);
1670
1544
  return reserved;
1671
1545
  });
1672
- const attachChildToReservation = Effect.fn("DoSubmissionLedger.attachChildToReservation")(function* (request) {
1546
+ const attachChildToReservation = Effect.fnUntraced(function* (request) {
1673
1547
  const operation = "ledger attach child to reservation";
1674
1548
  const validated = yield* Schema.decodeEffect(Schema.toType(AttachChildToReservationRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1675
1549
  yield* hitFailpoint("ledger:child-attach:before", operation);
@@ -1688,7 +1562,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1688
1562
  });
1689
1563
  }
1690
1564
  const parent = yield* requireSubmission(operation, existing.value.parent_submission_id);
1691
- yield* requireOwnership(operation, parent, validated.ownershipToken);
1565
+ yield* requireOwner(operation, parent);
1692
1566
  if (existing.value.status !== "reserved") return yield* ChildReservationConflict.make({
1693
1567
  reservationId: validated.reservationId,
1694
1568
  status: existing.value.status,
@@ -1706,7 +1580,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1706
1580
  yield* hitFailpoint("ledger:child-attach:after", operation);
1707
1581
  return attached;
1708
1582
  });
1709
- const beginChildBudgetRelease = Effect.fn("DoSubmissionLedger.beginChildBudgetRelease")(function* (request) {
1583
+ const beginChildBudgetRelease = Effect.fnUntraced(function* (request) {
1710
1584
  const operation = "ledger begin child budget release";
1711
1585
  const validated = yield* Schema.decodeEffect(Schema.toType(BeginChildBudgetReleaseRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1712
1586
  const accountingJson = yield* encodePersistedJsonText(validated.accounting).pipe(Effect.mapError(internalFailure(operation)));
@@ -1742,7 +1616,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1742
1616
  yield* hitFailpoint("ledger:child-release-pending:after", operation);
1743
1617
  return frozen;
1744
1618
  });
1745
- const releaseChildBudget = Effect.fn("DoSubmissionLedger.releaseChildBudget")(function* (request) {
1619
+ const releaseChildBudget = Effect.fnUntraced(function* (request) {
1746
1620
  const operation = "ledger release child budget";
1747
1621
  const validated = yield* Schema.decodeEffect(Schema.toType(ReleaseChildBudgetRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1748
1622
  yield* hitFailpoint("ledger:child-release:before", operation);
@@ -1771,7 +1645,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1771
1645
  yield* hitFailpoint("ledger:child-release:after", operation);
1772
1646
  return released;
1773
1647
  });
1774
- const scanPage = Effect.fn("DoSubmissionLedger.scanPage")(function* (cursor) {
1648
+ const scanPage = Effect.fnUntraced(function* (cursor) {
1775
1649
  const operation = "ledger scan nonterminal";
1776
1650
  const rows = yield* (cursor === void 0 ? sql`
1777
1651
  SELECT submission_id AS "submissionId", thread_id AS "threadId",
@@ -1816,7 +1690,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1816
1690
  }))).pipe(Effect.mapError(internalFailure("ledger scan nonterminal")));
1817
1691
  return Stream.fromIterable(work);
1818
1692
  })));
1819
- const readAbortIntentForSubmission = Effect.fn("DoSubmissionLedger.readAbortIntentForSubmission")(function* (request) {
1693
+ const readAbortIntentForSubmission = Effect.fnUntraced(function* (request) {
1820
1694
  const operation = "ledger read abort intent";
1821
1695
  const validated = yield* Schema.decodeEffect(Schema.toType(AbortIntentRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1822
1696
  return yield* state.read(Effect.gen(function* () {
@@ -1825,38 +1699,17 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1825
1699
  return Option.isNone(intent) ? void 0 : yield* abortIntentFromRow(operation, submission, validated.submissionId, intent.value);
1826
1700
  }));
1827
1701
  });
1828
- const loadRecoverySnapshot = Effect.fn("DoSubmissionLedger.loadRecoverySnapshot")(function* (request) {
1702
+ const loadRecoverySnapshot = Effect.fnUntraced(function* (request) {
1829
1703
  const operation = "ledger load recovery snapshot";
1830
1704
  const validated = yield* Schema.decodeEffect(Schema.toType(RecoverySnapshotRequest))(request).pipe(Effect.mapError(internalFailure(operation)));
1831
1705
  return yield* state.read(Effect.gen(function* () {
1832
1706
  const submissionRow = yield* requireSubmission(operation, validated.submissionId);
1833
1707
  const submission = yield* decodeSubmissionSnapshot(operation, submissionRow);
1834
- let ownership;
1835
- const ownershipRow = yield* readOwnership(operation, validated.submissionId);
1836
- if (Option.isSome(ownershipRow)) ownership = yield* decodeOwnershipSnapshot({
1837
- attemptId: ownershipRow.value.attempt_id,
1838
- ownerProducerId: ownershipRow.value.owner_producer_id,
1839
- producerEpoch: ownershipRow.value.producer_epoch,
1840
- leaseExpiresAt: ownershipRow.value.lease_expires_at
1841
- }).pipe(Effect.mapError(internalFailure(operation)));
1842
1708
  let inputApplied;
1843
1709
  if (submissionRow.input_applied_record_id !== null && submissionRow.input_applied_sequence !== null) inputApplied = yield* decodeInputAppliedMarker({
1844
1710
  recordId: submissionRow.input_applied_record_id,
1845
1711
  sequence: submissionRow.input_applied_sequence
1846
1712
  }).pipe(Effect.mapError(internalFailure(operation)));
1847
- let reservation;
1848
- const reservationRow = yield* readReservation(operation, validated.submissionId);
1849
- if (Option.isSome(reservationRow)) {
1850
- const record = yield* decodeRecordEnvelopeText(reservationRow.value.record_json).pipe(Effect.mapError((error) => corruptionFailure(operation, "effect_agent_settlement_reservations", validated.submissionId, error.message)));
1851
- const settlementId = yield* Schema.decodeEffect(SettlementReservationSnapshot.fields.settlementId)(reservationRow.value.settlement_id).pipe(Effect.mapError(internalFailure(operation)));
1852
- reservation = SettlementReservationSnapshot.make({
1853
- settlementId,
1854
- outcome: reservationRow.value.outcome,
1855
- record,
1856
- recordDigest: reservationRow.value.record_digest,
1857
- finalized: reservationRow.value.finalized_at !== null
1858
- });
1859
- }
1860
1713
  let abortIntent;
1861
1714
  const abortRow = yield* readAbortIntent(operation, validated.submissionId);
1862
1715
  if (Option.isSome(abortRow)) abortIntent = yield* abortIntentFromRow(operation, submissionRow, validated.submissionId, abortRow.value);
@@ -1923,9 +1776,8 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1923
1776
  ...parentLinkage === void 0 ? {} : { parentLinkage },
1924
1777
  ...hostSubmissionId === void 0 ? {} : { hostSubmissionId },
1925
1778
  ...suspension === void 0 ? {} : { suspension },
1926
- ...ownership === void 0 ? {} : { ownership },
1779
+ hasOwner: owner.hasOwner(submissionRow.thread_id, submissionRow.submission_id),
1927
1780
  ...inputApplied === void 0 ? {} : { inputApplied },
1928
- ...reservation === void 0 ? {} : { reservation },
1929
1781
  ...abortIntent === void 0 ? {} : { abortIntent }
1930
1782
  });
1931
1783
  }));
@@ -1935,20 +1787,18 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1935
1787
  admit,
1936
1788
  markReady,
1937
1789
  lookup,
1790
+ resolveFundingOwner,
1938
1791
  resolveAdmission,
1939
- claim,
1940
- renewOwnership,
1941
- releaseOwnership,
1792
+ selectHead,
1942
1793
  markInputApplied,
1943
- reserveSettlement,
1944
- finalizeSettlement,
1794
+ finalizeSettlement: (request) => owner.withMutation(finalizeSettlement(request)),
1945
1795
  requestAbort,
1946
1796
  stopWorker,
1947
1797
  inspectWorker,
1948
1798
  claimJoining,
1949
1799
  markJoined,
1950
1800
  revertJoining,
1951
- suspend,
1801
+ suspend: (request) => owner.withMutation(suspend(request)),
1952
1802
  recordApprovalDecision,
1953
1803
  markUnknown,
1954
1804
  recordUnknownResolution,
@@ -1960,17 +1810,17 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
1960
1810
  scanNonterminal,
1961
1811
  loadRecoverySnapshot,
1962
1812
  readAbortIntent: readAbortIntentForSubmission
1963
- }));
1813
+ })).pipe(Context.add(StorageOwner, owner), Context.add(SettlementPublisher, { publish }));
1964
1814
  });
1965
1815
  /**
1966
1816
  * Durable Object SubmissionLedger implementation sharing the journal's private SQLite
1967
- * database, storage-backed transaction discipline, and producer-epoch fencing substrate.
1817
+ * database, storage-backed transactions, and scoped local owner.
1968
1818
  * Configuration, failpoint, SQL, and Crypto authority stay visible in the input channel.
1969
1819
  */
1970
1820
  const submissionLedgerLayer = Layer.effectContext(makeServices());
1971
1821
  /**
1972
1822
  * A composition-root convenience Layer for the durable Submission Ledger. Point it at the
1973
- * same `ctx.storage` as the ThreadStore so claims fence the same producer epochs.
1823
+ * same `ctx.storage` as the ThreadStore so both share the same scoped storage owner.
1974
1824
  */
1975
1825
  const ledgerLayer = (options) => Layer.unwrap(Effect.map(DoStorageConfig, (config) => submissionLedgerLayer.pipe(Layer.provide(Layer.mergeAll(Layer.succeed(DoStorageConfig)(config), storageFailpointLayer(options), SqliteClient.layer({ storage: options.storage }), BrowserCrypto.layer))))).pipe(Layer.provide(storageConfigLayer(options)));
1976
1826
  //#endregion