@fabricorg/platform-host 8.4.0 → 8.5.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 +34 -0
- package/README.md +117 -4
- package/dist/index.cjs +579 -158
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +129 -5
- package/dist/index.d.ts +129 -5
- package/dist/index.js +576 -159
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# @fabricorg/platform-host
|
|
2
2
|
|
|
3
|
+
## 8.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 08274e6: Add host-owned approval deadlines/expiry, bounded JSON decision evidence, and opt-in atomic approval lifecycle events. Evaluators can supply an absolute deadline, or Hosts can set a default TTL. Due decisions and bounded worker/operator sweeps settle `failed` with expiry evidence and no approval decision; store serialization prevents a concurrent decision or sweep from applying twice. No deadline preserves indefinite waits, and no new ActionStatus is introduced.
|
|
8
|
+
|
|
9
|
+
Decision evidence is limited to 16 KiB of plain JSON verification references, validated and copied before resume store access and authorization. Credentials and tokens must not be included. Approval requested/decided/expired events default off. Opting in requires transactional approval/outbox capabilities at Host creation; event/state write failures roll back together, parking failures surface for recovery, and event IDs remain stable on retry. Event payloads include evidence presence rather than the evidence object. PostgreSQL users must run ensureSchema to apply additive migration 12 for the nullable expiry columns and due-expiry index before deploying; migrations 1-11 retain their original SQL/checksums.
|
|
10
|
+
|
|
11
|
+
Built-in HITL checkpoints are first-write-wins, preventing a delayed evaluator from changing a parked deadline. Resume of an unavailable action fails an approval without a deadline, preserving the previous terminal behavior; an approval with a deadline stays waiting with an error so a sweep can record its expiry verdict. That `Unknown action` failure appends no approval lifecycle event. Host fails before parking if a custom store drops the evaluator-supplied deadline. Custom approval stores must atomically enforce deadline expiry in beginApprovalDecision and serialize decisions with sweeps.
|
|
12
|
+
|
|
13
|
+
- 6002c61: Add the finalizing-action `ActionResult.adapterPreparation` channel and optional third
|
|
14
|
+
`AdapterStep.getInput` argument. The finalizing-action helper refuses returned preparation
|
|
15
|
+
without the Host's `adapterPreparationProtocol: "v1"` capability; actions returning none
|
|
16
|
+
retain their existing capability behavior. Non-finalizing actions returning preparation fail
|
|
17
|
+
before adapter delivery.
|
|
18
|
+
|
|
19
|
+
Preparation must be a strict-JSON plain-object record and counts toward the 256 KiB checkpoint
|
|
20
|
+
limit. It is persisted separately for crash replay, excluded from public results, handler data,
|
|
21
|
+
finalize input and event extraction, and removed with terminal settlement while retaining other
|
|
22
|
+
checkpoint evidence. Running and reconciliation-required invocations retain preparation.
|
|
23
|
+
Older checkpoints pass undefined to getInput. Derived adapter inputs remain subject to the
|
|
24
|
+
existing redactAdapterInput and adapter evidence retention contracts.
|
|
25
|
+
|
|
26
|
+
Preparation checkpoints use `atomic-after-adapters-v2`; checkpoints without preparation keep
|
|
27
|
+
`atomic-after-adapters-v1`. Replay accepts only the matching protocol/preparation shapes.
|
|
28
|
+
Settlement clears preparation and restores v1, including approval rejection. Handler capability
|
|
29
|
+
and manifest finalization protocols remain v1. Upgrade all workers before handlers return
|
|
30
|
+
preparation: older Hosts refuse in-flight v2 replay through interruption handling, requiring
|
|
31
|
+
reconciliation after recorded effects or settling failed without recorded effects. An older
|
|
32
|
+
Host does not clear preparation, so a row it settles keeps the v2 checkpoint and its
|
|
33
|
+
preparation; in PostgreSQL, find such rows by a terminal status with
|
|
34
|
+
`finalization_checkpoint->>'protocol' = 'atomic-after-adapters-v2'`. Reconcile in-flight v2
|
|
35
|
+
work before rolling workers back.
|
|
36
|
+
|
|
3
37
|
## 8.4.0
|
|
4
38
|
|
|
5
39
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -647,12 +647,46 @@ If transaction entry or COMMIT fails with an unknown outcome, Host raises
|
|
|
647
647
|
verdict and result remain intact and are returned on re-execution.
|
|
648
648
|
|
|
649
649
|
**Durable preparation and recovery.** The handler transaction commits a JSON
|
|
650
|
-
`finalizationCheckpoint` with protocol `atomic-after-adapters-v1
|
|
650
|
+
`finalizationCheckpoint` with protocol `atomic-after-adapters-v1` without preparation, or
|
|
651
|
+
`atomic-after-adapters-v2` with preparation, plus action version, ordered
|
|
651
652
|
adapter type/operation identities, normalized parameters and public handler data, handler
|
|
652
653
|
events, public result, optional pending transition event and skipped indexes. Adapter inputs
|
|
653
654
|
are excluded. Parameters derive from the already-redacted durable invocation input.
|
|
654
|
-
Finalizing `getInput` callbacks receive copies of
|
|
655
|
-
|
|
655
|
+
Finalizing `getInput(params, handlerResult, adapterPreparation?)` callbacks receive copies of
|
|
656
|
+
normalized parameters and public handler data on both paths. A handler can return a private
|
|
657
|
+
strict-JSON plain-object `ActionResult.adapterPreparation` alongside `data`. The optional third
|
|
658
|
+
argument receives a copy of that record (or `undefined`, including for older checkpoints).
|
|
659
|
+
It is separate from public handler data, public results, `finalize` input, event payloads and
|
|
660
|
+
`extractEvents` input. Only values the action explicitly derives into adapter inputs enter the
|
|
661
|
+
existing adapter-row and success-event input evidence, governed by `redactAdapterInput`.
|
|
662
|
+
|
|
663
|
+
Host validates the channel immediately after a successful handler returns, before staging,
|
|
664
|
+
event extraction, or adapter preparation. Non-finalizing actions returning it fail before
|
|
665
|
+
adapter delivery. Arrays, non-object records and lossy JSON values are rejected. Unlike public
|
|
666
|
+
handler data, preparation does not normalize Dates or drop undefined values. It is saved under
|
|
667
|
+
its own optional `finalizationCheckpoint.adapterPreparation` key and counts toward the 256 KiB
|
|
668
|
+
checkpoint limit. Settlement rewrites the checkpoint without that key and restores protocol
|
|
669
|
+
`atomic-after-adapters-v1`, preserving the remaining evidence:
|
|
670
|
+
completion clears it in the success transaction, and terminal failure, policy/validation refusal
|
|
671
|
+
or cancellation clears it with the status write. Running and `reconciliation_required`
|
|
672
|
+
invocations retain it for recovery or reconciliation. This retention applies to the private
|
|
673
|
+
checkpoint channel; derived adapter input evidence follows the existing retention contract.
|
|
674
|
+
|
|
675
|
+
The Host advertises `ctx.adapterPreparationProtocol === "v1"` for finalizing handlers.
|
|
676
|
+
`defineFinalizingAction` refuses a returned preparation record when that capability is absent,
|
|
677
|
+
after the handler returns; its existing finalization capability check still runs before the
|
|
678
|
+
handler. Returning no preparation preserves the earlier capability behavior.
|
|
679
|
+
|
|
680
|
+
Replay accepts v1 only without the preparation key and v2 only with it. Other combinations
|
|
681
|
+
use the existing interruption refusal path before adapter delivery. Older Hosts refuse v2
|
|
682
|
+
checkpoints through that path: recorded effects require reconciliation; no recorded effect
|
|
683
|
+
settles failed. An older Host does not clear preparation, so a row it settles keeps the v2
|
|
684
|
+
checkpoint and its preparation; in PostgreSQL, find such rows by a terminal status with
|
|
685
|
+
`finalization_checkpoint->>'protocol' = 'atomic-after-adapters-v2'`. Upgrade all workers before enabling handlers that return preparation, and
|
|
686
|
+
reconcile in-flight v2 work if workers are rolled back. The handler capability and manifest
|
|
687
|
+
finalization protocol remain `atomic-after-adapters-v1`; checkpoint versioning is separate.
|
|
688
|
+
|
|
689
|
+
Other actions retain raw two-argument inputs. Skip decisions are computed before
|
|
656
690
|
preparation commits. Replay does not call `getInput` for recorded skips or succeeded rows.
|
|
657
691
|
For an unstarted step, it recomputes input from the saved normalized values; callbacks must
|
|
658
692
|
be deterministic for those values.
|
|
@@ -731,7 +765,8 @@ The evaluator returns `auto-execute`, `needs-approval`, `escalate`, or `rejected
|
|
|
731
765
|
|
|
732
766
|
- `auto-execute` continues through policies, state validation, the handler, events, and adapters.
|
|
733
767
|
- `needs-approval` and `escalate` persist the route and risk evidence, clear any worker lease, and park
|
|
734
|
-
the invocation as `waiting_for_approval`.
|
|
768
|
+
the invocation as `waiting_for_approval`. The built-in polling stores exclude that status
|
|
769
|
+
from execution claims.
|
|
735
770
|
- `rejected` terminally fails before policies and mutation code run.
|
|
736
771
|
|
|
737
772
|
An approval workflow calls `resumeApprovedInvocation()` instead of invoking the handler directly. The
|
|
@@ -755,6 +790,84 @@ Custom stores remain source-compatible when HITL is unused. To enable HITL they
|
|
|
755
790
|
additive `ApprovalPlatformHostStore` capability. Both built-in stores implement it; Postgres users must
|
|
756
791
|
call `ensureSchema()` so the HITL evidence and approval-decision columns are added.
|
|
757
792
|
|
|
793
|
+
### Approval deadlines, evidence, and lifecycle events
|
|
794
|
+
|
|
795
|
+
`HostHitlDecision.approvalExpiresAt` accepts an absolute, valid `Date`. Alternatively,
|
|
796
|
+
`approvalDefaultTtlMs` supplies a positive safe integer of milliseconds from the successful
|
|
797
|
+
park attempt. The evaluator's deadline takes precedence. With neither option, the wait has
|
|
798
|
+
no deadline. An invalid deadline fails execution before parking; an invalid default TTL
|
|
799
|
+
throws when constructing the Host. A deadline already due is permitted and expires on a
|
|
800
|
+
sweep or decision attempt.
|
|
801
|
+
|
|
802
|
+
`host.expireDueApprovals({ tenantId?, spaceId?, now?, limit? })` settles due approval waits.
|
|
803
|
+
The default limit is 100, with valid limits from 1 to 1000. The worker calls it before claiming
|
|
804
|
+
work and reports a positive count as `expiredApprovals`, separately from execution failures.
|
|
805
|
+
The store compares the deadline to the transition's trusted `now`, including after approval
|
|
806
|
+
authorization: `decidedAt` cannot backdate acceptance. At or after the deadline, a decision
|
|
807
|
+
attempt settles expiry instead of recording that decision. Expiry uses `status: "failed"`,
|
|
808
|
+
retains `approvalExpiresAt`, records `approvalExpiry: { expiresAt, expiredAt }`, and leaves
|
|
809
|
+
`approvalDecision` absent. Rejections retain `approvalDecision.approved: false`. Repeated
|
|
810
|
+
sweeps and racing decisions return only the transition each caller applied. Expiry emits
|
|
811
|
+
ordinary failed-status telemetry. Saga coordination reads the child's terminal result and
|
|
812
|
+
uses its existing parent failure/compensation path, as it does for rejection; the Host does
|
|
813
|
+
not dispatch a saga signal itself.
|
|
814
|
+
|
|
815
|
+
A custom approval store needs `parkApproval` and `expireDueApprovals` to enable deadlines;
|
|
816
|
+
a Host requiring these capabilities fails the invocation before parking if they are missing.
|
|
817
|
+
A store implementing `parkApproval`/`expireDueApprovals` must also enforce the stored deadline
|
|
818
|
+
inside `beginApprovalDecision`: at or after it (`input.now >= approvalExpiresAt`), expire the row
|
|
819
|
+
instead of applying the decision. Decisions and sweeps must serialize so each wait settles once;
|
|
820
|
+
parking must check the lease and absence of a prior decision. Supplied lifecycle events/outbox
|
|
821
|
+
writes must commit atomically with their state changes. Host refuses to park if
|
|
822
|
+
`recordHitlDecision` drops a freshly evaluated deadline.
|
|
823
|
+
Existing stores without these optional capabilities retain indefinite approval behavior.
|
|
824
|
+
If an action definition is unavailable during resume, a wait without a deadline fails with
|
|
825
|
+
`Unknown action`; a wait with a deadline stays waiting with that error so the sweep can expire it.
|
|
826
|
+
That `Unknown action` failure is not an approval transition, so it appends no approval
|
|
827
|
+
lifecycle event even when `approvalLifecycleEvents` is enabled: consumers tracking open approvals
|
|
828
|
+
from these events should also close one when the invocation reaches a terminal status.
|
|
829
|
+
HITL checkpoints are first-write-wins in the built-in stores, so a late evaluator cannot revise a
|
|
830
|
+
parked deadline. Apply `PostgresPlatformHostStore.ensureSchema()` before deploying this feature:
|
|
831
|
+
migration 12 (`approval_lifecycle_expiry`) adds nullable `approval_expires_at` and `approval_expiry`
|
|
832
|
+
columns and a partial due-expiry index, preserving the SQL/checksums of migrations 1-11.
|
|
833
|
+
|
|
834
|
+
An approval decision may include `evidence`, a plain JSON object containing verification
|
|
835
|
+
references such as a binding ID, signature/verification reference, or receipt ID. Credentials
|
|
836
|
+
and tokens must not be included. `MAX_APPROVAL_EVIDENCE_BYTES` exports the 16 KiB serialized
|
|
837
|
+
UTF-8 limit. Host rejects non-JSON data, accessors, proxies, cycles, sparse arrays, non-finite
|
|
838
|
+
numbers, nesting beyond 64 levels, and oversized objects with a thrown error at resume entry,
|
|
839
|
+
before store reads or writes. It snapshots the decision before awaiting authorization and
|
|
840
|
+
passes independent decision and invocation copies to `authorizeApproval`. Both stores retain
|
|
841
|
+
evidence on `approvalDecision`; applications own the safety of its verification references.
|
|
842
|
+
|
|
843
|
+
Set `approvalLifecycleEvents: true` to enable `APPROVAL_REQUESTED_EVENT` (`ApprovalRequested`),
|
|
844
|
+
`APPROVAL_DECIDED_EVENT` (`ApprovalDecided`), and `APPROVAL_EXPIRED_EVENT` (`ApprovalExpired`).
|
|
845
|
+
Their typed payloads are `ApprovalRequestedPayload`, `ApprovalDecidedPayload`, and
|
|
846
|
+
`ApprovalExpiredPayload`. Request events contain route, risk tier, reason, and optional
|
|
847
|
+
`expiresAt`; decision events contain approval outcome, approver identity/type, optional reason,
|
|
848
|
+
`decidedAt`, and `evidencePresent`; expiry events contain `expiresAt` and `expiredAt`.
|
|
849
|
+
Payload dates are ISO strings. Decision events do not copy the evidence object.
|
|
850
|
+
|
|
851
|
+
The option defaults off. When enabled, Host construction requires `transactionalOutbox`,
|
|
852
|
+
`transactionWithEvents`, and `transactionalApprovalLifecycle` plus the approval methods.
|
|
853
|
+
Both built-in stores provide event atomicity when constructed with a transaction provider.
|
|
854
|
+
A state transition and its lifecycle event/outbox row commit together; append or update
|
|
855
|
+
failures roll them back. An expiry sweep commits its bounded batch as one unit, preventing
|
|
856
|
+
a later append failure from hiding earlier transitions from the result or status telemetry.
|
|
857
|
+
A parking transaction failure is surfaced for recovery without a
|
|
858
|
+
terminal failure overwrite. Earlier execution-start and HITL checkpoints remain durable.
|
|
859
|
+
Stable invocation-and-kind event IDs make transition retries first-write-wins. Event envelopes
|
|
860
|
+
reuse Host's domain/lifecycle builder, including tenant, actor, correlation, causation,
|
|
861
|
+
provenance, and declared consistency. The store allocates sequence inside the transaction.
|
|
862
|
+
|
|
863
|
+
Approval lifecycle events explicitly enabled by this option go to the outbox even when
|
|
864
|
+
`outbox.shouldPublish` excludes other Host lifecycle events. Existing classification,
|
|
865
|
+
producer-version, trace, actor-disclosure, and provenance hooks apply when configured.
|
|
866
|
+
Without `outbox` configuration, approval metadata uses `internal` classification, the action
|
|
867
|
+
version string as producer version, provenance included, and actor disclosure excluded from
|
|
868
|
+
the enterprise envelope. Configure those hooks before publishing approval reasons or approver
|
|
869
|
+
identifiers outside the mutation ledger.
|
|
870
|
+
|
|
758
871
|
Sensitive values must not be passed as action parameters. Stage them in tenant-bound encrypted storage
|
|
759
872
|
and pass an opaque identifier instead; action parameters are intentionally durable audit evidence.
|
|
760
873
|
Hosts should additionally configure `redactActionParameters` as a fail-safe allowlist for actions
|