Task: Move the trade export onto the anchor

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

The ORE export, the report export to storage and the instrument lookup read the anchor and its components, and none of them reads the wide trade.

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-04

3. Acceptance

  • The three export subjects are served by the trade operations handler and declared by the operations model; the wide trade's model no longer declares them.
  • An export item carries the trade's anchor and its ORE id, not the wide trade; trades of a node are found through their bookings.
  • The round trip and the report export behave as before.
  • Build, ctest and the local check set pass.

4. Plan

  • Declare the export messages on the operations model: trade_export_item carries the anchor, the trade's ORE id, the instrument and the envelope. The portfolio export and the export to storage keep their subjects.
  • Move the reads into a new trade_export_service. It finds the trades of a node or of a book list through their live bookings, reads the anchors and the ORE identifiers, then fills the instruments and the envelopes.
  • Serve both subjects from trade_operations_handler, which now takes the HTTP base URL for the storage upload. Remove the subjects, the handler methods and the instrument lookup from the wide trade's model. The instrument lookup had no caller.
  • Give the instrument payload and the envelope hand-written TypeScript twins in the wire-protocol package, registered in the codegen's table of hand-written types, so the operations model keeps its TypeScript protocol. The protocol twin check refuses a model that switches it off.
  • Point the ORE exporter, the round trip, the shell command, the report export and the tests at the new item.

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
Round trip, PortfolioIds sample: trades exported 13 of 13 reset_and_roundtrip.sh rtp (db recreate, build, provision, ore upload, ore import, ore export) 0a8cd19f65 + working tree
Round trip, in scope (bonds and the envelope) lost 0, unexplained 0 scripts/ore_mapper_roundtrip_diff.py 0a8cd19f65 + working tree
Round trip, out of scope lost 373, unexplained 0 (same as before the change) scripts/ore_mapper_roundtrip_diff.py 0a8cd19f65 + working tree
Envelopes as stated, empty elements ignored 13 of 13 per-trade compare of type and envelope 0a8cd19f65 + working tree
ctest 89 of 90; the one failure is the known eventing re-drive race, green 3 of 3 on rerun compass test run 0a8cd19f65 + 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
#2628 [trading] Move the trade export onto the anchor

9. Review

# Comment summary File Decision Notes
1 The first round trip failed: an empty node id reached the query as an empty uuid. The old path read an empty node as every trade. trade_export_service.cpp accept The query now treats an empty node as the whole tenant.
2 The export codec test still used the old item shape. messaging_export_portfolio_codec_tests.cpp accept It now sets the anchor and the ORE id.
3 ores.reporting.core.tests failed once on the delete check. report_definition_eventing_integration_tests.cpp decline Known re-drive race, captured in the inbox; green 3 of 3 on rerun.
4 PR review: a trade with two live bookings could repeat across pages. trade_export_service.cpp decline The bookings table's exclusion constraint allows one live booking per trade.
5 PR review: a booked trade with no anchor would end the storage paging early. trade_operations_handler.hpp decline The booking's foreign key to the anchor is enforced, so the case cannot occur.
6 PR review: a request that fails to decode replies with no message. trade_operations_handler.hpp accept The reply now names the invalid request.
7 PR review: neither export checks a permission. trade_operations_handler.hpp decline Same as the old handler; adding a permission changes who can export and is not part of this move.
8 PR review: sort the anchors by uuid, not by its string. trade_export_service.cpp accept Sorted by the id.
9 PR review: the per-type id lists, the in-memory storage export, the Phase 4 comment. trade_export_service.cpp decline Moved unchanged from the old handler; the comment is accurate, each item takes a copy of the payload.
10 PR review: a parameter list is misaligned. trade_export_service.cpp accept clang-format 18 applied to the new files.
11 CI drift: the protocol twin check refuses the switched-off TypeScript facet. ores.trading.trade_operations.org accept Facet back on; the two payload types have hand-written twins.

10. Result

The portfolio export and the export to storage now read the anchor, its bookings, its ORE identifier, its instrument and its envelope. The operations model declares both subjects and trade_operations_handler serves them. The wide trade's model no longer declares the exports or the unused instrument lookup. The PortfolioIds round trip exports 13 of 13 trades with in-scope lost 0, and every envelope comes back as stated.

Emacs 29.3 (Org mode 9.6.15)