KB-4DD6

03 — Threshold / Label Grouping Pack (Branch B)

4 min read Revision 1
registries-pivotthresholddisplay_policylabelPIV-311no-hardcodedieu24begin-rollback2026-05-31

title: 03 — Threshold / Label Grouping Pack (Branch B) date: 2026-05-31 status: rehearsed GREEN; COMMIT DEFERRED (RG5)

03 — Threshold / Label Grouping Pack (Branch B)

Goal: long-list grouping fully PG-backed, no frontend thresholds. 50 = MAX ungrouped ceiling, not a target; pagination ≠ semantic grouping.

Schema reality (live)

  • kg_thresholds(id, dimension, lower_is_better, threshold_green, threshold_yellow, threshold_red, is_active, …) — a green/yellow/red metric table, not a single display ceiling. Reusable as storage (one dimension per species) but semantically awkward (3 threshold cols).
  • taxonomy_facets(… max_labels_per_entity smallint …) — verified values 0–3; this is labels-per-entity, NOT a display ungrouped ceiling. Distinct concept.
  • label_rules(38; has skip_wide_warning boolean), taxonomy(58), species_collection_map(164) — existing classification machinery. Do not rebuild.

Decision: a dedicated display_policy table is the clearest model (single integer per species, hard ≤50 CHECK). Reuse path noted (kg_thresholds dimension row) for storage-minimal deployments. Either way the ceiling lives in PG data, never a frontend constant.

Rehearsal (live BEGIN..ROLLBACK, GREEN)

CREATE TABLE display_policy (
  species_code  text PRIMARY KEY,
  max_ungrouped smallint NOT NULL DEFAULT 50,
  note          text,
  CONSTRAINT display_policy_ceiling_ck CHECK (max_ungrouped > 0 AND max_ungrouped <= 50)
);
INSERT INTO display_policy VALUES
  ('__default__',50,'system default = MAX'), ('dot_tool',30,'smaller ceiling'), ('collection',50,'default');

Result (this run): table created, 3 seeds, CHECK valid; threshold_demo = 160 leaves evaluated, 28 exceed their PG-resolved ceiling; ROLLBACK → display_policy gone, idle_in_transaction = 0. The ceiling is resolved per-leaf with no literal 50 in any app:

COALESCE(dp.max_ungrouped, (SELECT max_ungrouped FROM display_policy WHERE species_code='__default__'))

(The 28 uses COALESCE(actual_count, record_count); a record_count-only proxy gives 27 — the ±1 is the live CAT-023 drift, not a logic difference.)

Label engine (PIV-311 — native now)

entity_labels = 726,864 rows, 23 distinct label_code → PIV-311 (group by label_code) returns 23 groups, proven against the real pivot_query() engine (Macro 1 doc 04). Top groups: LBL-105 181,245 · PROV-DOT 180,475 · LBL-031 177,026 · LBL-021 175,245 · then a long tail (LBL-011 2,018 … LBL-102 13). PIV-311 needs no helper view — it is engine-native.

Data contract fields (for API / UI / Nuxt shell)

A leaf/list node carries, all PG-sourced:

  • classification_statusclassified | CLASSIFICATION_REQUIRED
  • classification_dimension — facet code (FAC-01..FAC-09 / FAC-PROV)
  • label_ref — label_code(s) when classified
  • grouping_required — boolean = count > resolved_ceiling
  • grouping_reason — e.g. exceeds display_policy[dot_tool]=30
  • max_ungrouped_threshold — resolved from display_policy (never frontend)
  • suggested_next_grouping — facet to group by (from label_rules priority)
  • classification_workflow_trigger — event when unclassified+over-ceiling (Đ45; doc 05)

Rules: if a list is already classified, use the classification immediately; if unclassified and over ceiling, mark CLASSIFICATION_REQUIRED (do not silently paginate). Labels/groups are themselves registry-visible (label_rules/taxonomy) and pivot-countable (PIV-311).

Commit-ready SQL (RG5 — DEFERRED)

CREATE TABLE display_policy … (above) + seeds, additive/reversible (DROP TABLE display_policy). Activates PIV-321-style grouping signals and feeds grouping_required. No base-table mutation. Commit only on explicit RG5 approval.

Verdict

Threshold/label pack COMPLETE. Mechanism PG-backed, no-hardcode, reuse-first; the live signal (28/160 over ceiling, 23 label groups) is real; commit deferred to RG5.

Back to Knowledge Hub knowledge/dev/reports/architecture/registries-pivot-macro2-3-combined-ui-api-legacy-acceptance-2026-05-31/03-threshold-label-grouping.md