Task: Add the trade booking and state components
Table of Contents
This page documents a task in the Split the trade into an anchor and its components story. It captures the goal, current status, acceptance, and any notes or results.
1. Goal
A trade's booking and lifecycle state live in their own temporal tables keyed by the anchor, the netting set cannot name another counterparty's or entity's set, and a virtual book holds only drafts, tests and hypotheticals.
2. Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Split the trade into an anchor and its components |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-10-03 |
3. Acceptance
- trade_booking (book, netting set, trade date, execution time) and trade_state (status) are temporal and reference the anchor with an enforced foreign key.
- A booking's netting set is pinned to the anchor's party and counterparty.
- A booking names a virtual book only while its trade is a pre-agreement draft, a test or a hypothetical; going live needs a real book in the same write.
- Build, ctest, pgTAP and the local check set pass.
4. Plan
trade_bookingandtrade_state:uuid-identified-lookupprofile, keyed bytrade_id, temporal, with an enforced foreign key to the anchor.- Both copy the anchor's party, pinned to the anchor, because row-level
security reads it. The booking also copies the counterparty, pinned the
same way, so the netting set can be pinned to the trade's counterparty and
party. The book is pinned to the party. The anchor gains two unique keys,
(tenant_id, id, party_id)and(tenant_id, id, counterparty_id), for the database pins. - The state reuses the wide trade's guarded transition, keyed by the trade id.
- The virtual-book rule is checked by whichever row is written second: the booking reads the current state, the state reads the current booking. An actual trade goes live by writing its real booking before the state.
- The booking operation (anchor, booking and state in one write, with the duplicate trade id as a domain error) moves to its own task: it is the first consumer of the three tables and needs a service operation of its own.
5. Measurements
Every number this task claims about a tree, with the command that produced it
and the commit it was measured at. A number without both is not a measurement,
because a clean-up record describes a tree and trunk moves under a branch: a
consumer count, a gate's component count and a suite's size all change with no
code changing, and the reader of a bare number cannot tell which tree it came
from. Where the raw output matters, put it in an evidence/ file beside the
task and name that file in the Command column.
| What | Value | Command | Commit |
|---|---|---|---|
6. Notes
7. Test Scenarios
Manual QA scenarios (scaffolded via compass add test_scenario)
that verify this task. Link
new ones here as they're created; the scenario doc itself links back
via its "Verifies task" field.
| Scenario | State | Notes |
|---|---|---|
8. PRs
| PR | Title |
|---|---|
| #2573 | [trading] Add the trade booking and state components |
9. Review
| # | Comment summary | File | Decision | Notes |
|---|---|---|---|---|
| 1 | Local review: a netting set read from ORE has no counterparty or party, so the pin refuses it for a booking that names a counterparty | ores.trading.trade_booking.org |
Accept as follow-up | The ORE import task fills the set's parties from the trade it imports; recorded in the story's Decisions. |
| 2 | Local review: the purger's plain deletes of refdata close rows rather than remove them | iam_tenant_purger_create.sql |
Decline | Predates this task; the new component deletes bypass the rule because the anchor's foreign key needs every version gone. |
| 3 | ctest: the booking write test seeded a book of another party | generated test | Accept | The book is seeded under its own foreign key, which replaces the derived seed. |
10. Result
trade_booking(book, netting set, trade date, execution time) andtrade_state(activity, status) are temporal components keyed by the trade id, with an enforced foreign key to the anchor.- Both copy the anchor's party; the booking also copies its counterparty. Database pins hold the copies to the anchor; trigger pins hold the book to the party and the netting set to the counterparty and party.
- The state takes its status through the guarded
trade_statustransition. - A virtual book holds an actual trade only while it is a draft; whichever of the booking and the state is written second checks the other.
- The tenant purger removes every version of both components before it deletes the anchors.
- The booking operation moved to its own task, "Book a trade in one write".
- Verified: build clean (
linux-clang-debug-make); ctest 89/90 on the full run, the one failure fixed andores.trading.*3/3 andores.shell.*16/16 on the rerun; pgTAPtrading_trade_components_test.sql19/19, with the anchor and tenant tests still passing; codegen pytest 1120; drift shows only the knowntrade_service.cppdiff; sweep, paste blocks, reachability, permissions, populate references, CMake, inventory, ER strict, SQL hygiene, shell scripts, lint, structure notes, misspell and site page all clean.