Task: As-of window-join read-side composition query
This page documents a task in the Implement temporal composite entity versioning story. It captures the goal, current status, acceptance, and any notes or results.
Goal
Add a read-side query so a caller can fetch a parent entity as it stood at a specific version, and its children filtered to that same version's [valid_from, valid_to) window — reaching all the way to the repository and service layers, codegen-driven for any future entity.
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Implement temporal composite entity versioning |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-07-08 |
Acceptance
- Any versioned entity gets a generic
read_at_version=/=get_<entity>_at_versionrepository+service method (gated onhas_audit_columns, not a new opt-in flag — genuinely applies to every entity, per the author's direction). - Any child FK opted into
:list_by_as_of: truegets aread_by_<fk>_as_of=/=list_<entity>_by_<fk>_as_ofrepository+service method doing the interval-overlap query against a caller-supplied window. - Codegen-driven for counterparty; hand-patched to match for party (2 of its 3 entities use an older, pre-generic-facet hand-written repository/service shape — patched to match their own existing conventions rather than pulled onto the modern shape, which is a separate, larger modernization out of scope here).
- Proven with a new repository test
(
as_of_composition_reflects_party_version_history) exercising the full mechanism against a real party+identifier; full party/counterparty test suite (28 test cases, 47 assertions) passes. - NATS messaging/protocol layer deliberately deferred to the Qt composite history dialog task, which needs it anyway.
Plan
Extended the FK-flag mechanism from the touch-function task with a
mirror pair: :list_by_as_of: (child FK, read side) alongside
:bump_parent_version: (child FK, write side), and made
read_at_version=/=get_<entity>_at_version unconditional for any
has_audit_columns entity rather than a new flag, since a single-entity
as-of fetch is useful standalone, not just for composite parents.
Regenerated counterparty's repository+service layers via
compass codegen regenerate --component refdata-cpp --profile
repository|service, then manually scoped the diff to only the
entities actually touched — the regen run picks up large amounts of
unrelated pre-existing drift across ~20+ other refdata entities each
time (a known, separate gap — some entities are stuck on an older
hand-written shape: constructor-held context, boost::uuids::uuid
params instead of std::string=+per-call =context). Reverted those;
not this task's concern.
Notes
counterparty_contact_information and party_identifier's sibling
party_contact_information (plus party itself) are on that older
shape — regenerating them wholesale would have required also updating
their service/handler/test layers to the modern interface, well
beyond this task. Hand-patched the 2 new methods into each file
matching its own existing convention (context-in-constructor,
boost::uuids::uuid params) rather than the modern generated shape,
exactly as done for party's SQL in the touch-function task.
domain::party (and siblings) only surface recorded_at (=
valid_from), not valid_to, on the domain struct — discovered while
writing the proof test. Windows are contiguous by construction
(closing one version's valid_to is the next version's valid_from),
so the test derives v1's window end from v2's recorded_at rather
than needing a valid_to field; the still-open latest version's
open end is bounded with a safely-far-future instant instead of the
real infinity sentinel (domain objects don't carry that either).
PRs
| PR | Title |
|---|---|
| 1469 | [sql,codegen,qt] Temporal composite entity versioning |
Review
| Comment summary | File | Decision | Notes |
|---|---|---|---|
Result
Landed and proven: read_at_version=/=get_<entity>_at_version for any
versioned entity, read_by_<fk>_as_of=/=list_<entity>_by_<fk>_as_of
for any :list_by_as_of:-opted-in child FK, reaching repository and
service layers. Codegen-driven for counterparty; hand-patched to
match party's (and 2 other stale entities') existing conventions.
NATS/protocol layer deferred to the Qt composite history dialog task.
New test proves the composition end-to-end against real data; full
party/counterparty suite (28 cases, 47 assertions) passes.