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 HistoryDialog instance for (child_entity, child_id), ideally landing on/highlighting the child_version - 1child_version transition – HistoryDialog has 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 HistoryDialog gains 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
       

Result

Emacs 29.3 (Org mode 9.6.15)