@dbsp/core 3.2.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3430,8 +3430,15 @@ function capabilityAvailable(descriptor, context) {
3430
3430
  }
3431
3431
  }
3432
3432
  }
3433
+ var RETIRED_CONNECTION_OWNERSHIP_MEMBERS = ["checkout", "release"];
3434
+ function retiredConnectionOwnershipMember(value) {
3435
+ return RETIRED_CONNECTION_OWNERSHIP_MEMBERS.find((member) => member in value);
3436
+ }
3433
3437
  function isOperationRuntime(semantics) {
3434
- return "checkout" in semantics && "release" in semantics && "writeIntentJournal" in semantics && "begin" in semantics && "setLockTimeout" in semantics && "acquireLocks" in semantics && "observeContext" in semantics && "observeOperation" in semantics && "checkGuard" in semantics && "executeOperation" in semantics && "writeCompletionJournal" in semantics && "commit" in semantics && "rollback" in semantics && "writeObservedJournal" in semantics && "isLockTimeout" in semantics;
3438
+ if (retiredConnectionOwnershipMember(semantics) !== void 0) {
3439
+ return false;
3440
+ }
3441
+ return "writeIntentJournal" in semantics && "begin" in semantics && "setLockTimeout" in semantics && "acquireLocks" in semantics && "observeContext" in semantics && "observeOperation" in semantics && "checkGuard" in semantics && "executeOperation" in semantics && "writeCompletionJournal" in semantics && "commit" in semantics && "rollback" in semantics && "writeObservedJournal" in semantics && "isLockTimeout" in semantics;
3435
3442
  }
