Task: Link parent history to the specific child change that caused it
Table of Contents
This page documents a task in the Audit refdata entities for composite (temporal-versioned) child relationships story. It captures the goal, current status, acceptance, and any notes or results.
Goal
Today, when a composite child (party_identifier,
party_contact_information, and the counterparty equivalents) causes
its parent to bump, the parent's History dialog row only says that
some child changed (change_commentary reads "Bumped by child
party_identifier: …"), via free text – not which child row, or
what version transition of it. This task closes that gap: given two
adjacent parent versions, identify the exact child row + version that
caused the bump, and let the user drill from the parent's History
dialog straight into that child's own History dialog at the right
place.
This directly supersedes Qt composite history/version dialog (deferred
backlog): that task built a flat "Composite (as of this version)" tab
showing all children's state as of a version, and its own test
scenario's first reviewer already flagged the same concern raised
here independently: "should we not just say 'the component changed at
this version' and then open the component history dialog?" (see
Composite history dialog shows children as of a version, step 1
result). That task was blocked on binding the composite child-entity
widgets to party/counterparty – now DONE (this story's sibling
Commission: party, counterparty, and party_status) – but its
approach (a bespoke per-parent composite_as_of NATS message, a
custom tab wired into the now-deleted per-entity PartyHistoryDialog=/
=CounterpartyHistoryDialog classes) no longer fits: history dialogs
are now the single generic HistoryDialog widget shared by ~61
entities (Consolidate history dialogs onto HistoryDialogBase), so any
new UI needs to be a generic capability of that shared widget, not a
per-entity tab.
Design
No schema change needed
bump_parent_version guarantees a strict 1:1 relationship: every
composite-child row write triggers exactly one parent version bump,
via a row-level BEFORE INSERT trigger. So for any consecutive pair
of parent versions v_N (valid_from=t1, valid_to=t2) and
v_{N+1} (valid_from=t2), the child row responsible is the unique
row, across all sibling composite-child tables of that parent, whose
own valid_from falls in (t1, t2]. The existing bitemporal windows
already pin this down uniquely – no correlation key (transaction id,
a new stamped column, etc.) needs adding. (A txid-based correlation
column was considered and rejected: xmin isn't durable – autovacuum
freezing eventually replaces it with a sentinel on old rows, exactly
the historical rows this feature cares about – and an explicit
stamped column is unnecessary complexity given the window-join above
already gives an exact answer.)
Generic per-parent SQL function
Generated per parent (already knows its own composite children from the same =bump_parent_version=/FK metadata that wires the touch calls), e.g. for party:
create or replace function ores_refdata_find_child_change_for_party_version( p_tenant_id uuid, p_party_id uuid, p_version_valid_from timestamptz, p_version_valid_to timestamptz ) returns table(child_entity text, child_id uuid, child_version int, child_valid_from timestamptz) language plpgsql stable as $$ begin return query select 'party_identifier'::text, pi.id, pi.version, pi.valid_from from ores_refdata_party_identifiers_tbl pi where pi.tenant_id = p_tenant_id and pi.party_id = p_party_id and pi.valid_from > p_version_valid_from and pi.valid_from <= p_version_valid_to union all select 'party_contact_information'::text, pci.id, pci.version, pci.valid_from from ores_refdata_party_contact_informations_tbl pci where pci.tenant_id = p_tenant_id and pci.party_id = p_party_id and pci.valid_from > p_version_valid_from and pci.valid_from <= p_version_valid_to; end; $$;
Feeding it any two adjacent parent versions' own valid_from=/
=valid_to (already visible in the History dialog's existing data)
returns the exact child row + version responsible for that
transition – or nothing, if the transition wasn't child-caused (e.g.
a direct edit to the parent itself). This also replaces parsing
change_commentary text to detect a child-caused bump: the query
result itself is the detection.
Before/after for that child is then trivial once you have its
child_version: child_version - 1 and child_version. No new
concept needed there.
Qt integration (needs its own design pass once SQL lands)
The generic HistoryDialog widget is shared by ~61 entities, so
whatever surfaces this must be a generic capability, not a
party/counterparty-specific tab (the mistake the superseded task
made). Rough shape, not yet committed:
- A new NATS message per parent asking "what child changed between version N and N+1", backed by the function above.
- When a parent's History dialog row is a child-caused bump, render
it distinctly (e.g. an inline affordance) and, on request, open a
second
HistoryDialoginstance for(child_entity, child_id), ideally landing on/highlighting thechild_version - 1→child_versiontransition –HistoryDialoghas no "jump to / highlight a specific version" mode today, so that's new, generic capability it would need to gain.
Status
| Field | Value |
|---|---|
| State | BACKLOG |
| Parent story | Audit refdata entities for composite (temporal-versioned) child relationships |
| Now | Design sketched (SQL function + rough Qt shape); not started. |
| Waiting on | Nothing. |
| Next | Design the Qt integration in detail (new message shape, HistoryDialog's version-highlight capability), then implement SQL function + Qt wiring for party/counterparty first (existing reference case), generalise per the audit once other entities are confirmed to need it. |
| Last touched | 2026-07-15 |
Acceptance
- A generic (per-parent-generated) SQL function returns the exact composite-child row + version responsible for a given parent version transition, using only existing bitemporal windows.
- The generic
HistoryDialoggains a way to surface "this version was caused by a child change" and drill into that child's own history at the right version – without becoming party/counterparty-specific. - Qt composite history/version dialog (deferred) is marked ABANDONED, superseded by this task, once this lands.
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.)
Notes
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 |
|---|---|---|---|