IU Parent-Child Split/Merge Brief
IU Parent-Child Split/Merge Brief
Date: 2026-05-21 Macro:
v0.6-iu-parent-child-split-merge-briefResult:IU_PARENT_CHILD_SPLIT_MERGE_BRIEF_READYScope: design brief only Production mutation:NONEDB write:NODDL 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
- Vinh vien: parent-child, split, merge, replace, and SQL links must be durable lineage metadata, not text-only notes.
- Nham duoc khong: cycles, hard delete, ambiguous merge/split, and lost SQL dependencies must be blocked by constraints/DOT before any enactment.
- 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_unitalready hasid uuid, uniquecanonical_address,parent_or_container_ref uuid,sort_order,identity_profile jsonb, lifecycle fields.- Prior live count showed
parent_or_container_refexists but had no live parent rows at survey time. unit_versionalready versions content and hascontent_profile jsonb.iu_lifecycle_logrecords lifecycle transitions.- Existing generic
universal_edgescannot directly carry IU UUID links because its IDs are integer. - PG-native DDL brief already defines
iu_sql_link,iu_sql_event_route, andiu_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, thencanonical_address, thencreated_at;sort_orderuniqueness 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 mirrorsparent_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
containsparent per child; - multiple
split_fromandmerged_fromallowed; replaced_byshould be one active target unless explicitly supersession graph allows chain.
- one active
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
containsrelation and update/derive path. - Emit
IU_CHILD_ADDEDwhen parent is non-null; emitIU_ROOT_ADDEDif 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_fromrelation 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_SPLITonce per operation plusIU_CHILD_ADDEDper 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_fromrelation 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
supersedesfrom new to old andreplaced_byfrom 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_REPLACEDor reuseIU_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.metadatashould includeoperation_refwhen 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-> resolveiu_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_CHANGEDemitted whenever split/merge/reparent changesiu_sql_linkrows or their lifecycle.- Vector/report routes should listen to
IU_SPLIT,IU_MERGED,IU_DEPRECATED, andIU_REPARENTEDto 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_refremains nullable self-reference; no hard delete behavior. - If
iu_tree_pathis table, it has FK to IU and path uniqueness. -
iu_relationhas 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_outboxshape 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_ADDEDemitted for add under parent. -
IU_SPLITemitted once per split operation. -
IU_MERGEDemitted once per merge operation. -
IU_DEPRECATEDemitted for no-hard-delete deprecation. -
IU_REPARENTEDemitted for parent move. -
IU_SQL_LINK_CHANGEDemitted 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
- Should
iu_tree_pathbe a physical table, materialized view, or ordinary view in the first macro? - Should
containsbe stored only viaparent_or_container_ref, or duplicated iniu_relationfor uniform relation querying? - Should inverse relations (
replaced_by) be stored physically or derived fromsupersedes? - What lifecycle vocabulary should represent replaced/superseded:
deprecated,retired,superseded, or existing statuses only? - Should sibling
sort_orderbe unique within parent from day one? - Should SQL links inherit from parent to child by default on split, or default block/manual review?
- 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.