Task: Add the rates instrument verb units

Table of Contents

This page documents a task in the Manage trading entities from the shell story. It captures the goal, current status, acceptance, and any notes or results.

1. Goal

ores.shell extends the trading command folder to the nine rates instrument families, cloned from the fx-batch skeleton. Each unit (balance_guaranteed_swap, callable_swap, cap_floor, fra, inflation_swap, knock_out_swap, rpa, swaption, vanilla_swap) declares the entity-specific data: trading.v1 subject family, request and response classes from the per-product protocol, save columns from the org model, instrument_id key. The batch runs the live rates system test: every unit round-trips add, list, history and delete through the shell against the fleet, with each save verified at the DB layer and on the changed-event stream. Any quirk a family exposes (swap legs, product columns the fx convention does not cover) is resolved here and distilled back into the shared skeleton.

2. Status

Field Value
State DONE
Parent story Manage trading entities from the shell
Now Nothing.
Waiting on Nothing.
Next Generic instrument-family batch (task_shell-trading-generic-instrument-verbs).
Last touched 2026-09-08

3. Acceptance

  • The nine rates units exist under projects/ores.shell/src/app/commands/trading/ and compile; trading_commands registers them; the shell cmake component files regenerate with no drift.
  • Live against the fleet: add, list and page, history and delete round-trip every rates family through its trading.v1.*_instruments subjects; each save lands a DB row (verified via psql) and emits the changed event.
  • Full preset build and ctest green; shell cmake-sources drift clean. The batch lands on feature/shell-trading-instrument-verbs as one [ores.shell] commit.

4. Plan

The nine rates units clone the fx-batch skeleton and differ only in declared data: trading.v1. subject family, request and response classes from the instrument protocol, save columns from the org model, instrument_id key. The batch adds the service-surface audit the fx and equity families did not need: the rates families predate the codegen consolidation and still carried per-family messaging files, so the plan is to establish which wire contract the service serves after a full relink and restart, then delete the superseded per-family files — a deletion build tells us if anything still pulls them in.

Two service-side gaps the audit must close. The per-family event registrars exist in the component but the service application never calls them, so changes to these instruments reach no event bus: the plan registers each family's entity-to-event mapping in application.cpp, matching the fx and equity families. And the wire a probe sees is the wire of the library image the process linked at start, so the plan re-probes only after the service restarts onto the rebuilt lib.

The units also give the nine rates orgs Table display sections and their *_table.cpp column renders, and the shell a shared multi-party login helper: complete_login exchanges a single-use login token for the scoped token of the account's default party via iam select-party, mirroring the Qt client — the live runs log in as tenant_admin@acme, whose account spans parties.

Live-test protocol per family: scripted shell run as tenant_admin@acme adds, gets and pages, histories, deletes; each add verified at the DB layer (psql row under the acme tenant with modified_by = tenant_admin) and on the changed-event stream, with the subscriber on the prefixed subject ores.dev.brave_hopper.ores.trading.> and the per-family CHECK constraints exercised live (the callable swap call_type accepts Bermudan and rejects European).

5. Notes

6. Test Scenarios

Manual QA scenarios (scaffolded via compass add test_scenario, run through the QA Validation Runner panel) 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
     

7. PRs

PR Title
   

8. Review

Comment summary File Decision Notes
       

9. Result

Shipped the nine rates instrument verb units under projects/ores.shell/src/app/commands/trading/: balance_guaranteed_swap_instrument_commands.{hpp,cpp}, callable_swap_instrument_commands, cap_floor_instrument_commands, fra_instrument_commands, inflation_swap_instrument_commands, knock_out_swap_instrument_commands, rpa_instrument_commands, swaption_instrument_commands and vanilla_swap_instrument_commands, each cloning the fx-batch skeleton with its own trading.v1 subject family and positional add arguments from the org columns. trading_commands.cpp registers the nine plural menus; component_files.cmake lists the units. Each modeling org gained a Table display section and its *_table.cpp prints the fixed column set; the shell gained the multi-party login helper in login_helpers.hpp (complete_login party selection). The batch is three commits on feature/shell-trading-instrument-verbs: 3a4a0e4bd3 [ores.shell] (the units), 0dd2515fe4 [ores.trading] (event mapping registration in the service application) and 0ea3563012 [ores.trading] (legacy removal).

Live acceptance met on brave_hopper against the fleet. A scripted matrix per family as tenant_admin@acme round-tripped add, get, history and delete for all nine families — 45/45 operations OK. Every add landed a DB row under the acme tenant with modified_by = tenant_admin and emitted the family changed event; delete soft-closed the row at the bitemporal valid_to stamp and emitted a second changed event. Changed events were captured on the prefixed subject ores.dev.brave_hopper.ores.trading.> for probe adds and probe deletes (fra and callable_swap probes), proving add-to-NATs and delete-to-NATs. Per-family CHECK enforcement was seen live: the callable swap add with call_type European was rejected by the repository, and Bermudan passed. Probe rows were cleaned through the shell's own delete; per-family psql checks confirmed every row open after add and soft-closed out of the live view after delete.

The first matrix run failed get/history/delete on six families (fra, cap_floor, balance_guaranteed_swap, callable_swap, knock_out_swap, inflation_swap): those replies carried the legacy per-family wire member, not the consolidated instruments member. The root cause was not the in-flight deletion: the running service process had linked before the consolidated registrar wiring and kept executing the replaced lib's old inode — /proc/PID/map_files marks the file (deleted) — so it answered on the wire contract of the lib image it started with. A restart onto the rebuilt lib produced consolidated replies immediately and the matrix went 45/45. The story Decisions record the restart rule for later batches.

Closing battery green: full preset build exit 0; ctest 71/71 passed (919 s); roundtrip check exit 0 (pre-existing partial-fidelity baseline, no outputs touched); shell cmake-sources drift clean; codegen drift clean (--all); working tree clean after drift regeneration.

Status at close: Batch committed (3a4a0e4bd3, 0dd2515fe4, 0ea3563012); full preset build, ctest 71/71 and the roundtrip, cmake-sources and codegen drift checks green. The six-family legacy-wire anomaly was a stale service process mapping a pre-consolidation lib image; a restart onto the rebuilt lib resolved it.

Emacs 29.3 (Org mode 9.6.15)