KB-1657
IU Core MVP — 05 Parent-Child Multi-Level Model
4 min read Revision 1
dieu44iu-core-mvpparent-childmulti-levelcycle-preventionv0.62026-05-21
IU Core MVP — 05 Parent-Child Multi-Level Model
Date: 2026-05-21 · Authored in
003_parent_child_constraints.sql,005_*helpers, andcutter_agent/iu_core/tree.py.
Model
- Primary adjacency:
information_unit.parent_or_container_ref uuid(live, unchanged, NOT altered). - Durable cache:
iu_tree_path— one row per IU withroot_unit_id,parent_id,depth,path_ids uuid[],path_addresses text[],sibling_order,path_hashUNIQUE,valid_from/valid_to. - Live projection: view
v_iu_tree— recursive CTE over the adjacency pointer; usable immediately withoutiu_tree_pathbeing populated.
Behavior rules (encoded)
- Root:
parent_or_container_ref IS NULL⇒depth=0,root_unit_id=unit_id,parent_id IS NULL. CHECKiu_tree_path_root_chkenforces(depth=0)=(parent_id IS NULL). - Child: parent must exist (FK), must not be retired/deleted,
depth>=1. - Multi-level: descendants inherit
root_unit_idand extendpath_ids. Max depth is config-driven, not hardcoded (NEEDS_RULING R6). - Sibling ordering:
sort_order, thencanonical_address, thencreated_at(v_iu_treecarriessibling_order).sort_orderuniqueness deferred (R5). iu_tree_pathintegrity CHECKs:depth>=0;array_length(path_ids)=depth+1;path_idsandpath_addressesequal length; first element = root, last = unit;unit_id <> parent_id.
Cycle prevention
Three layers:
- View-level:
v_iu_treerecursive CTE hasNOT c.id = ANY(w.path_ids)— a corrupt graph cannot make the view loop forever. - SQL helper (
005):fn_iu_tree_is_descendant(ancestor,node)+fn_iu_tree_assert_acyclic(unit,new_parent)— raisescheck_violationwhen a reparent targets self or a descendant. To be called by the reparent path / a future BEFORE trigger. - Python mirror (
tree.py):detect_cycle,is_descendant,assert_acyclic_reparent— pure, used by callers + tests.
Reparent rules
- Reparent to root (
NULL) is always safe. - A unit may not be its own parent.
- New parent may not be a descendant of the unit (would create a cycle).
- Reparent never overwrites history silently: it emits
IU_REPARENTEDand creates aniu_relationprovenance row + aniu_structure_operationof typereparent_piece. - Moving an enacted unit must create a new version/change-set or be explicitly blocked (NEEDS_RULING — defaults to block until ruled).
Parent ↔ child lifecycle impact
- No hard delete: a parent is deprecated/retired, never DELETEd. Deprecating a
parent with active children requires an explicit
dependency_action(block_if_children|cascade_deprecate|reparent_children). - A child content change emits
IU_SQL_LINK_CHANGED/ lineage events so the parent's verification can be re-evaluated. - Propagation contract: parent events may fan out to children only when the relation/link policy permits inheritance; missing/ambiguous relation fails closed (no workflow mutation).
Rollback / compensation
iu_tree_pathrows are time-bounded (valid_to); rebuild is always possible fromparent_or_container_ref.- Reparent compensation = a new
reparent_pieceoperation back to the prior parent; the original op row moves torolled_back. - Rollback file
rollback/003_*drops the view freely and refuses to drop a non-emptyiu_tree_path.
Helper implemented + tested
cutter_agent/iu_core/tree.py — 8 tree tests in test_iu_core_ddl.py
(compute_path, is_descendant, detect_cycle clean/dirty, reparent self/
descendant blocked, reparent-to-root allowed, sibling-subtree allowed).