3436
3443
  function validateRulePrecedenceFacts(facts, rules) {
3437
3444
  const rulesByKey = new Map(rules.map((rule) => [ruleKey(rule), rule]));
@@ -3574,6 +3581,12 @@ var PackRegistry = class {
3574
3581
  compositionFactOwnerByKind.set(kind, owner);
3575
3582
  };
3576
3583
  for (const pack of packs) {
3584
+ const retiredMember = pack.executionCoordinator ? retiredConnectionOwnershipMember(pack.executionCoordinator) : void 0;
3585
+ if (retiredMember !== void 0) {
3586
+ throw new Error(
3587
+ `transition pack executionCoordinator declares retired connection ownership member ${retiredMember}`
3588
+ );
3589
+ }
3577
3590
  const transactionDomain = pack.transactionDomain ?? pack.executionCoordinator?.transactionDomain;
3578
3591
  if (pack.executionCoordinator && !transactionDomain) {
3579
3592
  throw new Error(
@@ -3892,6 +3905,129 @@ function assumptionAccepted(assumption, policy) {
3892
3905
  // src/transition/resume.ts
3893
3906
  import { createHash as createHash2 } from "crypto";
3894
3907
 
3908
+ // src/transition/transition-lessor.ts
3909
+ var TRANSITION_LESSOR_BRAND = /* @__PURE__ */ Symbol.for("dbsp.transition.lessor");
3910
+ var TRANSITION_LESSOR_PROTOCOL_VERSION = 1;
3911
+ var TRANSITION_LESSOR_REJECTION = "transition target must be a core-minted lessor";
3912
+ async function captureLease(value) {
3913
+ const { query, release } = leaseMembers(value);
3914
+ if (typeof query !== "function" || typeof release !== "function") {
3915
+ const rejection = new Error(
3916
+ "A transition lessor must acquire a lease exposing query() and release(). This acquisition returned an unusable member pair."
3917
+ );
3918
+ releaseMalformedAcquisition(value, release, rejection);
3919
+ throw rejection;
3920
+ }
3921
+ return {
3922
+ raw: value,
3923
+ query,
3924
+ release
3925
+ };
3926
+ }
3927
+ async function acquireTransitionLease(target) {
3928
+ const { raw: raw2, query, release } = await captureLease(await target.acquire());
3929
+ let closed = false;
3930
+ const session = Object.freeze({
3931
+ query: (sql2, params) => {
3932
+ if (closed) {
3933
+ return Promise.reject(
3934
+ new Error("transition lease was already given back")
3935
+ );
3936
+ }
3937
+ return query.call(raw2, sql2, params);
3938
+ }
3939
+ });
3940
+ const releaseLease = (failure) => {
3941
+ if (closed) {
3942
+ return Promise.resolve();
3943
+ }
3944
+ closed = true;
3945
+ try {
3946
+ const returned = failure ? release.call(raw2, releaseArgument(failure)) : release.call(raw2);
3947
+ void Promise.resolve(returned).catch(() => void 0);
3948
+ } catch {
3949
+ }
3950
+ return Promise.resolve();
3951
+ };
3952
+ return Object.freeze({
3953
+ session,
3954
+ release: releaseLease
3955
+ });
3956
+ }
3957
+ function leaseMembers(value) {
3958
+ if (value == null || typeof value !== "object" && typeof value !== "function") {
3959
+ return { query: void 0, release: void 0 };
3960
+ }
3961
+ const release = readMember(value, "release");
3962
+ return { query: readMember(value, "query"), release };
3963
+ }
3964
+ function readMember(value, member) {
3965
+ try {
3966
+ return value[member];
3967
+ } catch {
3968
+ return void 0;
3969
+ }
3970
+ }
3971
+ function releaseMalformedAcquisition(value, release, rejection) {
3972
+ if (typeof release !== "function") {
3973
+ return;
3974
+ }
3975
+ try {
3976
+ void Promise.resolve(release.call(value, rejection)).catch(() => void 0);
3977
+ } catch {
3978
+ }
3979
+ }
3980
+ function createTransitionLessor(acquire) {
3981
+ const lessor = { acquire };
3982
+ Object.defineProperty(lessor, TRANSITION_LESSOR_BRAND, {
3983
+ value: Object.freeze({
3984
+ protocolVersion: TRANSITION_LESSOR_PROTOCOL_VERSION
3985
+ }),
3986
+ writable: false,
3987
+ configurable: false
3988
+ });
3989
+ return Object.freeze(lessor);
3990
+ }
3991
+ function isTransitionLessor(value) {
3992
+ try {
3993
+ if (value == null || typeof value !== "object" && typeof value !== "function" || Array.isArray(value)) {
3994
+ return false;
3995
+ }
3996
+ const brand = value[TRANSITION_LESSOR_BRAND];
3997
+ return brand != null && typeof brand === "object" && brand.protocolVersion === TRANSITION_LESSOR_PROTOCOL_VERSION && typeof value.acquire === "function";
3998
+ } catch {
3999
+ return false;
4000
+ }
4001
+ }
4002
+ var TransitionLeaseFailureError = class extends Error {
4003
+ constructor(cause) {
4004
+ super("transition lease released after a failure", { cause });
4005
+ this.name = "TransitionLeaseFailureError";
4006
+ }
4007
+ };
4008
+ function releaseArgument(failure) {
4009
+ return failure.error ? failure.error : new TransitionLeaseFailureError(failure.error);
4010
+ }
4011
+ function transitionLessorRejectionAssessment(artifact, lifecycle = "planned") {
4012
+ return {
4013
+ decision: "blocked",
4014
+ assurance: "unproven",
4015
+ lifecycle,
4016
+ continuation: "human-intervention-required",
4017
+ reasons: [
4018
+ {
4019
+ code: "context-mismatch",
4020
+ artifact,
4021
+ fact: {
4022
+ key: "transition-lessor",
4023
+ value: TRANSITION_LESSOR_REJECTION
4024
+ },
4025
+ scope: []
4026
+ }
4027
+ ]
4028
+ };
4029
+ }
4030
+
3895
4031
  // src/transition/validation.ts
3896
4032
  function sameTrustRoot2(left, right) {
3897
4033
  return stableJson(left) === stableJson(right);
@@ -4787,6 +4923,11 @@ function latestIntent(step, events) {
4787
4923
  recordedAt: recordedAt()
4788
4924
  };
4789
4925
  }
4926
+ function orderedPlanSteps(plan2, stepById) {
4927
+ return plan2.segments.flatMap(
4928
+ (segment) => segment.stepIds.map((stepId2) => stepById.get(stepId2)).filter((step) => step !== void 0)
4929
+ );
4930
+ }
4790
4931
  function resumeRequiredResult(params) {
4791
4932
  const hasCompleted = params.completed.length > 0;
4792
4933
  return {
@@ -4925,80 +5066,71 @@ function completedJournal(intent, completion, observations) {
4925
5066
  observedOutcome
4926
5067
  };
4927
5068
  }
4928
- async function observeStep(registry, step, target, baseContext, phase) {
5069
+ async function withObservedStep(registry, step, target, baseContext, phase, use) {
4929
5070
  const resolution = registry.resolveOperation(step.operation);
4930
5071
  if (!resolution.ok || !isOperationRuntime(resolution.semantics)) {
4931
- return { ok: false, detail: "operation runtime missing" };
5072
+ return use({ ok: false, detail: "operation runtime missing" });
4932
5073
  }
4933
5074
  const runtime = resolution.semantics;
4934
5075
  const issuer = registry.resolveIssuer(step.operation.operationKind.artifact);
4935
5076
  if (!issuer) {
4936
- return { ok: false, detail: "operation observation issuer missing" };
5077
+ return use({ ok: false, detail: "operation observation issuer missing" });
4937
5078
  }
4938
- let client;
5079
+ let lease;
5080
+ let failed = false;
5081
+ let failure;
4939
5082
  try {
4940
- client = await runtime.checkout(target);
4941
- let context = await runtime.observeContext(
4942
- client,
4943
- step.operation,
4944
- baseContext
4945
- );
4946
- context = registry.contextWithDerivedCapabilities(context);
4947
- const liveContextMatch = matchLiveObservationContext({
4948
- expected: baseContext,
4949
- actual: context
4950
- });
4951
- if (!liveContextMatch.ok) {
4952
- await runtimeSafeRelease(runtime, client);
4953
- return { ok: false, detail: liveContextMatch.detail };
4954
- }
4955
- const observed = await runtime.observeOperation(
4956
- client,
4957
- step.operation,
4958
- context,
4959
- phase,
4960
- issuer
4961
- );
4962
- return {
4963
- ok: true,
4964
- observations: observed.observations,
4965
- fingerprint: observed.fingerprint,
4966
- client,
4967
- runtime
4968
- };
4969
- } catch (error) {
4970
- if (client) {
4971
- await runtimeSafeRelease(
4972
- resolution.ok && isOperationRuntime(resolution.semantics) ? resolution.semantics : void 0,
5083
+ let observation;
5084
+ try {
5085
+ lease = await acquireTransitionLease(target);
5086
+ const client = { opaqueClient: lease.session };
5087
+ let context = await runtime.observeContext(
4973
5088
  client,
4974
- error
5089
+ step.operation,
5090
+ baseContext
4975
5091
  );
5092
+ context = registry.contextWithDerivedCapabilities(context);
5093
+ const liveContextMatch = matchLiveObservationContext({
5094
+ expected: baseContext,
5095
+ actual: context
5096
+ });
5097
+ if (!liveContextMatch.ok) {
5098
+ observation = { ok: false, detail: liveContextMatch.detail };
5099
+ } else {
5100
+ const observed = await runtime.observeOperation(
5101
+ client,
5102
+ step.operation,
5103
+ context,
5104
+ phase,
5105
+ issuer
5106
+ );
5107
+ observation = {
5108
+ ok: true,
5109
+ observations: observed.observations,
5110
+ fingerprint: observed.fingerprint,
5111
+ writeJournal: async (journal) => runtime.writeObservedJournal(client, journal)
5112
+ };
5113
+ }
5114
+ } catch (error) {
5115
+ failed = true;
5116
+ failure = error;
5117
+ observation = {
5118
+ ok: false,
5119
+ detail: error instanceof Error ? error.message : "observation failed"
5120
+ };
5121
+ }
5122
+ try {
5123
+ return await use(observation);
5124
+ } catch (error) {
5125
+ failed = true;
5126
+ failure = error;
5127
+ throw error;
5128
+ }
5129
+ } finally {
5130
+ if (lease) {
5131
+ await lease.release(failed ? { error: failure } : void 0);
4976
5132
  }
4977
- return {
4978
- ok: false,
4979
- detail: error instanceof Error ? error.message : "observation failed"
4980
- };
4981
- }
4982
- }
4983
- async function runtimeSafeRelease(runtime, client, error) {
4984
- if (!runtime) {
4985
- return;
4986
- }
4987
- try {
4988
- await runtime.release(client, error);
4989
- } catch {
4990
- }
4991
- }
4992
- async function releaseObservedAttempt(attempt, error) {
4993
- if (attempt.ok) {
4994
- await runtimeSafeRelease(attempt.runtime, attempt.client, error);
4995
- }
4996
- }
4997
- async function writeObserved(attempt, journal) {
4998
- if (!attempt.ok) {
4999
- return;
5000
5133
  }
5001
- await attempt.runtime.writeObservedJournal(attempt.client, journal);
5002
5134
  }
5003
5135
  function stepAssumption(plan2, assumptionId2) {
5004
5136
  return plan2.assumptions.find((assumption) => assumption.id === assumptionId2);
@@ -5035,6 +5167,43 @@ async function resumeTransitionRun(registry, input) {
5035
5167
  if (!evidenceContext) {
5036
5168
  return contextMismatch("loaded plan contains no durable evidence context");
5037
5169
  }
5170
+ const referencedAssumptions = /* @__PURE__ */ new Set();
5171
+ for (const step of loaded.plan.steps) {
5172
+ for (const assumptionId2 of step.restsOnAssumptions) {
5173
+ referencedAssumptions.add(assumptionId2);
5174
+ }
5175
+ const policyFailure = validatePolicyForStep(
5176
+ loaded.plan,
5177
+ step,
5178
+ input.policy
5179
+ );
5180
+ if (policyFailure) {
5181
+ return policyFailure;
5182
+ }
5183
+ }
5184
+ for (const assumption of loaded.plan.assumptions) {
5185
+ if (!referencedAssumptions.has(assumption.id) && !assumptionAccepted(assumption, input.policy)) {
5186
+ return unacceptedPlanAssumption(loaded.plan, assumption);
5187
+ }
5188
+ }
5189
+ const stepById = new Map(
5190
+ loaded.plan.steps.map((step) => [step.stepId, step])
5191
+ );
5192
+ const groupedResult = groupEvents(loaded.events, loaded.run.runId, stepById);
5193
+ if (!groupedResult.ok) {
5194
+ return contextMismatch(groupedResult.detail);
5195
+ }
5196
+ const grouped = groupedResult.grouped;
5197
+ if (!isTransitionLessor(input.target)) {
5198
+ return {
5199
+ assessment: transitionLessorRejectionAssessment(
5200
+ RESUMER_ARTIFACT,
5201
+ loaded.events.length === 0 ? "planned" : "outcome-unknown"
5202
+ ),
5203
+ journals: [],
5204
+ observations: []
5205
+ };
5206
+ }
5038
5207
  const baseContext = await input.readContext(input.target, loaded.run);
5039
5208
  const runContextMatch = matchRunObservationContext({
5040
5209
  run: loaded.run,
@@ -5074,36 +5243,7 @@ async function resumeTransitionRun(registry, input) {
5074
5243
  `loaded plan failed semantic invariant validation: ${semanticDiagnostic.detail}`
5075
5244
  );
5076
5245
  }
5077
- const referencedAssumptions = /* @__PURE__ */ new Set();
5078
- for (const step of loaded.plan.steps) {
5079
- for (const assumptionId2 of step.restsOnAssumptions) {
5080
- referencedAssumptions.add(assumptionId2);
5081
- }
5082
- const policyFailure = validatePolicyForStep(
5083
- loaded.plan,
5084
- step,
5085
- input.policy
5086
- );
5087
- if (policyFailure) {
5088
- return policyFailure;
5089
- }
5090
- }
5091
- for (const assumption of loaded.plan.assumptions) {
5092
- if (!referencedAssumptions.has(assumption.id) && !assumptionAccepted(assumption, input.policy)) {
5093
- return unacceptedPlanAssumption(loaded.plan, assumption);
5094
- }
5095
- }
5096
- const stepById = new Map(
5097
- loaded.plan.steps.map((step) => [step.stepId, step])
5098
- );
5099
- const groupedResult = groupEvents(loaded.events, loaded.run.runId, stepById);
5100
- if (!groupedResult.ok) {
5101
- return contextMismatch(groupedResult.detail);
5102
- }
5103
- const grouped = groupedResult.grouped;
5104
- const orderedSteps = loaded.plan.segments.flatMap(
5105
- (segment) => segment.stepIds.map((stepId2) => stepById.get(stepId2)).filter(Boolean)
5106
- );
5246
+ const orderedSteps = orderedPlanSteps(loaded.plan, stepById);
5107
5247
  const completed = [];
5108
5248
  const observations = [];
5109
5249
  for (const step of orderedSteps) {
@@ -5138,65 +5278,90 @@ async function resumeTransitionRun(registry, input) {
5138
5278
  }
5139
5279
  const transactional = effects.effects.execution.transaction !== "forbids-transaction";
5140
5280
  if (observedJournal?.outcome === "completed" || completion) {
5141
- const after2 = await observeStep(
5281
+ const after2 = await withObservedStep(
5142
5282
  registry,
5143
5283
  step,
5144
5284
  input.target,
5145
5285
  baseContext,
5146
- "after"
5286
+ "after",
5287
+ async (observation) => {
5288
+ if (!observation.ok || !fingerprintMatches(step.expectedAfter, observation.fingerprint)) {
5289
+ return {
5290
+ kind: "unmatched",
5291
+ detail: observation.ok ? "current fingerprint no longer matches expectedAfter" : observation.detail
5292
+ };
5293
+ }
5294
+ const journal = observedJournal?.outcome === "completed" ? observedJournal : completedJournal(intent, completion, observation.observations);
5295
+ if (!observedJournal) {
5296
+ await observation.writeJournal(journal);
5297
+ }
5298
+ return {
5299
+ kind: "completed",
5300
+ observations: observation.observations,
5301
+ journal
5302
+ };
5303
+ }
5147
5304
  );
5148
- if (after2.ok && fingerprintMatches(step.expectedAfter, after2.fingerprint)) {
5305
+ if (after2.kind === "completed") {
5149
5306
  observations.push(...after2.observations);
5150
- const journal = observedJournal?.outcome === "completed" ? observedJournal : completedJournal(intent, completion, after2.observations);
5151
- if (!observedJournal) {
5152
- await writeObserved(after2, journal);
5153
- }
5154
- await releaseObservedAttempt(after2);
5155
- completed.push(journal);
5307
+ completed.push(after2.journal);
5156
5308
  continue;
5157
5309
  }
5158
- await releaseObservedAttempt(after2);
5159
5310
  return unknownResult({
5160
5311
  step,
5161
5312
  completed,
5162
5313
  observations,
5163
- detail: after2.ok ? "current fingerprint no longer matches expectedAfter" : after2.detail
5314
+ detail: after2.detail
5164
5315
  });
5165
5316
  }
5166
5317
  if (transactional) {
5167
- const before2 = await observeStep(
5318
+ const before2 = await withObservedStep(
5168
5319
  registry,
5169
5320
  step,
5170
5321
  input.target,
5171
5322
  baseContext,
5172
- "before"
5323
+ "before",
5324
+ async (observation) => ({
5325
+ kind: observation.ok && fingerprintMatches(step.expectedBefore, observation.fingerprint) ? "resume-required" : "unmatched",
5326
+ observations: observation.ok ? observation.observations : []
5327
+ })
5173
5328
  );
5174
- if (before2.ok && fingerprintMatches(step.expectedBefore, before2.fingerprint)) {
5329
+ if (before2.kind === "resume-required") {
5175
5330
  observations.push(...before2.observations);
5176
- await releaseObservedAttempt(before2);
5177
5331
  return resumeRequiredResult({
5178
5332
  step,
5179
5333
  completed,
5180
5334
  observations
5181
5335
  });
5182
5336
  }
5183
- await releaseObservedAttempt(before2);
5184
- const after2 = await observeStep(
5337
+ const after2 = await withObservedStep(
5185
5338
  registry,
5186
5339
  step,
5187
5340
  input.target,
5188
5341
  baseContext,
5189
- "after"
5342
+ "after",
5343
+ async (observation) => {
5344
+ if (!observation.ok || !fingerprintMatches(step.expectedAfter, observation.fingerprint)) {
5345
+ return { kind: "unmatched" };
5346
+ }
5347
+ const journal = completedJournal(
5348
+ intent,
5349
+ void 0,
5350
+ observation.observations
5351
+ );
5352
+ await observation.writeJournal(journal);
5353
+ return {
5354
+ kind: "completed",
5355
+ observations: observation.observations,
5356
+ journal
5357
+ };
5358
+ }
5190
5359
  );
5191
- if (after2.ok && fingerprintMatches(step.expectedAfter, after2.fingerprint)) {
5360
+ if (after2.kind === "completed") {
5192
5361
  observations.push(...after2.observations);
5193
- const journal = completedJournal(intent, void 0, after2.observations);
5194
- await writeObserved(after2, journal);
5195
- await releaseObservedAttempt(after2);
5196
- completed.push(journal);
5362
+ completed.push(after2.journal);
5197
5363
  continue;
5198
5364
  }
5199
- await releaseObservedAttempt(after2);
5200
5365
  return unknownResult({
5201
5366
  step,
5202
5367
  completed,
@@ -5204,39 +5369,53 @@ async function resumeTransitionRun(registry, input) {
5204
5369
  detail: "transactional intent had no completion and current state matches neither expectedBefore nor expectedAfter"
5205
5370
  });
5206
5371
  }
5207
- const after = await observeStep(
5372
+ const after = await withObservedStep(
5208
5373
  registry,
5209
5374
  step,
5210
5375
  input.target,
5211
5376
  baseContext,
5212
- "after"
5377
+ "after",
5378
+ async (observation) => {
5379
+ if (!observation.ok || !fingerprintMatches(step.expectedAfter, observation.fingerprint)) {
5380
+ return { kind: "unmatched" };
5381
+ }
5382
+ const journal = completedJournal(
5383
+ intent,
5384
+ void 0,
5385
+ observation.observations
5386
+ );
5387
+ await observation.writeJournal(journal);
5388
+ return {
5389
+ kind: "completed",
5390
+ observations: observation.observations,
5391
+ journal
5392
+ };
5393
+ }
5213
5394
  );
5214
- if (after.ok && fingerprintMatches(step.expectedAfter, after.fingerprint)) {
5395
+ if (after.kind === "completed") {
5215
5396
  observations.push(...after.observations);
5216
- const journal = completedJournal(intent, void 0, after.observations);
5217
- await writeObserved(after, journal);
5218
- await releaseObservedAttempt(after);
5219
- completed.push(journal);
5397
+ completed.push(after.journal);
5220
5398
  continue;
5221
5399
  }
5222
- await releaseObservedAttempt(after);
5223
- const before = await observeStep(
5400
+ const before = await withObservedStep(
5224
5401
  registry,
5225
5402
  step,
5226
5403
  input.target,
5227
5404
  baseContext,
5228
- "before"
5405
+ "before",
5406
+ async (observation) => ({
5407
+ kind: observation.ok && fingerprintMatches(step.expectedBefore, observation.fingerprint) ? "resume-required" : "unmatched",
5408
+ observations: observation.ok ? observation.observations : []
5409
+ })
5229
5410
  );
5230
- if (before.ok && fingerprintMatches(step.expectedBefore, before.fingerprint)) {
5411
+ if (before.kind === "resume-required") {
5231
5412
  observations.push(...before.observations);
5232
- await releaseObservedAttempt(before);
5233
5413
  return resumeRequiredResult({
5234
5414
  step,
5235
5415
  completed,
5236
5416
  observations
5237
5417
  });
5238
5418
  }
5239
- await releaseObservedAttempt(before);
5240
5419
  return unknownResult({
5241
5420
  step,
5242
5421
  completed,
@@ -5801,16 +5980,25 @@ function createApplier(registry) {
5801
5980
  );
5802
5981
  }
5803
5982
  let client;
5983
+ let lease;
5804
5984
  let committed = false;
5805
5985
  let transactionStarted = false;
5806
- let releaseError;
5986
+ let releaseFailure;
5807
5987
  let active = first;
5808
5988
  let activeContext = runtimeContext;
5809
5989
  let activeOperationAttempted = false;
5810
5990
  let activeNonRollbackableOperationExecuted = false;
5811
5991
  const completedInSegment = [];
5992
+ if (!isTransitionLessor(target)) {
5993
+ return resultWithCommittedJournals({
5994
+ assessment: transitionLessorRejectionAssessment(APPLIER_ARTIFACT),
5995
+ journals: [],
5996
+ observations: []
5997
+ });
5998
+ }
5812
5999
  try {
5813
- client = await segmentCoordinator.checkout(target);
6000
+ lease = await acquireTransitionLease(target);
6001
+ client = { opaqueClient: lease.session };
5814
6002
  const executionClient = client;
5815
6003
  if (transactional) {
5816
6004
  await segmentCoordinator.begin(executionClient);
@@ -6361,7 +6549,7 @@ function createApplier(registry) {
6361
6549
  journals.push(journal);
6362
6550
  }
6363
6551
  } catch (error) {
6364
- releaseError = error;
6552
+ releaseFailure = { error };
6365
6553
  if (error instanceof CommitOutcomeUncertainError) {
6366
6554
  if (client && transactionStarted) {
6367
6555
  await segmentCoordinator.rollback(client).catch(() => void 0);
@@ -6608,11 +6796,8 @@ function createApplier(registry) {
6608
6796
  outcomeDurable: true
6609
6797
  });
6610
6798
  } finally {
6611
- if (client) {
6612
- try {
6613
- await segmentCoordinator.release(client, releaseError);
6614
- } catch {
6615
- }
6799
+ if (lease) {
6800
+ await lease.release(releaseFailure);
6616
6801
  }
6617
6802
  }
6618
6803
  }
@@ -9674,22 +9859,8 @@ function validateIssuedObservation(observation, context) {
9674
9859
  detail: `observation ${observation.id ?? "unknown"} has an invalid role`
9675
9860
  };
9676
9861
  }
9677
- function transitionPool(target) {
9678
- if (isJsonObject3(target) && typeof target.connect === "function" && typeof target.release !== "function") {
9679
- return target;
9680
- }
9681
- throw new Error(
9682
- "transition proof target must be a Pool-like object with connect(); checked-out clients are not accepted"
9683
- );
9684
- }
9685
9862
  async function checkoutProofClient(target) {
9686
- const client = await transitionPool(target).connect();
9687
- if (!isJsonObject3(client) || typeof client.query !== "function" || typeof client.release !== "function") {
9688
- throw new Error(
9689
- "transition proof pool returned a client without query() and release()"
9690
- );
9691
- }
9692
- return client;
9863
+ return acquireTransitionLease(target);
9693
9864
  }
9694
9865
  function conclusionForObligation(obligation, evidence, expectedContext) {
9695
9866
  const result = concludeEvidenceForObligation({
@@ -10326,13 +10497,15 @@ async function proveTransitions(registry, compare, target, context, initialState
10326
10497
  let minServerVersionRequests = /* @__PURE__ */ new Map();
10327
10498
  const obligationClaimByCanonicalKey = /* @__PURE__ */ new Map();
10328
10499
  const multiCandidateSnapshot = !initialState && compare.candidates.length > 1;
10500
+ let sharedLease;
10329
10501
  let sharedClient;
10330
- let sharedReleaseError;
10502
+ let sharedReleaseFailure;
10331
10503
  const sharedIssuedRequestKeys = /* @__PURE__ */ new Set();
10332
10504
  let sharedIssued = { evidence: [], advisory: [] };
10333
10505
  try {
10334
10506
  if (multiCandidateSnapshot) {
10335
- sharedClient = await checkoutProofClient(target);
10507
+ sharedLease = await checkoutProofClient(target);
10508
+ sharedClient = sharedLease.session;
10336
10509
  const allRequiredObservations = [];
10337
10510
  for (const candidate of compare.candidates) {
10338
10511
  const rule = registry.resolveRule(candidate.rule);
@@ -10506,10 +10679,12 @@ async function proveTransitions(registry, compare, target, context, initialState
10506
10679
  issued = appendIssued(sharedIssued, additional);
10507
10680
  sharedIssued = appendIssued(sharedIssued, additional);
10508
10681
  } else {
10682
+ let lease;
10509
10683
  let client;
10510
- let releaseError;
10684
+ let releaseFailure;
10511
10685
  try {
10512
- client = await checkoutProofClient(target);
10686
+ lease = await checkoutProofClient(target);
10687
+ client = lease.session;
10513
10688
  proofContext = issuer.readContext ? await issuer.readContext(client, context, requiredObservations) : context;
10514
10689
  proofContext = registry.contextWithDerivedCapabilities(proofContext);
10515
10690
  const supportMismatch = ruleSupportMismatch(rule, proofContext);
@@ -10529,7 +10704,7 @@ async function proveTransitions(registry, compare, target, context, initialState
10529
10704
  proofContext
10530
10705
  );
10531
10706
  } catch (error) {
10532
- releaseError = error;
10707
+ releaseFailure = { error };
10533
10708
  return {
10534
10709
  kind: "blocked",
10535
10710
  assessment: artifactMismatch(
@@ -10538,8 +10713,8 @@ async function proveTransitions(registry, compare, target, context, initialState
10538
10713
  )
10539
10714
  };
10540
10715
  } finally {
10541
- if (client) {
10542
- client.release(releaseError);
10716
+ if (lease) {
10717
+ await lease.release(releaseFailure);
10543
10718
  }
10544
10719
  }
10545
10720
  }
@@ -10886,7 +11061,7 @@ async function proveTransitions(registry, compare, target, context, initialState
10886
11061
  planAssumptions.push(...assumptions2);
10887
11062
  }
10888
11063
  } catch (error) {
10889
- sharedReleaseError = error;
11064
+ sharedReleaseFailure = { error };
10890
11065
  return {
10891
11066
  kind: "blocked",
10892
11067
  assessment: artifactMismatch(
@@ -10895,8 +11070,8 @@ async function proveTransitions(registry, compare, target, context, initialState
10895
11070
  )
10896
11071
  };
10897
11072
  } finally {
10898
- if (sharedClient) {
10899
- sharedClient.release(sharedReleaseError);
11073
+ if (sharedLease) {
11074
+ await sharedLease.release(sharedReleaseFailure);
10900
11075
  }
10901
11076
  }
10902
11077
  let assumptions;
@@ -11041,11 +11216,13 @@ async function retryUnknownRecognition(registry, compare, target, context) {
11041
11216
  ]);
11042
11217
  const issuer = registry.resolveIssuer(recognition.rule.pack);
11043
11218
  if (issuer) {
11219
+ let lease;
11044
11220
  let client;
11045
- let releaseError;
11221
+ let releaseFailure;
11046
11222
  const issuedRequestKeys = /* @__PURE__ */ new Set();
11047
11223
  try {
11048
- client = await checkoutProofClient(target);
11224
+ lease = await checkoutProofClient(target);
11225
+ client = lease.session;
11049
11226
  let proofContext2 = issuer.readContext ? await issuer.readContext(client, context, recognitionRequests) : context;
11050
11227
  proofContext2 = registry.contextWithDerivedCapabilities(proofContext2);
11051
11228
  const supportMismatch2 = ruleSupportMismatch(rule, proofContext2);
@@ -11152,7 +11329,7 @@ async function retryUnknownRecognition(registry, compare, target, context) {
11152
11329
  }
11153
11330
  );
11154
11331
  } catch (error) {
11155
- releaseError = error;
11332
+ releaseFailure = { error };
11156
11333
  return {
11157
11334
  kind: "blocked",
11158
11335
  assessment: artifactMismatch(
@@ -11161,8 +11338,8 @@ async function retryUnknownRecognition(registry, compare, target, context) {
11161
11338
  )
11162
11339
  };
11163
11340
  } finally {
11164
- if (client) {
11165
- client.release(releaseError);
11341
+ if (lease) {
11342
+ await lease.release(releaseFailure);
11166
11343
  }
11167
11344
  }
11168
11345
  }
@@ -11259,7 +11436,7 @@ function createProver(registry) {
11259
11436
  case "unsupported":
11260
11437
  return { kind: "blocked", assessment: unsupported(compare) };
11261
11438
  case "unknown":
11262
- return retryUnknownRecognition(registry, compare, target, context);
11439
+ break;
11263
11440
  case "ambiguous":
11264
11441
  return { kind: "blocked", assessment: ambiguous(compare) };
11265
11442
  case "uncomposable":
@@ -11267,7 +11444,13 @@ function createProver(registry) {
11267
11444
  case "transitions":
11268
11445
  break;
11269
11446
  }
11270
- return proveTransitions(registry, compare, target, context);
11447
+ if (!isTransitionLessor(target)) {
11448
+ return {
11449
+ kind: "blocked",
11450
+ assessment: transitionLessorRejectionAssessment(PROVER_ARTIFACT)
11451
+ };
11452
+ }
11453
+ return compare.kind === "unknown" ? retryUnknownRecognition(registry, compare, target, context) : proveTransitions(registry, compare, target, context);
11271
11454
  }
11272
11455
  };
11273
11456
  }
@@ -11521,6 +11704,10 @@ function projectCompareToSingleCandidate(compare, entry) {
11521
11704
  }
11522
11705
 
11523
11706
  // src/transition/staged-orchestrator.ts
11707
+ var STAGED_ORCHESTRATOR_ARTIFACT = {
11708
+ id: semanticArtifactId("dbsp.core.transition.staged-orchestrator"),
11709
+ version: "0.1.0"
11710
+ };
11524
11711
  function uniqueResources2(resources) {
11525
11712
  const byKey = /* @__PURE__ */ new Map();
11526
11713
  for (const resource of resources) {
@@ -11679,6 +11866,20 @@ function createStagedTransitionOrchestrator(registry) {
11679
11866
  const observations = [];
11680
11867
  const completedProgress = /* @__PURE__ */ new Set();
11681
11868
  const maxIterations = input.maxIterations ?? 32;
11869
+ const targetRejectionResult = () => resultWithAggregate(
11870
+ transitionLessorRejectionAssessment(STAGED_ORCHESTRATOR_ARTIFACT),
11871
+ journals,
11872
+ observations
11873
+ );
11874
+ const proveAtTargetBoundary = async (compare, context) => {
11875
+ if (!isTransitionLessor(input.target)) {
11876
+ return { kind: "target-rejected" };
11877
+ }
11878
+ return {
11879
+ kind: "proof",
11880
+ proof: await prover.prove(compare, input.target, context)
11881
+ };
11882
+ };
11682
11883
  for (let iteration = 0; iteration < maxIterations; iteration += 1) {
11683
11884
  const context = await input.readContext();
11684
11885
  const current = await input.loadCurrent();
@@ -11696,7 +11897,14 @@ function createStagedTransitionOrchestrator(registry) {
11696
11897
  }
11697
11898
  if (compare.kind !== "transitions") {
11698
11899
  if (compare.kind === "unknown" && hasAppliedWork(journals)) {
11699
- const proof2 = await prover.prove(compare, input.target, context);
11900
+ const proofAtBoundary2 = await proveAtTargetBoundary(
11901
+ compare,
11902
+ context
11903
+ );
11904
+ if (proofAtBoundary2.kind === "target-rejected") {
11905
+ return targetRejectionResult();
11906
+ }
11907
+ const { proof: proof2 } = proofAtBoundary2;
11700
11908
  switch (proof2.kind) {
11701
11909
  case "no-drift":
11702
11910
  return {
@@ -11750,7 +11958,14 @@ function createStagedTransitionOrchestrator(registry) {
11750
11958
  );
11751
11959
  }
11752
11960
  const segmentCompare = projectCompareToSingleCandidate(compare, ready);
11753
- const proof = await prover.prove(segmentCompare, input.target, context);
11961
+ const proofAtBoundary = await proveAtTargetBoundary(
11962
+ segmentCompare,
11963
+ context
11964
+ );
11965
+ if (proofAtBoundary.kind === "target-rejected") {
11966
+ return targetRejectionResult();
11967
+ }
11968
+ const { proof } = proofAtBoundary;
11754
11969
  if (proof.kind !== "proven") {
11755
11970
  return resultWithAggregate(proof.assessment, journals, observations);
11756
11971
  }
@@ -21559,6 +21774,7 @@ export {
21559
21774
  SubqueryBuilder,
21560
21775
  SubqueryExpression,
21561
21776
  TABLE_META,
21777
+ TRANSITION_LESSOR_REJECTION,
21562
21778
  TableNotFoundError,
21563
21779
  UnhandledTypeInDialect,
21564
21780
  UnknownDialectError,
@@ -21569,6 +21785,7 @@ export {
21569
21785
  UpdateBuilder,
21570
21786
  UpsertBuilder,
21571
21787
  WindowBuilder,
21788
+ acquireTransitionLease,
21572
21789
  advisoryObservationId,
21573
21790
  aggOrderBy,
21574
21791
  and,
@@ -21630,6 +21847,7 @@ export {
21630
21847
  createRawCteBuilder,
21631
21848
  createRecursiveMetadata,
21632
21849
  createStagedTransitionOrchestrator,
21850
+ createTransitionLessor,
21633
21851
  decapitalize,
21634
21852
  defaultIndexName,
21635
21853
  defaultLogger,
@@ -21702,6 +21920,7 @@ export {
21702
21920
  isSubqueryExpression,
21703
21921
  export_isSubqueryRef as isSubqueryRef,
21704
21922
  isTableRef,
21923
+ isTransitionLessor,
21705
21924
  export_isUpdateIntent as isUpdateIntent,
21706
21925
  export_isUpsertIntent as isUpsertIntent,
21707
21926
  export_isWhereAnd as isWhereAnd,