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_activity is 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_fn writes 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 in book_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.

Emacs 29.3 (Org mode 9.6.15)