Task: Record each business event as a trade activity
Table of Contents
This page documents a task in the Record trade activities and trade links story. It captures the goal, current status, acceptance, and any notes or results.
1. Goal
Every business event on a trade is one append-only trade_activity row, and booking writes it in the same transaction.
2. Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Record trade activities and trade links |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-10-05 |
3. Acceptance
- trade_activity is append-only: type, actor, time, comment and an operational-error flag; an update or delete is refused.
- The booking operation writes the activity with the anchor, booking and state, and returns its id.
- A null amend is recorded as an activity and versions nothing.
4. Plan
trade_activityis an immutable entity, like the trade: id, party, activity type, actor, the time the event occurred, a comment and an operational-error flag. It takes the read-only profile, so its handler serves reads only and every write goes through an operation.- RLS: tenant isolation and strict party isolation, as on the trade. The purge loop already covers it, through the immutable purge switch.
ores_trading_book_trade_fnwrites the trade first, so a refused second booking writes nothing, then the activity, the booking and the state, and returns the activity id instead of a boolean. The service reads an empty result as the conflict and returns the id inbook_trade_response.- A null amend is an activity whose type is not real: recorded, and versioning nothing.
- The component versions still carry the activity type code; the next task replaces it with a foreign key to this row.
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 |
|---|---|---|---|
| pgTAP, trade activity, under the test DDL role | 8 of 8 | pg_prove test/trading_trade_activities_test.sql |
bbc41dd5f1 + working tree |
| pgTAP, every trading file, under the test DDL role | 6 files, 79 tests, pass | pg_prove test/trading_*_test.sql |
bbc41dd5f1 + working tree |
| Activities the ACME import wrote | 16 new_booking, one per trade |
psql count after the round trip | bbc41dd5f1 + working tree |
| Round trip, merged portfolio | 16 of 16, in scope lost 0 | reset_and_roundtrip.sh rtx |
bbc41dd5f1 + working tree |
| Tenant purge of ACME | 119 rows in 10 tables to 0 (rolled back) | purge probe | bbc41dd5f1 + working tree |
| codegen pytest | 1153 passed | pytest projects/ores.codegen/tests |
bbc41dd5f1 + working tree |
| ctest | 94 of 94 | compass test run |
bbc41dd5f1 + working tree |
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 |
|---|---|
| #2672 | [trading] Record each business event as a trade activity |
9. Review
| Comment summary | File | Decision | Notes |
|---|---|---|---|
10. Result
trade_activity records each business event once: type, actor, when it
happened, a comment and an operational-error flag, under tenant and party
isolation, and refuses any update or delete. Booking a trade writes its
activity in the same transaction and returns the activity id; a refused
second booking writes none. A null amend is recorded as an activity and
versions nothing. The ACME import now leaves one new_booking activity per
trade, and the round trip is unchanged.