Task: Keep the forward rate agreement's direction
Table of Contents
This page documents a task in the Carry the rates family's fields across the ORE boundary story. It captures the goal, current status, acceptance, and any notes or results.
1. Goal
A trade the document states as Short is stored as Short and exported as Short.
2. Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Carry the rates family's fields across the ORE boundary |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-10-18 |
3. Acceptance
- The import stores the direction the document states, not a constant.
- The export writes the stored direction back into the document.
- A test proves a Short forward rate agreement survives both directions, with the document's own value as its expectation.
4. Plan
- Write the failing test first: an XML portfolio stating
<LongShort>Short</LongShort>throughforward_fraand back throughreverse_fra, expecting Short in the row and in the exported document. The corpus states Long, so the document is the suite's only Short witness and the codec's parse is part of the proof. - Read the direction on import with
to_string, and write it back with the mapper's ownparse_code, which round-trips the generatedto_stringso the spelling has one source of truth. - Check the money path while the mapper is open, since the same function
converts a notional and a strike. Add nothing:
mapper_fra_holds_the_digits_of_its_strike_and_notionalalready asserts the lexical form and the serialised<Strike>0.005</Strike>.
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 | |
|---|---|---|---|---|
| Direction sites in the FRA mapper | 2, one in and one out | =grep -n 'fra.LongShort\ | fra.long_short' projects/ores.ore/core/src/domain/swap_instrument_mapper.cpp= | HEAD |
| Money conversions in the FRA mapper | 4 in, 3 out, all lexical | exact_decimal at :1431, :1433, :1448, :1450; set_exact at :1742, :1754, :1759 |
HEAD | |
| FRA cases in the IR mapper suite | 4, of which 1 new | sed -n '/FRA mapper tests/,/CapFloor mapper tests/p' then grep -c TEST_CASE |
HEAD |
6. Notes
- The review graded this finding Critical because the defect is silent: the store agrees with itself and the document disagrees with both. The fix is small because the column, its check and the schema member were already right; only the mapper ignored them.
- The direction parse uses
parse_code, the mapper's existing helper, rather than the ternary a neighbouring product uses.parse_codescans the generatedto_stringacrosslong_short_countentries, so the spellings have one source. The count itself is hand-maintained, as it is for every other enum in this mapper, so a third spelling would fall back silently until someone bumped it. Recorded because the first version of this note claimed the helper removed the second table, and it does not. - Money: this unit changes no money conversion, and the FRA's seven sites were
already exact.
mapper_fra_holds_the_digits_of_its_strike_and_notionalproves it by asserting the lexical form and the serialised<Strike>0.005</Strike>
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 |
|---|---|
| #3540 | [ore] Keep the forward rate agreement's direction |
9. Review
| # | Comment summary | File | Decision | Notes |
|---|---|---|---|---|
| 1 | The export parse sat inside the leg block, so a legless row exported the default | swap_instrument_mapper.cpp:1746 (was :1751) |
Fixed | The direction is the row's own; it is now written before the block. |
| 2 | long_short_count is hand-maintained, so the helper does not remove the second table |
swap_instrument_mapper.cpp:515 |
Fixed in prose | The mechanism is one generated spelling table plus a count beside every other enum's. |
| 3 | A data row written before the fix cannot be recovered | — | Recorded | Re-import the affected trades; the row holds a valid value, so nothing distinguishes it. |
| 4 | The case set the binding by hand and never parsed the spelling | xml_ir_mapper_roundtrip_tests.cpp:212 |
Fixed | The case is now a Short document, and asserts the exported text as well. |
| 5 | The generator states Long and no fixture states Short | fra_instrument_generator.cpp:52 |
Captured | let-the-generators-state-both-directions in the backlog. |
10. Result
A short forward rate agreement is now stored, exported and read back as Short.
The import reads fra.LongShort through the generated to_string, and the
export writes the stored code back through parse_code outside the leg block,
so a row whose leg children were never written still states its direction.
Index and Strike still come from those leg rows, so such a row exports its
direction and neither of those; that is the shared-leg design and the leg path
belongs to the unit that sets the leg's own direction.
The case is a document, not a hand-set binding:
mapper_fra_reads_a_short_document_and_writes_it_back parses an XML portfolio
stating <LongShort>Short</LongShort> and asserts both the stored code and the
exported <LongShort>Short</LongShort>. The codec's parse of the spelling is
therefore part of what it proves, and the suite carries its only Short witness.
Red then green, executed: with the mapper reverted to the merge base the case
fails at xml_ir_mapper_roundtrip_tests.cpp:236; with the fix it passes. The
same run also proves the second half, because the export assertion at :244
would fail on its own if the reverse wrote the constant back. The CTest artefact
holds only the last run, so the red half is reproduced by reverting the mapper
rather than by reading a file.
Money: the unit changes no conversion, and the FRA's four import and three
export sites were already exact. The family already proves it:
mapper_fra_holds_the_digits_of_its_strike_and_notional asserts the lexical
form and the serialised document text, so this unit's money check is a negative
result, recorded because a fix that touches a money-carrying mapper should say
so.
Rows imported before this fix hold Long for a Short trade and cannot be recovered from the row: the defect wrote a valid value, so nothing in the store distinguishes them. Re-import the affected trades from their documents.
The gate ran in both fleet states: build, db, ctest, ore-xml-roundtrip and the
fourteen static checks with the fleet down, and step-consumers with it up.