Task: Pin the rates instruments to the anchor's key

Table of Contents

This page documents a task in the Rework the rates instruments to the instrument table rule story. It captures the goal, current status, acceptance, and any notes or results.

1. Goal

Every rates instrument row, the header and the facts alike, is pinned to the trade anchor by a composite key over the tenant, the trade, the party and the trade type, so a row cannot disagree with the anchor it belongs to.

2. Status

Field Value
State DONE
Parent story Rework the rates instruments to the instrument table rule
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-10-09

3. Acceptance

  • ores_trading_trades_tbl carries the unique index the composite key needs.
  • Every rates instrument row, the header included, carries a foreign key to the anchor over the tenant, the trade, the party and the trade type.
  • A row whose party or trade type disagrees with the anchor's is refused, and a case proves it.
  • The generated models, the SQL and the repositories agree on the key.
  • The change to the trade model is recorded where the other families can join it.

4. Plan

  1. The anchor's index. Add the unique index the composite key needs to the trade model and regenerate. This is a change to ores_trading_trades_tbl, not to this family, so record it where the other families can join it.
  2. The instruments' key. Declare the composite foreign key on the header and on each of the eight fact tables, over the tenant, the trade, the party and the trade type.
  3. The fallout. The repositories key on the trade id today; the composite key adds the party and the type, so the generated key parameters, the services and the shell surfaces follow, and the tests that build a row without the anchor's party will start to fail — that is the check working.
  4. A refusing case. A repository case that writes a row whose party or trade type disagrees with the anchor's, and expects the store to refuse it.
  5. Before branching. The trade anchor is shared with every other family and with in-flight work; check the fleet for what else is touching it before starting.

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
Trading core suite after the pin 143 cases, all passed compass test run -- -R trading.core uncommitted (working tree over efa0aab1c2)
ORE core suite after the pin all passed compass test run -- -R ore.core same
Component documentation audit 41 of 41 components pass projects/ores.codegen/validate_docs.sh same

6. Notes

  • This is the only unit of the arc that changes the trade model, so it is the one that needs a fleet check and possibly a coordination note before it starts.
  • Story acceptance clause 2 is this task; it is the last clause that a structural change, rather than a proof, can satisfy.

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
#3032 [trading,sql] Pin the rates header to the trade anchor

9. Review

# Comment summary File Decision Notes
1 CHECK_THROWS accepts any exception repository_rate_instrument_cascade_tests.cpp Accepted The case now matches the pin's message text.
2 Say whether the anchor's trade type can change ores.trading.rate_instruments.org Accepted The model says the anchor documents it as fixed but its write path does not enforce it; the gap is open and belongs to the trade model.
3 Name which of party and trade type disagreed trading_rate_instruments_create.sql Declined The generated pin takes one message with one placeholder.
4 Confirm CI runs the drift check none Noted Drift is clean locally for trade and trading-cpp.

10. Result

The rates header is pinned to the trade anchor on the party and the trade type. The fleet check found one overlap: merry newton adds two columns to the trades table, which does not touch the pin.

The task's premise was out of date, so the delivery differs from the plan:

  • The anchor is a versioned table now (Make every trading entity a regular, versioned table), and its unique keys were removed because nothing referenced them (Stop the trade's children copying its party). A database composite foreign key cannot name a versioned table, and a unique index would serve nothing. No index was added. The pin is a codegen Pinned keys row on the header, which generates an insert-trigger check against the anchor's current row over (id, party_id, trade_type).
  • The fact and child tables carry neither party_id nor trade_type_code (design decision D3), so the header is the only row of the family that can disagree with the anchor. They are not pinned.
  • The trigger stamps party_id from the session and row-level security hides other parties' anchors, so the observable refusal is the trade type. The party column is held to the anchor as well.
  • Because the check runs at insert, a later amendment of the anchor does not re-check the header. The trade model's owner decides whether an amendment re-pins its children.

Tests: the cascade test now books its anchor as a ForwardRateAgreement, and a new case refuses a header whose trade type differs from the anchor's.

Emacs 29.3 (Org mode 9.6.15)