07 — PG-First Recursive Drill-Down Data Contract
title: 07 — PG-First Recursive Drill-Down Data Contract date: 2026-05-30
07 — PG-First Recursive Drill-Down Data Contract
Implements: count>1 ⇒ generate an inner layer; drill until singular; final layer = the object's own DB/relationships. Depth is NOT fixed (depends on object/species/level/child counts). All resolution in PG; Nuxt never knows the next layer in code.
A. Recursive node resolution (no fixed T1/T2/T3)
For node N (source_object, filter): 1) count_value = pivot_count(canonical_total_pivot(N)). 2) if ≤1 → leaf → final_substrate_ref (D). 3) else child_layer = next grouping by priority: (a) a grouping pivot_definitions row whose source_object=N.source_object adding a new dimension (meta_catalog→PIV-101 by level→PIV-201..206 by species; dot_tools→PIV-104 by group / v_pivot_dot_by_category by category; collection_registry→PIV-105 by classification); (b) else list source_object rows (collection_rows) via Directus read — record PIVOT_MISSING for the grouping; (c) else leaf. 4) recurse until every branch singular.
Depth varies: dot_tools(309)→group(16)→category(23)→tool→substrate; checkpoint_sets(2)→set→substrate.
B. Node shape (v_registries_pivot_tree)
node_id (node_type:source_object:filter_hash); node_type (root|registry_group|registry|group_layer|item|substrate); label; source_object; species_code/composition_level; parent_id; has_children (count>1 AND child_layer_kind≠substrate); children_count; child_layer_kind (pivot_group|collection_rows|substrate); child_grouping_dimension (group_spec dim); next_pivot_code (child grouping pivot, or NULL⇒collection_rows/PIVOT_MISSING); drilldown_query_ref (pivot_query(next) | directus:read(source,filter)); final_substrate_ref (leaf, D); count_pivot_code (canonical total, never literal); count_value (pivot_count, active only); count_status (fresh|stale|unknown); drift_status (ok|drift); birth_status; registry_ref/table_ref; iu_ref/kg_ref/dot_ref; governance_owner (domain-map); last_refreshed_at; evidence_refs; preview_url/detail_url.
C. Child-summary block (about NEXT layer; all PG-derived)
child_count (pivot) · child_species_count · child_collection_count · child_status_breakdown (pivot/pivot_matrix or PIVOT_MISSING) · child_orphan_count (meta_catalog.orphan_count; propose pivot) · child_drift_count (test_counting_contract — PIVOT_MISSING) · child_missing_birth_count / child_missing_pivot_count / child_missing_iu_count / child_missing_dot_count (coverage anti-joins — PIVOT_MISSING) · top_child_groups (pivot_query) · warning_flags (derived set) · next_recommended_action (derived). Agent may add data-driven columns; none computed in Nuxt.
D. Final object substrate (leaf, final_substrate_ref)
db_table/collection (table_registry/collection_registry) · primary_key/code · registry_entry (meta_catalog) · birth_entry (birth_registry) · iu_profile (information_unit/iu_relation) · kg_relations (v_kg_edges_all) · dots (dot_tools/dot_iu_command_catalog) · events (event_outbox) · governance_owner · audit/lifecycle (entity_audit_queue, birth inspect_/certified_) · preview/detail URL.
Contract invariants
- Nuxt computes no count/gap/group/status/depth. 2. Every count_value carries count_pivot_code; counts via pivot over is_active only. 3. Layer existence/kind declared by backend (has_children/child_layer_kind/next_pivot_code); UI renders generically. 4. Adding list/category/species/grouping = INSERT a row → appears with no deploy. 5. Missing grouping ⇒ PIVOT_MISSING, never Nuxt math.