Task: Migrate currency_pair_convention.advance_calendar onto the calendar model

Table of Contents

This page documents a task in the Model calendars as proper ORE Studio reference data story. It captures the goal, current status, acceptance, and any notes or results.

1. Goal

currency_pair_convention.advance_calendar is currently a comma-joined free-text value (e.g. "UnitedStates,SouthKorea") with no validation. Replace it with a junction table validated against the calendar entity, mirroring the currency-to-calendar relationship. Resolve the naming question along the way: is advance_calendar an idiomatic name for this field, or should it just be holiday_calendar=/=calendar (matching currency's field name)?

2. Status

Field Value
State DONE
Parent story Model calendars as proper ORE Studio reference data
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-07-17

3. Acceptance

  • [X] A junction table links currency_pair_convention to one or more calendar rows, replacing the free-text advance_calendar column.
  • [X] Naming decision made and recorded in this story's * Decisions; applied consistently across domain/SQL/UI.
  • [X] Existing seeded data (from Add librarian support for currency pairs) migrated onto the new model.

4. Plan

Modelled currency_pair_convention_calendar as an ores.codegen.junction and, for the first time in this story, generated the full C++ layer (domain/generator/repository) via codegen rather than hand-authoring it — the parent story's junction-codegen gap (Fix domain/generator/repository Mustache templates for junction-shaped data) was closed by a separate story between task 3 and this one. Required adding a * C++ ** Flags block with :subcomponent: api to the model (mirroring tenor_convention_resolution.org) to get the generated files into the correct api=/=core path split.

Naming decision: kept advance_calendar rather than renaming to calendar=/=holiday_calendar, since it matches ORE's own AdvanceCalendar XML field verbatim and denotes a distinct concept (date-advancing via Calendar::advance(), not business/settlement-day membership) per the existing AdvanceCalendar knowledge doc.

Retiring the free-text column broke ores.ore's conventions_mapper (XML export/import), which read/wrote convention.advance_calendar directly. Fixed by carrying the calendar codes on mapped_fx instead (same pattern already established for spot_days: the mapper has no database access), building/parsing the comma-joined XML value at the mapper boundary — this substantially delivers task 6's goal for the FX convention case specifically.

5. Notes

6. PRs

PR Title
#1612 [refdata,ore] Migrate currency_pair_convention.advance_calendar onto the calendar model

7. Review

Comment summary File Decision Notes
       

8. Result

Modelled currency_pair_convention_calendar as an ores.codegen.junction, generated end-to-end via codegen (domain, JSON/table I/O, generator, repository entity/mapper/repository) rather than hand-authored — the first junction in this story to use the newly-completed junction C++ codegen path. Wired create/drop scripts into the manifests (depends on both currency_pair_conventions and calendars, so creates after both, drops before both) and added its RLS tenant-isolation policy up front.

Seeded 16 real pair-to-calendar mappings (e.g. EUR/USD -> TARGET, UnitedStates.Settlement) via a SQL populate script only — ores.codegen.junction doesn't yet support a "real data" custom generator section the way ores.codegen.entity does, so the dual C++-generator-plus-SQL-populate seed pattern from tasks 2/3 couldn't be fully replicated here; captured as a gap for a future ores.codegen.junction enhancement rather than hand-editing the generated generator file.

Retired currency_pair_convention.advance_calendar by removing the field from its org model and regenerating the entity via codegen. Fixed the one non-codegen-owned consumer this broke: ores.ore's conventions_mapper, whose mapped_fx struct now carries advance_calendars (a std::vector<std::string>) instead of reading/writing the retired domain field, following the same "mapper has no DB access" pattern already used for spot_days. xml_fx_convention_mapper_roundtrip_tests.cpp's round-trip test (comparing the raw XML AdvanceCalendar string before/after map_fx=/=reverse) continues to exercise this path unchanged.

Verified locally before the final sync: full monorepo build clean; ctest — ores.refdata.core.tests (includes the new junction's repository tests), ores.ore.core.tests, ores.ore.api.tests, and ores.ore.service.tests all passed; the only failure across the whole suite was the same pre-existing, unrelated contact_type generator test seen throughout this sprint; validate_schemas.sh clean (0 warnings); compass db recreate verified with 16 currency_pair_convention_calendars rows seeded. The branch was re-synced twice more after that verification pass to keep up with a fast-moving main (each rebase was a clean, conflict-free replay); the full suite was not re-run to completion after the final sync before this push, per explicit instruction to push without re-verifying — the specific suites this change touches were last confirmed green pre-sync, and both rebases were clean fast-forward replays with no conflicting hunks in the touched files.

Emacs 29.3 (Org mode 9.6.15)