KB-61C2 rev 2

09 — Living-List Model (VERIFIED v2)

4 min read Revision 2
architectureliving-listpivotmeta-catalogmodel2026-05-30verified-v2

09 — Living-List Model (VERIFIED v2)

v2: field→home table corrected to real columns (registry_collection not sync_collection; refreshed_at not computed_at; counts only in meta_catalog).

A generic, data-driven descriptor for every "living list." It is a read-only projection over existing tables — not a new SoT.

Field → existing home (verified columns)

Field Source Status
list_id meta_catalog.code EXISTS
list_name meta_catalog.name / name_en EXISTS
composition_scope meta_catalog.composition_level (169/169 populated) + layer EXISTS (dirty values, doc 06)
species_scope join entity_species / species_collection_map (164) PARTIAL
live_pg_source meta_catalog.registry_collectionpivot_definitions.source_object EXISTS
pivot_definition_id pivot_definitions.code (match on source_object) EXISTS
pivot_result pivot_results.metric_values by pivot_code ({count}/{total}) EXISTS
stored_count meta_catalog.record_count / actual_count EXISTS (the drift surface)
birth_status birth_registry presence / collection_registry.status EXISTS
iu_profile_ref information_unit (219) PARTIAL
kg_relation_ref iu_relation (60) / v_kg_edges_all EXISTS
dot_refresh_ref refresh_pivot_results() / dot_tools op EXISTS (unbound)
ui_surface_ref meta_catalog.ui_page (18/169) SPARSE
reconciliation_status derived: pivot vs record_count vs actual_count NEW (derived, read-only)
last_checked_at pivot_results.refreshed_at EXISTS

Realization: read-only view v_living_lists (design; DDL deferred)

v_living_lists =
  meta_catalog mc
  LEFT JOIN pivot_definitions pd ON pd.source_object = mc.registry_collection
  LEFT JOIN LATERAL (
     SELECT pr.metric_values, pr.refreshed_at, pr.needs_refresh
     FROM pivot_results pr WHERE pr.pivot_code = pd.code
     ORDER BY pr.refreshed_at DESC LIMIT 1) px ON true
  -- expose: list_id=mc.code, list_name=mc.name, composition_level=mc.composition_level,
  --   source=mc.registry_collection, pivot_code=pd.code,
  --   pivot_total = (px.metric_values->>'count')::int (or 'total'),
  --   stored_record = mc.record_count, stored_actual = mc.actual_count,
  --   reconciliation_status = CASE
  --       WHEN px.metric_values IS NULL THEN 'no_pivot'
  --       WHEN pivot_total = mc.record_count AND mc.record_count = mc.actual_count THEN 'ok'
  --       ELSE 'drift' END,
  --   last_checked = px.refreshed_at, needs_refresh = px.needs_refresh, ui_page = mc.ui_page

This makes V1–V4 (doc 07) self-auditing with zero mutation: any list where pivot≠record_count≠actual_count, or needs_refresh, flags itself. Verified inputs: CAT-006 would show pivot 309 / record 309 / actual 163 → drift; CAT-DOT (no registry_collection) → no_pivot.

Counting-contract self-test (read-only, design)

For each pivot: assert total ≤ count(*) of source_object, and for grouped pivots sum(group rows) == total. Run as test_counting_contract() in CI/cron. (Note: the v1 "PIV-104 fails 309>219" example was wrong — PIV-104 is dot_tools-by-category; the real failing cases are the meta_catalog record≠actual rows.)

Principle

The living-list model operationalizes Đ26 MT5 ("list of lists") + MT6 ("self-describing"): one row per list, every number resolved from a pivot, no stored number that a pivot can compute. Where no composition_levels reference table exists yet (doc 06), the model exposes the gap rather than hardcoding the 6 layers.

Back to Knowledge Hub knowledge/dev/reports/architecture/registries-pivot-foundation-reuse-audit-rebuild-blueprint-2026-05-30/09-living-list-model.md