@fabricorg/platform-host 8.1.1 → 8.2.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/CHANGELOG.md +53 -0
- package/README.md +154 -14
- package/dist/index.cjs +450 -154
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -7
- package/dist/index.d.ts +39 -7
- package/dist/index.js +451 -155
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,58 @@
|
|
|
1
1
|
# @fabricorg/platform-host
|
|
2
2
|
|
|
3
|
+
## 8.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 71d306f: Add optional action-level finalization for atomic, immediate after_adapters actions, including non-idempotent actions. Export JSON finalization types, registration validation, a manifest protocol descriptor and defineFinalizingAction to guard handlers against older hosts.
|
|
8
|
+
|
|
9
|
+
Host requires atomic transactions, invocation locking and transaction-scoped lease fencing before application execution. On normal success the hook consumes stored redacted outputs and commits transaction-local domain writes, declared events, outbox and the merged result together. Hook or validation failure rolls back finalization and settles reconciliation_required when durable provider effects exist, or failed when there are none, with redacted error text. Lease loss escapes without a stale-worker verdict. Handler preparation writes remain separately committed.
|
|
10
|
+
|
|
11
|
+
If entry to or COMMIT of the final success transaction (after adapters) fails with an unknown outcome, Host raises RecoverableFinalizationError and records no verdict. An uncommitted invocation stays running for recovery; a committed completed invocation retains its stored verdict and result for re-execution.
|
|
12
|
+
|
|
13
|
+
Explicit undefined on the optional hook return fields events, resultPatch and eventSchemaVersion is treated as omitted. Payloads and result-patch values retain strict JSON validation and reject undefined.
|
|
14
|
+
|
|
15
|
+
Failed, ambiguous, degraded and accepted/handoff outcomes do not invoke the hook. Existing actions retain their lifecycle.
|
|
16
|
+
|
|
17
|
+
Before adapters, normalize hook parameters and public handler data through a JSON round-trip, stripping private handler carriers. Dates become ISO strings and undefined object properties drop; serialization failures such as cycles or BigInt fail before provider execution. Handler events are normalized separately. Hook event schema versions default to the action version when omitted.
|
|
18
|
+
|
|
19
|
+
Add optional Host eventContracts name validation for events returned by the finalize hook. It checks eventType and subjectType membership only; handler events, payload schemas and schema-version compatibility are outside this option's scope. Recovery refusal also preserves ambiguity evidence for in-flight adapter rows alongside succeeded effects.
|
|
20
|
+
|
|
21
|
+
- d5e7caa: Add optional adapter-step deadlineMs durations overriding the Host-wide duration,
|
|
22
|
+
including when the Host duration is unset or zero. Action and module registration, manifest export,
|
|
23
|
+
and manifest normalization reject step durations that are not positive finite integers.
|
|
24
|
+
Host validates resolved steps before handler or adapter execution. Invalid
|
|
25
|
+
durations fail a first execution without invoking adapters; retries with recorded
|
|
26
|
+
applied effects or unknown adapter outcomes require reconciliation instead.
|
|
27
|
+
Manifests preserve valid step durations.
|
|
28
|
+
|
|
29
|
+
Correct adapterDeadlineMs documentation: it is a duration starting just before
|
|
30
|
+
step retry execution, not an epoch timestamp. Host-wide behavior is unchanged.
|
|
31
|
+
Budgets span retries; expiry prevents a subsequent attempt and signals cooperative
|
|
32
|
+
cancellation of an in-flight call. An aborted call that throws retains the existing
|
|
33
|
+
ambiguous-outcome reconciliation semantics.
|
|
34
|
+
|
|
35
|
+
- f32aae6: Commit versioned finalization preparation with handler writes, bounded to 256 KiB of canonical UTF-8 JSON. Recovery skips the handler, reuses succeeded adapter outputs and durable skips, and finalizes through the locked success transaction, including non-idempotent actions. Finalizing getInput callbacks receive normalized public data; legacy callbacks retain raw inputs.
|
|
36
|
+
|
|
37
|
+
Unknown adapter outcomes, incompatible protocol/action versions/ordered step identities, handoffs and governance refusal after effects retain reconciliation semantics. Missing checkpoints retain prior recovery behavior. Preparation validation/write failures roll back before adapters; unknown commit outcomes and lease loss remain recoverable without a stale verdict.
|
|
38
|
+
|
|
39
|
+
Apply migration 10 with ensureSchema before deploying checkpoint-writing Hosts. Updates omitting the field do not require its column.
|
|
40
|
+
|
|
41
|
+
A checkpoint bypasses non-idempotent interruption gates only when recorded adapter rows are succeeded. Failed rows preserve interruption settlement without repeating the provider call: failed without recorded success, reconciliation_required after another step succeeded. Leased non-idempotent replay additionally requires store-level and transaction-level writeAdapterInvocation fencing. Idempotent finalizing actions retain failed-row retry under existing adapter retry rules, using saved preparation.
|
|
42
|
+
|
|
43
|
+
On the live path, a finalizing action with an earlier succeeded adapter and a later permanent adapter failure now settles reconciliation_required instead of failed, without running the hook. Subsequent execution failures after a recorded provider effect likewise preserve reconciliation of partial progress.
|
|
44
|
+
|
|
45
|
+
- 3d24330: Permit bounded leased retries of non-idempotent actions after retryable pre-effect governance errors. The additive deferredBeforeEffectAttempt record field proves only the immediately preceding claim deferred before handler or adapter effects. Missing or stale proof retains interruption failure. Marker-write failure retains terminal failure where persistence succeeds; lease loss preserves fencing errors. Approval resume, inline execution, exhausted budgets, and errors after the governance window retain their existing refusal semantics. Workspace dependents receive compatible patch releases.
|
|
46
|
+
|
|
47
|
+
PostgreSQL migration 9 (`pre_effect_governance_deferral`) adds the nullable marker column. Run `ensureSchema()` before deploying Hosts that may defer non-idempotent actions. Invocation updates that do not carry the marker omit its column, preserving writes on schemas without migration 9.
|
|
48
|
+
|
|
49
|
+
## 8.1.2
|
|
50
|
+
|
|
51
|
+
### Patch Changes
|
|
52
|
+
|
|
53
|
+
- a297fcd: Republish workspace dependents of `@fabricorg/platform`, including transitive dependents, alongside the optional keyed privacy before-checksums (issue #104). Workspace ranges resolve to the released version when packed. Runtime source is unchanged.
|
|
54
|
+
- 0a93df2: Republish workspace dependents of `@fabricorg/platform`, including transitive dependents, alongside the evidence-packet access-events section (issue #101). Workspace ranges resolve to the released version when packed. Runtime source is unchanged.
|
|
55
|
+
|
|
3
56
|
## 8.1.1
|
|
4
57
|
|
|
5
58
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -55,7 +55,8 @@ immediately before the first policy-evaluation record is written. Host rethrows
|
|
|
55
55
|
without a failure/status write when:
|
|
56
56
|
|
|
57
57
|
- the invocation has a lease owner and a defined lease token;
|
|
58
|
-
- the action declares `idempotent: true
|
|
58
|
+
- the action declares `idempotent: true`, or Host successfully records the current claim in
|
|
59
|
+
`deferredBeforeEffectAttempt` through a lease-fenced write;
|
|
59
60
|
- `attemptCount` is below `retryableExecutionMaxAttempts`, a positive integer option defaulting to 5; and
|
|
60
61
|
- execution is not a `resumeApprovedInvocation()` call.
|
|
61
62
|
|
|
@@ -65,12 +66,24 @@ deferrals do not increment `failed`. The worker counts by error type: a `Retryab
|
|
|
65
66
|
that a store wrapper throws from Host's own reads or status writes also lands in `deferred`. In that
|
|
66
67
|
case the row likewise stays leased for a later claim, but outside this cap. Retry becomes eligible when the current lease expires, so the
|
|
67
68
|
delay is the remaining lease duration, followed by the worker's next claim; there is no immediate
|
|
68
|
-
retry or separate backoff. The next claim increments `attemptCount`. At the cap, or for an unleased
|
|
69
|
-
|
|
69
|
+
retry or separate backoff. The next claim increments `attemptCount`. At the cap, or for an unleased
|
|
70
|
+
or approval-resume invocation, Host records `failed` with the reason retry was refused,
|
|
70
71
|
through the error-text redactor. Approval resume is excluded because its temporary lease does not
|
|
71
72
|
establish that a recovery worker is configured. Ordinary application governance errors retain terminal failure behavior. Host store errors after
|
|
72
73
|
a recorded provider effect follow the recovery rules below.
|
|
73
74
|
|
|
75
|
+
Non-idempotent governance retry requires a durable `deferredBeforeEffectAttempt` equal to the
|
|
76
|
+
preceding claim count. Each retry must record its own pre-effect deferral to permit another claim.
|
|
77
|
+
A missing or stale marker retains interruption failure and manual reconciliation. Marker-write
|
|
78
|
+
failure ends terminally when the verdict can be persisted; lease loss retains the recoverable
|
|
79
|
+
fencing error. Handler and adapter errors are outside this deferral window.
|
|
80
|
+
|
|
81
|
+
PostgreSQL migration 9 (`pre_effect_governance_deferral`) adds the nullable marker column.
|
|
82
|
+
Run `ensureSchema()` before deploying Hosts that may defer non-idempotent actions. Invocation
|
|
83
|
+
updates without the marker omit the column and continue to work on schemas without migration 9.
|
|
84
|
+
If a marker write fails, Host refuses retry with `pre-effect marker could not be recorded` when
|
|
85
|
+
it can persist the terminal verdict; lease loss preserves its fencing error.
|
|
86
|
+
|
|
74
87
|
The retry region includes Host's single-record store writes for mutation-governance and HITL
|
|
75
88
|
checkpoints. Later attempts reuse these persisted decisions through the `mutationFootprint` and
|
|
76
89
|
`hitlRoute` guards. Callbacks should be safe to repeat and return stable invocation-scoped evidence.
|
|
@@ -133,7 +146,7 @@ Lifecycle records use deterministic checkpoint ids. Recovered idempotent actions
|
|
|
133
146
|
adapter that already reached `succeeded`. Built-in stores deduplicate event and policy inserts by
|
|
134
147
|
checkpoint ID. Adapter rows are mutable; stale-worker protection requires the optional
|
|
135
148
|
`writeAdapterInvocation` capability described below. A
|
|
136
|
-
reclaimed non-idempotent action inspects its adapter checkpoints: running or succeeded rows route
|
|
149
|
+
reclaimed non-idempotent action without compatible finalization preparation inspects its adapter checkpoints: running or succeeded rows route
|
|
137
150
|
to reconciliation; no rows or only failed rows retain terminal failure for manual review.
|
|
138
151
|
|
|
139
152
|
Returned `ambiguous` or `timeout` outcomes, timeouts thrown from `adapter.execute` (including an
|
|
@@ -201,8 +214,8 @@ On a store without atomic support, if the verdict cannot be written at all, no d
|
|
|
201
214
|
call succeeds: neither verdict nor checkpoint is saved, as after a crash mid-call. A leftover `running`
|
|
202
215
|
step can then re-run on recovery for an idempotent action under the same `adapterInvocationId`
|
|
203
216
|
idempotency key, within the worker recovery budget. A reclaimed non-idempotent action with a
|
|
204
|
-
running row without a matching durable handoff settles that step as unknown.
|
|
205
|
-
durable handoff instead ends `reconciliation_required` as an applied effect. Adapters should be
|
|
217
|
+
running row without a matching durable handoff settles that step as unknown. Without compatible
|
|
218
|
+
finalization preparation, a succeeded row or durable handoff instead ends `reconciliation_required` as an applied effect. Adapters should be
|
|
206
219
|
idempotent or carry that stable key. The same exposure applies if two atomic attempts roll back
|
|
207
220
|
and the fallback verdict cannot be written.
|
|
208
221
|
|
|
@@ -212,7 +225,7 @@ This includes later adapter checkpoints, obligation reads, and the completion wr
|
|
|
212
225
|
`before_adapters` and `after_adapters`. Recovery also protects Host store operations before
|
|
213
226
|
and around the handler when an earlier provider effect is recorded. Authorization, policy and handler callbacks
|
|
214
227
|
retain their own failure semantics. Idempotent recovery repairs recorded success/handoff evidence
|
|
215
|
-
without calling that step's provider.
|
|
228
|
+
without calling that step's provider. Without compatible finalization preparation, non-idempotent interruption ends `reconciliation_required`
|
|
216
229
|
instead of repairing the invocation. Lease loss retains its fencing error.
|
|
217
230
|
|
|
218
231
|
If neither record is durable, Host retries the failed handoff or marker write once. After a
|
|
@@ -229,10 +242,12 @@ Recovery may replay that unrecorded outcome for an idempotent action under its s
|
|
|
229
242
|
`adapterInvocationId`, within the worker budget. A reclaimed non-idempotent action instead
|
|
230
243
|
ends `reconciliation_required`. A durable handoff takes precedence over its running row and is
|
|
231
244
|
recorded as an applied effect with its external reference. A running row without such a handoff
|
|
232
|
-
is settled as unknown.
|
|
245
|
+
is settled as unknown. Without compatible finalization preparation, a succeeded row also ends `reconciliation_required`. With no rows or only failed rows, the existing
|
|
233
246
|
non-idempotent interruption gates still fail the invocation.
|
|
234
247
|
|
|
235
|
-
For idempotent recovery, the handler still runs before the adapter loop.
|
|
248
|
+
For idempotent recovery without an action finalizer, the handler still runs before the adapter loop.
|
|
249
|
+
Finalizing actions with compatible durable preparation replay without rerunning the handler;
|
|
250
|
+
missing or incompatible preparation retains effect-aware refusal. See [action-level finalization](#action-level-finalization). The loop reads each
|
|
236
251
|
checkpoint before `getInput()` or adapter lookup. Succeeded rows and durable handoffs use stored
|
|
237
252
|
adapter type, operation, vendor, input and output; a falsy new input or removed adapter does not
|
|
238
253
|
skip evidence repair. Stable event IDs deduplicate replayed accepted and success events; newly
|
|
@@ -245,7 +260,7 @@ Host calls `extractExecutionAttestation` with stored input/output and checkpoint
|
|
|
245
260
|
it must return deterministic evidence for the same input. A handoff checkpoint with no output,
|
|
246
261
|
or withheld output, skips extraction. Repair errors remain recoverable within the budget.
|
|
247
262
|
|
|
248
|
-
Known limits: a later step's definite provider failure or pre-call refusal can still end the
|
|
263
|
+
Known limits for actions without checkpointed finalization: a later step's definite provider failure or pre-call refusal can still end the
|
|
249
264
|
invocation `failed` after an earlier step succeeded. Authorization denials, ordinary handler
|
|
250
265
|
failures, validation/state-guard rejection, and unsatisfied policy obligations on recovery retain
|
|
251
266
|
their existing terminal outcomes. These are decisions or application failures, not Host store
|
|
@@ -398,7 +413,6 @@ still treats a failure inside the handler callback as a handler outcome.
|
|
|
398
413
|
failing, the row stays `running` although the provider was not called. For an action not declared
|
|
399
414
|
idempotent, the next claim settles that step as an unknown provider outcome.
|
|
400
415
|
|
|
401
|
-
|
|
402
416
|
A deadline sweep retains the handoff and sets `pendingCompletion.overdueAt` with its
|
|
403
417
|
`reconciliation_required` verdict. A late callback settles that verdict only when the handoff also
|
|
404
418
|
has `parkedAt`, proving Host reached parking after its remaining steps and finalization. An unparked
|
|
@@ -466,6 +480,22 @@ in the `before_adapters` phase stay appended. If the breaker opens between retry
|
|
|
466
480
|
retrying and records the refusal. See the `@fabricorg/platform` README for thresholds and outcome
|
|
467
481
|
mapping.
|
|
468
482
|
|
|
483
|
+
## Adapter execution deadlines
|
|
484
|
+
|
|
485
|
+
`PlatformHostOptions.adapterDeadlineMs` is a duration in milliseconds, starting
|
|
486
|
+
after step input preparation, just before the step enters retry execution.
|
|
487
|
+
An action's `AdapterStep.deadlineMs` overrides that duration for the step, including
|
|
488
|
+
when the Host option is unset or zero. Step durations must be positive finite integers;
|
|
489
|
+
action and module registration, manifest normalization and export reject invalid
|
|
490
|
+
values. Host also validates resolved steps before handler or adapter execution.
|
|
491
|
+
|
|
492
|
+
The budget spans retry attempts and delays; it is not renewed per attempt.
|
|
493
|
+
Host supplies an epoch-millisecond `AdapterExecutionContext.deadlineMs` and an
|
|
494
|
+
`AbortSignal`. Adapters must observe the signal to abandon an in-flight call.
|
|
495
|
+
Expiry before an attempt prevents that attempt; an aborted call that throws follows
|
|
496
|
+
the existing ambiguous-outcome reconciliation path. With no step duration and an
|
|
497
|
+
unset or zero Host duration, neither context field is supplied.
|
|
498
|
+
|
|
469
499
|
## Execution authorization
|
|
470
500
|
|
|
471
501
|
Submission authorization proves that a caller may create an invocation. Applications that delegate
|
|
@@ -562,7 +592,116 @@ evidence, so concurrent transitions serialize. Retrying the same identity and ev
|
|
|
562
592
|
reusing it with contradictory evidence fails closed, and a distinct transition cannot rewrite a
|
|
563
593
|
terminal parent. `createSagaParentActivities()` supplies deterministic identities for Temporal callers.
|
|
564
594
|
|
|
565
|
-
|
|
595
|
+
### Action-level finalization
|
|
596
|
+
|
|
597
|
+
Define provider-dependent domain work with `defineFinalizingAction({ ...action, finalize })`.
|
|
598
|
+
This helper guards the handler against older hosts by requiring
|
|
599
|
+
`ctx.finalizationProtocol === "atomic-after-adapters-v1"`, including when no handler was
|
|
600
|
+
provided. An optional callback alone cannot make an older host refuse execution.
|
|
601
|
+
|
|
602
|
+
Finalizing actions declare `eventPhase: "after_adapters"`, atomic kind, and immediate
|
|
603
|
+
completion (or omit kind/completion). Non-idempotent actions are supported. Host refuses
|
|
604
|
+
execution before handler or adapters unless the store provides atomic mutation transactions,
|
|
605
|
+
invocation locking, and transaction-scoped lease fencing. There is no fallback.
|
|
606
|
+
|
|
607
|
+
`finalize(ctx, { params, handlerData, adapterOutputs })` receives transaction-bound `ctx.db`
|
|
608
|
+
and identity/provenance, without services. Each ordered step is either skipped or succeeded
|
|
609
|
+
with its index, adapter type, operation, invocation ID, and stored redacted JSON output.
|
|
610
|
+
Before adapters, Host strips `eventResultFields` from handler data and normalizes the hook's
|
|
611
|
+
parameters and public handler data using `JSON.parse(JSON.stringify(value))`, then validates
|
|
612
|
+
the normalized JSON. Dates become ISO strings, undefined object properties drop, and class
|
|
613
|
+
instances use their JSON representation; non-finite numbers become null. Cycles, BigInt or
|
|
614
|
+
other serialization failures fail the handler phase before adapter calls or adapter rows.
|
|
615
|
+
Handler events are normalized in that phase too. The hook receives copies of the saved
|
|
616
|
+
normalized inputs, so adapter input preparation cannot change them.
|
|
617
|
+
Perform transaction-local domain writes here; provider calls belong in adapters. Earlier
|
|
618
|
+
handler writes commit separately and are outside this atomic completion.
|
|
619
|
+
|
|
620
|
+
Return only `{ events?, resultPatch? }` with JSON-compatible values. Explicit `undefined` on
|
|
621
|
+
`events`, `resultPatch`, or an event's `eventSchemaVersion` is treated as omitted; undefined
|
|
622
|
+
payloads or nested payload/result-patch values are rejected. Events require non-empty
|
|
623
|
+
event/subject identifiers, a payload, and an optional positive integer schema version;
|
|
624
|
+
event types must be declared in `emitsEvents`. Host checks event and subject names against
|
|
625
|
+
`eventContracts` when supplied for hook-returned events only; handler events are outside
|
|
626
|
+
that option's scope. It checks name membership, without validating payload schemas or schema
|
|
627
|
+
version compatibility. Host owns envelope identity, scope, actor and timestamps.
|
|
628
|
+
Hook and handler events default `eventSchemaVersion` to `action.version` when omitted;
|
|
629
|
+
an explicit event schema version is preserved.
|
|
630
|
+
Handler events use `domain:<index>`, hook events use `finalize:<index>`, followed by
|
|
631
|
+
`state:transition`. The result patch merges shallowly over the public handler data; keys
|
|
632
|
+
in `eventResultFields` are rejected. For these actions `resultSchema` validates the merged
|
|
633
|
+
result inside the final transaction.
|
|
634
|
+
|
|
635
|
+
The final transaction locks and checks the running invocation before calling the hook.
|
|
636
|
+
Hook, validation, event-append or completion-write failure rolls back finalizer domain
|
|
637
|
+
writes, events, outbox and completion, then settles `reconciliation_required` through
|
|
638
|
+
applied-effect settlement when durable provider effects exist. With zero steps or all steps
|
|
639
|
+
skipped, it settles `failed` without claiming a provider effect. Error text uses Host error
|
|
640
|
+
redaction. Lease loss rolls back and
|
|
641
|
+
escapes without a stale-worker verdict. Failed, ambiguous, degraded and accepted/handoff
|
|
642
|
+
outcomes do not call the hook.
|
|
643
|
+
|
|
644
|
+
If transaction entry or COMMIT fails with an unknown outcome, Host raises
|
|
645
|
+
`RecoverableFinalizationError` and records no verdict. An uncommitted invocation stays
|
|
646
|
+
`running` for recovery; if COMMIT succeeded despite the error, its stored `completed`
|
|
647
|
+
verdict and result remain intact and are returned on re-execution.
|
|
648
|
+
|
|
649
|
+
**Durable preparation and recovery.** The handler transaction commits a JSON
|
|
650
|
+
`finalizationCheckpoint` with protocol `atomic-after-adapters-v1`, action version, ordered
|
|
651
|
+
adapter type/operation identities, normalized parameters and public handler data, handler
|
|
652
|
+
events, public result, optional pending transition event and skipped indexes. Adapter inputs
|
|
653
|
+
are excluded. Parameters derive from the already-redacted durable invocation input.
|
|
654
|
+
Finalizing `getInput` callbacks receive copies of normalized parameters and public handler
|
|
655
|
+
data on both paths; other actions retain raw inputs. Skip decisions are computed before
|
|
656
|
+
preparation commits. Replay does not call `getInput` for recorded skips or succeeded rows.
|
|
657
|
+
For an unstarted step, it recomputes input from the saved normalized values; callbacks must
|
|
658
|
+
be deterministic for those values.
|
|
659
|
+
|
|
660
|
+
On resumed running invocations or later attempts, a compatible checkpoint bypasses the
|
|
661
|
+
handler, including for non-idempotent actions. Succeeded rows supply their stored redacted
|
|
662
|
+
outputs; steps without rows can run because Host creates a running row before calling the
|
|
663
|
+
provider. Recovery retains entitlement, authorization and policy checks. A refusal after
|
|
664
|
+
an applied effect requires reconciliation. Finalization uses the same locked, lease-fenced
|
|
665
|
+
success transaction as live execution. A completed invocation returns its stored result.
|
|
666
|
+
|
|
667
|
+
Running or ambiguous adapter rows require reconciliation, before checkpoint compatibility
|
|
668
|
+
checks. Protocol, action version or step count/order/type/operation changes refuse replay;
|
|
669
|
+
retain compatible definitions while work is recoverable. Handoffs cannot replay a finalizer.
|
|
670
|
+
Refusals reconcile when an effect exists or may exist, and fail when no effect is recorded.
|
|
671
|
+
Missing checkpoints retain the previous recovery rules, including reconciliation after
|
|
672
|
+
succeeded adapter evidence. Recovery-attempt budgets and deadline/capability refusals remain
|
|
673
|
+
in force. A checkpoint bypasses non-idempotent interruption gates only when each recorded
|
|
674
|
+
adapter row is succeeded; missing rows may start, but failed rows retain interruption
|
|
675
|
+
settlement without another provider call. Failed rows do not prove the provider had no effect.
|
|
676
|
+
Settlement fails without recorded success and reconciles when another step succeeded.
|
|
677
|
+
For leased non-idempotent replay, both the store and mutation transaction must provide
|
|
678
|
+
`writeAdapterInvocation` for lease-fenced adapter creation and updates. Without either
|
|
679
|
+
capability the existing interruption gates remain in force. Idempotent finalizing actions
|
|
680
|
+
retain failed-row retry through the adapter loop under existing retry rules, using saved
|
|
681
|
+
preparation rather than rerunning the handler.
|
|
682
|
+
|
|
683
|
+
On the live path, a finalizing action whose earlier adapter succeeded and whose later adapter
|
|
684
|
+
returns a permanent failure settles `reconciliation_required`, rather than `failed`. The hook
|
|
685
|
+
does not run. This also applies to subsequent execution failures after a recorded provider
|
|
686
|
+
effect, so the terminal status preserves the need to reconcile partial progress.
|
|
687
|
+
|
|
688
|
+
Canonical checkpoint JSON is limited to **256 KiB in UTF-8**, a conservative fixed bound on
|
|
689
|
+
durable evidence and transaction size. Oversize or unserializable preparation fails before
|
|
690
|
+
adapters and rolls back handler writes. Store entry/commit errors with unknown outcomes raise
|
|
691
|
+
`RecoverableFinalizationError` without a terminal verdict. Checkpoint write failures roll
|
|
692
|
+
back preparation, so adapters do not run.
|
|
693
|
+
|
|
694
|
+
**Rollout:** apply PostgreSQL migration **10**, `finalization_preparation_checkpoint`, using
|
|
695
|
+
`ensureSchema()` before deploying Hosts that write checkpoints. It adds nullable
|
|
696
|
+
`action_invocations.finalization_checkpoint jsonb`. Updates without the checkpoint field do
|
|
697
|
+
not name the column and remain usable on older schemas. Custom stores must persist the field
|
|
698
|
+
atomically with handler writes and honor explicit `undefined` clearing. Memory storage follows
|
|
699
|
+
the same patch semantics. The saved transition event preserves the original emission decision;
|
|
700
|
+
`pendingStateTransition` alone cannot encode suppression by a handler-emitted transition.
|
|
701
|
+
|
|
702
|
+
### Result validation
|
|
703
|
+
|
|
704
|
+
Actions without a finalizer can optionally declare `resultSchema`. The Host validates public handler result data after
|
|
566
705
|
the handler and before canonical event append or adapter execution. Invalid results fail the invocation,
|
|
567
706
|
emit no domain/completion event, invoke no adapter, and persist only a sanitized validation failure.
|
|
568
707
|
Domain writes roll back only when the configured store transaction provider includes those writes.
|
|
@@ -891,7 +1030,7 @@ must match the current state and invocation. Missing provenance leaves normal va
|
|
|
891
1030
|
custom stores can continue compiling without the optional method.
|
|
892
1031
|
|
|
893
1032
|
After a valid transition for a binding with a non-empty entity ID and target, Host appends
|
|
894
|
-
`StateTransitioned`
|
|
1033
|
+
`StateTransitioned` after the handler's domain events and any action-finalizer events, following the action's
|
|
895
1034
|
`eventPhase`. Its subject is the bound entity and its version-1 payload is
|
|
896
1035
|
`{ entityType, entityId, fromState, toState, causedByAction }`. `fromState` is the trusted state
|
|
897
1036
|
read before the handler, and `causedByAction` is the action ID. The event uses a stable
|
|
@@ -900,7 +1039,8 @@ can filter it. It does not need an `emitsEvents` declaration from the action.
|
|
|
900
1039
|
|
|
901
1040
|
For `before_adapters`, the transition is appended with the handler's domain events. For
|
|
902
1041
|
`after_adapters`, it is deferred to finalization after required adapter steps succeed or
|
|
903
|
-
accept a parked external handoff,
|
|
1042
|
+
accept a parked external handoff, after domain events and any action-finalizer events.
|
|
1043
|
+
Actions with `finalize` reconcile accepted handoffs without calling the hook. Adapter failure withholds
|
|
904
1044
|
both domain events and the transition, leaving the built-in stores' ledger-derived entity state
|
|
905
1045
|
unchanged by that invocation even though handler writes may have committed. Custom stores that
|
|
906
1046
|
read domain rows can already observe the target state at this point. With `transactionWithEvents`, the
|