Task: Foundation-layer population for tenor reference data (tenor/tenor_anchor/tenor_convention)

Table of Contents

This page documents a task in the IR Rates synthetic data generation story. It captures the goal, current status, acceptance, and any notes or results.

Goal

Ship idempotent populate scripts seeding ores_marketdata_tenors_tbl, ores_marketdata_tenor_anchors_tbl, ores_marketdata_tenor_conventions_tbl, and ores_marketdata_tenor_convention_resolutions_tbl with the standard tenor set (O/N through 30Y, SPOT, and the special short-end labels) and the spot/forward and FX swap conventions (credit/CDS seeded as a convention row only, its resolution deferred — see the capture in Notes), plus the runtime resolver (resolve_end_date=/=resolve_window=/=windows_overlap) that reads these persisted rows instead of hardcoding tenor knowledge in code.

Status

Field Value
State DONE
Parent story IR Rates synthetic data generation
Now Complete.
Waiting on Nothing.
Next Nothing.
Last touched 2026-07-13

Acceptance

  • Standard tenor rows (per tenor.org's table) and SPOT/TODAY/TOMORROW/NEAR_LEG/IMM_ROLL anchors are seeded
  • Tenor convention rows (spot/forward, FX swap near-leg, credit/CDS IMM) and the documented tenor_convention_resolution membership/override rows are seeded
  • Seeded via the Foundation layer (universal, always-present reference data — see Notes for why this is not a DQ Librarian dataset), not a new standalone bundle
  • Populate scripts are idempotent and validate_schemas.sh passes
  • A runtime resolver reads the persisted tenor/tenor_convention/tenor_convention_resolution rows to resolve a tenor to a date — no per-label knowledge hardcoded in code

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.)

Originally scoped as a DQ Librarian dataset (catalog + dataset + artefact table + bundle membership, per the synthetic.fx_spot_configs=/ =refdata.currency_pairs pattern). Investigation of refdata_day_count_fraction_types_populate.sql showed that pattern is for optional, party-selectable seed data a user chooses to Apply — not what standard tenor labels are. Tenors are universal, always-needed reference data, same category as day-count conventions, so this task instead seeds directly into the Foundation layer (foundation_populate.sql), which every new environment gets unconditionally with no separate Librarian step.

Notes

This task also closes the loop flagged while reviewing the salvaged tenor_period value type (from PR #1522, briefly raised as PR #1535 and then closed/folded in here): a standalone runtime type that hardcodes tenor-resolution knowledge (e.g. "O/N = 1 day") duplicates, and can silently diverge from, what the persisted model is meant to be the single source of truth for. The fix was schema, not code: adding tenor_convention.resolution_algorithm and tenor_convention_resolution.offset_unit=/=offset_multiplier so a SPECIAL tenor's convention-specific duration is data, not a hardcoded switch statement. The runtime resolver (ores.marketdata.api::domain::tenor_resolution.hpp) is now a pure function over those persisted rows, proven by a test that resolves the same tenor (O/N) to two different dates under two different conventions purely because the input rows differ.

tenor_convention.resolution_algorithm also anticipates credit/CDS's genuinely different IMM-roll-based resolution (IMM_ROLL, vs. the ANCHOR_OFFSET algorithm implemented here) without needing a schema change later — see the capture Implement IMM_ROLL tenor resolution for credit/CDS conventions for the deferred resolver implementation and CDS-specific seed data.

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
#1539 [marketdata,codegen,sql] Combined tenor data model: entities, resolver, Foundation seed data

Review

PR #1539 review round (3 automated passes, issue-level comments, no line comments):

# Comment summary File Decision Notes
1 RLS policy comment labels the hand-authored junction table "codegen-generated", contradicting the PR description marketdata_rls_policies_create.sql Fixed Comment now reads "hand-authored junction table – codegen does not yet generate SQL for junctions"
2 measured_from's doc comment implies it's empty only for IMM_ROLL conventions, but FX_SWAP_NEAR_LEG (ANCHOR_OFFSET) also seeds it empty since every tenor carries its own anchor_override ores.marketdata.tenor_convention.org (regenerated tenor_convention.hpp) Fixed Fixed at the model source and regenerated, not hand-edited in the generated header

Result

Shipped: four Foundation-layer populate scripts (tenor anchors, tenors, tenor conventions, tenor convention resolutions) seeding 5 anchors, 37 tenors, 3 conventions, and 39 resolution rows, verified idempotent against a freshly recreated dev DB (re-running produces identical row counts). Added the SQL create/drop/RLS-policy wiring codegen doesn't generate for a junction entity. Added the runtime resolver (resolve_end_date=/=resolve_window=/=windows_overlap) as a pure function over the persisted rows — 9 test cases / 11 assertions, including a test proving the same tenor resolves differently under different conventions. Credit/CDS is modeled and its convention row seeded, but its IMM_ROLL resolution is deferred to a capture, not implemented or fabricated here.

Emacs 29.3 (Org mode 9.6.15)