@sekiban/dcb-runtime 0.2.0 → 0.2.2

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 (44) hide show
  1. package/README.md +32 -1
  2. package/dist/allocator/AllocatorDurableObject.js +25 -2
  3. package/dist/bootstrap/BootstrapCoordinatorDurableObject.d.ts +2 -0
  4. package/dist/bootstrap/BootstrapCoordinatorDurableObject.js +64 -1
  5. package/dist/bootstrap/OperatorBootstrap.d.ts +5 -1
  6. package/dist/bootstrap/OperatorBootstrap.js +17 -10
  7. package/dist/bootstrap/types.d.ts +6 -0
  8. package/dist/cli/OperatorRepairCli.d.ts +6 -1
  9. package/dist/cli/OperatorRepairCli.js +26 -6
  10. package/dist/cloudflare.d.ts +5 -0
  11. package/dist/cloudflare.js +1567 -465
  12. package/dist/commit/CommitWorker.d.ts +19 -0
  13. package/dist/commit/CommitWorker.js +176 -27
  14. package/dist/completeness/IncidentLifecycle.d.ts +75 -0
  15. package/dist/completeness/IncidentLifecycle.js +503 -0
  16. package/dist/completeness/types.d.ts +67 -0
  17. package/dist/cosmos.d.ts +34 -4
  18. package/dist/cosmos.js +214 -49
  19. package/dist/downstream/OutboxDrain.d.ts +4 -1
  20. package/dist/downstream/OutboxDrain.js +24 -2
  21. package/dist/generated/cosmos-layout.d.ts +87 -0
  22. package/dist/generated/cosmos-layout.js +99 -0
  23. package/dist/http/ConfiguredBearer.d.ts +12 -0
  24. package/dist/http/ConfiguredBearer.js +33 -0
  25. package/dist/http/IncidentMaintenance.d.ts +8 -0
  26. package/dist/http/IncidentMaintenance.js +124 -0
  27. package/dist/http/SerializedQueryWorker.js +37 -0
  28. package/dist/index.d.ts +13 -0
  29. package/dist/index.js +1627 -465
  30. package/dist/read/SerializedReadWorker.d.ts +3 -0
  31. package/dist/read/SerializedReadWorker.js +3 -3
  32. package/dist/shard/ShardRotation.d.ts +48 -0
  33. package/dist/shard/ShardRotation.js +120 -0
  34. package/dist/store/CosmosEventStore.js +56 -46
  35. package/dist/store/PostgresEventStore.d.ts +20 -0
  36. package/dist/store/PostgresEventStore.js +53 -0
  37. package/dist/store/provider.d.ts +4 -0
  38. package/dist/tag/TagDurableObject.js +66 -14
  39. package/dist/tag/TagSqlSchema.d.ts +6 -1
  40. package/dist/tag/TagSqlSchema.js +15 -0
  41. package/dist/tag/types.d.ts +9 -0
  42. package/dist/trace/CommitTrace.d.ts +3 -3
  43. package/dist/trace/CommitTraceVerifier.js +1 -1
  44. package/package.json +6 -15
