View Markdown Β· Documentation index (llms.txt)
State 016 β Offline Acceptance Flow
Reproducible request-to-outcome demonstration of the FDC3 post-trade cash-leg payment transfer (TraderX β clearing receiver β correlated outcome), served entirely from a checkout of this repository β no Kubernetes cluster, no external estate, no testnet. This is the acceptance script the maintainer review (finos/traderX PR #470) asked for as one of the two next steps.
Scope note (per spec.md Β§0): the flow demonstrates a currency cash-leg payment transfer β the cash leg of an executed trade keyed by UETR. It is not securities DvP and not PvP.
Run it (two commands)β
# 1. Serve the spec pack (from the repository root):
python3 -m http.server 8090 --directory specs/016-post-trade-settlement-bankerx
# 2. Open the blotter with the acceptance app directory:
# http://localhost:8090/generation/traderx-blotter-web/index.html?launcher=1&directory=http%3A%2F%2Flocalhost%3A8090%2Facceptance%2Fappd.json
Both apps run from the pack on one origin: finos-traderx-desk (the web
blotter) and bankerx-clearing-desk (the mock receiver,
generation/mock-receiver/). The desktop agent boots in the blotter page
(fdc3-agent.js, the same in-page DA the FINOS conformance surfaces use) and
resolves intents against acceptance/appd.json.
The flow (what a reviewer should do)β
- Connect the desk β click
LAUNCH BANKERX. The agent opens thebankerx-clearing-deskrecord in a separate window (popup) and the two windows complete the FINOS Web Connection Protocol handshake (WCP1Hello β WCP3Handshake β MessageChannel). The receiver console logs the getAgent() connection; the blotter logs the instance id. - Raise the intent β click
SETTLE (BANKERX)on a blotter row. The blotter raises the standard intentfdc3.raiseIntent("StartPayment", fdc3.payment)β machine-builtfdc3.paymentcontext (amount, pair, rate, debtor/creditor, RFC 4122 UUIDv4 UETR), never hand-typed (FR-01602/01603). - Receiver processes β the mock receiver validates the context
(
payment-lifecycle.mjs: RFC 4122 UETR, ISO 4217 currency, canonical pair, party completeness) and renders the verdict badge:Acsc β SETTLEDon a valid context (orRjct β REJECTEDwith an honest reason on an invalid one). - Correlated outcome β the receiver relays
synaptic.settlementStatus(Acsc, UETR-keyed) back to the blotter; the originating row flipsSETTLINGβ¦ β SETTLEDwith zero further clicks (FR-01606), and the receipt modal opens. - Duplicate suppression β a second
SETTLE (BANKERX)on the same row is suppressed (FR-01607): the blotter refuses with an explanatory message, and the receiver's duplicate-record side replays the first outcome, never a second settlement. - Honest rejection β dispatch a payment the receiver must refuse (e.g. a
malformed UETR via the in-page stub agent or a non-canonical pair): the
receiver renders
Rjct β REJECTEDwith the specific validation reason and nothing is settled (FR-01606'sRjctleg, spec.md user story 5).
Gates (what the E2E harness checks, run twice from a clean checkout)β
| Gate | Assert |
|---|---|
realFdc3 | blotter DA ready ([EstateDA] Desktop agent ready) |
deskOpen | receiver window opened through the agent's directory record (tracked instance) |
wcpConnected | receiver logged the getAgent() WCP connection |
tradeBooked | ticket execution created a blotter row with a machine-built UUIDv4 UETR |
intentReceived | receiver processed the raised fdc3.payment (UETR match) |
acsc | receiver verdict Acsc and blotter row SETTLED |
receiptModal | pacs.002-style receipt modal visible on Acsc |
dupSuppressed | second SETTLE on the same row refused (FR-01607 guard logged), no duplicate dispatch |
receiverDupReplay | re-raise of the same UETR through the agent β receiver duplicate record replays the first outcome, never a second settlement |
rejectHonest | invalid context β Rjct with the validation reason, no settlement |
Harness evidence (offline pack, headless): 10/10 Γ2 on the clean-checkout
bytes of the acceptance commit, plus 7 consecutive 10/10 runs in the debug
loop after fixing a getAgent discovery race in the receiver (its in-page stub
agent installed too early could win the DesktopAgentPreloadLoader
strategy race; the stub now installs only in the no-agent fallback).
Filesβ
| File | Purpose |
|---|---|
appd.json | Local two-record App Directory (blotter + mock receiver as bankerx-clearing-desk). |
sail-receiver-overlay.appd.json | Applications-wrapped fragment for merging the receiver record into the Sail runtime directory (sail/bootstrap/merge-traderx-appd.sh) when running the generated 014/016 Sail runtime. |
harness/ | Estate-owned headless 10-gate E2E harness (acceptance-016.cjs; see harness/README.md). |
The state start script lives at the repository root:
scripts/start-state-016-post-trade-settlement-bankerx-generated.sh (static
mode implements this flow; Sail runtime mode carries the documented
--dry-run contract).
What is real vs. simulated (read this before any claim)β
- Real: the FDC3 intent lifecycle β DA boot, directory resolution,
fdc3.open, the WCP handshake,raiseIntent("StartPayment", fdc3.payment)delivery, intent registration viagetAgent(), UETR correlation, duplicate suppression, and the honestRjctvalidation path. All bytes come from this checkout (no estate dependency). - Simulated: the settlement rail behind the receiver's
Acscverdict. The mock receiver is the DEFAULT offline receiver by design (FR-01608) β it proves the interop loop, not the settlement rail. The live terminal (terminal.synapticchain.xyz) and the estate E2E suite are the settlement rail's evidence; the acceptance flow's blotter-wired BankerX terminal variant is the opt-in reference adapter (App Directory URL swap, no TraderX change). Never present the offlineAcscas an on-chain settlement.
Relationship to the generated Sail runtimeβ
python3 -m http.server above is the static path. The generated runtime
path (state 014 baseline + this pack's overlay, Sail on :8090, TraderX
cluster on :8080) is covered by
scripts/start-state-016-post-trade-settlement-bankerx-generated.sh β see the
state quickstart Β§2. The full cluster boot needs kind/minikube; the script
honours the documented --dry-run contract so the wiring is verifiable from a
clean checkout without a cluster.