KB-58BD

04 — Dynamic Drill-Down Layer Model (count>1 → child layer, variable depth)

5 min read Revision 1
designregistries-pivotdrilldownchild-layerdieu265-layerparent-codevariable-depthno-hardcode2026-05-31

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)

  1. explicit child pivot via pivot_definitions.parent_code = N.code (after EXTEND);
  2. else composition_level → next finer level via v_pivot_by_level;
  3. else species breakdown via v_pivot_species_by_level / entity_species sub-tree;
  4. else a label/facet dimension via taxonomy_facets/taxonomy_matrix (doc 07);
  5. else collection breakdown via species_collection_map.discriminator_field;
  6. else the leaf substrate (the source_object rows / 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 > 1 resolves a next_pivot_code OR a final_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 in web/hardcode_violation.
  • T-DRILL-3: every node value is a pivot_count/pivot_query result or PIVOT_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.