package/dist/index.js CHANGED
@@ -942,15 +942,15 @@ function encodeOrderOrdinal(value) {
942
942
  function decodeOrderOrdinal(value) {
943
943
  try {
944
944
  return assertSortableUniqueId(value).ticks;
945
- } catch (error10) {
946
- const detail = error10 instanceof Error ? error10.message : "invalid SortableUniqueId";
947
- throw new OrderClockReadError(detail, { cause: error10 });
945
+ } catch (error11) {
946
+ const detail = error11 instanceof Error ? error11.message : "invalid SortableUniqueId";
947
+ throw new OrderClockReadError(detail, { cause: error11 });
948
948
  }
949
949
  }
950
- function asOrderClockError(error10) {
951
- if (error10 instanceof OrderClockReadError) return error10;
952
- const detail = error10 instanceof Error ? error10.message : "SortableUniqueId allocation failed";
953
- return new OrderClockReadError(detail, { cause: error10 });
950
+ function asOrderClockError(error11) {
951
+ if (error11 instanceof OrderClockReadError) return error11;
952
+ const detail = error11 instanceof Error ? error11.message : "SortableUniqueId allocation failed";
953
+ return new OrderClockReadError(detail, { cause: error11 });
954
954
  }
955
955
  function allocateOrderRange(watermark, count, clockTick, options = {}) {
956
956
  if (!Number.isSafeInteger(count) || count <= 0) {
@@ -979,9 +979,9 @@ function allocateOrderRange(watermark, count, clockTick, options = {}) {
979
979
  watermark: suids.at(-1),
980
980
  suids: Object.freeze(suids)
981
981
  });
982
- } catch (error10) {
983
- if (error10 instanceof SortableUniqueIdError) throw error10;
984
- throw asOrderClockError(error10);
982
+ } catch (error11) {
983
+ if (error11 instanceof SortableUniqueIdError) throw error11;
984
+ throw asOrderClockError(error11);
985
985
  }
986
986
  }
987
987
  function diagnosticAllocatedAt(ticks) {
@@ -999,7 +999,7 @@ function diagnosticAllocatedAt(ticks) {
999
999
  var commit_trace_manifest_default = {
1000
1000
  schemaVersion: 2,
1001
1001
  name: "commit-trace-manifest",
1002
- description: "Authority for the sdt.commit span manifests. means/19 links to RENDERED-VIEWS.md; it does not duplicate these tables.",
1002
+ description: "Repository-owned authority for the sdt.commit span manifests. The generated views are kept in RENDERED-VIEWS.md; the manifest does not duplicate those tables.",
1003
1003
  remoteInvocationUniverse: [
1004
1004
  "S16"
1005
1005
  ],
@@ -3412,9 +3412,9 @@ var commit_trace_manifest_default = {
3412
3412
 
3413
3413
  // src/trace/AttributionRatio.ts
3414
3414
  function failure(code, message) {
3415
- const error10 = new Error(`${code}:${message}`);
3416
- error10.name = "CommitTraceAttributionError";
3417
- throw error10;
3415
+ const error11 = new Error(`${code}:${message}`);
3416
+ error11.name = "CommitTraceAttributionError";
3417
+ throw error11;
3418
3418
  }
3419
3419
  function clip(interval, root) {
3420
3420
  const start = Math.max(interval[0], root[0]);
@@ -3731,8 +3731,8 @@ var CommitTrace = class {
3731
3731
  unattributed,
3732
3732
  attributionGatePassed: unattributed.unattributedRatio <= 0.05
3733
3733
  });
3734
- } catch (error10) {
3735
- this.noteObservationFailure(error10);
3734
+ } catch (error11) {
3735
+ this.noteObservationFailure(error11);
3736
3736
  return Object.freeze({
3737
3737
  ...base,
3738
3738
  runtimeVerification: this.observationVerification()
@@ -3799,9 +3799,9 @@ var CommitTrace = class {
3799
3799
  throw new Error(`Commit trace root ${rowId} is not active`);
3800
3800
  }
3801
3801
  attributes = this.attributesFor(row, options);
3802
- } catch (error10) {
3803
- if (!this.isFailOpen()) throw error10;
3804
- this.noteObservationFailure(error10);
3802
+ } catch (error11) {
3803
+ if (!this.isFailOpen()) throw error11;
3804
+ this.noteObservationFailure(error11);
3805
3805
  return callback(disabledChild());
3806
3806
  }
3807
3807
  const execute = async (native2) => {
@@ -3927,8 +3927,8 @@ var CommitTrace = class {
3927
3927
  for (const [key, value] of Object.entries(attributes)) {
3928
3928
  try {
3929
3929
  native.setAttribute(key, value);
3930
- } catch (error10) {
3931
- this.noteObservationFailure(error10);
3930
+ } catch (error11) {
3931
+ this.noteObservationFailure(error11);
3932
3932
  }
3933
3933
  }
3934
3934
  }
@@ -3945,8 +3945,8 @@ var CommitTrace = class {
3945
3945
  now() {
3946
3946
  try {
3947
3947
  return this.clock().now();
3948
- } catch (error10) {
3949
- this.noteObservationFailure(error10);
3948
+ } catch (error11) {
3949
+ this.noteObservationFailure(error11);
3950
3950
  return Date.now();
3951
3951
  }
3952
3952
  }
@@ -3954,8 +3954,8 @@ var CommitTrace = class {
3954
3954
  let beforeVerification;
3955
3955
  try {
3956
3956
  beforeVerification = this.snapshot();
3957
- } catch (error10) {
3958
- this.noteObservationFailure(error10);
3957
+ } catch (error11) {
3958
+ this.noteObservationFailure(error11);
3959
3959
  return;
3960
3960
  }
3961
3961
  if (this.options.runtimeVerifier !== void 0) {
@@ -3966,15 +3966,15 @@ var CommitTrace = class {
3966
3966
  this.options.runtimeVerifier.verify(beforeVerification);
3967
3967
  this.runtimeVerification = Object.freeze({ passed: true });
3968
3968
  }
3969
- } catch (error10) {
3970
- const code = typeof error10 === "object" && error10 !== null && "code" in error10 && typeof error10.code === "string" ? error10.code : "runtime-verifier-error";
3969
+ } catch (error11) {
3970
+ const code = typeof error11 === "object" && error11 !== null && "code" in error11 && typeof error11.code === "string" ? error11.code : "runtime-verifier-error";
3971
3971
  this.runtimeVerification = Object.freeze({ passed: false, code });
3972
3972
  }
3973
3973
  }
3974
3974
  try {
3975
3975
  this.options.sink?.record(this.snapshot());
3976
- } catch (error10) {
3977
- this.noteObservationFailure(error10);
3976
+ } catch (error11) {
3977
+ this.noteObservationFailure(error11);
3978
3978
  }
3979
3979
  }
3980
3980
  /**
@@ -3986,8 +3986,8 @@ var CommitTrace = class {
3986
3986
  async scheduleDetachedObservation() {
3987
3987
  try {
3988
3988
  await this.options.scheduleDetachedObservation?.();
3989
- } catch (error10) {
3990
- this.noteObservationFailure(error10);
3989
+ } catch (error11) {
3990
+ this.noteObservationFailure(error11);
3991
3991
  }
3992
3992
  }
3993
3993
  isFailOpen() {
@@ -3996,9 +3996,9 @@ var CommitTrace = class {
3996
3996
  observationVerification() {
3997
3997
  return Object.freeze({ passed: false, code: this.observationFailureCode ?? "trace-observation-error" });
3998
3998
  }
3999
- noteObservationFailure(error10) {
3999
+ noteObservationFailure(error11) {
4000
4000
  if (this.observationFailureCode !== void 0) return;
4001
- this.observationFailureCode = typeof error10 === "object" && error10 !== null && "code" in error10 && typeof error10.code === "string" ? error10.code : "trace-observation-error";
4001
+ this.observationFailureCode = typeof error11 === "object" && error11 !== null && "code" in error11 && typeof error11.code === "string" ? error11.code : "trace-observation-error";
4002
4002
  this.runtimeVerification = this.observationVerification();
4003
4003
  }
4004
4004
  /**
@@ -4019,9 +4019,9 @@ var CommitTrace = class {
4019
4019
  return callback(void 0);
4020
4020
  }
4021
4021
  return await result2;
4022
- } catch (error10) {
4023
- if (callbackStarted) throw error10;
4024
- this.noteObservationFailure(error10);
4022
+ } catch (error11) {
4023
+ if (callbackStarted) throw error11;
4024
+ this.noteObservationFailure(error11);
4025
4025
  return callback(void 0);
4026
4026
  }
4027
4027
  }
@@ -4119,8 +4119,8 @@ async function enterNativeSpanFailOpen(nativeTracing, name, callback) {
4119
4119
  const result2 = nativeTracing.enterSpan(name, wrapped);
4120
4120
  if (!callbackStarted) return callback(void 0);
4121
4121
  return await result2;
4122
- } catch (error10) {
4123
- if (callbackStarted) throw error10;
4122
+ } catch (error11) {
4123
+ if (callbackStarted) throw error11;
4124
4124
  return callback(void 0);
4125
4125
  }
4126
4126
  }
@@ -4152,9 +4152,9 @@ async function enterNativeCommitSpan(nativeTracing, name, input, callback) {
4152
4152
  if (response3 !== void 0 && input.face !== "reconcile-root") setNativeAttribute(span, "http.status", response3.status);
4153
4153
  }
4154
4154
  return result2;
4155
- } catch (error10) {
4155
+ } catch (error11) {
4156
4156
  setNativeAttribute(span, "outcome", "exception");
4157
- throw error10;
4157
+ throw error11;
4158
4158
  }
4159
4159
  });
4160
4160
  }
@@ -4193,9 +4193,9 @@ async function enterNativeActorHandleSpan(nativeTracing, input, resolveIdentity,
4193
4193
  if (response3 !== void 0) setNativeAttribute(span, "http.status", response3.status);
4194
4194
  }
4195
4195
  return result2;
4196
- } catch (error10) {
4196
+ } catch (error11) {
4197
4197
  if (identity2 !== void 0) setNativeAttribute(span, "outcome", "exception");
4198
- throw error10;
4198
+ throw error11;
4199
4199
  } finally {
4200
4200
  input.observation?.finish();
4201
4201
  }
@@ -4260,9 +4260,9 @@ async function enterNativeReconcileRootSpan(nativeTracing, resolveIdentity, call
4260
4260
  const result2 = await callback(identity2, facts);
4261
4261
  finish("success");
4262
4262
  return result2;
4263
- } catch (error10) {
4263
+ } catch (error11) {
4264
4264
  finish("exception");
4265
- throw error10;
4265
+ throw error11;
4266
4266
  }
4267
4267
  });
4268
4268
  }
@@ -4373,6 +4373,8 @@ var ATTEMPT_KEY_PREFIX2 = "attempt:";
4373
4373
  var ROLLBACK_WARNING_WINDOW_MS = 1000n;
4374
4374
  var AllocationTransactionFault = class extends Error {
4375
4375
  };
4376
+ var ImportSeedRequiredError = class extends Error {
4377
+ };
4376
4378
  function json(body, status = 200) {
4377
4379
  return new Response(JSON.stringify(body), {
4378
4380
  status,
@@ -4412,7 +4414,17 @@ function seedFrom(value) {
4412
4414
  } catch {
4413
4415
  return { error: "highWatermark must be an allocator SUID" };
4414
4416
  }
4415
- return { value: { importId: value.importId, leaseEpoch: value.leaseEpoch, highWatermark: value.highWatermark } };
4417
+ let storeMaximum;
4418
+ if (value.storeMaximum !== void 0) {
4419
+ if (!isNonEmptyString2(value.storeMaximum)) return { error: "storeMaximum must be an allocator SUID" };
4420
+ try {
4421
+ decodeSuid(value.storeMaximum);
4422
+ } catch {
4423
+ return { error: "storeMaximum must be an allocator SUID" };
4424
+ }
4425
+ storeMaximum = value.storeMaximum;
4426
+ }
4427
+ return { value: { importId: value.importId, leaseEpoch: value.leaseEpoch, highWatermark: value.highWatermark, ...storeMaximum !== void 0 ? { storeMaximum } : {} } };
4416
4428
  }
4417
4429
  function newAllocatorLineageId() {
4418
4430
  return crypto.randomUUID();
@@ -4467,7 +4479,8 @@ function allocateFrom(value) {
4467
4479
  faultInjection,
4468
4480
  ...serviceId !== void 0 ? { serviceId } : {},
4469
4481
  ...bootstrapCommandId !== void 0 ? { bootstrapCommandId } : {},
4470
- ...bootstrapEpoch !== void 0 ? { bootstrapEpoch } : {}
4482
+ ...bootstrapEpoch !== void 0 ? { bootstrapEpoch } : {},
4483
+ ...value.requiresImportSeed === true ? { requiresImportSeed: true } : {}
4471
4484
  }
4472
4485
  };
4473
4486
  }
@@ -4852,11 +4865,12 @@ var AllocatorDurableObject = class {
4852
4865
  };
4853
4866
  }
4854
4867
  const state = persistedState === void 0 ? currentState(lineage) : { ...persistedState, allocatorLineageId: lineage, schemaVersion: 5, bootstrapSeed: persistedState.bootstrapSeed ?? null, lastRollbackWarningFingerprint: persistedState.lastRollbackWarningFingerprint ?? null };
4868
+ if (input.requiresImportSeed === true && state.bootstrapSeed === null) throw new ImportSeedRequiredError();
4855
4869
  let clockTick;
4856
4870
  try {
4857
4871
  clockTick = this.orderClock.tick();
4858
- } catch (error10) {
4859
- throw error10 instanceof OrderClockReadError ? error10 : new OrderClockReadError("Order clock failed before allocation write", { cause: error10 });
4872
+ } catch (error11) {
4873
+ throw error11 instanceof OrderClockReadError ? error11 : new OrderClockReadError("Order clock failed before allocation write", { cause: error11 });
4860
4874
  }
4861
4875
  const range = allocateOrderRange(state.allocatedWatermark, input.candidates.length, clockTick);
4862
4876
  const rollback = range.observedTicks !== null && range.physicalTicks < range.observedTicks;
@@ -4930,6 +4944,9 @@ var AllocatorDurableObject = class {
4930
4944
  }
4931
4945
  return json(result2.vector, result2.created ? 201 : 200);
4932
4946
  } catch (failure3) {
4947
+ if (failure3 instanceof ImportSeedRequiredError) {
4948
+ return error(409, "allocator_seed_required", "commit before import seed");
4949
+ }
4933
4950
  if (failure3 instanceof AllocationTransactionFault) {
4934
4951
  return error(
4935
4952
  503,
@@ -4970,7 +4987,10 @@ var AllocatorDurableObject = class {
4970
4987
  return { status: 409, body: { code: "allocator_seed_rejected", error: "allocator already seeded" } };
4971
4988
  }
4972
4989
  if (state.allocatedWatermark !== null) return { status: 409, body: { code: "allocator_seed_rejected", error: "allocator already allocating" } };
4973
- const updated = { ...state, allocatedWatermark: input.highWatermark, bootstrapSeed: input };
4990
+ if (input.storeMaximum !== void 0 && input.highWatermark < input.storeMaximum) {
4991
+ return { status: 409, body: { code: "allocator_seed_below_store_max", error: "seed below store maximum" } };
4992
+ }
4993
+ const updated = { ...state, allocatedWatermark: input.highWatermark, bootstrapSeed: { importId: input.importId, leaseEpoch: input.leaseEpoch, highWatermark: input.highWatermark } };
4974
4994
  await txn.put(STATE_KEY, updated);
4975
4995
  return { status: 201, body: updated };
4976
4996
  });
@@ -4995,7 +5015,7 @@ var isPlainRecord = (value) => {
4995
5015
  return prototype === Object.prototype || prototype === null;
4996
5016
  };
4997
5017
  function assertJsonValue(value, boundary = "value") {
4998
- const active = /* @__PURE__ */ new WeakSet();
5018
+ const active2 = /* @__PURE__ */ new WeakSet();
4999
5019
  const visit = (candidate, path) => {
5000
5020
  if (candidate === null || typeof candidate === "string" || typeof candidate === "boolean") {
5001
5021
  return candidate;
@@ -5007,10 +5027,10 @@ function assertJsonValue(value, boundary = "value") {
5007
5027
  if (typeof candidate !== "object") {
5008
5028
  throw new JsonValidationError(`Value at ${path} is not JSON serializable`, boundary, path);
5009
5029
  }
5010
- if (active.has(candidate)) {
5030
+ if (active2.has(candidate)) {
5011
5031
  throw new JsonValidationError(`Cyclic JSON value at ${path}`, boundary, path);
5012
5032
  }
5013
- active.add(candidate);
5033
+ active2.add(candidate);
5014
5034
  try {
5015
5035
  if (Array.isArray(candidate)) {
5016
5036
  return candidate.map((item, index) => visit(item, `${path}[${index}]`));
@@ -5024,7 +5044,7 @@ function assertJsonValue(value, boundary = "value") {
5024
5044
  }
5025
5045
  return result2;
5026
5046
  } finally {
5027
- active.delete(candidate);
5047
+ active2.delete(candidate);
5028
5048
  }
5029
5049
  };
5030
5050
  return visit(value, "$");
@@ -5119,16 +5139,16 @@ function createUuidV7(nowMs = Date.now()) {
5119
5139
  if (typeof globalThis.crypto?.getRandomValues !== "function") {
5120
5140
  throw new Error("crypto.getRandomValues is required for UUIDv7");
5121
5141
  }
5122
- const bytes = new Uint8Array(16);
5123
- globalThis.crypto.getRandomValues(bytes);
5142
+ const bytes2 = new Uint8Array(16);
5143
+ globalThis.crypto.getRandomValues(bytes2);
5124
5144
  let time = BigInt(nowMs);
5125
5145
  for (let index = 5; index >= 0; index -= 1) {
5126
- bytes[index] = Number(time & 0xffn);
5146
+ bytes2[index] = Number(time & 0xffn);
5127
5147
  time >>= 8n;
5128
5148
  }
5129
- bytes[6] = bytes[6] & 15 | 112;
5130
- bytes[8] = bytes[8] & 63 | 128;
5131
- const hex = [...bytes].map((value) => value.toString(16).padStart(2, "0")).join("");
5149
+ bytes2[6] = bytes2[6] & 15 | 112;
5150
+ bytes2[8] = bytes2[8] & 63 | 128;
5151
+ const hex = [...bytes2].map((value) => value.toString(16).padStart(2, "0")).join("");
5132
5152
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
5133
5153
  }
5134
5154
  function isRfc4122Uuid(value) {
@@ -5244,7 +5264,7 @@ var epoch = (value) => typeof value === "number" && Number.isSafeInteger(value)
5244
5264
  var response = (body, status = 200) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json; charset=utf-8" } });
5245
5265
  var reject = (code, message, status = 409) => response({ code, error: message }, status);
5246
5266
  function empty(serviceId) {
5247
- return { schemaVersion: 1, status: "EMPTY", importId: null, targetServiceId: serviceId, allocatorLineageId: null, source: null, digest: null, manifest: null, progress: {}, leaseEpoch: 0, leaseUntil: null, failure: null, readyAt: null, normalInFlight: 0, normalCommands: {}, releasedCommands: {}, verifiedImportId: null, verifiedLeaseEpoch: null, storeCompletion: null };
5267
+ return { schemaVersion: 1, status: "EMPTY", importId: null, targetServiceId: serviceId, allocatorLineageId: null, source: null, digest: null, manifest: null, progress: {}, leaseEpoch: 0, leaseUntil: null, failure: null, readyAt: null, normalInFlight: 0, normalCommands: {}, releasedCommands: {}, writePermits: {}, verifiedImportId: null, verifiedLeaseEpoch: null, storeCompletion: null };
5248
5268
  }
5249
5269
  function expired(control) {
5250
5270
  return control.leaseUntil !== null && control.leaseUntil <= Date.now();
@@ -5255,12 +5275,22 @@ function commands(control) {
5255
5275
  function released(control) {
5256
5276
  return { ...control.releasedCommands ?? {} };
5257
5277
  }
5278
+ function permits(control) {
5279
+ return { ...control.writePermits ?? {} };
5280
+ }
5258
5281
  function fault(body, point) {
5259
5282
  return object(body) && body.faultAt === point;
5260
5283
  }
5261
5284
  function encodedBytes(value) {
5262
5285
  return new TextEncoder().encode(JSON.stringify(value)).byteLength;
5263
5286
  }
5287
+ async function readBody(request) {
5288
+ try {
5289
+ return await request.json();
5290
+ } catch {
5291
+ return void 0;
5292
+ }
5293
+ }
5264
5294
  var BootstrapCoordinatorDurableObject = class {
5265
5295
  constructor(ctx, env, nativeTracing = noOpNativeTracing) {
5266
5296
  this.ctx = ctx;
@@ -5282,6 +5312,8 @@ var BootstrapCoordinatorDurableObject = class {
5282
5312
  if (request.method === "POST" && url.pathname === "/command/admit") return this.traceCommand(request, serviceId, activation, observation, "admit");
5283
5313
  if (request.method === "POST" && url.pathname === "/command/finalize") return this.traceCommand(request, serviceId, activation, observation, "finalize");
5284
5314
  if (request.method === "POST" && url.pathname === "/command/release") return this.traceCommand(request, serviceId, activation, observation, "release");
5315
+ if (request.method === "POST" && url.pathname === "/command/permit") return this.permit(serviceId, await readBody(request));
5316
+ if (request.method === "POST" && url.pathname === "/command/permit-release") return this.permitRelease(serviceId, await readBody(request));
5285
5317
  let body;
5286
5318
  try {
5287
5319
  body = await request.json();
@@ -5338,6 +5370,7 @@ var BootstrapCoordinatorDurableObject = class {
5338
5370
  const control = await txn.get(CONTROL) ?? empty(serviceId);
5339
5371
  if (control.status === "READY") return { error: "bootstrap_ready_permanent" };
5340
5372
  if (control.status !== "EMPTY") return control.importId === importId && control.digest === dump.manifest.contentDigest ? { control } : { error: "bootstrap_plan_conflict" };
5373
+ if (Object.keys(permits(control)).length !== 0) return { error: "bootstrap_write_permit_active" };
5341
5374
  if (control.normalInFlight !== 0 || Object.keys(commands(control)).length !== 0) return { error: "bootstrap_normal_command_in_flight" };
5342
5375
  const next = { ...control, status: "PLANNED", importId, allocatorLineageId: dump.manifest.target.allocatorLineageId, source: dump.manifest.source, digest: dump.manifest.contentDigest, manifest: dump.manifest, leaseEpoch: control.leaseEpoch + 1, leaseUntil: Date.now() + 3e4, failure: null, progress: {}, verifiedImportId: null, verifiedLeaseEpoch: null, storeCompletion: null };
5343
5376
  await txn.put(CONTROL, next);
@@ -5350,17 +5383,17 @@ var BootstrapCoordinatorDurableObject = class {
5350
5383
  chunks(events) {
5351
5384
  const chunks = [];
5352
5385
  let current = [];
5353
- let bytes = 0;
5386
+ let bytes2 = 0;
5354
5387
  for (const event of events) {
5355
5388
  const eventBytes = encodedBytes(event);
5356
5389
  if (eventBytes > MAX_CHUNK_BYTES) throw new Error("bootstrap event exceeds encoded chunk bound");
5357
- if (current.length === MAX_CHUNK_EVENTS || bytes + eventBytes > MAX_CHUNK_BYTES) {
5390
+ if (current.length === MAX_CHUNK_EVENTS || bytes2 + eventBytes > MAX_CHUNK_BYTES) {
5358
5391
  chunks.push(current);
5359
5392
  current = [];
5360
- bytes = 0;
5393
+ bytes2 = 0;
5361
5394
  }
5362
5395
  current.push(event);
5363
- bytes += eventBytes;
5396
+ bytes2 += eventBytes;
5364
5397
  }
5365
5398
  if (current.length > 0) chunks.push(current);
5366
5399
  return chunks;
@@ -5506,6 +5539,45 @@ var BootstrapCoordinatorDurableObject = class {
5506
5539
  const control = await this.control(serviceId);
5507
5540
  return control.status === "EMPTY" || control.status === "READY" ? response({ admitted: true, leaseEpoch: control.leaseEpoch, route: body.route }) : reject("bootstrap_route_rejected", `${body.route} is unavailable during bootstrap`);
5508
5541
  }
5542
+ async permit(serviceId, body) {
5543
+ if (!object(body) || !string(body.commandId) || !string(body.digest)) return reject("bootstrap_permit_invalid", "commandId and digest are required", 400);
5544
+ const commandId = body.commandId;
5545
+ const digest = body.digest;
5546
+ const result2 = await this.ctx.storage.transaction(async (txn) => {
5547
+ const control = await txn.get(CONTROL) ?? empty(serviceId);
5548
+ const active2 = permits(control);
5549
+ if (active2[commandId] !== void 0) {
5550
+ return active2[commandId] === digest ? { control } : { digestMismatch: true };
5551
+ }
5552
+ if (control.status !== "EMPTY" && control.status !== "READY") return { rejected: true };
5553
+ const updated = { ...control, writePermits: { ...active2, [commandId]: digest } };
5554
+ await txn.put(CONTROL, updated);
5555
+ return { control: updated };
5556
+ });
5557
+ if ("digestMismatch" in result2) return reject("bootstrap_permit_digest_mismatch", "bootstrap permit digest does not match the held permit");
5558
+ if ("rejected" in result2) return reject("bootstrap_command_rejected", "bootstrap epoch rejects normal durable mutation");
5559
+ return response({ leaseEpoch: result2.control.leaseEpoch, admitted: true });
5560
+ }
5561
+ async permitRelease(serviceId, body) {
5562
+ if (!object(body) || !string(body.commandId)) return reject("bootstrap_permit_invalid", "commandId is required", 400);
5563
+ const commandId = body.commandId;
5564
+ const digest = string(body.digest) ? body.digest : void 0;
5565
+ const result2 = await this.ctx.storage.transaction(async (txn) => {
5566
+ const control = await txn.get(CONTROL) ?? empty(serviceId);
5567
+ const active2 = permits(control);
5568
+ if (active2[commandId] !== void 0 && digest !== void 0 && active2[commandId] !== digest) {
5569
+ return { digestMismatch: true };
5570
+ }
5571
+ if (active2[commandId] === void 0) return { control };
5572
+ const next = { ...active2 };
5573
+ delete next[commandId];
5574
+ const updated = { ...control, writePermits: next };
5575
+ await txn.put(CONTROL, updated);
5576
+ return { control: updated };
5577
+ });
5578
+ if ("digestMismatch" in result2) return reject("bootstrap_permit_digest_mismatch", "bootstrap permit digest does not match the held permit");
5579
+ return response({ leaseEpoch: result2.control.leaseEpoch, admitted: true });
5580
+ }
5509
5581
  async command(serviceId, body, action, observation) {
5510
5582
  if (!object(body) || !string(body.commandId)) return reject("bootstrap_command_invalid", "commandId is required", 400);
5511
5583
  const commandId = body.commandId;
@@ -5571,13 +5643,13 @@ function byteCompare(left, right) {
5571
5643
  }
5572
5644
  function concat(parts) {
5573
5645
  const length = parts.reduce((total, part) => total + part.byteLength, 0);
5574
- const bytes = new Uint8Array(length);
5646
+ const bytes2 = new Uint8Array(length);
5575
5647
  let offset = 0;
5576
5648
  for (const part of parts) {
5577
- bytes.set(part, offset);
5649
+ bytes2.set(part, offset);
5578
5650
  offset += part.byteLength;
5579
5651
  }
5580
- return bytes;
5652
+ return bytes2;
5581
5653
  }
5582
5654
  function lengthPrefix(length) {
5583
5655
  if (!Number.isSafeInteger(length) || length < 0 || length > 4294967295) {
@@ -5606,8 +5678,8 @@ function requiredUtf8(input, name) {
5606
5678
  function tagSetBytes(tags) {
5607
5679
  const canonical2 = canonicalDeclaredTagSet(tags);
5608
5680
  return concat(canonical2.map((tag) => {
5609
- const bytes = utf8(tag);
5610
- return concat([lengthPrefix(bytes.byteLength), bytes]);
5681
+ const bytes2 = utf8(tag);
5682
+ return concat([lengthPrefix(bytes2.byteLength), bytes2]);
5611
5683
  }));
5612
5684
  }
5613
5685
  function canonicalDeclaredTagSet(tags) {
@@ -5677,16 +5749,16 @@ function eventDigestBytes(input) {
5677
5749
  ]);
5678
5750
  }
5679
5751
  async function eventDigestHex(input) {
5680
- const bytes = eventDigestBytes(input);
5681
- const source = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
5752
+ const bytes2 = eventDigestBytes(input);
5753
+ const source = bytes2.buffer.slice(bytes2.byteOffset, bytes2.byteOffset + bytes2.byteLength);
5682
5754
  const digest = await crypto.subtle.digest("SHA-256", source);
5683
5755
  return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
5684
5756
  }
5685
5757
 
5686
5758
  // src/bootstrap/BootstrapStoreAdapter.ts
5687
- function arrayBufferBase64(bytes) {
5759
+ function arrayBufferBase64(bytes2) {
5688
5760
  let binary = "";
5689
- for (const byte of bytes) binary += String.fromCharCode(byte);
5761
+ for (const byte of bytes2) binary += String.fromCharCode(byte);
5690
5762
  return btoa(binary);
5691
5763
  }
5692
5764
  var BootstrapIdentityConflictError = class extends Error {
@@ -5826,6 +5898,34 @@ function createBootstrapStoreAdapter(provider, store, identityGuard) {
5826
5898
  return new BootstrapStoreAdapter(provider, store, identityGuard);
5827
5899
  }
5828
5900
 
5901
+ // src/http/ConfiguredBearer.ts
5902
+ async function sha256(value) {
5903
+ const encoded = new TextEncoder().encode(value);
5904
+ return new Uint8Array(await crypto.subtle.digest("SHA-256", encoded));
5905
+ }
5906
+ function digestsMatch(expected, supplied) {
5907
+ let difference = expected.length ^ supplied.length;
5908
+ const length = Math.max(expected.length, supplied.length);
5909
+ for (let index = 0; index < length; index += 1) {
5910
+ difference |= (expected[index] ?? 0) ^ (supplied[index] ?? 0);
5911
+ }
5912
+ return difference === 0;
5913
+ }
5914
+ async function checkConfiguredBearer(request, binding, options) {
5915
+ if (typeof binding !== "string" || binding.trim().length === 0) {
5916
+ return "unconfigured";
5917
+ }
5918
+ const effectiveToken = options?.tokenNormalization === "trim" ? binding.trim() : binding;
5919
+ const supplied = request.headers.get("authorization");
5920
+ if (supplied === null) return "missing";
5921
+ const expected = `Bearer ${effectiveToken}`;
5922
+ const [expectedDigest, suppliedDigest] = await Promise.all([
5923
+ sha256(expected),
5924
+ sha256(supplied)
5925
+ ]);
5926
+ return digestsMatch(expectedDigest, suppliedDigest) ? "authorized" : "invalid";
5927
+ }
5928
+
5829
5929
  // src/bootstrap/OperatorBootstrap.ts
5830
5930
  function json2(body, status = 200) {
5831
5931
  return new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json; charset=utf-8" } });
@@ -5833,15 +5933,17 @@ function json2(body, status = 200) {
5833
5933
  function object2(value) {
5834
5934
  return typeof value === "object" && value !== null && !Array.isArray(value);
5835
5935
  }
5836
- function bearer(request, token) {
5837
- const supplied = request.headers.get("authorization");
5838
- if (supplied === null) return json2({ code: "not_found", error: "not found" }, 404);
5839
- if (supplied !== `Bearer ${token}`) return json2({ code: "operator_auth_required", error: "valid bearer required" }, 403);
5936
+ async function authenticateOperatorBootstrap(request, binding) {
5937
+ const decision = await checkConfiguredBearer(request, binding);
5938
+ if (decision === "unconfigured" || decision === "missing") {
5939
+ return json2({ code: "not_found", error: "not found" }, 404);
5940
+ }
5941
+ if (decision === "invalid") {
5942
+ return json2({ code: "operator_auth_required", error: "valid bearer required" }, 403);
5943
+ }
5840
5944
  return void 0;
5841
5945
  }
5842
- async function handleOperatorBootstrap(request, env, storeProvider, hooks = {}) {
5843
- const denied = bearer(request, env.REPAIR_OPERATOR_TOKEN);
5844
- if (denied !== void 0) return denied;
5946
+ async function handleAuthorizedOperatorBootstrap(request, env, storeProvider, hooks = {}) {
5845
5947
  const url = new URL(request.url);
5846
5948
  const match = url.pathname.match(/^\/operator\/bootstrap\/([^/]+)\/(plan|import|status|abort|export)$/);
5847
5949
  if (match === null) return json2({ code: "not_found", error: "not found" }, 404);
@@ -6740,14 +6842,21 @@ function testFaultFromRequest(request) {
6740
6842
  const value = request.headers.get("x-sdt-g6-test-fault");
6741
6843
  return value === "after-lease-before-prepare" || value === "after-prepare-before-apply" || value === "after-apply-before-observation" || value === "after-verify-before-audit" || value === "after-audit-before-clear" || value === "after-clear-before-final-observation" ? value : void 0;
6742
6844
  }
6743
- async function handleOperatorRepair(request, env, nativeTracing = noOpNativeTracing, serviceIdentityProvider) {
6744
- if (request.method !== "POST") {
6745
- return error2(404, "operator_repair_route_not_found", "Operator repair requires POST");
6845
+ function nonPostOperatorRepairResponse(request) {
6846
+ if (request.method === "POST") return void 0;
6847
+ return error2(404, "operator_repair_route_not_found", "Operator repair requires POST");
6848
+ }
6849
+ async function authenticateOperatorRepair(request, binding) {
6850
+ const decision = await checkConfiguredBearer(request, binding);
6851
+ if (decision === "unconfigured") {
6852
+ return error2(404, "operator_repair_route_not_found", "Operator repair route not found");
6746
6853
  }
6747
- const token = env.REPAIR_OPERATOR_TOKEN;
6748
- if (!isNonEmptyString3(token) || request.headers.get("authorization") !== `Bearer ${token}`) {
6854
+ if (decision === "missing" || decision === "invalid") {
6749
6855
  return error2(401, "operator_auth_required", "A valid operator repair bearer token is required");
6750
6856
  }
6857
+ return void 0;
6858
+ }
6859
+ async function handleAuthorizedOperatorRepair(request, env, nativeTracing = noOpNativeTracing, serviceIdentityProvider) {
6751
6860
  let body;
6752
6861
  try {
6753
6862
  body = await request.json();
@@ -6797,6 +6906,22 @@ var COMMIT_TEST_FAULTS = [
6797
6906
  var INITIAL_OWNER_EPOCH = 0;
6798
6907
  var MAX_WRITE_ATTEMPTS = 2;
6799
6908
  var OUTCOME_UNDETERMINED_ERROR = "Commit outcome is undetermined; reread tag heads and event/query state before retrying because blind retry may create duplicate events.";
6909
+ function commitPermitDigest(allTags, candidates) {
6910
+ const normalized = JSON.stringify({
6911
+ tags: [...allTags].sort(),
6912
+ events: candidates.map((candidate) => ({
6913
+ eventId: candidate.eventId,
6914
+ payload: candidate.payload,
6915
+ tags: [...candidate.tags].sort()
6916
+ })).sort((left, right) => left.eventId < right.eventId ? -1 : left.eventId > right.eventId ? 1 : 0)
6917
+ });
6918
+ let hash = 2166136261;
6919
+ for (let index = 0; index < normalized.length; index += 1) {
6920
+ hash ^= normalized.charCodeAt(index);
6921
+ hash = Math.imul(hash, 16777619) >>> 0;
6922
+ }
6923
+ return `fnv1a32:${hash.toString(16).padStart(8, "0")}`;
6924
+ }
6800
6925
  var GLOBAL_ADMISSION_HEADER = "x-sdt-global-admission";
6801
6926
  var runtimeCommitTraceVerifier = {
6802
6927
  verify(snapshot) {
@@ -6850,19 +6975,19 @@ var CommitPayloadAdmissionError = class extends Error {
6850
6975
  code;
6851
6976
  };
6852
6977
  function decodeBase64Utf8Json(value) {
6853
- let bytes;
6978
+ let bytes2;
6854
6979
  try {
6855
6980
  const binary = atob(value);
6856
- bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
6981
+ bytes2 = Uint8Array.from(binary, (character) => character.charCodeAt(0));
6857
6982
  } catch {
6858
6983
  throw new CommitPayloadAdmissionError("invalid_payload_utf8", "Payload base64 could not be decoded");
6859
6984
  }
6860
- if (bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191) {
6985
+ if (bytes2[0] === 239 && bytes2[1] === 187 && bytes2[2] === 191) {
6861
6986
  throw new CommitPayloadAdmissionError("invalid_payload_utf8", "Payload must not start with a UTF-8 BOM");
6862
6987
  }
6863
6988
  let text;
6864
6989
  try {
6865
- text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
6990
+ text = new TextDecoder("utf-8", { fatal: true }).decode(bytes2);
6866
6991
  } catch {
6867
6992
  throw new CommitPayloadAdmissionError("invalid_payload_utf8", "Payload must be valid UTF-8 JSON text");
6868
6993
  }
@@ -6908,9 +7033,9 @@ function assertExactMemberPaths(actual, registered, path = "$") {
6908
7033
  }
6909
7034
  }
6910
7035
  function encodeBase64Utf8(value) {
6911
- const bytes = new TextEncoder().encode(value);
7036
+ const bytes2 = new TextEncoder().encode(value);
6912
7037
  let binary = "";
6913
- for (const byte of bytes) binary += String.fromCharCode(byte);
7038
+ for (const byte of bytes2) binary += String.fromCharCode(byte);
6914
7039
  return btoa(binary);
6915
7040
  }
6916
7041
  function faultFromRequest(request) {
@@ -7169,25 +7294,43 @@ var CommitWorker = class {
7169
7294
  return responseWithAttempt(error3(409, "bootstrap_command_rejected", "Bootstrap admission could not be released"), attemptId, fault2 !== void 0);
7170
7295
  }
7171
7296
  await this.retiredJournalMilestone("S04", traceState?.scope);
7297
+ const permitDigest = commitPermitDigest(input.allTags, candidates);
7298
+ let permitHeld = false;
7299
+ const releasePermit = async () => {
7300
+ if (!permitHeld) return;
7301
+ permitHeld = false;
7302
+ await this.bootstrapPermitRelease(attemptId, permitDigest);
7303
+ };
7304
+ const permit = await this.bootstrapPermitAcquire(attemptId, permitDigest);
7305
+ if (!permit.ok) {
7306
+ if (permit.code === "bootstrap_permit_digest_mismatch") {
7307
+ return responseWithAttempt(error3(409, "bootstrap_permit_digest_mismatch", "Bootstrap write permit digest does not match"), attemptId, fault2 !== void 0);
7308
+ }
7309
+ return responseWithAttempt(error3(409, "bootstrap_command_rejected", "Bootstrap is importing; commit is unavailable"), attemptId, fault2 !== void 0);
7310
+ }
7311
+ permitHeld = this.env.BOOTSTRAP !== void 0;
7172
7312
  const reservations = await this.acquireReservations(input, attemptId, fault2, traceState?.scope);
7173
7313
  await this.retiredJournalMilestone("S05a", traceState?.scope, 0);
7174
7314
  if (reservations.failure !== void 0) {
7175
- return this.finishReservationFailure(
7315
+ const failure3 = await this.finishReservationFailure(
7176
7316
  [...reservations.successes.keys()],
7177
7317
  attemptId,
7178
7318
  reservations.failure,
7179
7319
  fault2,
7180
7320
  traceState?.scope
7181
7321
  );
7322
+ if (failure3.resolved) await releasePermit();
7323
+ return failure3.response;
7182
7324
  }
7183
7325
  if (fault2 === "after-reservations-before-allocation") {
7184
- await this.cancelReservations([...reservations.successes.keys()], attemptId, fault2, traceState?.scope);
7326
+ const cleanup = await this.cancelReservations([...reservations.successes.keys()], attemptId, fault2, traceState?.scope);
7327
+ if (cleanup.failedTags.length === 0) await releasePermit();
7185
7328
  return this.noApplicationOutcome(attemptId, true);
7186
7329
  }
7187
7330
  await this.hooks.beforeBootstrapAllocation?.();
7188
7331
  const allocation = await this.allocate(candidates, attemptId, fault2, bootstrapEpoch, traceState?.scope);
7189
7332
  if (allocation === void 0) {
7190
- return this.finishReservationFailure(
7333
+ const failure3 = await this.finishReservationFailure(
7191
7334
  [...reservations.successes.keys()],
7192
7335
  attemptId,
7193
7336
  {
@@ -7198,10 +7341,13 @@ var CommitWorker = class {
7198
7341
  fault2,
7199
7342
  traceState?.scope
7200
7343
  );
7344
+ if (failure3.resolved) await releasePermit();
7345
+ return failure3.response;
7201
7346
  }
7202
7347
  const allocatedCandidates = this.withAllocatedSuids(candidates, allocation);
7203
7348
  if (allocatedCandidates === void 0) {
7204
- await this.cancelReservations([...reservations.successes.keys()], attemptId, fault2, traceState?.scope);
7349
+ const cleanup = await this.cancelReservations([...reservations.successes.keys()], attemptId, fault2, traceState?.scope);
7350
+ if (cleanup.failedTags.length === 0) await releasePermit();
7205
7351
  return this.noApplicationOutcome(attemptId, fault2 !== void 0);
7206
7352
  }
7207
7353
  for (const candidate of allocatedCandidates) {
@@ -7216,15 +7362,18 @@ var CommitWorker = class {
7216
7362
  }
7217
7363
  await this.retiredJournalMilestone("S05b", traceState?.scope, 1);
7218
7364
  if (fault2 === "journal-cas-after-allocator") {
7219
- await this.cancelReservations(input.allTags, attemptId, fault2, traceState?.scope);
7365
+ const cleanup = await this.cancelReservations(input.allTags, attemptId, fault2, traceState?.scope);
7366
+ if (cleanup.failedTags.length === 0) await releasePermit();
7220
7367
  return this.noApplicationOutcome(attemptId, true);
7221
7368
  }
7222
7369
  await this.retiredJournalMilestone("S05c", traceState?.scope, 2);
7223
7370
  await this.hooks.beforeBootstrapFinalization?.();
7224
7371
  if (await this.bootstrapCommand("finalize", attemptId, bootstrapEpoch, traceState?.scope, "S10") === void 0) {
7225
- await this.cancelReservations(input.allTags, attemptId, fault2, traceState?.scope);
7372
+ const cleanup = await this.cancelReservations(input.allTags, attemptId, fault2, traceState?.scope);
7373
+ if (cleanup.failedTags.length === 0) await releasePermit();
7226
7374
  return responseWithAttempt(error3(409, "bootstrap_command_rejected", "Bootstrap fencing epoch changed before commit write"), attemptId, fault2 !== void 0);
7227
7375
  }
7376
+ await this.hooks.beforeAuthoritativeAppend?.();
7228
7377
  const writes = await this.appendAllTags(
7229
7378
  input,
7230
7379
  allocatedCandidates,
@@ -7240,6 +7389,7 @@ var CommitWorker = class {
7240
7389
  return this.noApplicationOutcome(attemptId, true);
7241
7390
  }
7242
7391
  if (writes.committedTags.size === 0 && writes.registrationUnavailableTags.length === writes.pendingTags.length) {
7392
+ await releasePermit();
7243
7393
  return responseWithAttempt(
7244
7394
  error3(
7245
7395
  503,
@@ -7251,20 +7401,25 @@ var CommitWorker = class {
7251
7401
  fault2 !== void 0
7252
7402
  );
7253
7403
  }
7404
+ await releasePermit();
7254
7405
  return this.partialWriteOutcome(input, allocatedCandidates, writes, attemptId, fault2 !== void 0);
7255
7406
  }
7407
+ await this.hooks.afterAppendBeforeResponse?.();
7256
7408
  await this.retiredJournalMilestone("S05d", traceState?.scope, 3);
7257
7409
  if (fault2 === "sealing-after-cas") {
7410
+ await releasePermit();
7258
7411
  return this.noApplicationOutcome(attemptId, true);
7259
7412
  }
7260
7413
  try {
7261
- const success = await this.successResponse(input, allocatedCandidates, attemptId, startedAt, fault2, traceState?.scope);
7414
+ const success = await this.successResponse(input, allocatedCandidates, attemptId, startedAt, fault2, traceState?.scope, writes);
7415
+ await releasePermit();
7262
7416
  return responseWithAttempt(
7263
7417
  withGlobalAdmission(json4(success, 200), writes.globalAdmission),
7264
7418
  attemptId,
7265
7419
  fault2 !== void 0
7266
7420
  );
7267
7421
  } catch {
7422
+ await releasePermit();
7268
7423
  return responseWithAttempt(
7269
7424
  error3(500, "internal_error", "Committed records could not be read while preparing the response"),
7270
7425
  attemptId,
@@ -7311,6 +7466,51 @@ var CommitWorker = class {
7311
7466
  const result2 = traceScope === void 0 || rowId === void 0 ? await invoke() : await traceScope.span(rowId, {}, invoke);
7312
7467
  return result2.leaseEpoch;
7313
7468
  }
7469
+ /**
7470
+ * SDT-G36 bootstrap write permit. Deliberately untraced: it must not add
7471
+ * `sdt.commit/v2` rows or new trace identities. Only a bootstrap barrier,
7472
+ * never commit-outcome authority. No JOURNAL, no alarm, no lease expiry.
7473
+ */
7474
+ bootstrapStub() {
7475
+ if (this.env.BOOTSTRAP === void 0) return void 0;
7476
+ return this.env.BOOTSTRAP.get(scopeIdFor(this.env.BOOTSTRAP, {
7477
+ serviceId: this.serviceId,
7478
+ doClass: "bootstrap",
7479
+ identity: "coordinator"
7480
+ }));
7481
+ }
7482
+ async bootstrapPermitAcquire(commandId, digest) {
7483
+ const stub = this.bootstrapStub();
7484
+ if (stub === void 0) return { ok: true };
7485
+ try {
7486
+ const url = new URL(`https://commit-worker.internal/command/permit`);
7487
+ url.searchParams.set("__serviceId", this.serviceId);
7488
+ const response3 = await stub.fetch(new Request(url, {
7489
+ method: "POST",
7490
+ headers: { "content-type": "application/json" },
7491
+ body: JSON.stringify({ commandId, digest })
7492
+ }));
7493
+ if (response3.ok) return { ok: true };
7494
+ const body = await response3.clone().json().catch(() => void 0);
7495
+ return { ok: false, code: typeof body?.code === "string" ? body.code : void 0 };
7496
+ } catch {
7497
+ return { ok: false };
7498
+ }
7499
+ }
7500
+ async bootstrapPermitRelease(commandId, digest) {
7501
+ const stub = this.bootstrapStub();
7502
+ if (stub === void 0) return;
7503
+ try {
7504
+ const url = new URL(`https://commit-worker.internal/command/permit-release`);
7505
+ url.searchParams.set("__serviceId", this.serviceId);
7506
+ await stub.fetch(new Request(url, {
7507
+ method: "POST",
7508
+ headers: { "content-type": "application/json" },
7509
+ body: JSON.stringify({ commandId, digest })
7510
+ }));
7511
+ } catch {
7512
+ }
7513
+ }
7314
7514
  async postJson(stub, path, body, traceScope, rowId, traceOptions = {}) {
7315
7515
  const invoke = async () => {
7316
7516
  const response3 = await stub.fetch(
@@ -7456,6 +7656,7 @@ var CommitWorker = class {
7456
7656
  let pending = [...input.allTags];
7457
7657
  const committedTags = /* @__PURE__ */ new Set();
7458
7658
  const lastFailureCodeByTag = /* @__PURE__ */ new Map();
7659
+ const tagWriteFacts = /* @__PURE__ */ new Map();
7459
7660
  let globalAdmission = "admitted";
7460
7661
  for (let attempt = 0; attempt < MAX_WRITE_ATTEMPTS && pending.length > 0; attempt += 1) {
7461
7662
  const append = (stageScope) => Promise.allSettled(
@@ -7482,12 +7683,26 @@ var CommitWorker = class {
7482
7683
  retryIndex: attempt,
7483
7684
  attemptId
7484
7685
  });
7485
- const body = response3.status === 503 ? await response3.clone().json().catch(() => void 0) : void 0;
7686
+ const success = response3.status >= 200 && response3.status < 300;
7687
+ let appendVersion;
7688
+ let appendUpdatedAt;
7689
+ let code;
7690
+ try {
7691
+ const parsed = await response3.clone().json();
7692
+ if (isObject5(parsed)) {
7693
+ if (typeof parsed.version === "number" && Number.isSafeInteger(parsed.version)) appendVersion = parsed.version;
7694
+ if (typeof parsed.updatedAt === "string" && parsed.updatedAt.length > 0) appendUpdatedAt = parsed.updatedAt;
7695
+ if (typeof parsed.code === "string") code = parsed.code;
7696
+ }
7697
+ } catch {
7698
+ }
7486
7699
  return {
7487
7700
  tag,
7488
- success: response3.status >= 200 && response3.status < 300,
7701
+ success,
7489
7702
  globalAdmission: readGlobalAdmission(response3),
7490
- code: isObject5(body) && typeof body.code === "string" ? body.code : void 0
7703
+ code,
7704
+ appendVersion,
7705
+ appendUpdatedAt
7491
7706
  };
7492
7707
  })
7493
7708
  );
@@ -7499,6 +7714,9 @@ var CommitWorker = class {
7499
7714
  committedTags.add(tag);
7500
7715
  lastFailureCodeByTag.delete(tag);
7501
7716
  globalAdmission = mergeGlobalAdmission(globalAdmission, result2.value.globalAdmission);
7717
+ if (result2.value.appendVersion !== void 0 && result2.value.appendUpdatedAt !== void 0) {
7718
+ tagWriteFacts.set(tag, { version: result2.value.appendVersion, updatedAt: result2.value.appendUpdatedAt });
7719
+ }
7502
7720
  } else {
7503
7721
  lastFailureCodeByTag.set(
7504
7722
  tag,
@@ -7515,7 +7733,8 @@ var CommitWorker = class {
7515
7733
  registrationUnavailableTags: pending.filter(
7516
7734
  (tag) => lastFailureCodeByTag.get(tag) === "partition_registration_unavailable"
7517
7735
  ),
7518
- globalAdmission
7736
+ globalAdmission,
7737
+ tagWriteFacts
7519
7738
  };
7520
7739
  }
7521
7740
  /**
@@ -7548,10 +7767,13 @@ var CommitWorker = class {
7548
7767
  if (traceScope !== void 0) {
7549
7768
  await traceScope.span("S17", {}, async () => void 0);
7550
7769
  }
7551
- await this.cancelReservations(tags, attemptId, fault2, traceScope);
7770
+ const cleanup = await this.cancelReservations(tags, attemptId, fault2, traceScope);
7552
7771
  await this.retiredJournalMilestone("S05e", traceScope, 4);
7553
7772
  const response3 = failure3.outcome === "REFUSED" ? error3(400, "consistency_conflict", "serialized commit was refused by a consistency reservation") : failure3.failureCause === "reservation-timeout" ? error3(504, "timeout", "serialized commit timed out") : error3(500, "internal_error", "serialized commit failed before writing requested records");
7554
- return responseWithAttempt(response3, attemptId, fault2 !== void 0);
7773
+ return {
7774
+ response: responseWithAttempt(response3, attemptId, fault2 !== void 0),
7775
+ resolved: cleanup.failedTags.length === 0
7776
+ };
7555
7777
  }
7556
7778
  /**
7557
7779
  * Persist the local partial-write fact for each tag whose append did not
@@ -7588,22 +7810,25 @@ var CommitWorker = class {
7588
7810
  }
7589
7811
  }, 500), attemptId, exposeAttempt);
7590
7812
  }
7591
- async successResponse(input, candidates, attemptId, startedAt, fault2, traceScope) {
7592
- const readStates = (stageScope) => Promise.all(input.allTags.map(async (tag, memberIndex) => {
7813
+ async successResponse(input, candidates, attemptId, startedAt, fault2, traceScope, writes) {
7814
+ const assemble = async (stageScope) => {
7593
7815
  if (fault2 === "tag-state-unavailable") {
7594
7816
  throw new Error("Test fault made the committed tag state unavailable");
7595
7817
  }
7596
- const response3 = await this.tagRequest(tag, "/head-facts", void 0, stageScope?.fork(), stageScope === void 0 ? void 0 : "S14", { memberIndex, attemptId });
7597
- if (response3.status !== 200) {
7598
- throw new Error("Committed tag state was unavailable while preparing the response");
7599
- }
7600
- const body = await response3.json();
7601
- if (typeof body.version !== "number" || typeof body.updatedAt !== "string" || typeof body.head !== "string") {
7602
- throw new Error("Committed tag state was malformed while preparing the response");
7818
+ const results = [];
7819
+ for (const [memberIndex, tag] of input.allTags.entries()) {
7820
+ const read = () => {
7821
+ const fact = writes?.tagWriteFacts.get(tag);
7822
+ if (fact === void 0) {
7823
+ throw new Error("Committed tag state was unavailable while preparing the response");
7824
+ }
7825
+ return { tag, version: fact.version, writtenAt: fact.updatedAt };
7826
+ };
7827
+ results.push(stageScope === void 0 ? read() : await stageScope.fork().span("S14", { tag, memberIndex, attemptId }, async () => read()));
7603
7828
  }
7604
- return { tag, version: body.version, writtenAt: body.updatedAt };
7605
- }));
7606
- const tagWriteResults = traceScope === void 0 ? await readStates() : await traceScope.span("S13", {}, async (stage) => readStates(stage));
7829
+ return results;
7830
+ };
7831
+ const tagWriteResults = traceScope === void 0 ? await assemble() : await traceScope.span("S13", {}, async (stage) => assemble(stage));
7607
7832
  return {
7608
7833
  writtenEvents: candidates.map((candidate) => ({
7609
7834
  payload: encodeBase64Utf8(candidate.payload),
@@ -7817,15 +8042,15 @@ function observePostAdmission(observer, message, source, input) {
7817
8042
  observedAt: Date.now()
7818
8043
  });
7819
8044
  }
7820
- function errorText(error10) {
7821
- if (error10 instanceof Error && error10.message.length > 0) return error10.message;
7822
- return String(error10);
8045
+ function errorText(error11) {
8046
+ if (error11 instanceof Error && error11.message.length > 0) return error11.message;
8047
+ return String(error11);
7823
8048
  }
7824
8049
  function deliveryCorrelationId(message, source, supplied) {
7825
8050
  return supplied ?? `${source}:${message.serviceId}:${message.eventId}:${message.attemptId}`;
7826
8051
  }
7827
- function defaultFailureClass(error10) {
7828
- const candidate = error10;
8052
+ function defaultFailureClass(error11) {
8053
+ const candidate = error11;
7829
8054
  if (candidate.failureClass === "duplicate-race") return "duplicate-race";
7830
8055
  if (candidate.failureClass === "nonretryable-definition-poison") return "nonretryable-definition-poison";
7831
8056
  if (candidate.failureClass === "retryable-transient") return "retryable-transient";
@@ -7884,8 +8109,8 @@ async function applyViewHandlers(views, message, storedEvent, arrivedAt, source,
7884
8109
  result: { id: view.id, status, durationMs: Math.max(0, performance.now() - viewStartedAt) },
7885
8110
  failure: void 0
7886
8111
  };
7887
- } catch (error10) {
7888
- const failureClass = view.classifyError?.(error10) ?? defaultFailureClass(error10);
8112
+ } catch (error11) {
8113
+ const failureClass = view.classifyError?.(error11) ?? defaultFailureClass(error11);
7889
8114
  observePostAdmission(observer, message, source, {
7890
8115
  stage: "unsafe-view-apply",
7891
8116
  boundary: "end",
@@ -7897,10 +8122,10 @@ async function applyViewHandlers(views, message, storedEvent, arrivedAt, source,
7897
8122
  id: view.id,
7898
8123
  status: "failed",
7899
8124
  failureClass,
7900
- error: errorText(error10),
8125
+ error: errorText(error11),
7901
8126
  durationMs: Math.max(0, performance.now() - viewStartedAt)
7902
8127
  },
7903
- failure: { phase: "view", class: failureClass, viewId: view.id, error: errorText(error10) }
8128
+ failure: { phase: "view", class: failureClass, viewId: view.id, error: errorText(error11) }
7904
8129
  };
7905
8130
  }
7906
8131
  }));
@@ -7930,15 +8155,15 @@ async function processDeliveryCore(message, source, env, options = {}) {
7930
8155
  // settled; this is the durable post-batch observation boundary.
7931
8156
  observedAt: Date.now()
7932
8157
  });
7933
- } catch (error10) {
7934
- const failures2 = [{ phase: "recordDelivery", class: "retryable-transient", error: errorText(error10) }];
8158
+ } catch (error11) {
8159
+ const failures2 = [{ phase: "recordDelivery", class: "retryable-transient", error: errorText(error11) }];
7935
8160
  return result(source, message, "stored", arrivedAt, false, [], failures2, options.correlationId, startedAt);
7936
8161
  }
7937
8162
  if (outcome.outcome !== "stored") {
7938
8163
  try {
7939
8164
  await store.projectDeliveryIncidents?.(message.serviceId);
7940
- } catch (error10) {
7941
- const failures2 = [{ phase: "detector", class: "retryable-transient", error: errorText(error10) }];
8165
+ } catch (error11) {
8166
+ const failures2 = [{ phase: "detector", class: "retryable-transient", error: errorText(error11) }];
7942
8167
  return result(source, message, outcome.outcome, arrivedAt, false, [], failures2, options.correlationId, startedAt);
7943
8168
  }
7944
8169
  return result(source, message, outcome.outcome, arrivedAt, false, [], [], options.correlationId, startedAt);
@@ -7967,20 +8192,20 @@ async function processDeliveryCore(message, source, env, options = {}) {
7967
8192
  boundary: "end",
7968
8193
  outcome: "available"
7969
8194
  });
7970
- } catch (error10) {
8195
+ } catch (error11) {
7971
8196
  observePostAdmission(options.durableHopObserver, message, source, {
7972
8197
  stage: "post-record-delivery-global-receipt-readback",
7973
8198
  boundary: "end",
7974
8199
  outcome: "error"
7975
8200
  });
7976
- failures.push({ phase: "recordDelivery", class: "retryable-transient", error: errorText(error10) });
8201
+ failures.push({ phase: "recordDelivery", class: "retryable-transient", error: errorText(error11) });
7977
8202
  return result(source, message, "stored", arrivedAt, false, [], failures, options.correlationId, startedAt);
7978
8203
  }
7979
8204
  if (options.afterGlobalReceipt !== void 0) {
7980
8205
  try {
7981
8206
  await options.afterGlobalReceipt({ message, receipt, arrivedAt, source });
7982
- } catch (error10) {
7983
- failures.push({ phase: "drain", class: "retryable-transient", error: errorText(error10) });
8207
+ } catch (error11) {
8208
+ failures.push({ phase: "drain", class: "retryable-transient", error: errorText(error11) });
7984
8209
  }
7985
8210
  }
7986
8211
  }
@@ -7997,8 +8222,8 @@ async function processDeliveryCore(message, source, env, options = {}) {
7997
8222
  if (options.beforeViews !== void 0) {
7998
8223
  try {
7999
8224
  await options.beforeViews({ message, event: storedEvent, arrivedAt, source });
8000
- } catch (error10) {
8001
- failures.push({ phase: "completeness", class: "retryable-transient", error: errorText(error10) });
8225
+ } catch (error11) {
8226
+ failures.push({ phase: "completeness", class: "retryable-transient", error: errorText(error11) });
8002
8227
  return result(source, message, "stored", arrivedAt, false, viewResults, failures, options.correlationId, startedAt);
8003
8228
  }
8004
8229
  }
@@ -8018,14 +8243,14 @@ async function processDeliveryCore(message, source, env, options = {}) {
8018
8243
  boundary: "end",
8019
8244
  outcome: "applied"
8020
8245
  });
8021
- } catch (error10) {
8246
+ } catch (error11) {
8022
8247
  observePostAdmission(options.durableHopObserver, message, source, {
8023
8248
  stage: "detector",
8024
8249
  boundary: "end",
8025
8250
  outcome: "error"
8026
8251
  });
8027
8252
  try {
8028
- await options.onDetectorFailure?.({ message, arrivedAt, source, error: error10 });
8253
+ await options.onDetectorFailure?.({ message, arrivedAt, source, error: error11 });
8029
8254
  } catch (healthError) {
8030
8255
  failures.push({
8031
8256
  phase: "detector",
@@ -8033,7 +8258,7 @@ async function processDeliveryCore(message, source, env, options = {}) {
8033
8258
  error: `detector_health_record_failed:${errorText(healthError)}`
8034
8259
  });
8035
8260
  }
8036
- failures.push({ phase: "detector", class: "retryable-transient", error: errorText(error10) });
8261
+ failures.push({ phase: "detector", class: "retryable-transient", error: errorText(error11) });
8037
8262
  return result(source, message, "stored", arrivedAt, false, [], failures, options.correlationId, startedAt);
8038
8263
  }
8039
8264
  const views = gatedViews;
@@ -8050,16 +8275,16 @@ async function processDeliveryCore(message, source, env, options = {}) {
8050
8275
  if (options.onStored !== void 0) {
8051
8276
  try {
8052
8277
  await options.onStored({ message, event: storedEvent, arrivedAt, source });
8053
- } catch (error10) {
8054
- failures.push({ phase: "view", class: "retryable-transient", viewId: "legacy-onStored", error: errorText(error10) });
8278
+ } catch (error11) {
8279
+ failures.push({ phase: "view", class: "retryable-transient", viewId: "legacy-onStored", error: errorText(error11) });
8055
8280
  }
8056
8281
  }
8057
8282
  const preliminary = result(source, message, "stored", arrivedAt, detectorApplied, viewResults, failures, options.correlationId, startedAt);
8058
8283
  if (options.afterDelivery !== void 0 && failures.every((failure3) => failure3.class === "duplicate-race")) {
8059
8284
  try {
8060
8285
  await options.afterDelivery({ message, event: storedEvent, arrivedAt, source, result: preliminary });
8061
- } catch (error10) {
8062
- failures.push({ phase: "drain", class: "retryable-transient", error: errorText(error10) });
8286
+ } catch (error11) {
8287
+ failures.push({ phase: "drain", class: "retryable-transient", error: errorText(error11) });
8063
8288
  }
8064
8289
  }
8065
8290
  return result(source, message, "stored", arrivedAt, detectorApplied, viewResults, failures, options.correlationId, startedAt);
@@ -8081,8 +8306,8 @@ function asNumber(value, name) {
8081
8306
  if (!Number.isSafeInteger(number) || number < 0) throw new Error(`G44 ${name} must be a non-negative safe integer`);
8082
8307
  return number;
8083
8308
  }
8084
- function errorText2(error10) {
8085
- return error10 instanceof Error && error10.message.length > 0 ? error10.message : String(error10);
8309
+ function errorText2(error11) {
8310
+ return error11 instanceof Error && error11.message.length > 0 ? error11.message : String(error11);
8086
8311
  }
8087
8312
  function settledFrontierFromCursor(cursorJson) {
8088
8313
  const cursor = settledCursorFromJson(cursorJson);
@@ -8186,8 +8411,8 @@ var GlobalCompletenessReconciler = class {
8186
8411
  let snapshots;
8187
8412
  try {
8188
8413
  snapshots = await this.snapshotPartitions(serviceId);
8189
- } catch (error10) {
8190
- const message = errorText2(error10);
8414
+ } catch (error11) {
8415
+ const message = errorText2(error11);
8191
8416
  await this.appendFinding(serviceId, {
8192
8417
  identity: scannerFailureIdentity(serviceId),
8193
8418
  type: "GLOBAL_ARRAY_SCANNER_FAILURE",
@@ -8229,7 +8454,7 @@ var GlobalCompletenessReconciler = class {
8229
8454
  let joined;
8230
8455
  try {
8231
8456
  joined = this.options.globalReceiptMatcher === void 0 ? await this.globalReceiptMatches(snapshot.serviceId, snapshot.tag, obligation) : await this.options.globalReceiptMatcher(snapshot.serviceId, snapshot.tag, obligation);
8232
- } catch (error10) {
8457
+ } catch (error11) {
8233
8458
  await this.appendFinding(snapshot.serviceId, {
8234
8459
  identity: `GLOBAL_RECEIPT_UNAVAILABLE|${snapshot.serviceId}|${snapshot.tag}|${obligation.obligationSequence}|${obligation.eventId}|${obligation.eventDigest}`,
8235
8460
  type: "GLOBAL_ARRAY_RECEIPT_UNAVAILABLE",
@@ -8238,7 +8463,7 @@ var GlobalCompletenessReconciler = class {
8238
8463
  eventId: obligation.eventId,
8239
8464
  eventDigest: obligation.eventDigest
8240
8465
  }, nowMs).catch(() => void 0);
8241
- throw error10;
8466
+ throw error11;
8242
8467
  }
8243
8468
  if (!joined) {
8244
8469
  await this.appendObligationFinding(snapshot.serviceId, snapshot.tag, obligation, nowMs);
@@ -8257,8 +8482,8 @@ var GlobalCompletenessReconciler = class {
8257
8482
  if (afterSequence !== snapshot.upperBoundSequence) {
8258
8483
  throw new Error(`source_partition_range_incomplete:${snapshot.tag}`);
8259
8484
  }
8260
- } catch (error10) {
8261
- if (errorText2(error10).startsWith("source_")) {
8485
+ } catch (error11) {
8486
+ if (errorText2(error11).startsWith("source_")) {
8262
8487
  await this.appendFinding(snapshot.serviceId, {
8263
8488
  identity: sourceUnavailableIdentity(snapshot.serviceId, snapshot.tag),
8264
8489
  type: "GLOBAL_ARRAY_SOURCE_PARTITION_UNAVAILABLE",
@@ -8268,7 +8493,7 @@ var GlobalCompletenessReconciler = class {
8268
8493
  eventDigest: null
8269
8494
  }, nowMs).catch(() => void 0);
8270
8495
  }
8271
- throw error10;
8496
+ throw error11;
8272
8497
  }
8273
8498
  }
8274
8499
  const endSnapshots = await this.snapshotPartitions(serviceId);
@@ -8287,8 +8512,8 @@ var GlobalCompletenessReconciler = class {
8287
8512
  }
8288
8513
  await this.writeHealth(serviceId, "HEALTHY", JSON.stringify(cursor), nowMs, null, nowMs);
8289
8514
  return { kind: "FULL", partitions: snapshots, scannedObligations: scanned };
8290
- } catch (error10) {
8291
- const message = errorText2(error10);
8515
+ } catch (error11) {
8516
+ const message = errorText2(error11);
8292
8517
  const health = message.startsWith("source_") ? "UNKNOWN" : "FAILED";
8293
8518
  if (health === "FAILED") {
8294
8519
  await this.appendFinding(serviceId, {
@@ -8430,8 +8655,8 @@ var GlobalCompletenessReconciler = class {
8430
8655
  * without applying views. It shares the scanner's single health/finding
8431
8656
  * authority rather than creating a second incident workflow.
8432
8657
  */
8433
- async recordDetectorFailure(serviceId, error10, nowMs) {
8434
- const message = `detector_failure:${errorText2(error10).slice(0, 256)}`;
8658
+ async recordDetectorFailure(serviceId, error11, nowMs) {
8659
+ const message = `detector_failure:${errorText2(error11).slice(0, 256)}`;
8435
8660
  await this.appendFinding(serviceId, {
8436
8661
  identity: `GLOBAL_DETECTOR_FAILURE|${serviceId}`,
8437
8662
  type: "GLOBAL_ARRAY_DETECTOR_FAILURE",
@@ -8570,7 +8795,7 @@ var GlobalCompletenessReconciler = class {
8570
8795
  nowMs
8571
8796
  ).run();
8572
8797
  }
8573
- async writeHealth(serviceId, status, cursor, lastFullScanAt, error10, nowMs) {
8798
+ async writeHealth(serviceId, status, cursor, lastFullScanAt, error11, nowMs) {
8574
8799
  await this.database.prepare(
8575
8800
  `INSERT INTO serialized_dcb_completeness_scanner_health
8576
8801
  (service_id, scanner_version, status, cursor_json, last_full_scan_at, last_error, updated_at)
@@ -8582,7 +8807,7 @@ var GlobalCompletenessReconciler = class {
8582
8807
  last_full_scan_at = COALESCE(excluded.last_full_scan_at, serialized_dcb_completeness_scanner_health.last_full_scan_at),
8583
8808
  last_error = excluded.last_error,
8584
8809
  updated_at = excluded.updated_at`
8585
- ).bind(serviceId, this.scannerVersion, status, cursor, lastFullScanAt, error10, nowMs).run();
8810
+ ).bind(serviceId, this.scannerVersion, status, cursor, lastFullScanAt, error11, nowMs).run();
8586
8811
  }
8587
8812
  };
8588
8813
  function globalReceiptAcknowledgement(message, receivedAt) {
@@ -8674,7 +8899,7 @@ function sourceAcknowledgementOptions(env, options) {
8674
8899
  if (decision.kind !== "SETTLED") {
8675
8900
  throw new Error(`global_completeness_${decision.kind}:${decision.health.status}`);
8676
8901
  }
8677
- } catch (error10) {
8902
+ } catch (error11) {
8678
8903
  if (!completed) {
8679
8904
  observeG60PostAdmission(options.durableHopObserver, {
8680
8905
  stage: "completeness-coverage",
@@ -8689,7 +8914,7 @@ function sourceAcknowledgementOptions(env, options) {
8689
8914
  observedAt: Date.now()
8690
8915
  });
8691
8916
  }
8692
- throw error10;
8917
+ throw error11;
8693
8918
  }
8694
8919
  } : options.beforeViews;
8695
8920
  const observedSourceAcknowledgement = sourceAcknowledgement === void 0 ? void 0 : async (input) => {
@@ -8697,14 +8922,14 @@ function sourceAcknowledgementOptions(env, options) {
8697
8922
  try {
8698
8923
  await sourceAcknowledgement(input);
8699
8924
  observeSourceSubstep(options.durableHopObserver, { ...input, boundary: "end", outcome: "acknowledged" });
8700
- } catch (error10) {
8925
+ } catch (error11) {
8701
8926
  observeSourceSubstep(options.durableHopObserver, { ...input, boundary: "end", outcome: "error" });
8702
- throw error10;
8927
+ throw error11;
8703
8928
  }
8704
8929
  };
8705
- const detectorFailure = hasG44Authority ? async ({ message, arrivedAt, error: error10, source }) => {
8706
- await options.onDetectorFailure?.({ message, arrivedAt, error: error10, source });
8707
- await new GlobalCompletenessReconciler(env.D1, env.TAG).recordDetectorFailure(message.serviceId, error10, arrivedAt);
8930
+ const detectorFailure = hasG44Authority ? async ({ message, arrivedAt, error: error11, source }) => {
8931
+ await options.onDetectorFailure?.({ message, arrivedAt, error: error11, source });
8932
+ await new GlobalCompletenessReconciler(env.D1, env.TAG).recordDetectorFailure(message.serviceId, error11, arrivedAt);
8708
8933
  } : options.onDetectorFailure;
8709
8934
  return {
8710
8935
  ...options,
@@ -8719,9 +8944,9 @@ function sharedStore(env, provider) {
8719
8944
  async function admitBootstrapRoute(env, serviceId, route) {
8720
8945
  if (env.BOOTSTRAP === void 0) {
8721
8946
  if (env.G38_DOORBELL_DELIVERY_ROLE === "receiver") {
8722
- const error10 = new Error(`bootstrap_route_binding_missing:${route}`);
8723
- error10.name = "BootstrapRouteBindingMissingError";
8724
- throw error10;
8947
+ const error11 = new Error(`bootstrap_route_binding_missing:${route}`);
8948
+ error11.name = "BootstrapRouteBindingMissingError";
8949
+ throw error11;
8725
8950
  }
8726
8951
  return;
8727
8952
  }
@@ -8810,10 +9035,10 @@ async function handleDownstreamQueue(batch, env, options = {}) {
8810
9035
  if (outcome.outcome === "stored" && !outcome.failures.some((failure3) => failure3.phase === "recordDelivery")) {
8811
9036
  try {
8812
9037
  options.afterStoredQueueDelivery?.({ message: queued.body, result: outcome });
8813
- } catch (error10) {
9038
+ } catch (error11) {
8814
9039
  console.warn("safe_lane_kick", {
8815
9040
  status: "failed-to-schedule",
8816
- error: error10 instanceof Error ? error10.message : String(error10),
9041
+ error: error11 instanceof Error ? error11.message : String(error11),
8817
9042
  serviceId: queued.body.serviceId,
8818
9043
  eventId: queued.body.eventId,
8819
9044
  attemptId: queued.body.attemptId
@@ -8835,6 +9060,36 @@ async function stabilizeDownstream(env, options = {}, serviceId, serviceIdentity
8835
9060
  });
8836
9061
  }
8837
9062
 
9063
+ // src/scope/ControlRouteScope.ts
9064
+ function response2(status, code, error11) {
9065
+ return new Response(JSON.stringify({ code, error: error11 }), {
9066
+ status,
9067
+ headers: { "content-type": "application/json; charset=utf-8" }
9068
+ });
9069
+ }
9070
+ function scopeIdentityMissingResponse() {
9071
+ return response2(503, "scope.identity_missing", "A deployment service identity is required for this scope");
9072
+ }
9073
+ function enforceControlRouteScope(input) {
9074
+ let actual;
9075
+ try {
9076
+ actual = input.provider.forRequest(input.request, input.requestOptions).serviceId;
9077
+ } catch (caught) {
9078
+ if (caught instanceof ServiceIdentityMissingError) {
9079
+ console.error({ schema: "sdt.scope/v1", code: "scope.identity_missing", route: input.route });
9080
+ return { response: scopeIdentityMissingResponse() };
9081
+ }
9082
+ throw caught;
9083
+ }
9084
+ if (input.pathServiceId !== actual) {
9085
+ console.warn({ schema: "sdt.scope/v1", code: "scope.mismatch", route: input.route });
9086
+ return {
9087
+ response: response2(403, "scope.mismatch", "The path service identity is not authorized for this deployment")
9088
+ };
9089
+ }
9090
+ return { serviceId: actual };
9091
+ }
9092
+
8838
9093
  // src/downstream/OutboxDrain.ts
8839
9094
  function json5(body, status = 200) {
8840
9095
  return new Response(JSON.stringify(body), {
@@ -8931,15 +9186,32 @@ async function handleOutboxDrainRequest(request, env, options = {}) {
8931
9186
  if (!isDrainRequest(body)) {
8932
9187
  return json5({ error: "serviceId and unique non-empty tags are required", code: "invalid_outbox_drain_request" }, 400);
8933
9188
  }
9189
+ const requestedServiceId = body.serviceId;
9190
+ const tags = body.tags;
9191
+ const provider = options.serviceIdentityProvider ?? envServiceIdentity(env);
9192
+ let authority;
9193
+ try {
9194
+ authority = provider.forRequest(request).serviceId;
9195
+ } catch (caught) {
9196
+ if (caught instanceof ServiceIdentityMissingError) return scopeIdentityMissingResponse();
9197
+ throw caught;
9198
+ }
9199
+ if (requestedServiceId !== authority) {
9200
+ console.warn({ schema: "sdt.scope/v1", code: "scope.mismatch", route: "outbox-drain" });
9201
+ return json5({
9202
+ code: "scope.mismatch",
9203
+ error: "The request service identity is not authorized for this deployment"
9204
+ }, 403);
9205
+ }
8934
9206
  try {
8935
9207
  const results = [];
8936
- for (const tag of body.tags) {
8937
- results.push(await drainTagOutbox({ serviceId: body.serviceId, tag }, env, systemPipelineClock, options));
9208
+ for (const tag of tags) {
9209
+ results.push(await drainTagOutbox({ serviceId: authority, tag }, env, systemPipelineClock, options));
8938
9210
  }
8939
9211
  return json5({ results });
8940
- } catch (error10) {
9212
+ } catch (error11) {
8941
9213
  return json5({
8942
- error: error10 instanceof Error ? error10.message : "Outbox drain failed",
9214
+ error: error11 instanceof Error ? error11.message : "Outbox drain failed",
8943
9215
  code: "outbox_drain_failed"
8944
9216
  }, 503);
8945
9217
  }
@@ -9384,9 +9656,9 @@ function isNonEmptyString7(value) {
9384
9656
  return typeof value === "string" && value.length > 0;
9385
9657
  }
9386
9658
  function base64Json(value) {
9387
- const bytes = new TextEncoder().encode(JSON.stringify(value));
9659
+ const bytes2 = new TextEncoder().encode(JSON.stringify(value));
9388
9660
  let binary = "";
9389
- for (const byte of bytes) {
9661
+ for (const byte of bytes2) {
9390
9662
  binary += String.fromCharCode(byte);
9391
9663
  }
9392
9664
  return btoa(binary);
@@ -9564,9 +9836,9 @@ function decodeStoredJson(value) {
9564
9836
  }
9565
9837
  }
9566
9838
  function jsonBytes(value) {
9567
- const bytes = new TextEncoder().encode(JSON.stringify(value));
9839
+ const bytes2 = new TextEncoder().encode(JSON.stringify(value));
9568
9840
  let binary = "";
9569
- for (const byte of bytes) binary += String.fromCharCode(byte);
9841
+ for (const byte of bytes2) binary += String.fromCharCode(byte);
9570
9842
  return btoa(binary);
9571
9843
  }
9572
9844
  function isRuntimeBridgedProjector(definition) {
@@ -9639,9 +9911,9 @@ function isRuntimeCommandLike(value) {
9639
9911
  return typeof candidate.id === "string" && candidate.id.length > 0 && typeof candidate.name === "string" && typeof candidate.parseInput === "function" && typeof candidate.execute === "function" && typeof candidate.handle === "function";
9640
9912
  }
9641
9913
  function encodeJsonPayload(value) {
9642
- const bytes = new TextEncoder().encode(JSON.stringify(value));
9914
+ const bytes2 = new TextEncoder().encode(JSON.stringify(value));
9643
9915
  let binary = "";
9644
- for (const byte of bytes) binary += String.fromCharCode(byte);
9916
+ for (const byte of bytes2) binary += String.fromCharCode(byte);
9645
9917
  return btoa(binary);
9646
9918
  }
9647
9919
  function candidateToV1Envelope(candidate) {
@@ -9825,8 +10097,8 @@ function stateFromCheckpoint(projector, checkpoint) {
9825
10097
  if (checkpoint === void 0) return projector.initialState();
9826
10098
  try {
9827
10099
  return projector.deserializeState(checkpoint.stateJson);
9828
- } catch (error10) {
9829
- throw new ProjectionCheckpointCorruption(error10);
10100
+ } catch (error11) {
10101
+ throw new ProjectionCheckpointCorruption(error11);
9830
10102
  }
9831
10103
  }
9832
10104
  function projectionEventFromStored(event) {
@@ -10050,8 +10322,8 @@ async function admitBootstrapRoute2(env, serviceId) {
10050
10322
  })).fetch(new Request(url, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ route: "projection-rebuild" }) }));
10051
10323
  if (!admitted.ok) throw new Error("bootstrap_route_rejected:projection-rebuild");
10052
10324
  }
10053
- function boundedErrorReason(error10) {
10054
- const value = error10 instanceof Error ? error10.message : String(error10);
10325
+ function boundedErrorReason(error11) {
10326
+ const value = error11 instanceof Error ? error11.message : String(error11);
10055
10327
  return value.length > 256 ? `${value.slice(0, 253)}...` : value;
10056
10328
  }
10057
10329
  async function notifyObserver(observer, callback, input) {
@@ -10064,7 +10336,7 @@ async function notifyObserver(observer, callback, input) {
10064
10336
  } catch {
10065
10337
  }
10066
10338
  }
10067
- async function notifyOutcomes(observer, serviceId, projectorIds, attemptedAt, results, maximumSuid, env, error10) {
10339
+ async function notifyOutcomes(observer, serviceId, projectorIds, attemptedAt, results, maximumSuid, env, error11) {
10068
10340
  if (observer?.onOutcome === void 0) return;
10069
10341
  const byProjector = /* @__PURE__ */ new Map();
10070
10342
  for (const result2 of results) {
@@ -10077,9 +10349,9 @@ async function notifyOutcomes(observer, serviceId, projectorIds, attemptedAt, re
10077
10349
  let outcome = "invoked-but-no-work";
10078
10350
  let reason = null;
10079
10351
  let advancedSourceEvents = 0;
10080
- if (error10 !== void 0) {
10352
+ if (error11 !== void 0) {
10081
10353
  outcome = "invoked-and-threw";
10082
- reason = boundedErrorReason(error10);
10354
+ reason = boundedErrorReason(error11);
10083
10355
  } else {
10084
10356
  advancedSourceEvents = projectorResults.reduce((total, result2) => total + result2.advancedSourceEvents, 0);
10085
10357
  if (advancedSourceEvents > 0) {
@@ -10181,9 +10453,9 @@ async function pollLiveProjections(env, options = {}) {
10181
10453
  const results = await runtime.pollRegistered(serviceId, attemptedAt, options.maximumSuid);
10182
10454
  await notifyOutcomes(options.observer, serviceId, projectorIds, attemptedAt, results, options.maximumSuid, env);
10183
10455
  return results;
10184
- } catch (error10) {
10185
- await notifyOutcomes(options.observer, serviceId, projectorIds, attemptedAt, [], options.maximumSuid, env, error10);
10186
- throw error10;
10456
+ } catch (error11) {
10457
+ await notifyOutcomes(options.observer, serviceId, projectorIds, attemptedAt, [], options.maximumSuid, env, error11);
10458
+ throw error11;
10187
10459
  }
10188
10460
  }
10189
10461
  async function handleProjectionLag(request, env, registry = DEPLOYED_PROJECTOR_REGISTRY, storeProvider, serviceIdentityProvider) {
@@ -10421,15 +10693,15 @@ function integer(row, key) {
10421
10693
  function operationId() {
10422
10694
  return `mv-unsafe-${crypto.randomUUID()}`;
10423
10695
  }
10424
- function errorFrom(error10) {
10425
- const value = String(error10);
10696
+ function errorFrom(error11) {
10697
+ const value = String(error11);
10426
10698
  if (value.includes("mv_unsafe_receipts") && (value.includes("UNIQUE") || value.includes("constraint"))) {
10427
- return new UnsafeWindowMaterializedViewError("UNSAFE_DUPLICATE_RACE", false, "Concurrent unsafe receipt was committed by another delivery", { cause: error10 });
10699
+ return new UnsafeWindowMaterializedViewError("UNSAFE_DUPLICATE_RACE", false, "Concurrent unsafe receipt was committed by another delivery", { cause: error11 });
10428
10700
  }
10429
- if (value.includes("unsafe_row_cas")) return new UnsafeWindowMaterializedViewError("UNSAFE_ROW_CAS_MISMATCH", true, "Unsafe row version changed before atomic apply", { cause: error10 });
10430
- if (value.includes("unsafe_suid_contradiction")) return new UnsafeWindowMaterializedViewError("UNSAFE_SUID_CONTRADICTION", false, "Unsafe same-SUID payload contradiction", { cause: error10 });
10431
- if (value.includes("unsafe_safe_ahead")) return new UnsafeWindowMaterializedViewError("UNSAFE_SAFE_AHEAD", false, "Safe head already covers unsafe event", { cause: error10 });
10432
- if (value.includes("unsafe_behind_frontier")) return new UnsafeWindowMaterializedViewError("UNSAFE_BEHIND_FRONTIER", false, "Unsafe reads are fail-closed pending rebuild", { cause: error10 });
10701
+ if (value.includes("unsafe_row_cas")) return new UnsafeWindowMaterializedViewError("UNSAFE_ROW_CAS_MISMATCH", true, "Unsafe row version changed before atomic apply", { cause: error11 });
10702
+ if (value.includes("unsafe_suid_contradiction")) return new UnsafeWindowMaterializedViewError("UNSAFE_SUID_CONTRADICTION", false, "Unsafe same-SUID payload contradiction", { cause: error11 });
10703
+ if (value.includes("unsafe_safe_ahead")) return new UnsafeWindowMaterializedViewError("UNSAFE_SAFE_AHEAD", false, "Safe head already covers unsafe event", { cause: error11 });
10704
+ if (value.includes("unsafe_behind_frontier")) return new UnsafeWindowMaterializedViewError("UNSAFE_BEHIND_FRONTIER", false, "Unsafe reads are fail-closed pending rebuild", { cause: error11 });
10433
10705
  return void 0;
10434
10706
  }
10435
10707
  var UnsafeWindowMaterializedViewStore = class {
@@ -10562,8 +10834,8 @@ var UnsafeWindowMaterializedViewStore = class {
10562
10834
  await this.options.beforeApplyBatch?.(input, statements);
10563
10835
  try {
10564
10836
  await this.database.batch(statements);
10565
- } catch (error10) {
10566
- const text = String(error10);
10837
+ } catch (error11) {
10838
+ const text = String(error11);
10567
10839
  const guardFailure = text.includes("mv_atomic_guards") || text.includes("checkpoint_match") || text.includes("mv_unsafe_receipts") && (text.includes("UNIQUE") || text.includes("constraint"));
10568
10840
  if (guardFailure) {
10569
10841
  const receipt = await this.database.prepare(
@@ -10571,16 +10843,16 @@ var UnsafeWindowMaterializedViewStore = class {
10571
10843
  ).bind(input.serviceId, input.viewId, input.eventId).first();
10572
10844
  if (receipt !== null && receipt !== void 0) {
10573
10845
  if (string2(receipt, "suid") !== input.suid) {
10574
- throw new UnsafeWindowMaterializedViewError("UNSAFE_SUID_CONTRADICTION", false, "Duplicate event identity has a contradictory SUID", { cause: error10 });
10846
+ throw new UnsafeWindowMaterializedViewError("UNSAFE_SUID_CONTRADICTION", false, "Duplicate event identity has a contradictory SUID", { cause: error11 });
10575
10847
  }
10576
10848
  if (this.options.beforeApplyBatch !== void 0) {
10577
- throw new UnsafeWindowMaterializedViewError("UNSAFE_DUPLICATE_RACE", false, "Concurrent unsafe receipt was committed by another delivery", { cause: error10 });
10849
+ throw new UnsafeWindowMaterializedViewError("UNSAFE_DUPLICATE_RACE", false, "Concurrent unsafe receipt was committed by another delivery", { cause: error11 });
10578
10850
  }
10579
10851
  return { outcome: "no-change", duplicate: true };
10580
10852
  }
10581
10853
  return this.apply(input, true);
10582
10854
  }
10583
- throw error10;
10855
+ throw error11;
10584
10856
  }
10585
10857
  const result2 = { outcome: "applied", duplicate: false };
10586
10858
  await this.options.afterApplyBatch?.(input, result2);
@@ -10713,13 +10985,13 @@ var UnsafeWindowMaterializedViewStore = class {
10713
10985
  await this.options.beforeApplyBatch?.(input, statements);
10714
10986
  try {
10715
10987
  await this.database.batch(statements);
10716
- } catch (error10) {
10717
- if (sameSuidContradiction) throw new UnsafeWindowMaterializedViewError("UNSAFE_SUID_CONTRADICTION", false, "Unsafe same-SUID payload contradiction", { cause: error10 });
10718
- if (rowCasMismatch) throw new UnsafeWindowMaterializedViewError("UNSAFE_ROW_CAS_MISMATCH", true, "Unsafe row version changed before atomic apply", { cause: error10 });
10719
- if (safeAhead) throw new UnsafeWindowMaterializedViewError("UNSAFE_SAFE_AHEAD", false, "Safe head already covers unsafe event", { cause: error10 });
10720
- const typed = errorFrom(error10);
10988
+ } catch (error11) {
10989
+ if (sameSuidContradiction) throw new UnsafeWindowMaterializedViewError("UNSAFE_SUID_CONTRADICTION", false, "Unsafe same-SUID payload contradiction", { cause: error11 });
10990
+ if (rowCasMismatch) throw new UnsafeWindowMaterializedViewError("UNSAFE_ROW_CAS_MISMATCH", true, "Unsafe row version changed before atomic apply", { cause: error11 });
10991
+ if (safeAhead) throw new UnsafeWindowMaterializedViewError("UNSAFE_SAFE_AHEAD", false, "Safe head already covers unsafe event", { cause: error11 });
10992
+ const typed = errorFrom(error11);
10721
10993
  if (typed !== void 0) throw typed;
10722
- throw error10;
10994
+ throw error11;
10723
10995
  }
10724
10996
  const result2 = { outcome, duplicate: false };
10725
10997
  await this.options.afterApplyBatch?.(input, result2);
@@ -10786,8 +11058,8 @@ var UnsafeWindowMaterializedViewStore = class {
10786
11058
  this.database.prepare(`UPDATE mv_unsafe_kicks SET lease_owner = ?, lease_until = ?, dirty = 0 WHERE service_id = ? AND view_id = ?`).bind(owner, nowMs + leaseMs, serviceId, viewId),
10787
11059
  this.database.prepare("DELETE FROM mv_atomic_guards WHERE operation_id = ?").bind(operation)
10788
11060
  ]);
10789
- } catch (error10) {
10790
- throw new UnsafeWindowMaterializedViewError("UNSAFE_KICK_LEASE_HELD", true, "Unsafe kick is held by another lease owner", { cause: error10 });
11061
+ } catch (error11) {
11062
+ throw new UnsafeWindowMaterializedViewError("UNSAFE_KICK_LEASE_HELD", true, "Unsafe kick is held by another lease owner", { cause: error11 });
10791
11063
  }
10792
11064
  const row = await this.database.prepare(`SELECT target_suid, dirty FROM mv_unsafe_kicks WHERE service_id = ? AND view_id = ?`).bind(serviceId, viewId).first();
10793
11065
  return row === null || row === void 0 ? void 0 : { targetSuid: string2(row, "target_suid"), dirty: integer(row, "dirty") === 1 };
@@ -10861,9 +11133,9 @@ var UnsafeWindowMaterializedViewStore = class {
10861
11133
  try {
10862
11134
  await this.database.batch(statements);
10863
11135
  return true;
10864
- } catch (error10) {
10865
- if (String(error10).includes("mv_atomic_guards") || String(error10).includes("checkpoint_match")) return false;
10866
- throw error10;
11136
+ } catch (error11) {
11137
+ if (String(error11).includes("mv_atomic_guards") || String(error11).includes("checkpoint_match")) return false;
11138
+ throw error11;
10867
11139
  }
10868
11140
  }
10869
11141
  /**
@@ -11029,12 +11301,12 @@ function indexValue(row, type) {
11029
11301
  function operationId2() {
11030
11302
  return `mv-${crypto.randomUUID()}`;
11031
11303
  }
11032
- function isCasFailure(error10) {
11033
- const message = String(error10);
11304
+ function isCasFailure(error11) {
11305
+ const message = String(error11);
11034
11306
  return message.includes("mv_atomic_guards.checkpoint_match") || message.includes("MV_CAS_MISMATCH");
11035
11307
  }
11036
- function isPatchFailure(error10) {
11037
- return String(error10).includes("CHECK constraint failed: checkpoint_match");
11308
+ function isPatchFailure(error11) {
11309
+ return String(error11).includes("CHECK constraint failed: checkpoint_match");
11038
11310
  }
11039
11311
  var D1MaterializedViewStore = class {
11040
11312
  constructor(database) {
@@ -11070,8 +11342,8 @@ var D1MaterializedViewStore = class {
11070
11342
  VALUES (?, ?, ?, ?)`
11071
11343
  ).bind(input.serviceId, input.viewId, generation, input.updatedAt)
11072
11344
  ]);
11073
- } catch (error10) {
11074
- throw new MaterializedViewStoreError("create-active", "MV_INSTANCE_EXISTS", `Active materialized view already exists: ${String(error10)}`, { cause: error10 });
11345
+ } catch (error11) {
11346
+ throw new MaterializedViewStoreError("create-active", "MV_INSTANCE_EXISTS", `Active materialized view already exists: ${String(error11)}`, { cause: error11 });
11075
11347
  }
11076
11348
  return this.readInstance(input.serviceId, input.viewId, generation);
11077
11349
  }
@@ -11092,8 +11364,8 @@ var D1MaterializedViewStore = class {
11092
11364
  input.definitionVersion,
11093
11365
  input.updatedAt
11094
11366
  ).run();
11095
- } catch (error10) {
11096
- throw new MaterializedViewStoreError("create-candidate", "MV_INSTANCE_EXISTS", `Candidate generation already exists: ${String(error10)}`, { cause: error10 });
11367
+ } catch (error11) {
11368
+ throw new MaterializedViewStoreError("create-candidate", "MV_INSTANCE_EXISTS", `Candidate generation already exists: ${String(error11)}`, { cause: error11 });
11097
11369
  }
11098
11370
  return this.readInstance(input.serviceId, input.viewId, input.generation);
11099
11371
  }
@@ -11231,10 +11503,10 @@ var D1MaterializedViewStore = class {
11231
11503
  */
11232
11504
  async readListPage(serviceId, viewId, options = {}) {
11233
11505
  this.ready("initialize");
11234
- const active = await this.readActive(serviceId, viewId);
11235
- if (active === void 0) return { rows: [], totalCount: 0, readHead: "" };
11236
- const selected = options.generation ?? active.generation;
11237
- const selectedInstance = selected === active.generation ? active : await this.readInstance(serviceId, viewId, selected);
11506
+ const active2 = await this.readActive(serviceId, viewId);
11507
+ if (active2 === void 0) return { rows: [], totalCount: 0, readHead: "" };
11508
+ const selected = options.generation ?? active2.generation;
11509
+ const selectedInstance = selected === active2.generation ? active2 : await this.readInstance(serviceId, viewId, selected);
11238
11510
  if (selectedInstance === void 0) {
11239
11511
  throw new MaterializedViewStoreError("initialize", "MV_INSTANCE_MISSING", "Requested materialized-view generation is unavailable");
11240
11512
  }
@@ -11751,10 +12023,10 @@ var D1MaterializedViewStore = class {
11751
12023
  ).bind(operation, `${operation}:%`));
11752
12024
  try {
11753
12025
  await this.database.batch(statements);
11754
- } catch (error10) {
11755
- if (isPatchFailure(error10)) throw new MaterializedViewPatchError();
11756
- if (isCasFailure(error10)) throw new MaterializedViewCasError();
11757
- throw new MaterializedViewStoreError("apply", "MV_STORE_OPERATION_FAILED", `Materialized-view atomic apply failed: ${String(error10)}`, { cause: error10 });
12026
+ } catch (error11) {
12027
+ if (isPatchFailure(error11)) throw new MaterializedViewPatchError();
12028
+ if (isCasFailure(error11)) throw new MaterializedViewCasError();
12029
+ throw new MaterializedViewStoreError("apply", "MV_STORE_OPERATION_FAILED", `Materialized-view atomic apply failed: ${String(error11)}`, { cause: error11 });
11758
12030
  }
11759
12031
  const instance = await this.readInstance(input.serviceId, input.viewId, input.generation);
11760
12032
  if (instance === void 0) throw new MaterializedViewStoreError("apply", "MV_INSTANCE_MISSING", "Materialized-view instance disappeared after apply");
@@ -11859,13 +12131,13 @@ var D1MaterializedViewStore = class {
11859
12131
  ];
11860
12132
  try {
11861
12133
  await this.database.batch(statements);
11862
- } catch (error10) {
11863
- if (isCasFailure(error10)) throw new MaterializedViewPromotionCasError();
11864
- throw new MaterializedViewStoreError("promote", "MV_STORE_OPERATION_FAILED", `Materialized-view promotion failed: ${String(error10)}`, { cause: error10 });
12134
+ } catch (error11) {
12135
+ if (isCasFailure(error11)) throw new MaterializedViewPromotionCasError();
12136
+ throw new MaterializedViewStoreError("promote", "MV_STORE_OPERATION_FAILED", `Materialized-view promotion failed: ${String(error11)}`, { cause: error11 });
11865
12137
  }
11866
- const active = await this.readActive(input.serviceId, input.viewId);
11867
- if (active === void 0) throw new MaterializedViewStoreError("promote", "MV_INSTANCE_MISSING", "Active generation disappeared after promotion");
11868
- return active;
12138
+ const active2 = await this.readActive(input.serviceId, input.viewId);
12139
+ if (active2 === void 0) throw new MaterializedViewStoreError("promote", "MV_INSTANCE_MISSING", "Active generation disappeared after promotion");
12140
+ return active2;
11869
12141
  }
11870
12142
  /** Short aliases make the port convenient for runtime composition and tests. */
11871
12143
  apply = this.applyMutationsAndAdvanceCheckpoint.bind(this);
@@ -11874,8 +12146,8 @@ var D1MaterializedViewStore = class {
11874
12146
  let value;
11875
12147
  try {
11876
12148
  value = assertJsonValue(JSON.parse(asString2(row.value_json, "value_json")), "state-persistence");
11877
- } catch (error10) {
11878
- throw new MaterializedViewStoreError("initialize", "MV_VALUE_INVALID", `Materialized-view row JSON was invalid: ${String(error10)}`, { cause: error10 });
12149
+ } catch (error11) {
12150
+ throw new MaterializedViewStoreError("initialize", "MV_VALUE_INVALID", `Materialized-view row JSON was invalid: ${String(error11)}`, { cause: error11 });
11879
12151
  }
11880
12152
  return {
11881
12153
  serviceId: asString2(row.service_id, "service_id"),
@@ -11956,6 +12228,33 @@ function parseRequest(value) {
11956
12228
  }
11957
12229
  };
11958
12230
  }
12231
+ var QUERY_PARAMS_UNSUPPORTED_MESSAGE = "The selected query does not support these parameters.";
12232
+ var LIST_QUERY_PARAM_KEYS = /* @__PURE__ */ new Set(["PageNumber", "PageSize", "NewestFirst", "consistency"]);
12233
+ function unsupportedQueryParams(endpoint, queryParams) {
12234
+ if (endpoint === "query") {
12235
+ if (isObject9(queryParams) && Object.keys(queryParams).length === 0) {
12236
+ return void 0;
12237
+ }
12238
+ return {
12239
+ status: 400,
12240
+ code: "query_params_unsupported",
12241
+ error: QUERY_PARAMS_UNSUPPORTED_MESSAGE
12242
+ };
12243
+ }
12244
+ if (!isObject9(queryParams)) {
12245
+ return void 0;
12246
+ }
12247
+ for (const key of Object.keys(queryParams)) {
12248
+ if (!LIST_QUERY_PARAM_KEYS.has(key)) {
12249
+ return {
12250
+ status: 400,
12251
+ code: "query_params_unsupported",
12252
+ error: QUERY_PARAMS_UNSUPPORTED_MESSAGE
12253
+ };
12254
+ }
12255
+ }
12256
+ return void 0;
12257
+ }
11959
12258
  function paginationFrom(value) {
11960
12259
  if (!isObject9(value)) {
11961
12260
  return { error: "list-query queryParamsJson must contain an object" };
@@ -12136,6 +12435,10 @@ async function handleSerializedQuery(request, env, options = {}) {
12136
12435
  if (definition === void 0 || definition.endpoint !== endpoint) {
12137
12436
  return error6(400, "validation_error", "queryType is not mapped for this endpoint");
12138
12437
  }
12438
+ const unsupported = unsupportedQueryParams(endpoint, parsed.value.queryParams);
12439
+ if (unsupported !== void 0) {
12440
+ return error6(unsupported.status, unsupported.code, unsupported.error);
12441
+ }
12139
12442
  if (!definition.enabled || (options.projectors ?? DEPLOYED_PROJECTOR_REGISTRY).resolve(definition.tagProjector) === void 0) {
12140
12443
  return error6(503, "projection_unavailable", "The mapped query projection is unavailable");
12141
12444
  }
@@ -12522,10 +12825,10 @@ var SerializedReadWorker = class {
12522
12825
  });
12523
12826
  }
12524
12827
  async ensureWindowDeterminate() {
12525
- if (this.storeProvider === void 0 || this.storeProvider.isConfigured?.(this.env) === false) {
12526
- return;
12527
- }
12528
12828
  try {
12829
+ if (this.storeProvider === void 0 || this.storeProvider.isConfigured?.(this.env) === false) {
12830
+ return;
12831
+ }
12529
12832
  const store = this.storeProvider.create(this.env);
12530
12833
  await store.initialize();
12531
12834
  const nowMs = Date.now();
@@ -12566,13 +12869,662 @@ async function handleSerializedRead(request, env, registry = DEPLOYED_PROJECTOR_
12566
12869
  }), registry, storeProvider).handle(request);
12567
12870
  }
12568
12871
 
12872
+ // src/completeness/IncidentLifecycle.ts
12873
+ var MAX_IDENTITY_BYTES = 256;
12874
+ var MAX_LONG_TEXT_BYTES = 2048;
12875
+ var SHA256_DIGEST = /^sha256:[0-9a-f]{64}$/;
12876
+ var IncidentLifecycleError = class extends Error {
12877
+ code;
12878
+ status;
12879
+ constructor(code, status, message) {
12880
+ super(message);
12881
+ this.name = "IncidentLifecycleError";
12882
+ this.code = code;
12883
+ this.status = status;
12884
+ }
12885
+ };
12886
+ function isObject11(value) {
12887
+ return typeof value === "object" && value !== null && !Array.isArray(value);
12888
+ }
12889
+ function hasOnlyKeys(value, keys) {
12890
+ const allowed = new Set(keys);
12891
+ return Object.keys(value).every((key) => allowed.has(key));
12892
+ }
12893
+ function bytes(value) {
12894
+ return new TextEncoder().encode(value).byteLength;
12895
+ }
12896
+ function boundedString(value, name, maximum, required = true) {
12897
+ if (typeof value !== "string") {
12898
+ throw new IncidentLifecycleError("incident_invalid_request", 400, `${name} must be a string`);
12899
+ }
12900
+ const normalized = value.trim();
12901
+ if (required && normalized.length === 0) {
12902
+ throw new IncidentLifecycleError("incident_invalid_request", 400, `${name} must be non-empty`);
12903
+ }
12904
+ if (bytes(normalized) > maximum) {
12905
+ throw new IncidentLifecycleError("incident_invalid_request", 400, `${name} is too long`);
12906
+ }
12907
+ return normalized.length === 0 ? null : normalized;
12908
+ }
12909
+ function normalizeIncidentIdentity(value) {
12910
+ const identity2 = boundedString(value, "incidentIdentity", MAX_IDENTITY_BYTES);
12911
+ if (identity2 === null) {
12912
+ throw new IncidentLifecycleError("incident_invalid_request", 400, "incidentIdentity must be non-empty");
12913
+ }
12914
+ return identity2;
12915
+ }
12916
+ function safeInteger(value, name) {
12917
+ if (typeof value !== "number" || !Number.isSafeInteger(value)) {
12918
+ throw new IncidentLifecycleError("incident_invalid_request", 400, `${name} must be a safe integer`);
12919
+ }
12920
+ return value;
12921
+ }
12922
+ function normalizeCorrection(value) {
12923
+ if (!isObject11(value) || !hasOnlyKeys(value, ["kind", "reference", "digest"])) {
12924
+ throw new IncidentLifecycleError("incident_invalid_request", 400, "correction has unknown or missing keys");
12925
+ }
12926
+ if (value.kind !== "event" && value.kind !== "receipt") {
12927
+ throw new IncidentLifecycleError("incident_invalid_request", 400, "correction.kind must be event or receipt");
12928
+ }
12929
+ const reference = boundedString(value.reference, "correction.reference", MAX_LONG_TEXT_BYTES);
12930
+ const digest = boundedString(value.digest, "correction.digest", MAX_IDENTITY_BYTES);
12931
+ if (reference === null || digest === null || !SHA256_DIGEST.test(digest)) {
12932
+ throw new IncidentLifecycleError("incident_invalid_request", 400, "correction.digest must be lowercase sha256");
12933
+ }
12934
+ return { kind: value.kind, reference, digest };
12935
+ }
12936
+ function normalizeRequest(value) {
12937
+ if (!isObject11(value) || !hasOnlyKeys(value, ["action", "incidentIdentity", "transitionKey", "expectedVersion", "reason", "ownerId", "deadlineAt", "correction", "resolution"])) {
12938
+ throw new IncidentLifecycleError("incident_invalid_request", 400, "transition has unknown keys");
12939
+ }
12940
+ const action = value.action;
12941
+ if (typeof action !== "string" || !["ACKNOWLEDGE", "UPDATE_ASSIGNMENT", "RECORD_CORRECTION", "CLOSE", "REOPEN"].includes(action)) {
12942
+ throw new IncidentLifecycleError("incident_invalid_request", 400, "transition action is invalid");
12943
+ }
12944
+ const actionKeys = action === "RECORD_CORRECTION" ? ["action", "incidentIdentity", "transitionKey", "expectedVersion", "reason", "correction"] : action === "CLOSE" ? ["action", "incidentIdentity", "transitionKey", "expectedVersion", "reason", "resolution"] : ["action", "incidentIdentity", "transitionKey", "expectedVersion", "reason", "ownerId", "deadlineAt"];
12945
+ if (!hasOnlyKeys(value, actionKeys)) {
12946
+ throw new IncidentLifecycleError("incident_invalid_request", 400, "transition has keys not valid for its action");
12947
+ }
12948
+ const base = {
12949
+ incidentIdentity: normalizeIncidentIdentity(value.incidentIdentity),
12950
+ transitionKey: boundedString(value.transitionKey, "transitionKey", MAX_IDENTITY_BYTES),
12951
+ expectedVersion: safeInteger(value.expectedVersion, "expectedVersion"),
12952
+ reason: boundedString(value.reason, "reason", MAX_LONG_TEXT_BYTES)
12953
+ };
12954
+ if (base.expectedVersion < 0) {
12955
+ throw new IncidentLifecycleError("incident_invalid_request", 400, "expectedVersion must not be negative");
12956
+ }
12957
+ if (action === "ACKNOWLEDGE" || action === "UPDATE_ASSIGNMENT" || action === "REOPEN") {
12958
+ const ownerId = boundedString(value.ownerId, "ownerId", MAX_IDENTITY_BYTES);
12959
+ const deadlineAt = safeInteger(value.deadlineAt, "deadlineAt");
12960
+ if (ownerId === null) throw new IncidentLifecycleError("incident_invalid_request", 400, "ownerId must be non-empty");
12961
+ return { ...base, action, ownerId, deadlineAt };
12962
+ }
12963
+ if (action === "RECORD_CORRECTION") {
12964
+ return { ...base, action, correction: normalizeCorrection(value.correction) };
12965
+ }
12966
+ if (!isObject11(value.resolution) || !hasOnlyKeys(value.resolution, ["kind", "explanation"])) {
12967
+ throw new IncidentLifecycleError("incident_invalid_request", 400, "resolution has unknown or missing keys");
12968
+ }
12969
+ if (value.resolution.kind === "CORRECTED") {
12970
+ if (value.resolution.explanation !== void 0) {
12971
+ throw new IncidentLifecycleError("incident_invalid_request", 400, "CORRECTED resolution has unknown keys");
12972
+ }
12973
+ return { ...base, action, resolution: { kind: "CORRECTED" } };
12974
+ }
12975
+ if (value.resolution.kind === "ACCEPTED_AS_IS") {
12976
+ const explanation = boundedString(value.resolution.explanation, "resolution.explanation", MAX_LONG_TEXT_BYTES);
12977
+ if (explanation === null) throw new IncidentLifecycleError("incident_invalid_request", 400, "resolution.explanation is required");
12978
+ return { ...base, action, resolution: { kind: "ACCEPTED_AS_IS", explanation } };
12979
+ }
12980
+ throw new IncidentLifecycleError("incident_invalid_request", 400, "resolution.kind is invalid");
12981
+ }
12982
+ function canonicalize(value) {
12983
+ if (Array.isArray(value)) return value.map(canonicalize);
12984
+ if (!isObject11(value)) return value;
12985
+ return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonicalize(value[key])]));
12986
+ }
12987
+ async function digestFor(request, actorId) {
12988
+ const envelope = canonicalize({ action: request, actor: actorId });
12989
+ const encoded = new TextEncoder().encode(JSON.stringify(envelope));
12990
+ const digest = await crypto.subtle.digest("SHA-256", encoded);
12991
+ return `sha256:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
12992
+ }
12993
+ function projectionFromRow(row, finding2) {
12994
+ if (row === null) {
12995
+ return {
12996
+ serviceId: finding2.service_id,
12997
+ incidentIdentity: finding2.incident_identity,
12998
+ lifecycleState: "OPEN",
12999
+ ownerId: null,
13000
+ deadlineAt: null,
13001
+ correction: null,
13002
+ closeResolution: null,
13003
+ closeReason: null,
13004
+ version: 0,
13005
+ lastTransitionKey: "",
13006
+ updatedAt: finding2.last_observed_at
13007
+ };
13008
+ }
13009
+ return {
13010
+ serviceId: row.service_id,
13011
+ incidentIdentity: row.incident_identity,
13012
+ lifecycleState: row.lifecycle_state,
13013
+ ownerId: row.owner_id,
13014
+ deadlineAt: row.deadline_at,
13015
+ correction: row.correction_kind === null ? null : {
13016
+ kind: row.correction_kind,
13017
+ reference: row.correction_reference,
13018
+ digest: row.correction_digest
13019
+ },
13020
+ closeResolution: row.close_resolution,
13021
+ closeReason: row.close_reason,
13022
+ version: row.version,
13023
+ lastTransitionKey: row.last_transition_key,
13024
+ updatedAt: row.updated_at
13025
+ };
13026
+ }
13027
+ function findingFromRow(row) {
13028
+ return {
13029
+ serviceId: row.service_id,
13030
+ incidentIdentity: row.incident_identity,
13031
+ incidentType: row.incident_type,
13032
+ partitionTag: row.partition_tag,
13033
+ obligationSequence: row.obligation_sequence,
13034
+ eventId: row.event_id,
13035
+ eventDigest: row.event_digest,
13036
+ state: row.finding_state,
13037
+ firstObservedAt: row.first_observed_at,
13038
+ lastObservedAt: row.last_observed_at
13039
+ };
13040
+ }
13041
+ function transitionFromRow(row) {
13042
+ const before = projectionFromAudit(row, "before");
13043
+ const after = projectionFromAudit(row, "after");
13044
+ return {
13045
+ transitionId: row.transition_id,
13046
+ serviceId: row.service_id,
13047
+ incidentIdentity: row.incident_identity,
13048
+ transitionKey: row.transition_key,
13049
+ requestDigest: row.request_digest,
13050
+ action: row.action,
13051
+ fromState: row.from_state,
13052
+ toState: row.to_state,
13053
+ fromVersion: row.from_version,
13054
+ toVersion: row.to_version,
13055
+ actorId: row.actor_id,
13056
+ before,
13057
+ after,
13058
+ reason: row.reason,
13059
+ occurredAt: row.occurred_at
13060
+ };
13061
+ }
13062
+ function projectionFromAudit(row, prefix) {
13063
+ const state = prefix === "before" ? row.from_state : row.to_state;
13064
+ const version = prefix === "before" ? row.from_version : row.to_version;
13065
+ const ownerId = row[`${prefix}_owner_id`];
13066
+ const correctionKind = row[`${prefix}_correction_kind`];
13067
+ return {
13068
+ serviceId: row.service_id,
13069
+ incidentIdentity: row.incident_identity,
13070
+ lifecycleState: state,
13071
+ ownerId,
13072
+ deadlineAt: row[`${prefix}_deadline_at`],
13073
+ correction: correctionKind === null ? null : {
13074
+ kind: correctionKind,
13075
+ reference: row[`${prefix}_correction_reference`],
13076
+ digest: row[`${prefix}_correction_digest`]
13077
+ },
13078
+ closeResolution: row[`${prefix}_close_resolution`],
13079
+ closeReason: row[`${prefix}_close_reason`],
13080
+ version,
13081
+ lastTransitionKey: prefix === "after" ? row.transition_key : "",
13082
+ updatedAt: row.occurred_at
13083
+ };
13084
+ }
13085
+ function active(state) {
13086
+ return state !== "CLOSED";
13087
+ }
13088
+ function compareItems(left, right) {
13089
+ if (left.overdue !== right.overdue) return left.overdue ? -1 : 1;
13090
+ if (left.unowned !== right.unowned) return left.unowned ? -1 : 1;
13091
+ if (left.finding.lastObservedAt !== right.finding.lastObservedAt) return right.finding.lastObservedAt - left.finding.lastObservedAt;
13092
+ return left.finding.incidentIdentity < right.finding.incidentIdentity ? -1 : left.finding.incidentIdentity > right.finding.incidentIdentity ? 1 : 0;
13093
+ }
13094
+ var IncidentLifecycle = class {
13095
+ constructor(database, now2 = () => Date.now()) {
13096
+ this.database = database;
13097
+ this.now = now2;
13098
+ }
13099
+ database;
13100
+ now;
13101
+ static parseTransition(value) {
13102
+ return normalizeRequest(value);
13103
+ }
13104
+ async finding(serviceId, incidentIdentity) {
13105
+ const row = await this.database.prepare(
13106
+ `SELECT service_id, incident_identity, incident_type, partition_tag,
13107
+ obligation_sequence, event_id, event_digest, state AS finding_state,
13108
+ first_observed_at, last_observed_at
13109
+ FROM serialized_dcb_completeness_findings
13110
+ WHERE service_id = ? AND incident_identity = ?`
13111
+ ).bind(serviceId, incidentIdentity).first();
13112
+ if (row === null || row === void 0) {
13113
+ throw new IncidentLifecycleError("incident_not_found", 404, "The completeness incident was not found");
13114
+ }
13115
+ return row;
13116
+ }
13117
+ async lifecycle(serviceId, incidentIdentity) {
13118
+ const row = await this.database.prepare(
13119
+ `SELECT service_id, incident_identity, lifecycle_state, owner_id, deadline_at,
13120
+ correction_kind, correction_reference, correction_digest,
13121
+ close_resolution, close_reason, version, last_transition_key, updated_at
13122
+ FROM serialized_dcb_incident_lifecycles
13123
+ WHERE service_id = ? AND incident_identity = ?`
13124
+ ).bind(serviceId, incidentIdentity).first();
13125
+ return row === void 0 ? null : row;
13126
+ }
13127
+ async transitions(serviceId, incidentIdentity) {
13128
+ const result2 = await this.database.prepare(
13129
+ `SELECT * FROM serialized_dcb_incident_transitions
13130
+ WHERE service_id = ? AND incident_identity = ?
13131
+ ORDER BY transition_id ASC`
13132
+ ).bind(serviceId, incidentIdentity).all();
13133
+ return result2.results.map(transitionFromRow);
13134
+ }
13135
+ async closeTime(serviceId, incidentIdentity) {
13136
+ const row = await this.database.prepare(
13137
+ `SELECT occurred_at FROM serialized_dcb_incident_transitions
13138
+ WHERE service_id = ? AND incident_identity = ? AND to_state = 'CLOSED'
13139
+ ORDER BY transition_id DESC LIMIT 1`
13140
+ ).bind(serviceId, incidentIdentity).first();
13141
+ return row?.occurred_at ?? null;
13142
+ }
13143
+ async item(finding2, row, at) {
13144
+ const lifecycle = projectionFromRow(row, finding2);
13145
+ const closeTime = lifecycle.lifecycleState === "OPEN" && row === null ? null : await this.closeTime(finding2.service_id, finding2.incident_identity);
13146
+ const observedAfterClose = closeTime !== null && finding2.last_observed_at > closeTime;
13147
+ const overdue = active(lifecycle.lifecycleState) && lifecycle.deadlineAt !== null && lifecycle.deadlineAt < at;
13148
+ return {
13149
+ finding: findingFromRow(finding2),
13150
+ lifecycle,
13151
+ overdue,
13152
+ unowned: lifecycle.ownerId === null,
13153
+ observedAfterClose
13154
+ };
13155
+ }
13156
+ async list(serviceId, filters = {}, at = this.now()) {
13157
+ const rows = await this.database.prepare(
13158
+ `SELECT service_id, incident_identity, incident_type, partition_tag,
13159
+ obligation_sequence, event_id, event_digest, state AS finding_state,
13160
+ first_observed_at, last_observed_at
13161
+ FROM serialized_dcb_completeness_findings
13162
+ WHERE service_id = ?`
13163
+ ).bind(serviceId).all();
13164
+ const items = [];
13165
+ for (const finding2 of rows.results) {
13166
+ const item = await this.item(finding2, await this.lifecycle(serviceId, finding2.incident_identity), at);
13167
+ if (filters.state !== void 0 && item.lifecycle.lifecycleState !== filters.state) continue;
13168
+ if (filters.owner !== void 0 && item.lifecycle.ownerId !== filters.owner) continue;
13169
+ if (filters.unowned === true && !item.unowned) continue;
13170
+ if (filters.overdue === true && !item.overdue) continue;
13171
+ if (filters.observedAfterClose === true && !item.observedAfterClose) continue;
13172
+ items.push(item);
13173
+ }
13174
+ items.sort(compareItems);
13175
+ const byState = {
13176
+ OPEN: 0,
13177
+ ACKNOWLEDGED: 0,
13178
+ CORRECTION_RECORDED: 0,
13179
+ CLOSED: 0,
13180
+ REOPENED: 0
13181
+ };
13182
+ for (const item of items) byState[item.lifecycle.lifecycleState] += 1;
13183
+ return {
13184
+ items,
13185
+ summary: {
13186
+ total: items.length,
13187
+ byState,
13188
+ unowned: items.filter((item) => item.unowned).length,
13189
+ overdue: items.filter((item) => item.overdue).length,
13190
+ observedAfterClose: items.filter((item) => item.observedAfterClose).length
13191
+ }
13192
+ };
13193
+ }
13194
+ async detail(serviceId, incidentIdentity, at = this.now()) {
13195
+ const finding2 = await this.finding(serviceId, incidentIdentity);
13196
+ const lifecycle = await this.lifecycle(serviceId, incidentIdentity);
13197
+ const item = await this.item(finding2, lifecycle, at);
13198
+ return {
13199
+ finding: item.finding,
13200
+ lifecycle: item.lifecycle,
13201
+ flags: { overdue: item.overdue, unowned: item.unowned, observedAfterClose: item.observedAfterClose },
13202
+ transitions: await this.transitions(serviceId, incidentIdentity)
13203
+ };
13204
+ }
13205
+ async transition(serviceId, value, actor) {
13206
+ const request = normalizeRequest(value);
13207
+ let actorId;
13208
+ try {
13209
+ actorId = boundedString(actor, "x-sdt-maintainer", MAX_IDENTITY_BYTES);
13210
+ } catch (caught) {
13211
+ if (caught instanceof IncidentLifecycleError && caught.code === "incident_invalid_request") {
13212
+ throw new IncidentLifecycleError("incident_invalid_actor", 400, caught.message);
13213
+ }
13214
+ throw caught;
13215
+ }
13216
+ if (actorId === null) throw new IncidentLifecycleError("incident_invalid_actor", 400, "x-sdt-maintainer must be non-empty");
13217
+ const finding2 = await this.finding(serviceId, request.incidentIdentity);
13218
+ const digest = await digestFor(request, actorId);
13219
+ const existing = await this.database.prepare(
13220
+ `SELECT * FROM serialized_dcb_incident_transitions
13221
+ WHERE service_id = ? AND incident_identity = ? AND transition_key = ?`
13222
+ ).bind(serviceId, request.incidentIdentity, request.transitionKey).first();
13223
+ if (existing !== null && existing !== void 0) {
13224
+ if (existing.request_digest !== digest) {
13225
+ throw new IncidentLifecycleError("incident_idempotency_conflict", 409, "The transition key was already used with different content");
13226
+ }
13227
+ return { projection: transitionFromRow(existing).after, transition: transitionFromRow(existing), idempotent: true };
13228
+ }
13229
+ const priorRow = await this.lifecycle(serviceId, request.incidentIdentity);
13230
+ const before = projectionFromRow(priorRow, finding2);
13231
+ if (before.version !== request.expectedVersion) {
13232
+ throw new IncidentLifecycleError("incident_version_conflict", 409, "The incident version is stale");
13233
+ }
13234
+ const at = this.now();
13235
+ const after = this.nextProjection(before, request, at);
13236
+ const newVersion = before.version + 1;
13237
+ const occurredAt = at;
13238
+ const seed = this.database.prepare(
13239
+ `INSERT INTO serialized_dcb_incident_lifecycles
13240
+ (service_id, incident_identity, lifecycle_state, version, last_transition_key, updated_at)
13241
+ VALUES (?, ?, 'OPEN', 0, '', ?)
13242
+ ON CONFLICT (service_id, incident_identity) DO NOTHING`
13243
+ ).bind(serviceId, request.incidentIdentity, occurredAt);
13244
+ const update = this.database.prepare(
13245
+ `UPDATE serialized_dcb_incident_lifecycles
13246
+ SET lifecycle_state = ?, owner_id = ?, deadline_at = ?,
13247
+ correction_kind = ?, correction_reference = ?, correction_digest = ?,
13248
+ close_resolution = ?, close_reason = ?, version = ?,
13249
+ last_transition_key = ?, updated_at = ?
13250
+ WHERE service_id = ? AND incident_identity = ? AND version = ?
13251
+ AND last_transition_key <> ?`
13252
+ ).bind(
13253
+ after.lifecycleState,
13254
+ after.ownerId,
13255
+ after.deadlineAt,
13256
+ after.correction?.kind ?? null,
13257
+ after.correction?.reference ?? null,
13258
+ after.correction?.digest ?? null,
13259
+ after.closeResolution,
13260
+ after.closeReason,
13261
+ newVersion,
13262
+ request.transitionKey,
13263
+ occurredAt,
13264
+ serviceId,
13265
+ request.incidentIdentity,
13266
+ before.version,
13267
+ request.transitionKey
13268
+ );
13269
+ const audit = this.database.prepare(
13270
+ `INSERT INTO serialized_dcb_incident_transitions
13271
+ (service_id, incident_identity, transition_key, request_digest, action,
13272
+ from_state, to_state, from_version, to_version, actor_id,
13273
+ before_owner_id, before_deadline_at, before_correction_kind,
13274
+ before_correction_reference, before_correction_digest, before_close_resolution,
13275
+ before_close_reason, after_owner_id, after_deadline_at, after_correction_kind,
13276
+ after_correction_reference, after_correction_digest, after_close_resolution,
13277
+ after_close_reason, reason, occurred_at)
13278
+ SELECT ?, ?, ?, ?, ?, ?, lifecycle_state, ?, version, ?,
13279
+ ?, ?, ?, ?, ?, ?, ?, owner_id, deadline_at, correction_kind,
13280
+ correction_reference, correction_digest, close_resolution, close_reason, ?, ?
13281
+ FROM serialized_dcb_incident_lifecycles
13282
+ WHERE service_id = ? AND incident_identity = ? AND version = ? AND last_transition_key = ?`
13283
+ ).bind(
13284
+ serviceId,
13285
+ request.incidentIdentity,
13286
+ request.transitionKey,
13287
+ digest,
13288
+ request.action,
13289
+ before.lifecycleState,
13290
+ before.version,
13291
+ actorId,
13292
+ before.ownerId,
13293
+ before.deadlineAt,
13294
+ before.correction?.kind ?? null,
13295
+ before.correction?.reference ?? null,
13296
+ before.correction?.digest ?? null,
13297
+ before.closeResolution,
13298
+ before.closeReason,
13299
+ request.reason,
13300
+ occurredAt,
13301
+ serviceId,
13302
+ request.incidentIdentity,
13303
+ newVersion,
13304
+ request.transitionKey
13305
+ );
13306
+ await this.database.batch([seed, update, audit]);
13307
+ const inserted = await this.database.prepare(
13308
+ `SELECT * FROM serialized_dcb_incident_transitions
13309
+ WHERE service_id = ? AND incident_identity = ? AND transition_key = ?`
13310
+ ).bind(serviceId, request.incidentIdentity, request.transitionKey).first();
13311
+ if (inserted === null || inserted === void 0) {
13312
+ const competing = await this.database.prepare(
13313
+ `SELECT * FROM serialized_dcb_incident_transitions
13314
+ WHERE service_id = ? AND incident_identity = ? AND transition_key = ?`
13315
+ ).bind(serviceId, request.incidentIdentity, request.transitionKey).first();
13316
+ if (competing !== null && competing !== void 0 && competing.request_digest !== digest) {
13317
+ throw new IncidentLifecycleError("incident_idempotency_conflict", 409, "The transition key was already used with different content");
13318
+ }
13319
+ if (competing !== null && competing !== void 0) {
13320
+ const transition2 = transitionFromRow(competing);
13321
+ return { projection: transition2.after, transition: transition2, idempotent: true };
13322
+ }
13323
+ throw new IncidentLifecycleError("incident_version_conflict", 409, "The incident version is stale");
13324
+ }
13325
+ const transition = transitionFromRow(inserted);
13326
+ return { projection: transition.after, transition, idempotent: false };
13327
+ }
13328
+ nextProjection(before, request, at) {
13329
+ const laterDeadline = (deadlineAt2) => {
13330
+ if (deadlineAt2 <= at) throw new IncidentLifecycleError("incident_invalid_deadline", 400, "deadlineAt must be in the future");
13331
+ return deadlineAt2;
13332
+ };
13333
+ let lifecycleState;
13334
+ let ownerId = before.ownerId;
13335
+ let deadlineAt = before.deadlineAt;
13336
+ let correction = before.correction;
13337
+ let closeResolution = before.closeResolution;
13338
+ let closeReason = before.closeReason;
13339
+ if (request.action === "ACKNOWLEDGE") {
13340
+ if (before.lifecycleState !== "OPEN" && before.lifecycleState !== "REOPENED") {
13341
+ throw new IncidentLifecycleError("incident_invalid_transition", 409, "The incident cannot be acknowledged from its current state");
13342
+ }
13343
+ lifecycleState = "ACKNOWLEDGED";
13344
+ ownerId = request.ownerId;
13345
+ deadlineAt = laterDeadline(request.deadlineAt);
13346
+ correction = null;
13347
+ closeResolution = null;
13348
+ closeReason = null;
13349
+ } else if (request.action === "UPDATE_ASSIGNMENT") {
13350
+ if (before.lifecycleState !== "ACKNOWLEDGED" && before.lifecycleState !== "CORRECTION_RECORDED") {
13351
+ throw new IncidentLifecycleError("incident_invalid_transition", 409, "The incident cannot be reassigned from its current state");
13352
+ }
13353
+ lifecycleState = before.lifecycleState;
13354
+ ownerId = request.ownerId;
13355
+ deadlineAt = laterDeadline(request.deadlineAt);
13356
+ } else if (request.action === "RECORD_CORRECTION") {
13357
+ if (before.lifecycleState !== "ACKNOWLEDGED") {
13358
+ throw new IncidentLifecycleError("incident_invalid_transition", 409, "The incident must be acknowledged before correction evidence");
13359
+ }
13360
+ lifecycleState = "CORRECTION_RECORDED";
13361
+ correction = request.correction;
13362
+ } else if (request.action === "CLOSE") {
13363
+ if (before.ownerId === null || before.deadlineAt === null) {
13364
+ throw new IncidentLifecycleError("incident_invalid_transition", 409, "The incident must have an owner and deadline before closure");
13365
+ }
13366
+ if (request.resolution.kind === "CORRECTED") {
13367
+ if (before.lifecycleState !== "CORRECTION_RECORDED") {
13368
+ throw new IncidentLifecycleError("incident_invalid_transition", 409, "A corrected closure requires recorded correction evidence");
13369
+ }
13370
+ lifecycleState = "CLOSED";
13371
+ closeResolution = "CORRECTED";
13372
+ closeReason = request.reason;
13373
+ } else {
13374
+ if (before.lifecycleState !== "ACKNOWLEDGED") {
13375
+ throw new IncidentLifecycleError("incident_invalid_transition", 409, "Accepted-as-is closure requires an acknowledged incident");
13376
+ }
13377
+ lifecycleState = "CLOSED";
13378
+ closeResolution = "ACCEPTED_AS_IS";
13379
+ closeReason = request.resolution.explanation;
13380
+ correction = null;
13381
+ }
13382
+ } else {
13383
+ if (before.lifecycleState !== "CLOSED") {
13384
+ throw new IncidentLifecycleError("incident_invalid_transition", 409, "Only a closed incident can be reopened");
13385
+ }
13386
+ lifecycleState = "REOPENED";
13387
+ ownerId = request.ownerId;
13388
+ deadlineAt = laterDeadline(request.deadlineAt);
13389
+ correction = null;
13390
+ closeResolution = null;
13391
+ closeReason = null;
13392
+ }
13393
+ return {
13394
+ ...before,
13395
+ lifecycleState,
13396
+ ownerId,
13397
+ deadlineAt,
13398
+ correction,
13399
+ closeResolution,
13400
+ closeReason,
13401
+ version: before.version + 1,
13402
+ lastTransitionKey: request.transitionKey,
13403
+ updatedAt: at
13404
+ };
13405
+ }
13406
+ };
13407
+
13408
+ // src/http/IncidentMaintenance.ts
13409
+ function json10(body, status = 200) {
13410
+ return new Response(JSON.stringify(body), {
13411
+ status,
13412
+ headers: { "content-type": "application/json; charset=utf-8" }
13413
+ });
13414
+ }
13415
+ function error8(status, code, message) {
13416
+ return json10({ error: message, code }, status);
13417
+ }
13418
+ function boolFilter(value, name) {
13419
+ if (value === null) return void 0;
13420
+ if (value === "true") return true;
13421
+ if (value === "false") return false;
13422
+ throw new IncidentLifecycleError("incident_invalid_filter", 400, `${name} must be true or false`);
13423
+ }
13424
+ function parseFilters(url) {
13425
+ const allowed = /* @__PURE__ */ new Set(["state", "owner", "unowned", "overdue", "observedAfterClose"]);
13426
+ let unknown = false;
13427
+ url.searchParams.forEach((_value, key) => {
13428
+ if (!allowed.has(key)) unknown = true;
13429
+ });
13430
+ if (unknown) throw new IncidentLifecycleError("incident_invalid_filter", 400, "Unknown incident list filter");
13431
+ const state = url.searchParams.get("state");
13432
+ if (state !== null && !["OPEN", "ACKNOWLEDGED", "CORRECTION_RECORDED", "CLOSED", "REOPENED"].includes(state)) {
13433
+ throw new IncidentLifecycleError("incident_invalid_filter", 400, "state is invalid");
13434
+ }
13435
+ const owner = url.searchParams.get("owner");
13436
+ if (owner !== null) {
13437
+ const normalizedOwner = owner.trim();
13438
+ if (normalizedOwner.length === 0) {
13439
+ throw new IncidentLifecycleError("incident_invalid_filter", 400, "owner must be non-empty");
13440
+ }
13441
+ if (new TextEncoder().encode(normalizedOwner).byteLength > 256) {
13442
+ throw new IncidentLifecycleError("incident_invalid_filter", 400, "owner is too long");
13443
+ }
13444
+ }
13445
+ const unowned = boolFilter(url.searchParams.get("unowned"), "unowned");
13446
+ const overdue = boolFilter(url.searchParams.get("overdue"), "overdue");
13447
+ const observedAfterClose = boolFilter(url.searchParams.get("observedAfterClose"), "observedAfterClose");
13448
+ return {
13449
+ ...state === null ? {} : { state },
13450
+ ...owner === null ? {} : { owner: owner.trim() },
13451
+ ...unowned === void 0 ? {} : { unowned },
13452
+ ...overdue === void 0 ? {} : { overdue },
13453
+ ...observedAfterClose === void 0 ? {} : { observedAfterClose }
13454
+ };
13455
+ }
13456
+ function incidentIdentityFromPath(pathname) {
13457
+ const encoded = pathname.slice("/maintenance/incidents/".length);
13458
+ if (encoded.length === 0 || encoded.includes("/")) {
13459
+ throw new IncidentLifecycleError("incident_route_not_found", 404, "Incident route not found");
13460
+ }
13461
+ try {
13462
+ return normalizeIncidentIdentity(decodeURIComponent(encoded));
13463
+ } catch {
13464
+ throw new IncidentLifecycleError("incident_invalid_request", 400, "Incident identity must be URI encoded");
13465
+ }
13466
+ }
13467
+ function isIncidentMaintenancePath(pathname) {
13468
+ return pathname === "/maintenance/incidents" || pathname.startsWith("/maintenance/incidents/");
13469
+ }
13470
+ async function handleIncidentMaintenance(request, env, serviceIdentityProvider) {
13471
+ const decision = await checkConfiguredBearer(request, env.INCIDENT_MAINTAINER_TOKEN, {
13472
+ tokenNormalization: "trim"
13473
+ });
13474
+ if (decision === "unconfigured") {
13475
+ return error8(503, "incident_maintenance_unavailable", "Incident maintenance is not configured");
13476
+ }
13477
+ if (decision === "missing" || decision === "invalid") {
13478
+ return error8(401, "incident_auth_required", "A valid incident maintenance bearer token is required");
13479
+ }
13480
+ let serviceId;
13481
+ try {
13482
+ serviceId = requestServiceIdentity(request, serviceIdentityProvider, {
13483
+ allowG11Verification: env.G11_VERIFICATION_ENABLED === "true"
13484
+ });
13485
+ } catch {
13486
+ return error8(503, "scope.identity_missing", "A deployment service identity is required for this scope");
13487
+ }
13488
+ if (env.D1 === void 0) {
13489
+ return error8(503, "incident_maintenance_unavailable", "Incident maintenance storage is unavailable");
13490
+ }
13491
+ const lifecycle = new IncidentLifecycle(env.D1);
13492
+ const url = new URL(request.url);
13493
+ try {
13494
+ if (request.method === "GET" && url.pathname === "/maintenance/incidents") {
13495
+ return json10(await lifecycle.list(serviceId, parseFilters(url)));
13496
+ }
13497
+ if (request.method === "GET" && url.pathname.startsWith("/maintenance/incidents/")) {
13498
+ return json10(await lifecycle.detail(serviceId, incidentIdentityFromPath(url.pathname)));
13499
+ }
13500
+ if (request.method === "POST" && url.pathname === "/maintenance/incidents/transitions") {
13501
+ let body;
13502
+ try {
13503
+ body = await request.json();
13504
+ } catch {
13505
+ return error8(400, "incident_invalid_request", "Transition request must be JSON");
13506
+ }
13507
+ const requestValue = IncidentLifecycle.parseTransition(body);
13508
+ const actor = request.headers.get("x-sdt-maintainer");
13509
+ if (actor === null) {
13510
+ return error8(400, "incident_invalid_actor", "x-sdt-maintainer is required");
13511
+ }
13512
+ return json10(await lifecycle.transition(serviceId, requestValue, actor));
13513
+ }
13514
+ return error8(404, "incident_route_not_found", "Incident maintenance route not found");
13515
+ } catch (caught) {
13516
+ if (caught instanceof IncidentLifecycleError) return error8(caught.status, caught.code, caught.message);
13517
+ return error8(500, "incident_maintenance_failed", "Incident maintenance could not complete the request");
13518
+ }
13519
+ }
13520
+
12569
13521
  // src/downstream/Doorbell.ts
12570
13522
  var MAX_SERVICE_BINDING_INVOCATIONS_PER_REQUEST = 32;
12571
- function classifyDirectDoorbellFailure(error10) {
12572
- const structured = typeof error10 === "object" && error10 !== null ? error10 : {};
13523
+ function classifyDirectDoorbellFailure(error11) {
13524
+ const structured = typeof error11 === "object" && error11 !== null ? error11 : {};
12573
13525
  const status = typeof structured.status === "number" ? structured.status : typeof structured.statusCode === "number" ? structured.statusCode : void 0;
12574
13526
  if (status !== void 0 && (status < 200 || status >= 300)) return "non-2xx";
12575
- const value = String(error10).toLowerCase();
13527
+ const value = String(error11).toLowerCase();
12576
13528
  const code = `${String(structured.code ?? "")} ${String(structured.name ?? "")}`.toLowerCase();
12577
13529
  if (value.includes("timeout") || value.includes("deadline") || code.includes("timeout")) return "timeout";
12578
13530
  if (value.includes("cancel") || value.includes("abort") || code.includes("abort")) return "cancel";
@@ -12815,6 +13767,7 @@ var TAG_SQL_SCHEMA_DDL = `
12815
13767
  committed_event_count INTEGER NOT NULL,
12816
13768
  head_suid TEXT NOT NULL,
12817
13769
  reservation_confirmed INTEGER NOT NULL CHECK (reservation_confirmed IN (0, 1)),
13770
+ written_version INTEGER NOT NULL,
12818
13771
  PRIMARY KEY (attempt_id, epoch)
12819
13772
  );
12820
13773
 
@@ -12865,6 +13818,12 @@ var TAG_READ_AFTER_THROUGH_SQL = `
12865
13818
  `;
12866
13819
  function initializeTagSqlSchema(sql) {
12867
13820
  sql.exec(TAG_SQL_SCHEMA_DDL);
13821
+ ensureTagCommitReceiptWrittenVersion(sql);
13822
+ }
13823
+ function ensureTagCommitReceiptWrittenVersion(sql) {
13824
+ const columns = sql.exec("SELECT name FROM pragma_table_info('tag_commit_receipt')").toArray();
13825
+ if (columns.some((column) => column.name === "written_version")) return;
13826
+ sql.exec("ALTER TABLE tag_commit_receipt ADD COLUMN written_version INTEGER");
12868
13827
  }
12869
13828
  function hasTagSqlStorage(storage) {
12870
13829
  return typeof storage.sql?.exec === "function";
@@ -13042,8 +14001,8 @@ function asNumber2(value, name) {
13042
14001
  if (!Number.isSafeInteger(number)) throw new Error(`D1 ${name} was not a safe integer`);
13043
14002
  return number;
13044
14003
  }
13045
- function errorText3(error10) {
13046
- return (error10 instanceof Error ? error10.message : String(error10)).slice(0, 256);
14004
+ function errorText3(error11) {
14005
+ return (error11 instanceof Error ? error11.message : String(error11)).slice(0, 256);
13047
14006
  }
13048
14007
  function sortedUnique2(values) {
13049
14008
  return [...new Set(values)].sort((left, right) => binaryCompare(left, right));
@@ -13230,8 +14189,8 @@ var D1EventStore = class {
13230
14189
  let failure3;
13231
14190
  try {
13232
14191
  result2 = await this.recordDeliveryCore(message, arrivedAt, deliverySource, mutationAttemptId);
13233
- } catch (error10) {
13234
- failure3 = { error: error10 };
14192
+ } catch (error11) {
14193
+ failure3 = { error: error11 };
13235
14194
  }
13236
14195
  const retryReason = failure3 === void 0 ? null : errorText3(failure3.error);
13237
14196
  const suppressDiagnostic = failure3?.error instanceof D1IdentityConflictError && failure3.error.beforeMutation;
@@ -13802,8 +14761,8 @@ var D1EventStore = class {
13802
14761
  if (replay === null || replay === void 0) return finish({ kind: "miss" });
13803
14762
  const event = await this.eventById(serviceId, asString3(replay.event_id, "replay.event_id"));
13804
14763
  return event === void 0 ? finish({ kind: "unknown", reason: "replay-event-row-disappeared" }) : finish({ kind: "replay", event });
13805
- } catch (error10) {
13806
- return finish({ kind: "unknown", reason: errorText3(error10) });
14764
+ } catch (error11) {
14765
+ return finish({ kind: "unknown", reason: errorText3(error11) });
13807
14766
  }
13808
14767
  }
13809
14768
  /**
@@ -14278,8 +15237,8 @@ var D1EventStore = class {
14278
15237
  try {
14279
15238
  try {
14280
15239
  await this.options.beforeG69AdmissionAttempt?.();
14281
- } catch (error10) {
14282
- await appendFailedDiagnostic(errorText3(error10));
15240
+ } catch (error11) {
15241
+ await appendFailedDiagnostic(errorText3(error11));
14283
15242
  return;
14284
15243
  }
14285
15244
  const afterObservedAt = Date.now();
@@ -14353,14 +15312,14 @@ var TagIdentityConflict = class extends Error {
14353
15312
  this.name = "TagIdentityConflict";
14354
15313
  }
14355
15314
  };
14356
- function json10(body, status = 200) {
15315
+ function json11(body, status = 200) {
14357
15316
  return new Response(JSON.stringify(body), {
14358
15317
  status,
14359
15318
  headers: { "content-type": "application/json; charset=utf-8" }
14360
15319
  });
14361
15320
  }
14362
- function error8(status, code, message, retryable = false) {
14363
- return json10(retryable ? { error: message, code, retryable: true } : { error: message, code }, status);
15321
+ function error9(status, code, message, retryable = false) {
15322
+ return json11(retryable ? { error: message, code, retryable: true } : { error: message, code }, status);
14364
15323
  }
14365
15324
  function withGlobalAdmission2(response3, status) {
14366
15325
  response3.headers.set(G65_GLOBAL_ADMISSION_HEADER, status);
@@ -14379,7 +15338,7 @@ function repairRejected(reason, status = 409) {
14379
15338
  body: { code: "repair_operation_rejected", error: reason, reason }
14380
15339
  };
14381
15340
  }
14382
- function isObject11(value) {
15341
+ function isObject12(value) {
14383
15342
  return typeof value === "object" && value !== null && !Array.isArray(value);
14384
15343
  }
14385
15344
  function isNonEmptyString11(value) {
@@ -14424,8 +15383,8 @@ function sqlJson(value, column) {
14424
15383
  throw new Error(`Tag SQL column ${column} is not valid JSON`);
14425
15384
  }
14426
15385
  }
14427
- function copyArrayBuffer(bytes) {
14428
- return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
15386
+ function copyArrayBuffer(bytes2) {
15387
+ return bytes2.buffer.slice(bytes2.byteOffset, bytes2.byteOffset + bytes2.byteLength);
14429
15388
  }
14430
15389
  function arrayBufferBase642(value) {
14431
15390
  let binary = "";
@@ -14436,7 +15395,7 @@ function nowIso() {
14436
15395
  return (/* @__PURE__ */ new Date()).toISOString();
14437
15396
  }
14438
15397
  function epochFrom(value) {
14439
- if (!isObject11(value) || !isNonEmptyString11(value.attemptId) || !isEpoch(value.epoch)) {
15398
+ if (!isObject12(value) || !isNonEmptyString11(value.attemptId) || !isEpoch(value.epoch)) {
14440
15399
  return { error: "attemptId and a non-negative safe-integer epoch are required" };
14441
15400
  }
14442
15401
  return { value: { attemptId: value.attemptId, epoch: value.epoch } };
@@ -14456,7 +15415,7 @@ function consistencyTagsFrom(value) {
14456
15415
  }
14457
15416
  const entries = [];
14458
15417
  for (const rawEntry of value) {
14459
- if (!isObject11(rawEntry) || !isNonEmptyString11(rawEntry.tag)) {
15418
+ if (!isObject12(rawEntry) || !isNonEmptyString11(rawEntry.tag)) {
14460
15419
  return { error: "each consistency tag needs a non-empty tag" };
14461
15420
  }
14462
15421
  if (!("lastSortableUniqueId" in rawEntry)) {
@@ -14484,7 +15443,7 @@ function consistencyTagsFrom(value) {
14484
15443
  }
14485
15444
  function acquireFrom(value, tag) {
14486
15445
  const epoch2 = epochFrom(value);
14487
- if (epoch2.value === void 0 || !isObject11(value)) {
15446
+ if (epoch2.value === void 0 || !isObject12(value)) {
14488
15447
  return { error: epoch2.error ?? "Invalid acquire request" };
14489
15448
  }
14490
15449
  const eventTags = stringArrayFrom(value.eventTags, "eventTags");
@@ -14507,7 +15466,7 @@ function acquireFrom(value, tag) {
14507
15466
  }
14508
15467
  function reservationFrom(value) {
14509
15468
  const epoch2 = epochFrom(value);
14510
- if (epoch2.value === void 0 || !isObject11(value)) {
15469
+ if (epoch2.value === void 0 || !isObject12(value)) {
14511
15470
  return { error: epoch2.error ?? "Invalid reservation request" };
14512
15471
  }
14513
15472
  if (value.reservationToken !== void 0 && !isNonEmptyString11(value.reservationToken)) {
@@ -14526,7 +15485,7 @@ function reservationFrom(value) {
14526
15485
  }
14527
15486
  function appendFrom(value, tag) {
14528
15487
  const reservation = reservationFrom(value);
14529
- if (reservation.value === void 0 || !isObject11(value) || !Array.isArray(value.candidates)) {
15488
+ if (reservation.value === void 0 || !isObject12(value) || !Array.isArray(value.candidates)) {
14530
15489
  return { error: reservation.error ?? "candidates must be an array" };
14531
15490
  }
14532
15491
  if (value.candidates.length === 0) {
@@ -14534,7 +15493,7 @@ function appendFrom(value, tag) {
14534
15493
  }
14535
15494
  const candidates = [];
14536
15495
  for (const rawCandidate of value.candidates) {
14537
- if (!isObject11(rawCandidate) || !isNonEmptyString11(rawCandidate.eventId) || !isNonEmptyString11(rawCandidate.suid) || typeof rawCandidate.payload !== "string") {
15496
+ if (!isObject12(rawCandidate) || !isNonEmptyString11(rawCandidate.eventId) || !isNonEmptyString11(rawCandidate.suid) || typeof rawCandidate.payload !== "string") {
14538
15497
  return { error: "each candidate needs eventId, suid, and payload" };
14539
15498
  }
14540
15499
  const eventTags = stringArrayFrom(rawCandidate.eventTags, "candidate eventTags");
@@ -14548,8 +15507,8 @@ function appendFrom(value, tag) {
14548
15507
  assertSortableUniqueId(rawCandidate.suid);
14549
15508
  if (!isUuidV7(rawCandidate.eventId)) return { error: "candidate eventId must be a UUID v7" };
14550
15509
  JSON.parse(rawCandidate.payload);
14551
- } catch (error10) {
14552
- return { error: error10 instanceof Error ? error10.message : "candidate identity or payload is invalid" };
15510
+ } catch (error11) {
15511
+ return { error: error11 instanceof Error ? error11.message : "candidate identity or payload is invalid" };
14553
15512
  }
14554
15513
  if (rawCandidate.provenance !== "g32") return { error: "candidate provenance must be g32" };
14555
15514
  if (!isNonEmptyString11(rawCandidate.allocatorLineageId)) return { error: "candidate allocatorLineageId is required" };
@@ -14583,10 +15542,10 @@ function appendFrom(value, tag) {
14583
15542
  return { value: { ...reservation.value, candidates: ordered, faultInjection } };
14584
15543
  }
14585
15544
  function bootstrapAppendFrom(value, tag) {
14586
- if (!isObject11(value) || !isNonEmptyString11(value.importId) || !isEpoch(value.leaseEpoch) || !isNonEmptyString11(value.manifestDigest) || !isNonEmptyString11(value.targetServiceId) || !Array.isArray(value.candidates) || value.candidates.length === 0) return { error: "bootstrap identity and candidates are required" };
15545
+ if (!isObject12(value) || !isNonEmptyString11(value.importId) || !isEpoch(value.leaseEpoch) || !isNonEmptyString11(value.manifestDigest) || !isNonEmptyString11(value.targetServiceId) || !Array.isArray(value.candidates) || value.candidates.length === 0) return { error: "bootstrap identity and candidates are required" };
14587
15546
  const candidates = [];
14588
15547
  for (const candidate of value.candidates) {
14589
- if (!isObject11(candidate) || !isNonEmptyString11(candidate.eventId) || !isNonEmptyString11(candidate.suid) || typeof candidate.payload !== "string") return { error: "bootstrap candidate is invalid" };
15548
+ if (!isObject12(candidate) || !isNonEmptyString11(candidate.eventId) || !isNonEmptyString11(candidate.suid) || typeof candidate.payload !== "string") return { error: "bootstrap candidate is invalid" };
14590
15549
  const tags = stringArrayFrom(candidate.eventTags, "bootstrap candidate eventTags");
14591
15550
  if (tags.value === void 0 || !tags.value.includes(tag) || !isNonEmptyString11(candidate.allocatorLineageId)) return { error: "bootstrap candidate must include this tag and lineage" };
14592
15551
  let eventType;
@@ -14596,8 +15555,8 @@ function bootstrapAppendFrom(value, tag) {
14596
15555
  assertSortableUniqueId(candidate.suid);
14597
15556
  if (!isRfc4122Uuid(candidate.eventId)) return { error: "bootstrap candidate eventId must be an RFC 4122 UUID" };
14598
15557
  JSON.parse(candidate.payload);
14599
- } catch (error10) {
14600
- return { error: error10 instanceof Error ? error10.message : "bootstrap candidate identity or payload is invalid" };
15558
+ } catch (error11) {
15559
+ return { error: error11 instanceof Error ? error11.message : "bootstrap candidate identity or payload is invalid" };
14601
15560
  }
14602
15561
  if (candidate.provenance !== "g32") return { error: "bootstrap candidate provenance must be g32" };
14603
15562
  if (!isNonEmptyString11(candidate.timestamp) || !CANONICAL_UTC_TIMESTAMP_PATTERN.test(candidate.timestamp)) return { error: "bootstrap candidate timestamp must be canonical UTC" };
@@ -14609,13 +15568,13 @@ function bootstrapAppendFrom(value, tag) {
14609
15568
  }
14610
15569
  function fenceFrom(value) {
14611
15570
  const epoch2 = epochFrom(value);
14612
- if (epoch2.value === void 0 || !isObject11(value) || !isNonEmptyString11(value.reason)) {
15571
+ if (epoch2.value === void 0 || !isObject12(value) || !isNonEmptyString11(value.reason)) {
14613
15572
  return { error: epoch2.error ?? "reason is required" };
14614
15573
  }
14615
15574
  return { value: { ...epoch2.value, reason: value.reason } };
14616
15575
  }
14617
15576
  function repairScopeItemFrom(value, tag) {
14618
- if (!isObject11(value) || !isNonEmptyString11(value.attemptId) || !isNonEmptyString11(value.eventId) || !isNonEmptyString11(value.suid) || typeof value.payload !== "string" || !isNonEmptyString11(value.allocatorLineageId) || !isNonEmptyString11(value.eventType) || value.provenance !== "g32" || !isNonEmptyString11(value.timestamp)) {
15577
+ if (!isObject12(value) || !isNonEmptyString11(value.attemptId) || !isNonEmptyString11(value.eventId) || !isNonEmptyString11(value.suid) || typeof value.payload !== "string" || !isNonEmptyString11(value.allocatorLineageId) || !isNonEmptyString11(value.eventType) || value.provenance !== "g32" || !isNonEmptyString11(value.timestamp)) {
14619
15578
  return { error: "each repair scope item needs attemptId, eventId, suid, and payload" };
14620
15579
  }
14621
15580
  const eventTags = stringArrayFrom(value.eventTags, "repair eventTags");
@@ -14663,13 +15622,13 @@ function repairScopeFrom(value, tag) {
14663
15622
  return { value: sortRepairScope(scope) };
14664
15623
  }
14665
15624
  function repairLeaseFrom(value) {
14666
- if (!isObject11(value) || !isNonEmptyString11(value.owner) || !isEpoch(value.epoch)) {
15625
+ if (!isObject12(value) || !isNonEmptyString11(value.owner) || !isEpoch(value.epoch)) {
14667
15626
  return { error: "repair owner and non-negative safe-integer epoch are required" };
14668
15627
  }
14669
15628
  return { value: { owner: value.owner, epoch: value.epoch } };
14670
15629
  }
14671
15630
  function repairAcquireFrom(value, tag) {
14672
- if (!isObject11(value) || !isNonEmptyString11(value.owner)) {
15631
+ if (!isObject12(value) || !isNonEmptyString11(value.owner)) {
14673
15632
  return { error: "repair owner is required" };
14674
15633
  }
14675
15634
  const leaseMs = value.leaseMs ?? DEFAULT_REPAIR_LEASE_MS;
@@ -14681,7 +15640,7 @@ function repairAcquireFrom(value, tag) {
14681
15640
  }
14682
15641
  function repairScopeUnionFrom(value, tag) {
14683
15642
  const lease = repairLeaseFrom(value);
14684
- if (lease.value === void 0 || !isObject11(value)) {
15643
+ if (lease.value === void 0 || !isObject12(value)) {
14685
15644
  return { error: lease.error };
14686
15645
  }
14687
15646
  const scope = repairScopeFrom(value.scope, tag);
@@ -14689,7 +15648,7 @@ function repairScopeUnionFrom(value, tag) {
14689
15648
  }
14690
15649
  function repairApplyFrom(value, tag) {
14691
15650
  const lease = repairLeaseFrom(value);
14692
- if (lease.value === void 0 || !isObject11(value)) {
15651
+ if (lease.value === void 0 || !isObject12(value)) {
14693
15652
  return { error: lease.error };
14694
15653
  }
14695
15654
  const item = repairScopeItemFrom(value.item, tag);
@@ -14697,20 +15656,20 @@ function repairApplyFrom(value, tag) {
14697
15656
  }
14698
15657
  function repairAuditFrom(value, tag) {
14699
15658
  const apply = repairApplyFrom(value, tag);
14700
- if (apply.value === void 0 || !isObject11(value) || !isNonEmptyString11(value.actor)) {
15659
+ if (apply.value === void 0 || !isObject12(value) || !isNonEmptyString11(value.actor)) {
14701
15660
  return { error: apply.error ?? "audit actor is required" };
14702
15661
  }
14703
15662
  return { value: { ...apply.value, actor: value.actor } };
14704
15663
  }
14705
15664
  function repairClearFrom(value) {
14706
15665
  const lease = repairLeaseFrom(value);
14707
- if (lease.value === void 0 || !isObject11(value) || !isNonEmptyString11(value.attemptId) || !isNonNegativeInteger4(value.scopeVersion)) {
15666
+ if (lease.value === void 0 || !isObject12(value) || !isNonEmptyString11(value.attemptId) || !isNonNegativeInteger4(value.scopeVersion)) {
14708
15667
  return { error: lease.error ?? "attemptId and scopeVersion are required" };
14709
15668
  }
14710
15669
  return { value: { ...lease.value, attemptId: value.attemptId, scopeVersion: value.scopeVersion } };
14711
15670
  }
14712
15671
  function outboxPendingFrom(value) {
14713
- if (!isObject11(value)) {
15672
+ if (!isObject12(value)) {
14714
15673
  return { error: "outbox pending request must be an object" };
14715
15674
  }
14716
15675
  const nowMs = value.nowMs ?? Date.now();
@@ -14728,22 +15687,22 @@ function outboxPendingFrom(value) {
14728
15687
  return { value: { nowMs, limit, force } };
14729
15688
  }
14730
15689
  function outboxMarkFrom(value) {
14731
- if (!isObject11(value) || !Array.isArray(value.deliveries) || value.deliveries.length === 0) {
15690
+ if (!isObject12(value) || !Array.isArray(value.deliveries) || value.deliveries.length === 0) {
14732
15691
  return { error: "deliveries must be a non-empty array" };
14733
15692
  }
14734
15693
  const deliveries = [];
14735
15694
  for (const raw of value.deliveries) {
14736
- if (!isObject11(raw) || !isNonEmptyString11(raw.attemptId) || !isNonEmptyString11(raw.eventId) || !isNonEmptyString11(raw.suid) || typeof raw.payload !== "string" || !isSafeInteger(raw.enqueuedAt)) {
15695
+ if (!isObject12(raw) || !isNonEmptyString11(raw.attemptId) || !isNonEmptyString11(raw.eventId) || !isNonEmptyString11(raw.suid) || typeof raw.payload !== "string" || !isSafeInteger(raw.enqueuedAt)) {
14737
15696
  return { error: "each delivery needs attemptId, eventId, suid, payload, and enqueuedAt" };
14738
15697
  }
14739
15698
  if (!isNonEmptyString11(raw.eventType) || raw.provenance !== "g32" || !isNonEmptyString11(raw.timestamp) || !isNonEmptyString11(raw.allocatorLineageId)) return { error: "delivery must carry the complete G32 identity" };
14740
- if (!isObject11(raw.completeness)) {
15699
+ if (!isObject12(raw.completeness)) {
14741
15700
  return { error: "delivery must carry a readable global receipt/membership join proof" };
14742
15701
  }
14743
15702
  const completeness = raw.completeness;
14744
15703
  const obligationSequence = completeness.obligationSequence;
14745
15704
  const memberships = completeness.localCommittedMembership;
14746
- if (!isNonEmptyString11(completeness.eventDigest) || !isSafeInteger(obligationSequence) || obligationSequence < 1 || !Array.isArray(memberships) || memberships.length !== 1 || !isObject11(memberships[0]) || memberships[0].eventId !== raw.eventId || typeof memberships[0].tag !== "string") {
15705
+ if (!isNonEmptyString11(completeness.eventDigest) || !isSafeInteger(obligationSequence) || obligationSequence < 1 || !Array.isArray(memberships) || memberships.length !== 1 || !isObject12(memberships[0]) || memberships[0].eventId !== raw.eventId || typeof memberships[0].tag !== "string") {
14747
15706
  return { error: "delivery must carry a readable global receipt/membership join proof" };
14748
15707
  }
14749
15708
  const membership = memberships[0];
@@ -14782,7 +15741,7 @@ function outboxMarkFrom(value) {
14782
15741
  return { value: { deliveries, nowMs } };
14783
15742
  }
14784
15743
  function g44SourceScanFrom(value) {
14785
- if (!isObject11(value) || !isNonEmptyString11(value.serviceId) || !isNonEmptyString11(value.tag) || !isSafeInteger(value.upperBoundSequence) || value.upperBoundSequence < 0 || !isSafeInteger(value.afterSequence) || value.afterSequence < 0 || !isSafeInteger(value.limit) || value.limit < 1 || value.limit > 256) {
15744
+ if (!isObject12(value) || !isNonEmptyString11(value.serviceId) || !isNonEmptyString11(value.tag) || !isSafeInteger(value.upperBoundSequence) || value.upperBoundSequence < 0 || !isSafeInteger(value.afterSequence) || value.afterSequence < 0 || !isSafeInteger(value.limit) || value.limit < 1 || value.limit > 256) {
14786
15745
  return { error: "source scan needs serviceId, tag, and a bounded compound cursor" };
14787
15746
  }
14788
15747
  return {
@@ -14796,7 +15755,7 @@ function g44SourceScanFrom(value) {
14796
15755
  };
14797
15756
  }
14798
15757
  function g46TagStateSourceFrom(value) {
14799
- if (!isObject11(value) || !isNonEmptyString11(value.tag) || typeof value.cursor !== "string" || !isSafeInteger(value.limit) || value.limit < 1 || value.limit > 256 || value.through !== void 0 && typeof value.through !== "string") {
15758
+ if (!isObject12(value) || !isNonEmptyString11(value.tag) || typeof value.cursor !== "string" || !isSafeInteger(value.limit) || value.limit < 1 || value.limit > 256 || value.through !== void 0 && typeof value.through !== "string") {
14800
15759
  return { error: "G46 source read needs tag, cursor, optional through, and a bounded limit" };
14801
15760
  }
14802
15761
  try {
@@ -14970,8 +15929,8 @@ function logicalNow(record) {
14970
15929
  return record.clockNowMs ?? Date.now() + record.clockOffsetMs;
14971
15930
  }
14972
15931
  function expireReservation(record) {
14973
- const active = record.activeReservation;
14974
- if (active === null || active.expiresAt > logicalNow(record)) {
15932
+ const active2 = record.activeReservation;
15933
+ if (active2 === null || active2.expiresAt > logicalNow(record)) {
14975
15934
  return { record, expired: false };
14976
15935
  }
14977
15936
  return {
@@ -15089,7 +16048,7 @@ var TagDurableObject = class {
15089
16048
  const url = new URL(request.url);
15090
16049
  const tag = url.searchParams.get("__tag");
15091
16050
  if (!isNonEmptyString11(tag)) {
15092
- return error8(400, "tag_identity_required", "Tag identity is required");
16051
+ return error9(400, "tag_identity_required", "Tag identity is required");
15093
16052
  }
15094
16053
  const serviceId = url.searchParams.get("__serviceId");
15095
16054
  if (request.method === "GET" && url.pathname === "/head-facts") {
@@ -15097,10 +16056,10 @@ var TagDurableObject = class {
15097
16056
  observation.markFirstStorageRead();
15098
16057
  try {
15099
16058
  const facts = this.readHeadFacts(tag);
15100
- return facts === void 0 ? error8(404, "tag_not_found", "Tag has no durable state yet") : json10(facts);
16059
+ return facts === void 0 ? error9(404, "tag_not_found", "Tag has no durable state yet") : json11(facts);
15101
16060
  } catch (caught) {
15102
16061
  if (caught instanceof TagIdentityConflict) {
15103
- return error8(409, "tag_identity_conflict", "Tag Durable Object identity changed");
16062
+ return error9(409, "tag_identity_conflict", "Tag Durable Object identity changed");
15104
16063
  }
15105
16064
  throw caught;
15106
16065
  }
@@ -15111,9 +16070,9 @@ var TagDurableObject = class {
15111
16070
  observation.markFirstStorageRead();
15112
16071
  const record = this.readStoredRecord(tag);
15113
16072
  if (record === void 0) {
15114
- return error8(404, "tag_not_found", "Tag has no durable state yet");
16073
+ return error9(404, "tag_not_found", "Tag has no durable state yet");
15115
16074
  }
15116
- return record.tag === tag ? json10(requireG32TagRecord(record)) : error8(409, "tag_identity_conflict", "Tag Durable Object identity changed");
16075
+ return record.tag === tag ? json11(requireG32TagRecord(record)) : error9(409, "tag_identity_conflict", "Tag Durable Object identity changed");
15117
16076
  });
15118
16077
  }
15119
16078
  if (request.method === "GET" && url.pathname === "/repair/facts") {
@@ -15121,7 +16080,7 @@ var TagDurableObject = class {
15121
16080
  }
15122
16081
  if (request.method === "POST" && url.pathname === "/debug/alarm") {
15123
16082
  const record = await this.runAlarm();
15124
- return record === void 0 ? error8(404, "tag_not_found", "Tag has no durable state yet") : json10(record);
16083
+ return record === void 0 ? error9(404, "tag_not_found", "Tag has no durable state yet") : json11(record);
15125
16084
  }
15126
16085
  if (request.method === "POST" && url.pathname === "/acquire") {
15127
16086
  return this.traceCommitActor(request, tag, serviceId, activation, observation, (body2) => this.acquire(tag, body2, observation));
@@ -15144,37 +16103,37 @@ var TagDurableObject = class {
15144
16103
  }
15145
16104
  const body = await this.jsonBody(request);
15146
16105
  if (body === void 0) {
15147
- return error8(400, "malformed_tag_request", "Request body must be JSON");
16106
+ return error9(400, "malformed_tag_request", "Request body must be JSON");
15148
16107
  }
15149
16108
  if (request.method === "POST" && url.pathname === "/__internal/g46/tag-state-incremental") {
15150
16109
  if (request.headers.get("x-sdt-g46-source-read") !== "1") {
15151
- return error8(403, "g46_source_read_forbidden", "G46 source reads require the TagState adapter");
16110
+ return error9(403, "g46_source_read_forbidden", "G46 source reads require the TagState adapter");
15152
16111
  }
15153
16112
  const parsed = g46TagStateSourceFrom(body);
15154
16113
  if (parsed.value === void 0 || parsed.value.tag !== tag) {
15155
- return error8(400, "g46_source_read_invalid", parsed.error ?? "G46 source read is invalid");
16114
+ return error9(400, "g46_source_read_invalid", parsed.error ?? "G46 source read is invalid");
15156
16115
  }
15157
16116
  try {
15158
- return json10(await this.g43TagStateIncrementalCatchUp(parsed.value));
16117
+ return json11(await this.g43TagStateIncrementalCatchUp(parsed.value));
15159
16118
  } catch (caught) {
15160
16119
  if (caught instanceof TagIdentityConflict) {
15161
- return error8(409, "tag_identity_conflict", "Tag Durable Object identity changed");
16120
+ return error9(409, "tag_identity_conflict", "Tag Durable Object identity changed");
15162
16121
  }
15163
- return error8(503, "g46_source_read_unavailable", caught instanceof Error ? caught.message : "G46 source read is unavailable");
16122
+ return error9(503, "g46_source_read_unavailable", caught instanceof Error ? caught.message : "G46 source read is unavailable");
15164
16123
  }
15165
16124
  }
15166
16125
  if (request.method === "POST" && url.pathname === "/__internal/g44/source-obligations") {
15167
16126
  if (request.headers.get("x-sdt-g44-source-scan") !== "1") {
15168
- return error8(403, "g44_source_scan_forbidden", "G44 source scan requires the internal scanner seam");
16127
+ return error9(403, "g44_source_scan_forbidden", "G44 source scan requires the internal scanner seam");
15169
16128
  }
15170
16129
  const parsed = g44SourceScanFrom(body);
15171
16130
  if (parsed.value === void 0 || parsed.value.tag !== tag || parsed.value.serviceId !== serviceId) {
15172
- return error8(400, "g44_source_scan_invalid", parsed.error ?? "G44 source scan identity is invalid");
16131
+ return error9(400, "g44_source_scan_invalid", parsed.error ?? "G44 source scan identity is invalid");
15173
16132
  }
15174
16133
  try {
15175
- return json10(await this.g44ReadSourceObligations(parsed.value));
16134
+ return json11(await this.g44ReadSourceObligations(parsed.value));
15176
16135
  } catch (failure3) {
15177
- return error8(503, "g44_source_scan_unavailable", failure3 instanceof Error ? failure3.message : "G44 source scan is unavailable");
16136
+ return error9(503, "g44_source_scan_unavailable", failure3 instanceof Error ? failure3.message : "G44 source scan is unavailable");
15178
16137
  }
15179
16138
  }
15180
16139
  if (request.method === "POST" && url.pathname === "/bootstrap/admit") {
@@ -15221,7 +16180,7 @@ var TagDurableObject = class {
15221
16180
  }
15222
16181
  if (request.method === "POST" && url.pathname === "/__internal/g77/inspect-target") {
15223
16182
  if (!isNonEmptyString11(serviceId)) {
15224
- return error8(400, "service_identity_required", "Service identity is required");
16183
+ return error9(400, "service_identity_required", "Service identity is required");
15225
16184
  }
15226
16185
  return this.inspectG77Target(serviceId, tag, body);
15227
16186
  }
@@ -15229,28 +16188,28 @@ var TagDurableObject = class {
15229
16188
  return this.fenceAbsentG77Target(tag, body);
15230
16189
  }
15231
16190
  if (request.method === "POST" && url.pathname === "/__internal/g77/capabilities") {
15232
- return json10({ inspectTarget: true, fenceAbsentTarget: true });
16191
+ return json11({ inspectTarget: true, fenceAbsentTarget: true });
15233
16192
  }
15234
- return error8(404, "tag_route_not_found", "Tag route was not found");
16193
+ return error9(404, "tag_route_not_found", "Tag route was not found");
15235
16194
  }
15236
16195
  /** Identity-bound installed/absent inspection for G77 issuance reconciliation. */
15237
16196
  inspectG77Target(serviceId, tag, body) {
15238
- if (!isObject11(body) || !isNonEmptyString11(body.eventId) || !isNonEmptyString11(body.suid)) {
15239
- return error8(400, "invalid_inspect_target", "eventId and suid are required");
16197
+ if (!isObject12(body) || !isNonEmptyString11(body.eventId) || !isNonEmptyString11(body.suid)) {
16198
+ return error9(400, "invalid_inspect_target", "eventId and suid are required");
15240
16199
  }
15241
16200
  const pinnedWriterEpoch = isNonNegativeInteger4(body.pinnedWriterEpoch) ? body.pinnedWriterEpoch : 0;
15242
16201
  const attemptId = isNonEmptyString11(body.attemptId) ? body.attemptId : void 0;
15243
16202
  const sql = this.sqlStorage();
15244
- if (sql === void 0) return error8(503, "tag_sql_unavailable", "Tag SQL storage is unavailable");
16203
+ if (sql === void 0) return error9(503, "tag_sql_unavailable", "Tag SQL storage is unavailable");
15245
16204
  const identity2 = sql.exec("SELECT tag FROM tag_identity WHERE singleton = 1").toArray()[0];
15246
16205
  if (identity2 === void 0) {
15247
16206
  if (attemptId !== void 0) {
15248
- return json10({ inspectionStatus: "absent-never-contacted" });
16207
+ return json11({ inspectionStatus: "absent-never-contacted" });
15249
16208
  }
15250
- return error8(409, "target_not_terminal", "Target is not terminal under pinned writer authority");
16209
+ return error9(409, "target_not_terminal", "Target is not terminal under pinned writer authority");
15251
16210
  }
15252
16211
  if (sqlString(identity2.tag, "tag_identity.tag") !== tag) {
15253
- return error8(409, "tag_identity_mismatch", "Tag identity does not match the inspected tag");
16212
+ return error9(409, "tag_identity_mismatch", "Tag identity does not match the inspected tag");
15254
16213
  }
15255
16214
  const event = sql.exec(`
15256
16215
  SELECT attempt_id, event_id, suid FROM tag_event
@@ -15263,41 +16222,41 @@ var TagDurableObject = class {
15263
16222
  `, serviceId, body.eventId).toArray()[0];
15264
16223
  if (obligation !== void 0) {
15265
16224
  const obligationDigest = sqlString(obligation.event_digest, "tag_outbox_obligation.event_digest");
15266
- return json10({
16225
+ return json11({
15267
16226
  terminalStatus: "installed-and-covered",
15268
16227
  eventId: body.eventId,
15269
16228
  obligationDigest
15270
16229
  });
15271
16230
  }
15272
- return error8(409, "target_not_terminal", "Target append is in flight under pinned writer authority");
16231
+ return error9(409, "target_not_terminal", "Target append is in flight under pinned writer authority");
15273
16232
  }
15274
16233
  if (attemptId !== void 0) {
15275
16234
  const tombstone = sql.exec("SELECT epoch FROM tag_tombstone WHERE attempt_id = ?", attemptId).toArray()[0];
15276
16235
  const tombstoneEpoch = tombstone === void 0 ? void 0 : sqlNumber(tombstone.epoch, "tag_tombstone.epoch");
15277
16236
  if (tombstoneEpoch !== void 0 && pinnedWriterEpoch <= tombstoneEpoch) {
15278
- return json10({ terminalStatus: "absent-and-irrevocably-fenced", tombstoneEpoch });
16237
+ return json11({ terminalStatus: "absent-and-irrevocably-fenced", tombstoneEpoch });
15279
16238
  }
15280
16239
  const reservation = sql.exec("SELECT attempt_id, expires_at FROM tag_reservation WHERE singleton = 1").toArray()[0];
15281
16240
  if (reservation !== void 0 && sqlString(reservation.attempt_id, "tag_reservation.attempt_id") === attemptId) {
15282
- return error8(409, "target_not_terminal", "Active reservation holds writer authority");
16241
+ return error9(409, "target_not_terminal", "Active reservation holds writer authority");
15283
16242
  }
15284
16243
  const epoch2 = sql.exec("SELECT attempt_id FROM tag_epoch WHERE attempt_id = ?", attemptId).toArray()[0];
15285
16244
  if (epoch2 !== void 0) {
15286
- return json10({ inspectionStatus: "absent-but-unfenced" });
16245
+ return json11({ inspectionStatus: "absent-but-unfenced" });
15287
16246
  }
15288
- return json10({ inspectionStatus: "absent-never-contacted" });
16247
+ return json11({ inspectionStatus: "absent-never-contacted" });
15289
16248
  }
15290
- return error8(409, "target_not_terminal", "Target is not terminal under pinned writer authority");
16249
+ return error9(409, "target_not_terminal", "Target is not terminal under pinned writer authority");
15291
16250
  }
15292
16251
  /** Reconciler-only fence for never-contacted targets; creates durable tombstone state. */
15293
16252
  async fenceAbsentG77Target(tag, body) {
15294
16253
  const parsed = reservationFrom(body);
15295
16254
  if (parsed.value === void 0) {
15296
- return error8(400, "invalid_fence_target", parsed.error ?? "attemptId and epoch are required");
16255
+ return error9(400, "invalid_fence_target", parsed.error ?? "attemptId and epoch are required");
15297
16256
  }
15298
16257
  this.ensureSqlTag(tag);
15299
16258
  const result2 = await this.cancelSql(tag, { ...parsed.value, forceTombstone: true });
15300
- return result2.status >= 200 && result2.status < 300 ? json10(result2.body) : error8(result2.status, "fence_target_rejected", "Absent target fence was rejected");
16259
+ return result2.status >= 200 && result2.status < 300 ? json11(result2.body) : error9(result2.status, "fence_target_rejected", "Absent target fence was rejected");
15301
16260
  }
15302
16261
  async alarm() {
15303
16262
  this.activation.beginHandler();
@@ -15644,21 +16603,21 @@ var TagDurableObject = class {
15644
16603
  const result2 = await authority;
15645
16604
  g44GlobalArrayAuthorityResultByD1.set(database, result2);
15646
16605
  return result2;
15647
- } catch (error10) {
16606
+ } catch (error11) {
15648
16607
  if (g44GlobalArrayAuthorityByD1.get(database) === authority) {
15649
16608
  g44GlobalArrayAuthorityByD1.delete(database);
15650
16609
  }
15651
- throw error10;
16610
+ throw error11;
15652
16611
  }
15653
16612
  }
15654
16613
  async readG44GlobalArrayAuthority(database) {
15655
16614
  try {
15656
16615
  await database.prepare('SELECT "EventDigest" FROM dcb_events WHERE 1 = 0').all();
15657
16616
  return true;
15658
- } catch (error10) {
15659
- const message = error10 instanceof Error ? error10.message : String(error10);
16617
+ } catch (error11) {
16618
+ const message = error11 instanceof Error ? error11.message : String(error11);
15660
16619
  if (/no such (?:table|column):\s*(?:dcb_events|EventDigest)/i.test(message)) return false;
15661
- throw error10;
16620
+ throw error11;
15662
16621
  }
15663
16622
  }
15664
16623
  async traceCommitActor(request, tag, serviceId, activation, observation, callback) {
@@ -15668,10 +16627,10 @@ var TagDurableObject = class {
15668
16627
  { actorClass: "TAG", actorKey: `tag:${serviceId}:${tag}`, activation, observation },
15669
16628
  async () => {
15670
16629
  body = await this.jsonBody(request);
15671
- const attemptId = isObject11(body) && isNonEmptyString11(body.attemptId) ? body.attemptId : void 0;
16630
+ const attemptId = isObject12(body) && isNonEmptyString11(body.attemptId) ? body.attemptId : void 0;
15672
16631
  return attemptId === void 0 || !isNonEmptyString11(serviceId) ? void 0 : { attemptId, serviceId };
15673
16632
  },
15674
- async () => body === void 0 ? error8(400, "malformed_tag_request", "Request body must be JSON") : callback(body)
16633
+ async () => body === void 0 ? error9(400, "malformed_tag_request", "Request body must be JSON") : callback(body)
15675
16634
  );
15676
16635
  }
15677
16636
  /**
@@ -15885,6 +16844,7 @@ var TagDurableObject = class {
15885
16844
  ensureSqlTag(tag) {
15886
16845
  const sql = this.sqlStorage();
15887
16846
  if (sql === void 0) throw new Error("Tag SQL storage is unavailable");
16847
+ ensureTagCommitReceiptWrittenVersion(sql);
15888
16848
  const existing = sql.exec("SELECT tag FROM tag_identity WHERE singleton = 1").toArray()[0];
15889
16849
  if (existing !== void 0) {
15890
16850
  if (sqlString(existing.tag, "tag_identity.tag") !== tag) throw new Error("Tag Durable Object identity changed");
@@ -15997,17 +16957,18 @@ var TagDurableObject = class {
15997
16957
  sql.exec("UPDATE tag_control SET head_suid = ?, version = ?, updated_at = ? WHERE singleton = 1", head, version, committedAt);
15998
16958
  sql.exec("UPDATE tag_head SET service_id = ?, head_suid = ? WHERE singleton = 1", serviceId, head);
15999
16959
  }
16000
- writeCommittedSqlReceipt(sql, input, committedAt, eventCount, head, confirmsReservation) {
16960
+ writeCommittedSqlReceipt(sql, input, committedAt, eventCount, head, confirmsReservation, writtenVersion) {
16001
16961
  sql.exec(`
16002
16962
  INSERT INTO tag_commit_receipt
16003
- (attempt_id, epoch, committed_at, committed_event_count, head_suid, reservation_confirmed)
16004
- VALUES (?, ?, ?, ?, ?, ?)
16963
+ (attempt_id, epoch, committed_at, committed_event_count, head_suid, reservation_confirmed, written_version)
16964
+ VALUES (?, ?, ?, ?, ?, ?, ?)
16005
16965
  ON CONFLICT(attempt_id, epoch) DO UPDATE SET
16006
16966
  committed_at = excluded.committed_at,
16007
16967
  committed_event_count = excluded.committed_event_count,
16008
16968
  head_suid = excluded.head_suid,
16009
- reservation_confirmed = excluded.reservation_confirmed
16010
- `, input.attemptId, input.epoch, committedAt, eventCount, head, confirmsReservation ? 1 : 0);
16969
+ reservation_confirmed = excluded.reservation_confirmed,
16970
+ written_version = excluded.written_version
16971
+ `, input.attemptId, input.epoch, committedAt, eventCount, head, confirmsReservation ? 1 : 0, writtenVersion);
16011
16972
  }
16012
16973
  /**
16013
16974
  * The normal append path deliberately probes only the indexed rows it needs
@@ -16049,11 +17010,29 @@ var TagDurableObject = class {
16049
17010
  return { event, obligation, exact };
16050
17011
  });
16051
17012
  if (existingCandidates.every(({ exact }) => exact)) {
16052
- return { status: 200, body: { status: "duplicate", version } };
17013
+ const receipts = sql.exec(`
17014
+ SELECT epoch, committed_at, written_version, head_suid FROM tag_commit_receipt
17015
+ WHERE attempt_id = ?
17016
+ `, input.attemptId).toArray();
17017
+ const sameEpoch = receipts.find((row) => sqlNumber(row.epoch, "tag_commit_receipt.epoch") === input.epoch);
17018
+ const replayHead = input.candidates.at(-1)?.suid;
17019
+ const byHead = replayHead === void 0 ? [] : receipts.filter((row) => sqlString(row.head_suid, "tag_commit_receipt.head_suid") === replayHead);
17020
+ const receipt = sameEpoch ?? (byHead.length === 1 ? byHead[0] : void 0) ?? (receipts.length === 1 ? receipts[0] : void 0);
17021
+ if (receipt === void 0) {
17022
+ await this.rearmScheduler(txn);
17023
+ return { ...rejected("duplicate_receipt_missing"), body: { ...rejected("duplicate_receipt_missing").body, version } };
17024
+ }
17025
+ const storedAt = sqlNullableString(receipt.committed_at, "tag_commit_receipt.committed_at");
17026
+ const storedVersion = sqlNullableNumber(receipt.written_version, "tag_commit_receipt.written_version");
17027
+ if (storedAt === null || storedVersion === null) {
17028
+ await this.rearmScheduler(txn);
17029
+ return { ...rejected("duplicate_receipt_unknown"), body: { ...rejected("duplicate_receipt_unknown").body, version } };
17030
+ }
17031
+ return { status: 200, body: { status: "duplicate", version: storedVersion, updatedAt: storedAt } };
16053
17032
  }
16054
- const active = sql.exec("SELECT * FROM tag_reservation WHERE singleton = 1").toArray()[0];
17033
+ const active2 = sql.exec("SELECT * FROM tag_reservation WHERE singleton = 1").toArray()[0];
16055
17034
  const now2 = sqlNullableNumber(control.clock_now_ms, "tag_control.clock_now_ms") ?? Date.now() + sqlNumber(control.clock_offset_ms, "tag_control.clock_offset_ms");
16056
- let reservation = active;
17035
+ let reservation = active2;
16057
17036
  if (reservation !== void 0 && sqlNumber(reservation.expires_at, "tag_reservation.expires_at") <= now2) {
16058
17037
  sql.exec("DELETE FROM tag_reservation WHERE singleton = 1");
16059
17038
  version += 1;
@@ -16146,7 +17125,7 @@ var TagDurableObject = class {
16146
17125
  if (input.faultInjection === "after-append-before-confirm") {
16147
17126
  throw new AppendTransactionFault("Simulated interruption before atomic commit receipt");
16148
17127
  }
16149
- this.writeCommittedSqlReceipt(sql, input, committedAt, events.length, head, confirmsReservation);
17128
+ this.writeCommittedSqlReceipt(sql, input, committedAt, events.length, head, confirmsReservation, version + 1);
16150
17129
  if (confirmsReservation) sql.exec("DELETE FROM tag_reservation WHERE singleton = 1");
16151
17130
  await this.rearmScheduler(txn);
16152
17131
  return {
@@ -16154,7 +17133,8 @@ var TagDurableObject = class {
16154
17133
  body: {
16155
17134
  status: "appended",
16156
17135
  fenceGate: { checked: true, activeFenceCount: 0 },
16157
- version: version + 1
17136
+ version: version + 1,
17137
+ updatedAt: committedAt
16158
17138
  },
16159
17139
  hopFacts
16160
17140
  };
@@ -16555,18 +17535,20 @@ var TagDurableObject = class {
16555
17535
  );
16556
17536
  this.upsertSourcePartitionRegistration(sql, eventServiceId, record.tag, outbox.eventId);
16557
17537
  }
17538
+ ensureTagCommitReceiptWrittenVersion(sql);
16558
17539
  for (const confirmation of record.confirmations) {
16559
17540
  sql.exec(
16560
17541
  `
16561
17542
  INSERT OR IGNORE INTO tag_commit_receipt
16562
- (attempt_id, epoch, committed_at, committed_event_count, head_suid, reservation_confirmed)
16563
- VALUES (?, ?, ?, ?, ?, 1)
17543
+ (attempt_id, epoch, committed_at, committed_event_count, head_suid, reservation_confirmed, written_version)
17544
+ VALUES (?, ?, ?, ?, ?, 1, ?)
16564
17545
  `,
16565
17546
  confirmation.attemptId,
16566
17547
  confirmation.epoch,
16567
17548
  record.updatedAt,
16568
17549
  record.events.filter((event) => event.attemptId === confirmation.attemptId).length,
16569
- record.head
17550
+ record.head,
17551
+ record.version
16570
17552
  );
16571
17553
  }
16572
17554
  }
@@ -16615,13 +17597,13 @@ var TagDurableObject = class {
16615
17597
  async acquire(tag, body, observation) {
16616
17598
  const parsed = acquireFrom(body, tag);
16617
17599
  if (parsed.value === void 0) {
16618
- return error8(400, "invalid_tag_acquire", parsed.error ?? "Invalid acquire request");
17600
+ return error9(400, "invalid_tag_acquire", parsed.error ?? "Invalid acquire request");
16619
17601
  }
16620
17602
  const input = parsed.value;
16621
17603
  observation?.markFirstStorageRead();
16622
17604
  if (this.sqlStorage() !== void 0) {
16623
17605
  const result3 = await this.acquireSql(tag, input);
16624
- return json10(result3.body, result3.status);
17606
+ return json11(result3.body, result3.status);
16625
17607
  }
16626
17608
  const result2 = await this.ctx.storage.transaction(async (txn) => {
16627
17609
  const loaded = await this.recordFor(txn, tag);
@@ -16656,9 +17638,9 @@ var TagDurableObject = class {
16656
17638
  body: { ...rejected("consistency_head_mismatch").body, version: record.version }
16657
17639
  };
16658
17640
  }
16659
- const active = record.activeReservation;
16660
- if (active !== null) {
16661
- if (active.attemptId === input.attemptId && active.epoch === input.epoch) {
17641
+ const active2 = record.activeReservation;
17642
+ if (active2 !== null) {
17643
+ if (active2.attemptId === input.attemptId && active2.epoch === input.epoch) {
16662
17644
  return {
16663
17645
  status: 200,
16664
17646
  body: {
@@ -16703,18 +17685,18 @@ var TagDurableObject = class {
16703
17685
  body: { status: "reserved", reservation, fenceGate, version: updated.version }
16704
17686
  };
16705
17687
  });
16706
- return json10(result2.body, result2.status);
17688
+ return json11(result2.body, result2.status);
16707
17689
  }
16708
17690
  async cancel(tag, body, observation) {
16709
17691
  const parsed = reservationFrom(body);
16710
17692
  if (parsed.value === void 0) {
16711
- return error8(400, "invalid_tag_cancel", parsed.error ?? "Invalid cancel request");
17693
+ return error9(400, "invalid_tag_cancel", parsed.error ?? "Invalid cancel request");
16712
17694
  }
16713
17695
  const input = parsed.value;
16714
17696
  observation?.markFirstStorageRead();
16715
17697
  if (this.sqlStorage() !== void 0) {
16716
17698
  const result3 = await this.cancelSql(tag, input);
16717
- return json10(result3.body, result3.status);
17699
+ return json11(result3.body, result3.status);
16718
17700
  }
16719
17701
  const result2 = await this.ctx.storage.transaction(async (txn) => {
16720
17702
  const loaded = await this.recordFor(txn, tag);
@@ -16749,8 +17731,8 @@ var TagDurableObject = class {
16749
17731
  record = await this.commitExpiryIfNeeded(txn, expiry);
16750
17732
  return { ...rejected(epochError), body: { ...rejected(epochError).body, version: record.version } };
16751
17733
  }
16752
- const active = record.activeReservation;
16753
- if (active === null || active.attemptId !== input.attemptId || active.epoch !== input.epoch || active.token !== input.reservationToken) {
17734
+ const active2 = record.activeReservation;
17735
+ if (active2 === null || active2.attemptId !== input.attemptId || active2.epoch !== input.epoch || active2.token !== input.reservationToken) {
16754
17736
  record = await this.commitExpiryIfNeeded(txn, expiry);
16755
17737
  return {
16756
17738
  ...rejected("reservation_token_required"),
@@ -16765,12 +17747,12 @@ var TagDurableObject = class {
16765
17747
  });
16766
17748
  return { status: 200, body: { status: "cancelled", idempotent: false, version: updated.version } };
16767
17749
  });
16768
- return json10(result2.body, result2.status);
17750
+ return json11(result2.body, result2.status);
16769
17751
  }
16770
17752
  async seal(tag, body, observation) {
16771
17753
  const parsed = epochFrom(body);
16772
17754
  if (parsed.value === void 0) {
16773
- return error8(400, "invalid_tag_seal", parsed.error ?? "Invalid seal request");
17755
+ return error9(400, "invalid_tag_seal", parsed.error ?? "Invalid seal request");
16774
17756
  }
16775
17757
  const input = parsed.value;
16776
17758
  observation?.markFirstStorageRead();
@@ -16805,7 +17787,7 @@ var TagDurableObject = class {
16805
17787
  });
16806
17788
  return { status: 200, body: { status: "sealed", highestEpoch: input.epoch, version: updated.version } };
16807
17789
  });
16808
- return json10(result2.body, result2.status);
17790
+ return json11(result2.body, result2.status);
16809
17791
  }
16810
17792
  /**
16811
17793
  * Coordinator-only import admission. This is deliberately not a flag on
@@ -16814,8 +17796,8 @@ var TagDurableObject = class {
16814
17796
  */
16815
17797
  async bootstrapAppend(tag, body, serviceId) {
16816
17798
  const parsed = bootstrapAppendFrom(body, tag);
16817
- if (parsed.value === void 0) return error8(400, "invalid_bootstrap_admission", parsed.error ?? "Invalid bootstrap admission");
16818
- if (serviceId !== parsed.value.targetServiceId) return error8(409, "bootstrap_service_scope_mismatch", "Bootstrap target service does not match tag scope");
17799
+ if (parsed.value === void 0) return error9(400, "invalid_bootstrap_admission", parsed.error ?? "Invalid bootstrap admission");
17800
+ if (serviceId !== parsed.value.targetServiceId) return error9(409, "bootstrap_service_scope_mismatch", "Bootstrap target service does not match tag scope");
16819
17801
  const input = parsed.value;
16820
17802
  const result2 = await this.ctx.storage.transaction(async (txn) => {
16821
17803
  const loaded = await this.recordFor(txn, tag);
@@ -16836,10 +17818,10 @@ var TagDurableObject = class {
16836
17818
  );
16837
17819
  return { status: 201, body: { status: "bootstrap_admitted", version: updated.version } };
16838
17820
  });
16839
- return json10(result2.body, result2.status);
17821
+ return json11(result2.body, result2.status);
16840
17822
  }
16841
17823
  async closeBootstrap(tag, body) {
16842
- if (!isObject11(body) || !isNonEmptyString11(body.importId) || !isEpoch(body.leaseEpoch)) return error8(400, "invalid_bootstrap_close", "bootstrap importId and leaseEpoch are required");
17824
+ if (!isObject12(body) || !isNonEmptyString11(body.importId) || !isEpoch(body.leaseEpoch)) return error9(400, "invalid_bootstrap_close", "bootstrap importId and leaseEpoch are required");
16843
17825
  const result2 = await this.ctx.storage.transaction(async (txn) => {
16844
17826
  const loaded = await this.recordFor(txn, tag);
16845
17827
  const record = loaded.record;
@@ -16849,7 +17831,7 @@ var TagDurableObject = class {
16849
17831
  const updated = await this.commit(txn, record, { bootstrapAdmission: { ...admission, closed: true } });
16850
17832
  return { status: 200, body: { status: "closed", version: updated.version } };
16851
17833
  });
16852
- return json10(result2.body, result2.status);
17834
+ return json11(result2.body, result2.status);
16853
17835
  }
16854
17836
  directDoorbellPreflight(domainDeliveryClass) {
16855
17837
  const domainClass = domainDeliveryClass === void 0 ? void 0 : readDomainDeliveryClass({ DOMAIN_DELIVERY_CLASS: domainDeliveryClass });
@@ -16868,7 +17850,7 @@ var TagDurableObject = class {
16868
17850
  async append(tag, body, serviceId, domainDeliveryClass, observation) {
16869
17851
  const parsed = appendFrom(body, tag);
16870
17852
  if (parsed.value === void 0) {
16871
- return error8(400, "invalid_tag_append", parsed.error ?? "Invalid append request");
17853
+ return error9(400, "invalid_tag_append", parsed.error ?? "Invalid append request");
16872
17854
  }
16873
17855
  const input = parsed.value;
16874
17856
  try {
@@ -16906,7 +17888,7 @@ var TagDurableObject = class {
16906
17888
  if ((result3.status === 201 || result3.status === 200) && serviceId !== null && serviceId.length > 0 && sourcePartitionRegistration !== "unconfigured") {
16907
17889
  this.scheduleSourcePartitionWatermark(tag, serviceId);
16908
17890
  }
16909
- const response4 = json10(result3.body, result3.status);
17891
+ const response4 = json11(result3.body, result3.status);
16910
17892
  if (result3.status === 201 && serviceId !== null && serviceId.length > 0 && this.env.AUTO_DRAIN_OUTBOX === "true" && (this.env.DOWNSTREAM_QUEUE !== void 0 || doorbellPreflight.status === "ready" && this.env.DOWNSTREAM_DOORBELL !== void 0)) {
16911
17893
  const directRows = doorbellPreflight.status === "ready" && this.env.DOWNSTREAM_DOORBELL !== void 0 ? await this.directDeliveryBeforeResponse(tag, serviceId) : void 0;
16912
17894
  if (sourcePartitionRegistration !== "unconfigured" || sourcePartitionWasRegistered) {
@@ -16923,7 +17905,19 @@ var TagDurableObject = class {
16923
17905
  const loaded = await this.recordFor(txn, tag);
16924
17906
  let record = loaded.record;
16925
17907
  if (batchIsExactDuplicate(record, input)) {
16926
- return { status: 200, body: { status: "duplicate", version: record.version } };
17908
+ const receipts = record.writeReceipts ?? {};
17909
+ const sameEpoch = receipts[`${input.attemptId}:${input.epoch}`];
17910
+ const forAttempt = Object.entries(receipts).filter(([key]) => key.startsWith(`${input.attemptId}:`));
17911
+ const replayHead = input.candidates.at(-1)?.suid;
17912
+ const byHead = replayHead === void 0 ? [] : forAttempt.filter(([, value]) => value.headSuid === replayHead);
17913
+ const receipt = sameEpoch ?? (byHead.length === 1 ? byHead[0]?.[1] : void 0) ?? (forAttempt.length === 1 ? forAttempt[0]?.[1] : void 0);
17914
+ if (receipt === void 0) {
17915
+ return {
17916
+ ...rejected("duplicate_receipt_unknown"),
17917
+ body: { ...rejected("duplicate_receipt_unknown").body, version: record.version }
17918
+ };
17919
+ }
17920
+ return { status: 200, body: { status: "duplicate", version: receipt.version, updatedAt: receipt.updatedAt } };
16927
17921
  }
16928
17922
  const expiry = expireReservation(record);
16929
17923
  record = expiry.record;
@@ -16936,9 +17930,9 @@ var TagDurableObject = class {
16936
17930
  await this.commitExpiryIfNeeded(txn, expiry);
16937
17931
  return fenceGateRejected();
16938
17932
  }
16939
- const active = record.activeReservation;
16940
- const confirmsReservation = active !== null && active.attemptId === input.attemptId && active.epoch === input.epoch && active.token === input.reservationToken;
16941
- if (active !== null && (active.attemptId !== input.attemptId || active.epoch !== input.epoch || active.token !== input.reservationToken)) {
17933
+ const active2 = record.activeReservation;
17934
+ const confirmsReservation = active2 !== null && active2.attemptId === input.attemptId && active2.epoch === input.epoch && active2.token === input.reservationToken;
17935
+ if (active2 !== null && (active2.attemptId !== input.attemptId || active2.epoch !== input.epoch || active2.token !== input.reservationToken)) {
16942
17936
  record = await this.commitExpiryIfNeeded(txn, expiry);
16943
17937
  return {
16944
17938
  ...rejected("reservation_token_required"),
@@ -17000,13 +17994,20 @@ var TagDurableObject = class {
17000
17994
  alarmDueAt: null,
17001
17995
  confirmations: confirmsReservation ? withMaxEpoch(record.confirmations, input.attemptId, input.epoch) : record.confirmations
17002
17996
  });
17003
- await this.write(txn, updated, serviceId ?? "");
17997
+ const recorded = {
17998
+ ...updated,
17999
+ writeReceipts: {
18000
+ ...record.writeReceipts ?? {},
18001
+ [`${input.attemptId}:${input.epoch}`]: { version: updated.version, updatedAt: updated.updatedAt, headSuid: updated.head }
18002
+ }
18003
+ };
18004
+ await this.write(txn, recorded, serviceId ?? "");
17004
18005
  return {
17005
18006
  status: 201,
17006
- body: { status: "appended", fenceGate, version: updated.version }
18007
+ body: { status: "appended", fenceGate, version: recorded.version, updatedAt: recorded.updatedAt }
17007
18008
  };
17008
18009
  });
17009
- const response3 = json10(result2.body, result2.status);
18010
+ const response3 = json11(result2.body, result2.status);
17010
18011
  if (result2.status === 201 && serviceId !== null && serviceId.length > 0 && this.env.AUTO_DRAIN_OUTBOX === "true" && (this.env.DOWNSTREAM_QUEUE !== void 0 || doorbellPreflight.status === "ready" && this.env.DOWNSTREAM_DOORBELL !== void 0)) {
17011
18012
  const directRows = doorbellPreflight.status === "ready" && this.env.DOWNSTREAM_DOORBELL !== void 0 ? await this.directDeliveryBeforeResponse(tag, serviceId) : void 0;
17012
18013
  withGlobalAdmission2(
@@ -17018,16 +18019,16 @@ var TagDurableObject = class {
17018
18019
  return response3;
17019
18020
  } catch (failure3) {
17020
18021
  if (failure3 instanceof PartitionRegistrationUnavailableError) {
17021
- return error8(503, "partition_registration_unavailable", "Source partition registration is unavailable; retry the commit.", true);
18022
+ return error9(503, "partition_registration_unavailable", "Source partition registration is unavailable; retry the commit.", true);
17022
18023
  }
17023
18024
  if (failure3 instanceof AppendTransactionFault) {
17024
- return error8(
18025
+ return error9(
17025
18026
  503,
17026
18027
  "simulated_append_crash",
17027
18028
  "Simulated interruption before atomic append and confirmation commit"
17028
18029
  );
17029
18030
  }
17030
- return error8(500, "tag_append_failure", "Tag append could not be persisted");
18031
+ return error9(500, "tag_append_failure", "Tag append could not be persisted");
17031
18032
  }
17032
18033
  }
17033
18034
  /**
@@ -17143,7 +18144,7 @@ var TagDurableObject = class {
17143
18144
  let timer;
17144
18145
  const operationResult = Promise.resolve().then(operation).then(
17145
18146
  (value) => ({ status: "completed", value }),
17146
- (error10) => ({ status: "failed", error: error10 })
18147
+ (error11) => ({ status: "failed", error: error11 })
17147
18148
  );
17148
18149
  const timeout = new Promise((resolve) => {
17149
18150
  timer = setTimeout(() => resolve({ status: "timeout" }), budgetMs);
@@ -17194,14 +18195,14 @@ var TagDurableObject = class {
17194
18195
  envelopeBytes,
17195
18196
  status: "success"
17196
18197
  });
17197
- } catch (error10) {
18198
+ } catch (error11) {
17198
18199
  console.warn("direct_doorbell_status", {
17199
18200
  status: "queued-degraded",
17200
18201
  reason: "direct_doorbell_delivery_failed",
17201
18202
  correlationId,
17202
18203
  envelopeBytes,
17203
- failureKind: classifyDirectDoorbellFailure(error10),
17204
- error: String(error10)
18204
+ failureKind: classifyDirectDoorbellFailure(error11),
18205
+ error: String(error11)
17205
18206
  });
17206
18207
  }
17207
18208
  }
@@ -17262,7 +18263,7 @@ var TagDurableObject = class {
17262
18263
  async confirm(tag, body) {
17263
18264
  const parsed = reservationFrom(body);
17264
18265
  if (parsed.value === void 0) {
17265
- return error8(400, "invalid_tag_confirm", parsed.error ?? "Invalid confirm request");
18266
+ return error9(400, "invalid_tag_confirm", parsed.error ?? "Invalid confirm request");
17266
18267
  }
17267
18268
  const input = parsed.value;
17268
18269
  const result2 = await this.ctx.storage.transaction(async (txn) => {
@@ -17278,8 +18279,8 @@ var TagDurableObject = class {
17278
18279
  record = await this.commitExpiryIfNeeded(txn, expiry);
17279
18280
  return { ...rejected(epochError), body: { ...rejected(epochError).body, version: record.version } };
17280
18281
  }
17281
- const active = record.activeReservation;
17282
- if (active !== null && active.attemptId === input.attemptId && active.epoch === input.epoch && active.token === input.reservationToken) {
18282
+ const active2 = record.activeReservation;
18283
+ if (active2 !== null && active2.attemptId === input.attemptId && active2.epoch === input.epoch && active2.token === input.reservationToken) {
17283
18284
  record = await this.commitExpiryIfNeeded(txn, expiry);
17284
18285
  return {
17285
18286
  ...rejected("append_required_for_confirmation"),
@@ -17292,12 +18293,12 @@ var TagDurableObject = class {
17292
18293
  body: { ...rejected("confirmation_not_found").body, version: record.version }
17293
18294
  };
17294
18295
  });
17295
- return json10(result2.body, result2.status);
18296
+ return json11(result2.body, result2.status);
17296
18297
  }
17297
18298
  async installFence(tag, body) {
17298
18299
  const parsed = fenceFrom(body);
17299
18300
  if (parsed.value === void 0) {
17300
- return error8(400, "invalid_tag_fence", parsed.error ?? "Invalid fence request");
18301
+ return error9(400, "invalid_tag_fence", parsed.error ?? "Invalid fence request");
17301
18302
  }
17302
18303
  const input = parsed.value;
17303
18304
  const result2 = await this.ctx.storage.transaction(async (txn) => {
@@ -17334,12 +18335,12 @@ var TagDurableObject = class {
17334
18335
  });
17335
18336
  return { status: 201, body: { status: "fence-installed", idempotent: false, version: updated.version } };
17336
18337
  });
17337
- return json10(result2.body, result2.status);
18338
+ return json11(result2.body, result2.status);
17338
18339
  }
17339
18340
  async clearFence(tag, body) {
17340
18341
  const parsed = fenceFrom(body);
17341
18342
  if (parsed.value === void 0) {
17342
- return error8(400, "invalid_tag_fence", parsed.error ?? "Invalid fence request");
18343
+ return error9(400, "invalid_tag_fence", parsed.error ?? "Invalid fence request");
17343
18344
  }
17344
18345
  const input = parsed.value;
17345
18346
  const result2 = await this.ctx.storage.transaction(async (txn) => {
@@ -17365,7 +18366,7 @@ var TagDurableObject = class {
17365
18366
  });
17366
18367
  return { status: 200, body: { status: "fence-cleared", idempotent: false, version: updated.version } };
17367
18368
  });
17368
- return json10(result2.body, result2.status);
18369
+ return json11(result2.body, result2.status);
17369
18370
  }
17370
18371
  /**
17371
18372
  * Returns Tag-side repair facts only. Journal observations are deliberately
@@ -17401,7 +18402,7 @@ var TagDurableObject = class {
17401
18402
  }
17402
18403
  };
17403
18404
  });
17404
- return json10(result2.body, result2.status);
18405
+ return json11(result2.body, result2.status);
17405
18406
  }
17406
18407
  /**
17407
18408
  * Internal outbox handoff: enqueuedAt is persisted before any Queue send so
@@ -17411,10 +18412,10 @@ var TagDurableObject = class {
17411
18412
  async pendingOutbox(tag, serviceId, body) {
17412
18413
  const parsed = outboxPendingFrom(body);
17413
18414
  if (parsed.value === void 0) {
17414
- return error8(400, "invalid_outbox_pending", parsed.error ?? "Invalid outbox pending request");
18415
+ return error9(400, "invalid_outbox_pending", parsed.error ?? "Invalid outbox pending request");
17415
18416
  }
17416
18417
  if (!isNonEmptyString11(serviceId)) {
17417
- return error8(400, "outbox_service_identity_required", "Outbox delivery requires a service identity");
18418
+ return error9(400, "outbox_service_identity_required", "Outbox delivery requires a service identity");
17418
18419
  }
17419
18420
  const input = parsed.value;
17420
18421
  const result2 = await this.ctx.storage.transaction(async (txn) => {
@@ -17490,7 +18491,7 @@ var TagDurableObject = class {
17490
18491
  }
17491
18492
  return { status: 200, body: { rows } };
17492
18493
  });
17493
- return json10(result2.body, result2.status);
18494
+ return json11(result2.body, result2.status);
17494
18495
  }
17495
18496
  /**
17496
18497
  * Source-side obligation scanner. It deliberately does not call the Queue,
@@ -17560,7 +18561,7 @@ var TagDurableObject = class {
17560
18561
  }
17561
18562
  };
17562
18563
  });
17563
- return json10(result2.body, result2.status);
18564
+ return json11(result2.body, result2.status);
17564
18565
  }
17565
18566
  /** Record a retry or terminal poison disposition without changing the source event. */
17566
18567
  async recordOutboxFailure(tag, delivery, failure3) {
@@ -17614,16 +18615,16 @@ var TagDurableObject = class {
17614
18615
  async markOutboxDelivered(tag, body) {
17615
18616
  const parsed = outboxMarkFrom(body);
17616
18617
  if (parsed.value === void 0) {
17617
- return error8(400, "invalid_outbox_delivery_mark", parsed.error ?? "Invalid outbox delivery mark");
18618
+ return error9(400, "invalid_outbox_delivery_mark", parsed.error ?? "Invalid outbox delivery mark");
17618
18619
  }
17619
18620
  const input = parsed.value;
17620
18621
  try {
17621
18622
  const joined = await Promise.all(input.deliveries.map((delivery) => this.globalReceiptMatches(delivery)));
17622
18623
  if (joined.some((value) => !value)) {
17623
- return error8(409, "outbox_global_receipt_unverified", "Global receipt and membership must be read back before source acknowledgement");
18624
+ return error9(409, "outbox_global_receipt_unverified", "Global receipt and membership must be read back before source acknowledgement");
17624
18625
  }
17625
18626
  } catch {
17626
- return error8(503, "outbox_global_receipt_unavailable", "Global receipt verification is unavailable");
18627
+ return error9(503, "outbox_global_receipt_unavailable", "Global receipt verification is unavailable");
17627
18628
  }
17628
18629
  const result2 = await this.ctx.storage.transaction(async (txn) => {
17629
18630
  if (this.sqlStorage() !== void 0) {
@@ -17662,7 +18663,7 @@ var TagDurableObject = class {
17662
18663
  }
17663
18664
  return { status: 200, body: { marked, idempotent: marked === 0 } };
17664
18665
  });
17665
- return json10(result2.body, result2.status);
18666
+ return json11(result2.body, result2.status);
17666
18667
  }
17667
18668
  /**
17668
18669
  * The delivery handoff reads its universe from `tag_outbox_obligation`.
@@ -17801,7 +18802,7 @@ var TagDurableObject = class {
17801
18802
  async acquireRepairLease(tag, body) {
17802
18803
  const parsed = repairAcquireFrom(body, tag);
17803
18804
  if (parsed.value === void 0) {
17804
- return error8(400, "invalid_repair_acquire", parsed.error ?? "Invalid repair acquire request");
18805
+ return error9(400, "invalid_repair_acquire", parsed.error ?? "Invalid repair acquire request");
17805
18806
  }
17806
18807
  const input = parsed.value;
17807
18808
  const result2 = await this.ctx.storage.transaction(async (txn) => {
@@ -17847,12 +18848,12 @@ var TagDurableObject = class {
17847
18848
  }
17848
18849
  };
17849
18850
  });
17850
- return json10(result2.body, result2.status);
18851
+ return json11(result2.body, result2.status);
17851
18852
  }
17852
18853
  async renewRepairLease(tag, body) {
17853
18854
  const parsed = repairLeaseFrom(body);
17854
18855
  if (parsed.value === void 0) {
17855
- return error8(400, "invalid_repair_renew", parsed.error ?? "Invalid repair renew request");
18856
+ return error9(400, "invalid_repair_renew", parsed.error ?? "Invalid repair renew request");
17856
18857
  }
17857
18858
  const input = parsed.value;
17858
18859
  const result2 = await this.ctx.storage.transaction(async (txn) => {
@@ -17876,12 +18877,12 @@ var TagDurableObject = class {
17876
18877
  }
17877
18878
  };
17878
18879
  });
17879
- return json10(result2.body, result2.status);
18880
+ return json11(result2.body, result2.status);
17880
18881
  }
17881
18882
  async unionRepairScope(tag, body) {
17882
18883
  const parsed = repairScopeUnionFrom(body, tag);
17883
18884
  if (parsed.value === void 0) {
17884
- return error8(400, "invalid_repair_scope_union", parsed.error ?? "Invalid repair scope union request");
18885
+ return error9(400, "invalid_repair_scope_union", parsed.error ?? "Invalid repair scope union request");
17885
18886
  }
17886
18887
  const input = parsed.value;
17887
18888
  const result2 = await this.ctx.storage.transaction(async (txn) => {
@@ -17917,12 +18918,12 @@ var TagDurableObject = class {
17917
18918
  body: { status: "repair-scope-unioned", scopeVersion: updated.repairScopeVersion, scope: updated.repairScope }
17918
18919
  };
17919
18920
  });
17920
- return json10(result2.body, result2.status);
18921
+ return json11(result2.body, result2.status);
17921
18922
  }
17922
18923
  async applyRepair(tag, body) {
17923
18924
  const parsed = repairApplyFrom(body, tag);
17924
18925
  if (parsed.value === void 0) {
17925
- return error8(400, "invalid_repair_apply", parsed.error ?? "Invalid repair apply request");
18926
+ return error9(400, "invalid_repair_apply", parsed.error ?? "Invalid repair apply request");
17926
18927
  }
17927
18928
  const input = parsed.value;
17928
18929
  const result2 = await this.ctx.storage.transaction(async (txn) => {
@@ -18003,12 +19004,12 @@ var TagDurableObject = class {
18003
19004
  body: { status: branch, idempotent: false, head: (updatedRecord ?? record).head, version: (updatedRecord ?? record).version }
18004
19005
  };
18005
19006
  });
18006
- return json10(result2.body, result2.status);
19007
+ return json11(result2.body, result2.status);
18007
19008
  }
18008
19009
  async auditRepair(tag, body) {
18009
19010
  const parsed = repairAuditFrom(body, tag);
18010
19011
  if (parsed.value === void 0) {
18011
- return error8(400, "invalid_repair_audit", parsed.error ?? "Invalid repair audit request");
19012
+ return error9(400, "invalid_repair_audit", parsed.error ?? "Invalid repair audit request");
18012
19013
  }
18013
19014
  const input = parsed.value;
18014
19015
  const result2 = await this.ctx.storage.transaction(async (txn) => {
@@ -18045,12 +19046,12 @@ var TagDurableObject = class {
18045
19046
  await txn.put(REPAIR_FACTS_KEY, { ...facts, audits: [...facts.audits, audit] });
18046
19047
  return { status: 200, body: { status: "repair-audited", idempotent: false, branch: audit.branch } };
18047
19048
  });
18048
- return json10(result2.body, result2.status);
19049
+ return json11(result2.body, result2.status);
18049
19050
  }
18050
19051
  async clearRepairFence(tag, body) {
18051
19052
  const parsed = repairClearFrom(body);
18052
19053
  if (parsed.value === void 0) {
18053
- return error8(400, "invalid_repair_clear", parsed.error ?? "Invalid repair clear request");
19054
+ return error9(400, "invalid_repair_clear", parsed.error ?? "Invalid repair clear request");
18054
19055
  }
18055
19056
  const input = parsed.value;
18056
19057
  const result2 = await this.ctx.storage.transaction(async (txn) => {
@@ -18081,16 +19082,16 @@ var TagDurableObject = class {
18081
19082
  });
18082
19083
  return { status: 200, body: { status: "repair-fence-cleared", idempotent: false, version: updated.version } };
18083
19084
  });
18084
- return json10(result2.body, result2.status);
19085
+ return json11(result2.body, result2.status);
18085
19086
  }
18086
19087
  async setClockOffset(tag, body) {
18087
- if (!isObject11(body)) {
18088
- return error8(400, "invalid_tag_clock", "offsetMs or nowMs must be a safe integer");
19088
+ if (!isObject12(body)) {
19089
+ return error9(400, "invalid_tag_clock", "offsetMs or nowMs must be a safe integer");
18089
19090
  }
18090
19091
  const nowMs = body.nowMs;
18091
19092
  const offsetMs = body.offsetMs;
18092
19093
  if (!isSafeInteger(nowMs) && !isSafeInteger(offsetMs)) {
18093
- return error8(400, "invalid_tag_clock", "offsetMs or nowMs must be a safe integer");
19094
+ return error9(400, "invalid_tag_clock", "offsetMs or nowMs must be a safe integer");
18094
19095
  }
18095
19096
  const result2 = await this.ctx.storage.transaction(async (txn) => {
18096
19097
  const loaded = await this.recordFor(txn, tag);
@@ -18108,7 +19109,7 @@ var TagDurableObject = class {
18108
19109
  }
18109
19110
  };
18110
19111
  });
18111
- return json10(result2.body, result2.status);
19112
+ return json11(result2.body, result2.status);
18112
19113
  }
18113
19114
  async runAlarm() {
18114
19115
  const due = await this.ctx.storage.transaction(async (txn) => {
@@ -18227,16 +19228,16 @@ var TagStateCheckpointFault = class extends Error {
18227
19228
  this.name = "TagStateCheckpointFault";
18228
19229
  }
18229
19230
  };
18230
- function json11(body, status = 200) {
19231
+ function json12(body, status = 200) {
18231
19232
  return new Response(JSON.stringify(body), {
18232
19233
  status,
18233
19234
  headers: { "content-type": "application/json; charset=utf-8" }
18234
19235
  });
18235
19236
  }
18236
- function error9(status, code, message) {
18237
- return json11({ code, error: message }, status);
19237
+ function error10(status, code, message) {
19238
+ return json12({ code, error: message }, status);
18238
19239
  }
18239
- function isObject12(value) {
19240
+ function isObject13(value) {
18240
19241
  return typeof value === "object" && value !== null && !Array.isArray(value);
18241
19242
  }
18242
19243
  function isNonEmptyString12(value) {
@@ -18251,7 +19252,7 @@ function sqlNullableString2(value, column) {
18251
19252
  return sqlString2(value, column);
18252
19253
  }
18253
19254
  function parseIdentity(value) {
18254
- if (!isObject12(value) || !isNonEmptyString12(value.serviceId) || !isNonEmptyString12(value.tag) || !isNonEmptyString12(value.projectorId)) {
19255
+ if (!isObject13(value) || !isNonEmptyString12(value.serviceId) || !isNonEmptyString12(value.tag) || !isNonEmptyString12(value.projectorId)) {
18255
19256
  return void 0;
18256
19257
  }
18257
19258
  return { serviceId: value.serviceId, tag: value.tag, projectorId: value.projectorId };
@@ -18313,17 +19314,17 @@ var TagStateDurableObject = class {
18313
19314
  async fetch(request) {
18314
19315
  const url = new URL(request.url);
18315
19316
  if (request.method !== "POST" || url.pathname !== "/read") {
18316
- return error9(404, "tag_state_route_not_found", "TagState route was not found");
19317
+ return error10(404, "tag_state_route_not_found", "TagState route was not found");
18317
19318
  }
18318
19319
  let body;
18319
19320
  try {
18320
19321
  body = await request.json();
18321
19322
  } catch {
18322
- return error9(400, "tag_state_validation_error", "TagState request must be JSON");
19323
+ return error10(400, "tag_state_validation_error", "TagState request must be JSON");
18323
19324
  }
18324
19325
  const identity2 = parseIdentity(body);
18325
19326
  if (identity2 === void 0) {
18326
- return error9(400, "tag_state_validation_error", "TagState identity requires serviceId, tag, and projectorId");
19327
+ return error10(400, "tag_state_validation_error", "TagState identity requires serviceId, tag, and projectorId");
18327
19328
  }
18328
19329
  return await this.read(identity2);
18329
19330
  }
@@ -18345,16 +19346,16 @@ var TagStateDurableObject = class {
18345
19346
  return await this.continueRebuild(identity2, projector, cache);
18346
19347
  } catch (caught) {
18347
19348
  if (caught instanceof TagStateIdentityConflict) {
18348
- return error9(409, "tag_state_identity_conflict", "TagState Durable Object identity changed");
19349
+ return error10(409, "tag_state_identity_conflict", "TagState Durable Object identity changed");
18349
19350
  }
18350
19351
  if (caught instanceof TagStateUnknownProjector) {
18351
- return error9(404, "tag_state_unknown_projector", "TagState projector is not registered");
19352
+ return error10(404, "tag_state_unknown_projector", "TagState projector is not registered");
18352
19353
  }
18353
19354
  if (caught instanceof TagStateRegistryFailure) {
18354
- return error9(503, "tag_state_projector_registry_failure", "TagState projector registry is unavailable");
19355
+ return error10(503, "tag_state_projector_registry_failure", "TagState projector registry is unavailable");
18355
19356
  }
18356
19357
  if (caught instanceof TagStateSourceFailure) {
18357
- return error9(caught.status, "tag_state_source_frontier_failure", "TagState source frontier could not be read");
19358
+ return error10(caught.status, "tag_state_source_frontier_failure", "TagState source frontier could not be read");
18358
19359
  }
18359
19360
  if (caught instanceof TagStateCacheCorruption) {
18360
19361
  try {
@@ -18362,21 +19363,21 @@ var TagStateDurableObject = class {
18362
19363
  this.beginRebuild(projector, "cache-corrupt");
18363
19364
  } catch (rebuildFailure) {
18364
19365
  if (rebuildFailure instanceof TagStateRegistryFailure) {
18365
- return error9(503, "tag_state_projector_registry_failure", "TagState projector registry is unavailable");
19366
+ return error10(503, "tag_state_projector_registry_failure", "TagState projector registry is unavailable");
18366
19367
  }
18367
19368
  }
18368
- return error9(409, "tag_state_cache_corrupt", "TagState cache is corrupt and is rebuilding");
19369
+ return error10(409, "tag_state_cache_corrupt", "TagState cache is corrupt and is rebuilding");
18369
19370
  }
18370
19371
  if (caught instanceof TagStateCheckpointFault) {
18371
- return error9(503, "tag_state_rebuild_interrupted", "TagState replay checkpoint was interrupted");
19372
+ return error10(503, "tag_state_rebuild_interrupted", "TagState replay checkpoint was interrupted");
18372
19373
  }
18373
- return error9(503, "tag_state_unavailable", "TagState cache is unavailable");
19374
+ return error10(503, "tag_state_unavailable", "TagState cache is unavailable");
18374
19375
  }
18375
19376
  }
18376
19377
  async beginDeltaOrRespondReady(identity2, projector, cache) {
18377
19378
  const page = await this.readSource(identity2, cache.lastSuid, void 0);
18378
19379
  if (page.events.length === 0 && page.completeThrough !== null) {
18379
- return json11(this.success(projector, cache.stateJson, cache.lastSuid));
19380
+ return json12(this.success(projector, cache.stateJson, cache.lastSuid));
18380
19381
  }
18381
19382
  const rebuild = this.beginRebuild(projector, "normal-delta", {
18382
19383
  cursor: cache.lastSuid,
@@ -18425,9 +19426,9 @@ var TagStateDurableObject = class {
18425
19426
  });
18426
19427
  if (this.g46CheckpointFault === "after-checkpoint") throw new TagStateCheckpointFault();
18427
19428
  if (!complete) {
18428
- return error9(503, "tag_state_rebuild_in_progress", "TagState replay is rebuilding to its frozen frontier");
19429
+ return error10(503, "tag_state_rebuild_in_progress", "TagState replay is rebuilding to its frozen frontier");
18429
19430
  }
18430
- return json11(this.success(projector, accumulatorJson, page.completeThrough));
19431
+ return json12(this.success(projector, accumulatorJson, page.completeThrough));
18431
19432
  }
18432
19433
  success(projector, stateJson, lastSortedUniqueId) {
18433
19434
  try {
@@ -18484,7 +19485,7 @@ var TagStateDurableObject = class {
18484
19485
  }
18485
19486
  }
18486
19487
  assertSourcePage(cursor, expectedThrough, page) {
18487
- if (!isObject12(page) || !Array.isArray(page.events) || typeof page.lastSortableUniqueId !== "string" || typeof page.through !== "string" || page.completeThrough !== null && typeof page.completeThrough !== "string") {
19488
+ if (!isObject13(page) || !Array.isArray(page.events) || typeof page.lastSortableUniqueId !== "string" || typeof page.through !== "string" || page.completeThrough !== null && typeof page.completeThrough !== "string") {
18488
19489
  throw new TagStateSourceFailure(503, new Error("TagState source response was malformed"));
18489
19490
  }
18490
19491
  if (expectedThrough !== void 0 && expectedThrough !== null && page.through !== expectedThrough) {
@@ -18909,9 +19910,9 @@ var PostgresEventStore = class {
18909
19910
  await transaction.unsafe(SCHEMA, [], { prepare: false });
18910
19911
  });
18911
19912
  this.sql = sql;
18912
- } catch (error10) {
19913
+ } catch (error11) {
18913
19914
  await sql.end({ timeout: 1 });
18914
- throw error10;
19915
+ throw error11;
18915
19916
  }
18916
19917
  }
18917
19918
  /** Close an owned request/test client without touching durable records. */
@@ -19102,6 +20103,65 @@ var PostgresEventStore = class {
19102
20103
  }
19103
20104
  return outcome === "stored" ? { outcome, kind: "stored", event: stored } : { outcome, kind: outcome, incident };
19104
20105
  }
20106
+ /**
20107
+ * DDL columns only. C# rows have no provenance and no allocator lineage;
20108
+ * this read must not invent either, and it must not treat Tags as
20109
+ * committed membership.
20110
+ */
20111
+ async readLogicalEvents(serviceId) {
20112
+ const rows = await this.query(
20113
+ `SELECT "ServiceId" AS service_id, "Id"::text AS event_id,
20114
+ "SortableUniqueId" AS suid, "Payload"::text AS payload,
20115
+ "EventType" AS event_type, "Tags" AS event_tags,
20116
+ "Timestamp"::text AS timestamp, "CausationId" AS causation_id,
20117
+ "CorrelationId" AS correlation_id, "ExecutedUser" AS executed_user
20118
+ FROM dcb_events
20119
+ WHERE "ServiceId" = $1
20120
+ ORDER BY "SortableUniqueId" ASC, "Id" ASC`,
20121
+ [serviceId]
20122
+ );
20123
+ return rows.map((row) => ({
20124
+ serviceId: asString4(row.service_id, "service_id"),
20125
+ id: asString4(row.event_id, "event_id"),
20126
+ sortableUniqueId: asString4(row.suid, "suid"),
20127
+ eventType: asString4(row.event_type, "event_type"),
20128
+ payload: asString4(row.payload, "payload"),
20129
+ tags: asStringArray2(row.event_tags, "event_tags"),
20130
+ timestamp: canonicalUtcTimestamp(row.timestamp, "timestamp"),
20131
+ causationId: nullableString2(row.causation_id, "causation_id"),
20132
+ correlationId: nullableString2(row.correlation_id, "correlation_id"),
20133
+ executedUser: nullableString2(row.executed_user, "executed_user")
20134
+ }));
20135
+ }
20136
+ /** Insert one DDL row. Does not write dcb_event_ops or a provenance stamp. */
20137
+ async appendLogicalEvent(event) {
20138
+ JSON.parse(event.payload);
20139
+ assertSortableUniqueId(event.sortableUniqueId);
20140
+ if (!isRfc4122Uuid(event.id)) {
20141
+ throw new CanonicalEventIdentityConflictError("postgres", event.id, "logical event id must be an RFC 4122 UUID");
20142
+ }
20143
+ assertUtcTimestamp(event.timestamp, event.id);
20144
+ const sql = this.requireSql();
20145
+ await sql.begin(async (transaction) => {
20146
+ await transaction.unsafe(
20147
+ `INSERT INTO dcb_events
20148
+ ("ServiceId", "Id", "SortableUniqueId", "EventType", "Payload", "Tags", "Timestamp", "CausationId", "CorrelationId", "ExecutedUser")
20149
+ VALUES ($1, $2::uuid, $3, $4, $5::text::json, $6::text::jsonb, $7::timestamptz, $8, $9, $10)`,
20150
+ [
20151
+ event.serviceId,
20152
+ event.id,
20153
+ event.sortableUniqueId,
20154
+ event.eventType,
20155
+ event.payload,
20156
+ JSON.stringify(event.tags),
20157
+ event.timestamp,
20158
+ event.causationId,
20159
+ event.correlationId,
20160
+ event.executedUser
20161
+ ]
20162
+ );
20163
+ });
20164
+ }
19105
20165
  async readAllEvents(serviceId, since) {
19106
20166
  if (since.length !== 0) assertSortableUniqueId(since);
19107
20167
  const rows = await this.query(
@@ -19574,12 +20634,12 @@ function encodedCommitSnapshotLogBytes(record) {
19574
20634
  function assertCommitSnapshotLogSize(record) {
19575
20635
  const encodedBytes2 = encodedCommitSnapshotLogBytes(record);
19576
20636
  if (encodedBytes2 > COMMIT_SNAPSHOT_LOG_BYTE_BUDGET) {
19577
- const error10 = new Error(
20637
+ const error11 = new Error(
19578
20638
  `Commit snapshot log is ${encodedBytes2} bytes; budget is ${COMMIT_SNAPSHOT_LOG_BYTE_BUDGET} bytes`
19579
20639
  );
19580
- error10.code = "commit-snapshot-log-size";
19581
- error10.encodedBytes = encodedBytes2;
19582
- throw error10;
20640
+ error11.code = "commit-snapshot-log-size";
20641
+ error11.encodedBytes = encodedBytes2;
20642
+ throw error11;
19583
20643
  }
19584
20644
  return encodedBytes2;
19585
20645
  }
@@ -19598,8 +20658,8 @@ function createCommitTraceConsoleSink(options = {}) {
19598
20658
  if (record === void 0) return;
19599
20659
  try {
19600
20660
  assertCommitSnapshotLogSize(record);
19601
- } catch (error10) {
19602
- const encodedBytes2 = typeof error10 === "object" && error10 !== null && "encodedBytes" in error10 && typeof error10.encodedBytes === "number" ? error10.encodedBytes : encodedCommitSnapshotLogBytes(record);
20661
+ } catch (error11) {
20662
+ const encodedBytes2 = typeof error11 === "object" && error11 !== null && "encodedBytes" in error11 && typeof error11.encodedBytes === "number" ? error11.encodedBytes : encodedCommitSnapshotLogBytes(record);
19603
20663
  reportRejectedSize(Object.freeze({
19604
20664
  schema: COMMIT_SNAPSHOT_LOG_SCHEMA,
19605
20665
  event: "commit.snapshot-size-rejected",
@@ -19608,41 +20668,107 @@ function createCommitTraceConsoleSink(options = {}) {
19608
20668
  encodedBytes: encodedBytes2,
19609
20669
  byteBudget: COMMIT_SNAPSHOT_LOG_BYTE_BUDGET
19610
20670
  }));
19611
- throw error10;
20671
+ throw error11;
19612
20672
  }
19613
20673
  log(record);
19614
20674
  }
19615
20675
  });
19616
20676
  }
19617
20677
 
19618
- // src/scope/ControlRouteScope.ts
19619
- function response2(status, code, error10) {
19620
- return new Response(JSON.stringify({ code, error: error10 }), {
19621
- status,
19622
- headers: { "content-type": "application/json; charset=utf-8" }
19623
- });
20678
+ // src/shard/ShardRotation.ts
20679
+ var ShardRotationError = class extends Error {
20680
+ diagnostic;
20681
+ constructor(diagnostic) {
20682
+ super(diagnostic.code);
20683
+ this.name = "ShardRotationError";
20684
+ this.diagnostic = diagnostic;
20685
+ }
20686
+ };
20687
+ function fail(code, path, reason) {
20688
+ throw new ShardRotationError({ code, path, reason });
19624
20689
  }
19625
- function scopeIdentityMissingResponse() {
19626
- return response2(503, "scope.identity_missing", "A deployment service identity is required for this scope");
20690
+ function createRotationState(active2) {
20691
+ if (typeof active2 !== "string" || active2.length === 0) fail("ACTIVE_COUNT", "active", "active-missing");
20692
+ return { sealed: [], active: active2, ledger: [] };
19627
20693
  }
19628
- function enforceControlRouteScope(input) {
19629
- let actual;
19630
- try {
19631
- actual = input.provider.forRequest(input.request, input.requestOptions).serviceId;
19632
- } catch (caught) {
19633
- if (caught instanceof ServiceIdentityMissingError) {
19634
- console.error({ schema: "sdt.scope/v1", code: "scope.identity_missing", route: input.route });
19635
- return { response: scopeIdentityMissingResponse() };
19636
- }
19637
- throw caught;
20694
+ function assertStrictlyBefore(lastD1_1, firstD1_2) {
20695
+ assertSortableUniqueId(lastD1_1);
20696
+ assertSortableUniqueId(firstD1_2);
20697
+ if (compareSortableUniqueId(lastD1_1, firstD1_2) >= 0) {
20698
+ fail("ORDER_INVARIANT", "last(D1-1)<first(D1-2)", "order-invariant");
19638
20699
  }
19639
- if (input.pathServiceId !== actual) {
19640
- console.warn({ schema: "sdt.scope/v1", code: "scope.mismatch", route: input.route });
19641
- return {
19642
- response: response2(403, "scope.mismatch", "The path service identity is not authorized for this deployment")
19643
- };
20700
+ }
20701
+ function maxSortableUniqueId(values) {
20702
+ if (values.length === 0) fail("SEAL_MAX_MISSING", "d1-1", "seal-max-missing");
20703
+ let max = assertSortableUniqueId(values[0]).value;
20704
+ for (let i = 1; i < values.length; i += 1) {
20705
+ const next = assertSortableUniqueId(values[i]).value;
20706
+ if (next > max) max = next;
19644
20707
  }
19645
- return { serviceId: actual };
20708
+ return max;
20709
+ }
20710
+ function rotateAppend(state, sealedId, shardSuids, nextActiveId) {
20711
+ if (sealedId !== state.active) fail("UNKNOWN_SHARD", sealedId, "unknown-shard");
20712
+ if (typeof nextActiveId !== "string" || nextActiveId.length === 0 || nextActiveId === sealedId) {
20713
+ fail("ACTIVE_COUNT", "active", "active-count");
20714
+ }
20715
+ if (state.sealed.some((entry) => entry.id === nextActiveId)) {
20716
+ fail("ACTIVE_COUNT", nextActiveId, "active-already-sealed");
20717
+ }
20718
+ if (state.sealed.some((entry) => entry.id === sealedId)) fail("IDENTITY_SWAP", sealedId, "identity-swap");
20719
+ const lastSuid = maxSortableUniqueId(shardSuids);
20720
+ return {
20721
+ sealed: [...state.sealed, { id: sealedId, lastSuid }],
20722
+ active: nextActiveId,
20723
+ ledger: state.ledger
20724
+ };
20725
+ }
20726
+ function replaceSealedIdentity(state, index, nextId) {
20727
+ if (index < 0 || index >= state.sealed.length) fail("UNKNOWN_SHARD", "sealed", "unknown-shard");
20728
+ fail("IDENTITY_SWAP", state.sealed[index].id, `identity-swap:${nextId}`);
20729
+ }
20730
+ function refuseSealedWrite(state, shardId) {
20731
+ if (state.sealed.some((entry) => entry.id === shardId)) fail("SEALED_WRITE", shardId, "sealed-write");
20732
+ }
20733
+ function admitActiveWrite(state, shardId, firstOrNextSuid) {
20734
+ assertSortableUniqueId(firstOrNextSuid);
20735
+ refuseSealedWrite(state, shardId);
20736
+ if (shardId !== state.active) fail("UNKNOWN_SHARD", shardId, "unknown-shard");
20737
+ if (state.sealed.length === 0) return;
20738
+ const previous = state.sealed[state.sealed.length - 1];
20739
+ assertStrictlyBefore(previous.lastSuid, firstOrNextSuid);
20740
+ }
20741
+ function recordLateArrival(state, sealedShardId, suid) {
20742
+ assertSortableUniqueId(suid);
20743
+ const sealed = state.sealed.find((entry) => entry.id === sealedShardId);
20744
+ if (sealed === void 0) fail("UNKNOWN_SHARD", sealedShardId, "unknown-shard");
20745
+ if (compareSortableUniqueId(suid, sealed.lastSuid) > 0) {
20746
+ fail("ORDER_INVARIANT", sealedShardId, "not-late-arrival");
20747
+ }
20748
+ return {
20749
+ ...state,
20750
+ ledger: [...state.ledger, { shardId: sealed.id, suid, reason: "sealed-range" }]
20751
+ };
20752
+ }
20753
+ var SILENT_MERGE_TOKENS = [
20754
+ ["merge", "Ledger", "Into", "Sealed"],
20755
+ ["merge", "OnRead", "Sealed"],
20756
+ ["silent", "Merge", "Ledger"],
20757
+ ["ledger", ".concat(", "sealed"],
20758
+ ["sealed", ".concat(", "ledger"]
20759
+ ];
20760
+ function assertNoSilentLedgerMerge(source) {
20761
+ const lowered = source.toLowerCase();
20762
+ for (const token of SILENT_MERGE_TOKENS) {
20763
+ const needle = token.join("").toLowerCase();
20764
+ if (lowered.includes(needle)) fail("SILENT_MERGE", "ledger", "silent-merge");
20765
+ }
20766
+ }
20767
+ function refuseSilentLedgerMerge() {
20768
+ fail("SILENT_MERGE", "ledger", "silent-merge");
20769
+ }
20770
+ function refuseSafeWindowSeal() {
20771
+ fail("SAFEWINDOW_SEAL", "safe-window", "safewindow-seal");
19646
20772
  }
19647
20773
 
19648
20774
  // src/index.ts
@@ -19652,6 +20778,22 @@ function createRuntimeWorker(options = {}) {
19652
20778
  const storeProvider = options.storeProvider ?? POSTGRES_STORE_PROVIDER;
19653
20779
  return {
19654
20780
  async fetch(request, env, ctx) {
20781
+ const url = new URL(request.url);
20782
+ if (isIncidentMaintenancePath(url.pathname)) {
20783
+ return handleIncidentMaintenance(request, env, options.serviceIdentityProvider ?? envServiceIdentity(env));
20784
+ }
20785
+ const operatorRepair = url.pathname === "/operator/repair";
20786
+ const operatorBootstrap = url.pathname.startsWith("/operator/bootstrap/");
20787
+ if (operatorRepair) {
20788
+ const methodDenied = nonPostOperatorRepairResponse(request);
20789
+ if (methodDenied !== void 0) return methodDenied;
20790
+ const denied = await authenticateOperatorRepair(request, env.REPAIR_OPERATOR_TOKEN);
20791
+ if (denied !== void 0) return denied;
20792
+ }
20793
+ if (operatorBootstrap) {
20794
+ const denied = await authenticateOperatorBootstrap(request, env.REPAIR_OPERATOR_TOKEN);
20795
+ if (denied !== void 0) return denied;
20796
+ }
19655
20797
  const serviceIdentity = options.serviceIdentityProvider ?? envServiceIdentity(env);
19656
20798
  const requestIdentityOptions = { allowG11Verification: env.G11_VERIFICATION_ENABLED === "true" };
19657
20799
  let requestServiceId;
@@ -19660,7 +20802,6 @@ function createRuntimeWorker(options = {}) {
19660
20802
  } catch {
19661
20803
  return scopeIdentityMissingResponse();
19662
20804
  }
19663
- const url = new URL(request.url);
19664
20805
  if (url.pathname === "/api/sekiban/serialized/commit") {
19665
20806
  return handleSerializedCommit(request, env, {
19666
20807
  domainDeliveryClass: options.config?.deliveryClass,
@@ -19682,14 +20823,19 @@ function createRuntimeWorker(options = {}) {
19682
20823
  serviceIdentityProvider: serviceIdentity
19683
20824
  });
19684
20825
  }
19685
- if (url.pathname === "/operator/repair") {
19686
- return handleOperatorRepair(request, env, nativeTracingFromContext(ctx), serviceIdentity);
20826
+ if (operatorRepair) {
20827
+ return handleAuthorizedOperatorRepair(
20828
+ request,
20829
+ env,
20830
+ nativeTracingFromContext(ctx),
20831
+ serviceIdentity
20832
+ );
19687
20833
  }
19688
- if (url.pathname.startsWith("/operator/bootstrap/")) {
19689
- return handleOperatorBootstrap(request, env, storeProvider);
20834
+ if (operatorBootstrap) {
20835
+ return handleAuthorizedOperatorBootstrap(request, env, storeProvider);
19690
20836
  }
19691
20837
  if (url.pathname === "/internal/downstream/drain" && request.method === "POST") {
19692
- return handleOutboxDrainRequest(request, env);
20838
+ return handleOutboxDrainRequest(request, env, { serviceIdentityProvider: serviceIdentity });
19693
20839
  }
19694
20840
  if (url.pathname === "/internal/projection/lag") {
19695
20841
  return handleProjectionLag(request, env, composition.projectors, storeProvider, serviceIdentity);
@@ -19822,6 +20968,8 @@ export {
19822
20968
  GLOBAL_COMPLETENESS_INTERIM_DISPOSITION,
19823
20969
  GlobalCompletenessReconciler,
19824
20970
  IDLE_EXPERIMENT_SCHEDULE_MS,
20971
+ IncidentLifecycle,
20972
+ IncidentLifecycleError,
19825
20973
  JournalDurableObject,
19826
20974
  MAX_PUBLISHED_SAFE_WINDOW_MS,
19827
20975
  MAX_SERVICE_BINDING_INVOCATIONS_PER_REQUEST,
@@ -19842,22 +20990,27 @@ export {
19842
20990
  SORTABLE_UNIQUE_ID_TICKS_DIGITS,
19843
20991
  ScopeNameError,
19844
20992
  ServiceIdentityMissingError,
20993
+ ShardRotationError,
19845
20994
  SortableUniqueIdError,
19846
20995
  TEST_SERVICE_ID_HEADER,
19847
20996
  TagDurableObject,
19848
20997
  TagStateDurableObject,
19849
20998
  UnsafeWindowMaterializedViewError,
19850
20999
  UnsafeWindowMaterializedViewStore,
21000
+ admitActiveWrite,
19851
21001
  allocateOrderRange,
19852
21002
  assertCanonicalEventType,
19853
21003
  assertCommitSnapshotLogSize,
19854
21004
  assertDotNetTicks,
21005
+ assertNoSilentLedgerMerge,
19855
21006
  assertSortableUniqueId,
21007
+ assertStrictlyBefore,
19856
21008
  beginWorkerInvocationObservation,
19857
21009
  bootstrapDigest,
19858
21010
  buildScopeName,
19859
21011
  calculateUnattributedRatio,
19860
21012
  canonicalEventType,
21013
+ checkConfiguredBearer,
19861
21014
  chooseQueryBacking,
19862
21015
  classifyDirectDoorbellFailure,
19863
21016
  classifyReactivationCause,
@@ -19869,6 +21022,7 @@ export {
19869
21022
  createCommitTraceConsoleSink,
19870
21023
  createPostgresBootstrapAdapter,
19871
21024
  createPostgresStoreProvider,
21025
+ createRotationState,
19872
21026
  createRuntimeCommitPort,
19873
21027
  createRuntimeWorker,
19874
21028
  createTraceCorrelationId,
@@ -19893,8 +21047,10 @@ export {
19893
21047
  isScopeServiceId,
19894
21048
  isSortableUniqueId,
19895
21049
  isUuidV7,
21050
+ maxSortableUniqueId,
19896
21051
  nativeTracingFromContext,
19897
21052
  noOpNativeTracing,
21053
+ normalizeIncidentIdentity,
19898
21054
  observeLegacySortableUniqueIdDecision,
19899
21055
  observedIdleGapLowerBoundMs,
19900
21056
  parseBootstrapDump,
@@ -19904,10 +21060,16 @@ export {
19904
21060
  processDownstreamDoorbell,
19905
21061
  readDirectDoorbellConfig,
19906
21062
  readDomainDeliveryClass,
21063
+ recordLateArrival,
21064
+ refuseSafeWindowSeal,
21065
+ refuseSealedWrite,
21066
+ refuseSilentLedgerMerge,
19907
21067
  registeredEventParsers,
21068
+ replaceSealedIdentity,
19908
21069
  requestServiceIdentity,
19909
21070
  requireServiceIdentity,
19910
21071
  resolveDeliveryIdentity,
21072
+ rotateAppend,
19911
21073
  safeWindowCeilingExceeded,
19912
21074
  safeWindowCutoffSuid,
19913
21075
  safeWindowMs,