Feature Specification: Post-Trade Compliance Bundle
Feature Branch: YU05-post-trade-compliance
Created: 2026-07-06
Status: Implemented
Input: Combines four production-realism capabilities into one spec-kit state at the user's
request; post-trade settlement + reconciliation, regulatory reporting, TCA, and real
auth/entitlements. Parented on YU04-durable-control-feeds (lineage YU03 β YU04 β YU05). New
requirement namespace PTC (FR-PTCxx, NFR-PTCxx); per-requirement status in
requirements/functional-delta.md and requirements/nonfunctional-delta.md.
The four capabilities are three views over one underlying record; an executed fill (symbol, price, qty, side, account, timestamp, deterministic trade id) sourced from the order-matcher journal; plus the access-control layer that makes exposing that record as real reports/APIs safe. This state is a back-office layer strictly downstream of the BLP: it never sits on the order-admission path and never mutates journal or BLP state.
User Storiesβ
- As an operations user, I want a booked trade to move through a real settlement lifecycle
(
Processing β Settledon a T+N schedule, with a manual override) instead of being marked settled the instant it books. - As an operations user, I want reconciliation to flag any trade that is in the journal but missing or mismatched in the projection; and, on demand, any projection row with no journal fill behind it at all; so a divergence between the authoritative journal and the read model is caught.
- As a compliance reviewer, I want a reproducible, journal-sourced audit export of every order/trade lifecycle event in an input-sequence range, so a regulator query can be answered byte-for-byte from the source of truth rather than the projection.
- As an execution analyst, I want per-trade transaction-cost analysis (arrival price, benchmark, signed slippage) so execution quality can be measured without touching the trading hot path.
- As a security owner, I want every one of these endpoints gated by a verified token and an entitlement check, so account-scoped data is only visible to an entitled caller and cross-account data only to an admin.
Functional Requirementsβ
- FR-PTC01: A MariaDB trade row SHALL carry the deterministic trade id derived from the journal fill
that produced it (
OrderSnapshot.tradeIdFor(tradeSeq)), on both the live projector path and the legacy NATS booking path. - FR-PTC02: A booked trade SHALL start
Processingwith a computed settlement date and advance toSettledby a scheduled T+N sweep or a manual force. - FR-PTC03: order-matcher SHALL maintain a replay-safe in-memory trade blotter, rebuilt from journal replay on recovery (no snapshot-format change).
- FR-PTC04: Reconciliation SHALL classify each blotter entry against the local trade row as
MATCHED,MISSING_IN_PROJECTION, orFIELD_MISMATCH, and (full-history sweep)ORPHAN_IN_PROJECTION. - FR-PTC05: Reconciliation SHALL expose a status summary (
GET /recon/status) and bounded Prometheus counters with no per-trade labels. - FR-PTC06: The settlement date SHALL default to T+1 business day (
settlement.t-plus-days) and be overridable viaPOST /trades/{id}/settlement/force. - FR-PTC07: Settlement and reconciliation SHALL never mutate journal or BLP state; their writes are MariaDB-side only, and full-history reindex and regulatory reports are read-only shadow replays.
- FR-PTC08: Trade booking SHALL be idempotent on the deterministic trade id; a duplicate delivery is a no-op.
- FR-PTC10: A full-history sweep SHALL replay the entire journal on demand (
POST /recon/full-history/reindex) into an unbounded index and cross-check every local trade id (POST /recon/orphan-sweep), flaggingORPHAN_IN_PROJECTIONwith full confidence. - FR-PTC20:
GET /regulatory/report?fromSeq=&toSeq=SHALL return every order/trade lifecycle event (accept, reject, partial-fill, fill, cancel, trade-booked) in that input-sequence range, sourced from journal replay, never from the MariaDB projection. - FR-PTC21: The audit export SHALL be a pure function of (journal range, seed); no wall-clock, no external query; so identical inputs always produce identical records.
- FR-PTC22: The audit export SHALL require an admin caller and never run on the BLP admission path.
- FR-PTC30: TCA SHALL compute arrival price, a TWAP benchmark, and signed slippage-in-bps per trade
(
GET /tca/report/{tradeId}). - FR-PTC31: TCA SHALL be read-side only, entirely in trade-processor, never calling the admission path.
- FR-PTC32: TCA SHALL compute its benchmark against a pluggable historical price source; a
PriceHistoryStorefed by price-publisher's existingpricing.*feed; with no new data source and no BLP involvement. - FR-PTC40: Every endpoint this state adds SHALL require a real HS256-verified JWT (
JwtAuthenticator, JDK crypto only), replacing the shared-token stopgap;POST /auth/dev-tokenmints tokens for local dev/testing. - FR-PTC41: Account-scoped endpoints (settlement force, TCA) SHALL check the caller's entitlement
against the trade's own account; cross-account endpoints (blotter, full-history, orphan-sweep,
regulatory report) SHALL require an
adminclaim. - FR-PTC42: The order-admission path (
OrderMatcherService.createOrder/createOrderBatch/bookMarketTrade) SHALL resolve the caller's JWT principal and reject a caller not entitled to the order's account before the command is screened or sequenced; a memory-only check against the token claim, adding no synchronous lookup to the admission path (FR-IMRG01). Enforcement is gated byrisk.entitlement.enforced(default off) so the token-less UI is unaffected until it is enabled; the mechanism closes FR-IMRG02 (entitlement resolution) and FR-IMRG30 (full authn on the command path).
Non-Functional Requirementsβ
- NFR-PTC01: Blotter capture SHALL happen on the existing output-ring consumer thread, off the BLP decision path; recon and settlement SHALL make no synchronous call into the admission path.
- NFR-PTC02: Reconciliation classification SHALL be a pure function of (blotter state, DB state) at sweep time and reproduce the same result on an unchanged rerun.
- NFR-PTC03: Metric cardinality SHALL stay bounded; classification labels only, no trade-id/account/security labels.
- NFR-PTC05: The live blotter SHALL be bounded (
recon.blotter.capacity, default 500,000) with oldest-first eviction; the on-demand full-history index is unbounded but populated only on demand. - NFR-PTC06: This state SHALL touch order-matcher and trade-processor overrides only; the inherited build/publish/deploy harness is unchanged.
- NFR-PTC07: No new runtime dependency SHALL be added (JDK crypto/HttpClient, existing Jackson/Micrometer/Spring).
- NFR-PTC09: The only admission-path change SHALL be the entitlement gate (an authn/authz check
before screening); the BLP decision path, risk screening logic, journal/replication wire format,
and snapshot format SHALL be unchanged (
GatewayReplicaStore.screen/BlpRiskStateuntouched).
Success Criteriaβ
- SC-PTC01: order-matcher unit tests validate the replay-safe blotter (capture, bounded eviction, replay-safety), the deterministic-id wiring, the audit-log handler (kind coverage, range filtering), and the JWT authenticator.
- SC-PTC02: trade-processor unit tests validate idempotent booking, the T+N settlement transition, reconciliation classification, the full-history orphan sweep, TWAP/slippage computation, and the JWT authenticator.
- SC-PTC03: A Grafana dashboard (
traderx-post-trade-compliance.json) visualizes the recon, settlement, TCA, and regulatory-report metric set. - SC-PTC04:
bash pipeline/generate-state.sh YU05-post-trade-complianceexits 0 and the generated order-matcher/trade-processor trees test green. - SC-PTC05: Generated shared files retain every ancestor state's content alongside this state's additions.