KB-7F84

IU Parent-Child Split/Merge Brief

16 min read Revision 1
dieu44iuparent-childsplit-mergedependencybriefv0.62026-05-21no-mutation

IU Parent-Child Split/Merge Brief

Date: 2026-05-21 Macro: v0.6-iu-parent-child-split-merge-brief Result: IU_PARENT_CHILD_SPLIT_MERGE_BRIEF_READY Scope: design brief only Production mutation: NONE DB write: NO DDL executed: NO

G0 Context Check

KB read/upload works. Read before authoring:

  • knowledge/dev/laws/dieu44-trien-khai/v0.6-iu-pg-native-trigger-model-survey-design/
  • knowledge/dev/laws/dieu44-trien-khai/v0.6-iu-pg-native-ddl-authoring-brief/
  • knowledge/dev/laws/dieu44-trien-khai/reviews/dot-iu-cutter-v0.6-iu-pg-native-ddl-brief-ready-gpt-ruling-2026-05-21.md
  • Workspace search found no separate file matching THIEU/THIẾU/Miếng thông tin.

This is a brief only. It is not DDL, not execution approval, and must not be used for production mutation.

3 Cau Tuyen Ngon

  1. Vinh vien: parent-child, split, merge, replace, and SQL links must be durable lineage metadata, not text-only notes.
  2. Nham duoc khong: cycles, hard delete, ambiguous merge/split, and lost SQL dependencies must be blocked by constraints/DOT before any enactment.
  3. 100% tu dong: operations emit IU events and route through iu_sql_link, inbound/outbound routes, and DOT verifiers without manual dependency tracing.

Existing Foundation

From the prior live survey:

  • information_unit already has id uuid, unique canonical_address, parent_or_container_ref uuid, sort_order, identity_profile jsonb, lifecycle fields.
  • Prior live count showed parent_or_container_ref exists but had no live parent rows at survey time.
  • unit_version already versions content and has content_profile jsonb.
  • iu_lifecycle_log records lifecycle transitions.
  • Existing generic universal_edges cannot directly carry IU UUID links because its IDs are integer.
  • PG-native DDL brief already defines iu_sql_link, iu_sql_event_route, and iu_outbound_route.

Design implication: keep information_unit.parent_or_container_ref as the primary adjacency pointer, but add a dedicated IU relation/lineage layer for typed relations and multi-operation provenance.

G1 Parent-Child Model

Core Tree Fields

Minimum model for DDL/design macro:

information_unit.parent_or_container_ref uuid NULL FK -> information_unit(id)
information_unit.sort_order integer NULL
information_unit.canonical_address text UNIQUE

Recommended sidecar for multi-level path/query:

iu_tree_path
- unit_id uuid PK FK -> information_unit(id)
- root_unit_id uuid NOT NULL FK -> information_unit(id)
- parent_id uuid NULL FK -> information_unit(id)
- depth int NOT NULL
- path_ids uuid[] NOT NULL
- path_addresses text[] NOT NULL
- sibling_order int NULL
- path_hash text NOT NULL UNIQUE
- valid_from timestamptz NOT NULL default now()
- valid_to timestamptz NULL

Alternative if DDL macro wants less surface area: make iu_tree_path a view/materialized view later, not a table. GPT/User decision required.

Behavior Rules

  • Root/top-level IU: parent_or_container_ref IS NULL; root_unit_id = unit_id; depth = 0.
  • Child IU: parent must exist, must not be retired/deleted, and must be in compatible lifecycle state.
  • Multi-level tree: descendants inherit root and path; max depth should be configurable, not hardcoded in code.
  • Sibling ordering: order by sort_order, then canonical_address, then created_at; sort_order uniqueness under same parent is recommended but may be deferred.
  • Cycle prevention: new parent cannot be self and cannot be any descendant of the unit.
  • Move/reparent: never overwrite history silently. Reparent emits event and creates relation/provenance row.
  • Enacted unit rule: moving enacted units must either create a new version/change-set or be explicitly blocked until GPT/User rule.

Relationship Vocabulary

Use a dedicated IU relation table or relation records in a future UUID-capable graph layer.

Recommended relation table:

iu_relation
- id uuid PK
- source_unit_id uuid NOT NULL FK -> information_unit(id)
- target_unit_id uuid NOT NULL FK -> information_unit(id)
- relation_type text NOT NULL
- relation_subtype text NULL
- relation_status text NOT NULL default 'active'
- operation_ref uuid NULL
- source_version_id uuid NULL FK -> unit_version(id)
- target_version_id uuid NULL FK -> unit_version(id)
- metadata jsonb NOT NULL default '{}'
- valid_from timestamptz NOT NULL default now()
- valid_to timestamptz NULL
- created_by text NOT NULL
- created_at timestamptz NOT NULL default now()

Required relation types:

  • contains: parent/container contains child. Usually mirrors parent_or_container_ref.
  • derived_from: new IU was derived from an earlier IU/source.
  • supersedes: source IU supersedes target IU.
  • replaced_by: source IU is replaced by target IU.
  • merged_from: new IU merged content from one or more prior IUs.
  • split_from: new child/IU split from a prior larger IU.

