04 — Dynamic Drill-Down Layer Model (count>1 → child layer, variable depth)
title: 04 — Dynamic Drill-Down Layer Model date: 2026-05-31
04 — Dynamic Drill-Down Layer Model
Principle
If a node has count > 1, the backend MUST be able to expose a child layer.
Drill path is dynamic depth, decided by species/composition/grouping — never a fixed depth hardcoded in Nuxt:
root → layer → species/type → group/label → collection/registry → item group → individual object → final DB/relationship substrate
This generalizes Điều 26's 5-Layer model (L1 Summary → L2 Group → L3 Entity → L4 Detail, with L5 Matrix attachable at any layer) to variable depth: some branches reach the substrate in 3 hops, others in 7.
Mapping to Điều 26 layers
| Generic step | Đ26 layer | Backend source |
|---|---|---|
| root / layer | L1 Summary (9 rows) | v_pivot_by_level, meta_catalog virtual rows |
| species/type | L1↔L2 | entity_species (parent_id+depth), v_pivot_species_by_level |
| group/label | L2 Group List | taxonomy/label_rules (doc 07), collection_groups, species_collection_map.discriminator_* |
| collection/registry | L2/L3 | collection_registry, registry tables |
| item group | L3 | pivot GROUP-BY on the source_object |
| individual object | L3→L4 | Directus readItems (filtered) |
| final substrate | L4 Detail | the object row + its relations/IU (v_kg_edges_all, iu_relation) |
| (matrix, any layer) | L5 | pivot matrix (≤7 dims) via pivot_definitions/matrix_spec |
The gap that makes drill data-driven
pivot_definitions.parent_code exists but is NULL on all 37 rows (verified). Today drill is implied by registry_group string convention (cross-table → PIV-101..106, l2-drill → PIV-201..206). EXTEND: populate parent_code (and add child-pivots for dimensions not yet expressed) so the layer graph is an explicit data edge, not a naming convention. This is additive data, design-only.
Backend algorithm (server-side; Nuxt holds no depth logic)
resolve_node(pivot_code, filter_path):
value := pivot_count(pivot_code, filter_path)
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 / edges / IU
dim := child_grouping_dimension(pivot_code, filter_path)
children := pivot_query(child_pivot_of(pivot_code, dim), filter_path)
return layer(children, next_layer_kind=kind_of(dim), next_pivot_code=child_pivot_of(...),
child_summary = summarize(children), # doc 03 §C
requires_auto_label = children.count > max_ungrouped_threshold(species)) # doc 07
next_layer_exists / child_grouping_dimension — priority order (data-driven, never a frontend switch)
- explicit child pivot via
pivot_definitions.parent_code = N.code(after EXTEND); - else
composition_level→ next finer level viav_pivot_by_level; - else
speciesbreakdown viav_pivot_species_by_level/entity_speciessub-tree; - else a label/facet dimension via
taxonomy_facets/taxonomy_matrix(doc 07); - else collection breakdown via
species_collection_map.discriminator_field; - else the leaf substrate (the
source_objectrows / relations / IU).
Depth terminates when
value ≤ 1 OR no further dimension resolves OR the node reaches the substrate (a real row / relationship edge / IU). entity_species.depth and taxonomy.depth already make species/label trees natively variable-depth (Đ24 cycle-check depth<5 on taxonomy).
Child-summary on every parent
Before a human/AI drills, a parent node carries the doc-03 §C child-summary (child_count, child_species/collection_count, child_orphan/phantom/drift_count, child_missing_birth/pivot/iu/dot_count, top_child_groups, warning_flags, next_recommended_action) — all pivot/aggregate, served from PG. This is what makes the surface an OS agency (you can triage a layer without expanding it). Sub-metrics with no pivot yet are PIVOT_MISSING.
No-hardcode tests (doc 11)
- T-DRILL-1: every node with
children_count > 1resolves anext_pivot_codeOR afinal_substrate_ref(no dead-end mid-tree). - T-DRILL-2: no Nuxt depth/level branching (
level === N, fixed layer arrays) — depth is data; a literal layer list inweb/⇒hardcode_violation. - T-DRILL-3: every node
valueis apivot_count/pivot_queryresult orPIVOT_MISSING.
Verdict
REUSE (engine + 5-layer model) + EXTEND (populate parent_code, add child-pivots). No new table; the only changes are additive data + (optionally) new pivot rows — all design-only, gated.