Task: Wire ORE export path for user-authored calendar templates
Table of Contents
This page documents a task in the Calendar entity follow-ups: date picker, list-pagination fix, QuantLib materialization story. It captures the goal, current status, acceptance, and any notes or results.
Goal
Assemble the transient calendar_adjustment DTOs ORE needs for
source'user'= calendar templates – built from
calendar +
calendar_exception rows,
never persisted – and expose them via a NATS request/response so a
future ORE-run export feature can turn them into a
CalendarAdjustments XML file via the already-implemented, already
round-trip-tested ores::ore::domain::calendar_adjustment_mapper=/
=ores::ore::xml::exporter::export_calendar_adjustments().
source'quantlib'= calendars are never exported – ORE resolves them
natively via a bare code reference.
Split out of the parent task's step 5 because no ORE-run export mechanism (conventions.xml, curve config, etc.) exists yet anywhere in the codebase to hang this off of – portfolio export (trades) is the only export feature built so far. This task ships the assembly/export-service piece standalone, ready for whichever future feature needs it, rather than blocking on that feature existing first.
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Calendar entity follow-ups: date picker, list-pagination fix, QuantLib materialization |
| Now | Done. |
| Waiting on | Nothing. |
| Next | None – ready for a future ORE-run export feature to call. |
| Last touched | 2026-07-25 |
Acceptance
[X]A service assemblescalendar_adjustmentDTOs for a requested set of calendar codes (or every activesource'user'= calendar), in base-first (dependency) order, so a chain of user templates based on other user templates always has itsBaseCalendarreference already defined earlier in the assembled list.[X]Asource'quantlib'= calendar code passed in the request is silently skipped, not an error.[X]Abase_calendar_codecycle is detected and rejected rather than looping forever.[X]Reachable via NATS (refdata.v1.calendar_adjustments.export), mirroring the existing per-entity handler/registrar shape.[X]No new persisted table or entity –calendar_adjustmentremains exactly the existing domain struct/ORE-XML-mapper pair, used only as a transient export-time shape.
Plan
(Implementation strategy. Written when work starts; key decisions
are distilled into the parent story's * Decisions at close, but the
plan itself stays — it is the historical record of what we did.)
Findings
ores::ore::xml::exporter::export_calendar_adjustments(vector<calendar_adjustment>)andores::ore::domain::calendar_adjustment_mapper::reverse()already exist and are already round-trip tested (xml_calendaradjustment_roundtrip_tests.cpp) – confirmed the parent task's "no new mapper code needed" assumption. The actual gap was narrower than first framed: not "no export path exists" but "no assembly-from-refdata function exists yet to feed this already-built exporter."ores.dq'sdataset_dependency+publication_service'sboost::graph::topological_sortis the referenced "respect the dependency graph" precedent, but is overkill here: a calendar'sbase_calendar_codechain is a forest (each node has at most one parent), not a general DAG, so a plain recursive post-order DFS (cycle-guarded via an in-progress set) gives a valid base-first order directly, with no boost::graph dependency needed.- Reused the exact same recursive base-chain-walk shape from
calendar_materialisation_service(step 4) for cycle detection – same problem, different output (an ordered DTO list here, dates there).
Sequencing
calendar_adjustment_export_service(ores.refdata.core): given a set of calendar codes (or empty = every activesource'user'= calendar), walks each one'sbase_calendar_codechain (skipping/never-emittingsource'quantlib'= links, since ORE resolves those natively), and returns a base-first list ofcalendar_adjustmentDTOs assembled from that calendar's own row (calendar_name,base_calendar) and itscalendar_exceptionrows split intoadditional_holidays=/=additional_business_daysbyis_business_day.- NATS protocol (
get_calendar_adjustments_request=/=response), handler, and registrar wiring, mirroring the existing per-entity shape exactly (no new permission scope – unguarded like every other list/get endpoint, consistent with e.g.calendar_handler::list).
Notes
- Reuses
system.calendar_materialisation(the change-reason code added in step 4) for these DTOs'change_reason_code– they are never persisted, so this is purely a placeholder value the mapper never reads (confirmed by inspectingcalendar_adjustment_mapper::reverse(): onlycalendar_name,base_calendar,additional_holidays,additional_business_daysare read). - Not yet built: the actual ORE-run export feature (conventions.xml/ curve-config assembly, or whatever form it eventually takes) that would call this service and write the resulting XML to disk/bundle it with other files. That's future work with its own task, once that feature's shape is decided – this task only had to ship the refdata-side assembly piece, which is now ready for it.
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 |
|---|---|---|
PRs
| PR | Title |
|---|---|
Review
| Comment summary | File | Decision | Notes |
|---|---|---|---|
Result
All five acceptance criteria met. Shipped
calendar_adjustment_export_service (ores.refdata.core): given a
set of calendar codes (or none, meaning every active source'user'=
calendar), walks each one's base_calendar_code chain via a
cycle-guarded recursive DFS – a forest, not a general DAG, so no
boost::graph dependency was needed – and returns a base-first list
of transient calendar_adjustment DTOs assembled from each
calendar's own row plus its calendar_exception rows, skipping
source'quantlib'= links since ORE resolves those natively. Exposed
via refdata.v1.calendar_adjustments.export (request/response,
handler, registrar), mirroring the existing per-entity handler shape
exactly, unguarded like other list/get endpoints. No new persisted
table or entity – calendar_adjustment stays exactly the existing
domain struct/ORE-XML-mapper pair, used purely as an export-time DTO.
Deliberately out of scope: the actual ORE-run export feature (conventions.xml/curve-config assembly, or whatever form it eventually takes) that would call this service and write the resulting XML to disk – no such feature exists in the codebase yet. This task shipped the refdata-side assembly piece only, ready for that future feature to call.