Minimum constraints:

  • No self relation except if GPT/User explicitly allows a special identity relation.
  • CHECK relation type vocabulary.
  • Unique active relation where needed:
    • one active contains parent per child;
    • multiple split_from and merged_from allowed;
    • replaced_by should be one active target unless explicitly supersession graph allows chain.

G2 Structure Operations Model

add_new_piece

Purpose: create new IU under optional parent.

Minimum metadata:

{
  "operation": "add_new_piece",
  "parent_id": null,
  "parent_address": null,
  "sibling_order": 10,
  "source_reference": null,
  "sql_link_policy": "none|inherit_from_parent|explicit"
}

Rules:

  • Parent optional for root.
  • If parent exists, add contains relation and update/derive path.
  • Emit IU_CHILD_ADDED when parent is non-null; emit IU_ROOT_ADDED if root event is needed.
  • SQL links are not inherited by default unless explicit policy says so.

split_large_piece

Purpose: split one large IU into multiple child or replacement units.

Minimum metadata:

{
  "operation": "split_large_piece",
  "source_unit_id": "...",
  "source_version_id": "...",
  "split_mode": "children|replacement_set",
  "source_spans": [{"start": 1, "end": 10, "target_unit_id": "..."}],
  "sql_link_policy": "copy|move|block|manual_review"
}

Rules:

  • Create split_from relation from each new unit to source.
  • If split mode is children, source remains parent/container.
  • If split mode is replacement_set, source should move to deprecated/superseded lifecycle after review.
  • SQL links must be copied/moved/blocked by policy in iu_sql_link.metadata.dependency.
  • Emit IU_SPLIT once per operation plus IU_CHILD_ADDED per child if containment changes.

merge_small_pieces

Purpose: combine multiple IUs into one target IU.

Minimum metadata:

{
  "operation": "merge_small_pieces",
  "source_unit_ids": ["..."],
  "target_unit_id": "...",
  "merge_mode": "new_target|existing_target",
  "conflict_policy": "block|prefer_target|manual_review",
  "sql_link_policy": "merge|copy|block|manual_review"
}

Rules:

  • Create merged_from relation from target to every source.
  • Sources should become deprecated/replaced only after review/enactment.
  • Conflicting child order, duplicate canonical claims, and SQL link collisions must fail closed.
  • Emit IU_MERGED.

delete/deprecate_piece

Purpose: retire IU without hard delete by default.

Minimum metadata:

{
  "operation": "deprecate_piece",
  "unit_id": "...",
  "reason": "...",
  "dependency_action": "block_if_children|cascade_deprecate|reparent_children",
  "sql_link_action": "disable|retire|block"
}

Rules:

  • No hard delete default.
  • If children exist, require explicit dependency action.
  • Active SQL links block deprecation unless policy disables/retires them.
  • Emit IU_DEPRECATED.

replace/supersede_piece

Purpose: replace one IU with another while preserving lineage.

Minimum metadata:

{
  "operation": "replace_piece",
  "old_unit_id": "...",
  "new_unit_id": "...",
  "reason": "...",
  "replace_mode": "content_replacement|structural_replacement",
  "sql_link_policy": "move|copy|block"
}

Rules:

  • Create supersedes from new to old and replaced_by from old to new, or one canonical relation plus inverse view.
  • Old IU moves to superseded/deprecated lifecycle after review.
  • SQL links move/copy/block by explicit policy.
  • Emit IU_REPLACED or reuse IU_DEPRECATED + lineage event if event vocabulary is kept small.

G3 Lineage And Provenance

Required metadata keys across operation records, relation rows, and version profiles:

split_from: source unit/version for split outputs
merged_from: source unit/version array for merge target
derived_from: source unit/version or source document span
supersedes: old unit replaced by new unit
replaced_by: new unit replacing old unit
source_span: line/paragraph/byte/section range from source version or source doc
source_reference: source document/version/ref URL or registry id
operation_ref: stable id for one add/split/merge/deprecate/replace operation

Recommended operation sidecar:

iu_structure_operation
- id uuid PK
- operation_type text NOT NULL
- operation_status text NOT NULL default 'draft'
- actor_ref text NOT NULL
- reason text NULL
- idempotency_key text NOT NULL UNIQUE
- source_unit_ids uuid[] NOT NULL default '{}'
- target_unit_ids uuid[] NOT NULL default '{}'
- parent_unit_id uuid NULL
- payload jsonb NOT NULL default '{}'
- impact_summary jsonb NOT NULL default '{}'
- created_at timestamptz NOT NULL default now()
- enacted_at timestamptz NULL

Version/lifecycle impact:

  • Draft operations can create draft units/relations.
  • Enactment should happen through current review/change-set/lifecycle path.
  • Enacted source content is immutable; split/merge/replace creates new units/versions rather than rewriting enacted content.
  • iu_lifecycle_log.metadata should include operation_ref when lifecycle transition is caused by split/merge/deprecate/replace.

G4 Trigger/Event Integration

Required event vocabulary:

