The store never canonicalises the business day convention
Table of Contents
This page is a capture in the inbox bucket of the product backlog — a pre-sprint idea, not yet pulled into a sprint as a story.
1. What
The store keeps whichever business day convention spelling a document states,
so the same adjustment reaches the table as F, Following or FOLLOWING,
while ores.refdata.core's curve-configuration round trip expects one code.
2. Why
bc083d5498 states the invariant — "the mapping layer converts the stated
spelling to the code before a row is written, so the table holds one
representation" — and adds stored_business_day_convention, but never wires it
into the write path. Its only caller is the round-trip test, which therefore
asserts behaviour nothing implements.
Two assertions fail on origin/main and on branches that hold no codec work:
Example_63 ... CapFloorVolatilities.CapFloorVolatility[1]: BusinessDayConvention was lostInput/curveconfig.xml ... InflationCapFloorVolatilities .InflationCapFloorVolatility[0]: BusinessDayConvention: 'ModifiedFollowing' became 'MF'
Both come from the test's own expectation, as_stored in
curve_configuration_database_roundtrip_tests.cpp:
- It assigns the helper's
std::stringinto anstd::optional<std::string>member. For a row that states no convention the helper answers"",so the expectation gains an empty convention where the document states none. The store gives none back, and the comparison reads that as a lost value. Keeping the empty optional instead makes the case pass, which was verified by rebuilding the test that way. - It canonicalises a spelling the store never canonicalises. The document says
MF,as_storedmaps it toModifiedFollowing, the database holdsMF. The SQL value guard (refdata_curve_configuration_columns_validate_create.sql) only refuses a value outside the set; it never rewrites one.
3. What to decide
Either the mapping layer canonicalises, which is what its commit says it does, or the round trip stops expecting it and states that the table may hold every spelling a document uses. The first keeps the invariant the SQL guard and the grouped queries rely on; the second is honest about today's behaviour.
4. References
projects/ores.refdata/core/include/ores.refdata.core/repository/business_day_convention_code.hppprojects/ores.refdata/core/tests/curve_configuration_database_roundtrip_tests.cppprojects/ores.sql/create/refdata/refdata_curve_configuration_columns_validate_create.sql
5. Resolution (2026-11-03)
Both defects are fixed. This capture was filed against a tree whose guards refused a value outside the set but stored a rendering as it stood, and both halves landed after it was written.
- The store canonicalises. Every write path reaches a guard that asks
ores_reporting_value_in_set_fnwhether the value is one the set states, thenores_reporting_canonical_value_fnfor the form it holds (refdata_curve_configuration_columns_validate_create.sql:71). Measured on the running environment:select count(*) from ores_reporting_canonical_value_violations_fn()answers0, and the function names every row holding a rendering instead of its form. - The round trip no longer states
as_stored. It writes through the store and states the promise on what the store returns: the document the rows hold exports and reads back unchanged (curve_configuration_database_roundtrip_tests.cpp:259). stored_business_day_conventionis deleted, with the header this page names. The value set'sore_renderings.jsonis the only statement of the mapping, and the reader's tables are generated from it.
Worked in Normalise every ORE parameter representation.