03 — Dynamic Child-Layer Drill-Down Algorithm + Node Data Contract
title: 03 — Dynamic Child-Layer Drill-Down Algorithm + Node Data Contract date: 2026-05-31
03 — Dynamic Child-Layer Drill-Down
Principle (preserved from the original Registries design)
If a node has count > 1, the backend MUST be able to expose a child layer.
Drill path is dynamic depth, decided by the object's species/composition and grouping needs — never a fixed depth hardcoded in Nuxt:
root → layer → species/type → group/label → collection/registry → item group → individual object → final DB/relationship substrate
Leaf = the object DB substrate (a real row, or a relationship edge).
Live anchor + the gap
- Anchor:
pivot_definitions.parent_code(text) exists for exactly this purpose;pivot_count/pivot_query/pivot_matrixalready produce node values; drill groupings already exist byregistry_group(cross-table→ PIV-101..106;l2-drill→ PIV-201..206 bycomposition_levelatom/molecule/compound/material/product/building) andcomposition_level/speciescolumns are populated. - Gap (verified):
parent_codeis NULL on all 37 pivot rows — the hierarchy field is present but unwired. Drill today is implied byregistry_groupnaming, not by an explicit parent→child edge. EXTEND: populateparent_codeso the layer graph is data-driven, not string-convention-driven.
Backend-driven algorithm (no Nuxt depth logic)
For any node N the backend returns a node contract (doc 09). The drill decision is computed server-side:
function resolve_node(pivot_code, filter_path):
value := pivot_count(pivot_code, filter_path) # the node's count
has_children := value > 1 AND next_layer_exists(pivot_code, filter_path)
if not has_children:
return leaf(final_substrate_ref = source_object + filter_path) # → real rows
next := child_grouping_dimension(pivot_code, filter_path) # species | composition_level | group | facet | ...
children := pivot_query(child_pivot_of(pivot_code, next), filter_path)
group_size := children.count
requires_auto_label := group_size > max_ungrouped_threshold(species) # doc 04 (default 50, may be smaller)
return layer(children, next_layer_kind = kind_of(next), next_pivot_code = child_pivot_of(...),
requires_auto_label, label_ref = grouping_label(next))
next_layer_exists / child_grouping_dimension — data-driven sources (REUSE)
The next dimension is chosen from PG, in priority order, never from a frontend switch:
- an explicit child pivot via
pivot_definitions.parent_code = N.code(after EXTEND); - else the node's
composition_level→ next finer level (atom←molecule←compound←material←product←building) viav_pivot_by_level; - else
speciesbreakdown viav_pivot_species_by_level/entity_species(hasparent_id+depth→ species sub-tree); - else a label/facet dimension via
taxonomy_facets(doc 04); - else collection breakdown via
species_collection_map(discriminator_field); - else the leaf substrate (the
source_objectrows themselves).
Depth is a property of data, not code
entity_species.depthandtaxonomy.depthalready exist → species/label trees are natively variable-depth (Đ24 enforces cycle-check depth<5 ontaxonomy).- A node terminates when
value ≤ 1OR no further dimension resolves OR it reaches the substrate. Nuxt renders whatevernext_layer_kindthe backend declares; it contains noif level === Nlogic.
Node contract fields (count>1 child-layer set; full contract in doc 09)
has_children · children_count · next_layer_kind · child_grouping_dimension · next_pivot_code · drilldown_query_ref · final_substrate_ref · group_size · requires_auto_label · label_ref · pin_state.
No-hardcode tests (detail in doc 08)
- T-DRILL-1: for every node with
children_count > 1,has_children = trueand a resolvablenext_pivot_codeORfinal_substrate_refexists. Fail ⇒ a dead-end mid-tree. - T-DRILL-2: no Nuxt file contains literal depth/level branching for registries-pivot (
grepforlevel ===, fixed layer arrays). Reusessystem_issues('hardcode_violation'). - T-DRILL-3: every node
valueequals apivot_count/pivot_queryresult (or is markedPIVOT_MISSING); no client-side arithmetic.
Verdict
Drill-down is REUSE (engine) + EXTEND (populate parent_code). No new table required; the only schema change proposed is data (parent_code values) plus optionally child-pivot rows for dimensions not yet expressed as pivots — both additive, design-only.