approval; the comparison is code. A condition takes no
constructor dependencies; needed(input, ctx, settings) gets a reader limited to the keys in its static reads.
The reader sits over the ApprovalSettingsSource port of @stackra/contracts, and @stackra/settings ships its driver,
SettingsApprovalSourceDriver. A condition that declares reads while no source is bound refuses the call with
approval_settings_unavailable (503, not retryable) rather than reading it as “not needed”.
What happens to a call
- The call passes permission, preconditions, entitlement, flag and validation. The approval step then asks the
condition, asks the tenant’s rules (
ApprovalGate.decide, which may only add) and keeps the larger requirement. - A call that needs approval is refused
approval_requiredwithapprovalId,approvalsRequiredandexpiresAt. The owning service writes its own binding (who, where, which action, input hash, assurance, times) and the store parks the request, after the refused call’s transaction rolled back. - Approvers vote in the store. Each is a different person, none is the requester’s person or in the requester’s chain,
and none is an agent. At the last vote the store re-checks every voter and publishes
ApprovalGranted. ApprovalExecutorListener, in the owning service, runs the call as the requester under the keyapproval:<id>. The pipeline checks permission, preconditions, entitlement and flag again, andverifyApprovalcompares the call with the binding and the store’s row beforehandleruns.
CallOptions.approvalId instead of running on grant.
What a service binds
A declared stage fails closed when any of the first three is missing, and with
OpenApprovalGateDriver. Call
executor.connect(container) after boot.
Failure and retry
Test it
@stackra/actions/testing ships MemoryApprovalGateDriver (votes, grant, reject, withdraw, expiry, the pending cap),
MemoryApprovalBindingStoreDriver and MemoryActorStatusCheckDriver, and describeApprovalGateConformance and
describeApprovalBindingStoreConformance for a real store’s driver. The billing case in the executor’s tests is the
pattern: park above the threshold, refuse the requester as approver, grant with another person, deliver the event twice,
and assert the handler ran once.