Story: Implement temporal composite entity versioning

Table of Contents

This page documents a story in Sprint 22. It captures the goal, current status, acceptance criteria, and the tasks that compose it.

Goal

Land the SQL/codegen mechanism for child writes to bump a parent's version in the same transaction, the as-of read-side composition query, and the Qt composite history dialog, proven against party and counterparty as the reference entities.

Status

Field Value
State DONE
Parent sprint Sprint 22
Now Core mechanism (SQL touch-function, as-of query, architecture decisions) shipped.
Waiting on Nothing.
Next None — the 2 remaining Qt/doc tasks moved to Commission party, counterparty, and party_status together, since both are gated on that story's own binding prerequisite.
Last touched 2026-07-10

Acceptance

  • Child writes to party_identifier/party_contact_information (and counterparty equivalents) bump the parent's version in the same transaction, with a standardized change-reason comment. A read-side as-of query composes parent+children by matching valid_from/valid_to windows. The Qt history dialog shows composite state as of a selected parent version. Zero-diff regen where codegen already covers the entity; hand-patched first for party (not yet under codegen), matching the established pattern.

Tasks

Task State Start End Description
Resolve open decisions from the versioning architecture doc DONE 2026-07-07 2026-07-07 Sign off on: whether child DELETE also bumps the parent version, the actual codegen flag names (bump_parent_version, list_by_as_of), and whether reason-code stamping on the touch function is mandatory. Record decisions in the architecture doc and this story.
SQL touch-function mechanism: child writes bump parent version DONE 2026-07-07 2026-07-08 Hand-patch party_identifier/party_contact_information (and generate the equivalent for counterparty via the codegen facet) so their insert trigger and soft-delete rule call a shared touch function that bumps the parent's version in the same transaction, with a standardized change-reason comment.
As-of window-join read-side composition query DONE 2026-07-08 2026-07-08 Add the parent as-of-version fetch and the child as-of-window list query (interval overlap against the parent's valid_from/valid_to), plus a service-level helper composing parent+children as of a given parent version.
Qt composite history/version dialog (moved) 2026-07-08 2026-07-10 Moved to Commission party, counterparty, and party_status together — blocked on that story's own party/counterparty binding prerequisite.
Document the composite versioning approach and codegen recipe (moved)   2026-07-10 Moved to Commission party, counterparty, and party_status together — gated on the Qt dialog task above landing for real.

Decisions

  • Child DELETE bumps the parent version, symmetric with insert/update.
  • Codegen FK flag: :bump_parent_version: (alongside :list_by:); read-side counterpart :list_by_as_of:.
  • Reason-code stamping on the touch function is mandatory.
  • This is codegen-driven from the start, not a hand-patch-only affair: both flags are variability knobs on the entity .org model — enabling them on any FK generates the full mechanism (touch function call from the trigger and delete rule, the as-of query). Party is hand-patched first only because party itself isn't yet under codegen; counterparty gets it purely by flipping the flags.
  • Composite tab UX finding (from the test scenario's first step, marked FAIL by the author): showing a flat "identifiers/contacts as of this version" list is not the right end-state UX — a tester wants to know which child changed at a version and drill into that child's own history (old value → new value), not re-derive the diff by eye against the previous version's flat list.
    • That drill-down needs a per-child-entity history dialog (PartyIdentifierHistoryDialog, PartyContactInformationHistoryDialog, and counterparty equivalents), which doesn't exist yet.
    • Per-child history dialogs must be codegen-generated (the same facet that generates HistoryDialogBase-derived dialogs for top-level entities today), not hand-built one-off for this task.
    • That commissioning is exactly task 3 ("Bind composite child-entity and hierarchy widgets to party and counterparty") of the sibling Composite child-entity and hierarchy Qt widgets for codegen story — so the Composite tab's flat-list view here is a deliberate interim, blocked from reaching its real end-state UX until that story's task 3 lands. Not a defect to chase further in this task.

Out of scope

Emacs 29.3 (Org mode 9.6.15)