IU_CHILD_ADDED
IU_SPLIT
IU_MERGED
IU_DEPRECATED
IU_REPARENTED
IU_SQL_LINK_CHANGED

Optional vocabulary:

IU_REPLACED
IU_ROOT_ADDED
IU_CHILD_ORDER_CHANGED
IU_LINEAGE_CHANGED

Event payload minimum:

{
  "operation_ref": "...",
  "unit_id": "...",
  "canonical_address": "...",
  "parent_id": "...",
  "source_unit_ids": [],
  "target_unit_ids": [],
  "relation_types": [],
  "sql_link_policy": "copy|move|merge|disable|block",
  "lifecycle_status": "draft|active|deprecated|retired",
  "idempotency_key": "..."
}

Inbound interaction with PG-native trigger model:

  • SQL event -> iu_sql_event_route -> resolve iu_sql_link -> if the linked IU has parent/children, DOT/worker performs impact analysis before workflow action.
  • SQL row changed for parent-linked object may emit candidate events for children only if relation/link policy allows inheritance.
  • Missing/ambiguous parent-child relation fails closed; no workflow mutation.

Outbound interaction with PG-native trigger model:

  • IU structure event -> event_outbox/pending -> iu_outbound_route.
  • IU_SQL_LINK_CHANGED emitted whenever split/merge/reparent changes iu_sql_link rows or their lifecycle.
  • Vector/report routes should listen to IU_SPLIT, IU_MERGED, IU_DEPRECATED, and IU_REPARENTED to rebuild affected derived artifacts.
  • Direct SQL/workflow execution remains outside PG row trigger; route worker/DOT owns retry.

Fail-closed:

  • No active route: record no downstream action.
  • Duplicate operation idempotency key: no duplicate relations/events.
  • Cycle risk: block operation before event emission.
  • Active child/link dependencies unresolved: block deprecate/merge/split enactment.

G5 Acceptance Checklist For Later Macro

Schema constraints

  • parent_or_container_ref remains nullable self-reference; no hard delete behavior.
  • If iu_tree_path is table, it has FK to IU and path uniqueness.
  • iu_relation has CHECK relation vocabulary and no self relation.
  • One active containment parent per child.
  • Operation table has unique idempotency_key.
  • Metadata columns default to {} JSONB.

Cycle prevention

  • Reparent self is blocked.
  • Reparent to descendant is blocked.
  • Move root under descendant is blocked.
  • DOT verifier can detect existing cycle or orphan path.

Idempotency

  • Add/split/merge/deprecate/replace operation key is deterministic.
  • Re-running same operation does not duplicate relations/events.
  • Event idempotency integrates with event_outbox shape from DDL brief.

Rollback/disable

  • Operation can be marked cancelled/rolled_back before enactment.
  • Route/event emission can be disabled by route enabled=false.
  • Relation retirement uses valid_to/status, not delete.
  • SQL links are disabled/retired by policy before structural lifecycle changes.

Trigger tests

  • IU_CHILD_ADDED emitted for add under parent.
  • IU_SPLIT emitted once per split operation.
  • IU_MERGED emitted once per merge operation.
  • IU_DEPRECATED emitted for no-hard-delete deprecation.
  • IU_REPARENTED emitted for parent move.
  • IU_SQL_LINK_CHANGED emitted when link policy changes links.

Impact analysis

  • Split checks child/source spans and SQL link policy.
  • Merge checks duplicate child order, duplicate canonical address, SQL link collisions.
  • Deprecate checks active children, active links, downstream routes.
  • Replace checks supersedes/replaced_by lineage and lifecycle transition.

Lifecycle/enactment

  • Enacted content is not rewritten by structural operation.
  • Lifecycle transition logs include operation_ref.
  • Cut/change-set can list affected IU rows, relations, links, and events.
  • Draft operation cannot self-advance to production enactment.

No hard delete default

  • Delete operation is modeled as deprecate/retire.
  • Physical delete requires separate GPT/User rule and exceptional evidence.

Open Decisions For GPT/User

  1. Should iu_tree_path be a physical table, materialized view, or ordinary view in the first macro?
  2. Should contains be stored only via parent_or_container_ref, or duplicated in iu_relation for uniform relation querying?
  3. Should inverse relations (replaced_by) be stored physically or derived from supersedes?
  4. What lifecycle vocabulary should represent replaced/superseded: deprecated, retired, superseded, or existing statuses only?
  5. Should sibling sort_order be unique within parent from day one?
  6. Should SQL links inherit from parent to child by default on split, or default block/manual review?
  7. What max depth, if any, should be enforced by config?

Final Verdict

IU_PARENT_CHILD_SPLIT_MERGE_BRIEF_READY

No contradiction found with the PG-native trigger model. Parent-child and split/merge behavior should be implemented as sidecar relation/operation metadata plus event routes, preserving current IU lifecycle and SQL-link design.

STOP / Route Back

Stop after KB upload and route to GPT/User. Next macro should be design/DDL authoring only; no live DDL without separate approval.

Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.6-iu-parent-child-split-merge-brief/iu-parent-child-split-merge-brief-2026-05-21.md