KB-6BE1
RP Classification Cleanup — 06 UI/API Read Contract
5 min read Revision 1
registries-pivotui-apiread-contractjsonbadges2026-06-03
06 — UI/API Read Contract (consumes the live view)
All read-only over v_rp_classification_governance_map + the L2 gate counters. No writes. Safe to wire into RP UI immediately.
Endpoints
1. Classification map — GET /rp/classification-map
SELECT * FROM v_rp_classification_governance_map ORDER BY pivot_code;
Returns 37 rows (35 after retires). Each row carries governed projection + status flags.
2. Cleanup dashboard — GET /rp/cleanup-dashboard
SELECT 'species' AS dim, species_status AS bucket, count(*) FROM v_rp_classification_governance_map GROUP BY species_status
UNION ALL
SELECT 'composition', composition_status, count(*) FROM v_rp_classification_governance_map GROUP BY composition_status
UNION ALL
SELECT 'lane', classification_lane, count(*) FROM v_rp_classification_governance_map GROUP BY classification_lane;
Plus the actionable list (the 3 mismatches + 2 retires + Action-B candidates) for an operator worklist.
3. Single pivot status — GET /rp/pivot/{code}/status
SELECT * FROM v_rp_classification_governance_map WHERE pivot_code = :code;
4. L2-gate banner — GET /rp/governance-gate
SELECT (SELECT count(*) FROM os_proposal_approvals) AS ospa,
(SELECT count(*) FROM v_object_owner_gap) AS gap,
(SELECT count(*) FROM os_proposal_approvals) >= 1 AS l2_open;
When l2_open = false (current state), the UI MUST show a banner:
⚠️ L1 classification is live and governed. L2 ownership/axis/topic governance is GATED (ospa=0, gap=210). Classification operates without L2; do not present any “fully governed / gap=0” claim.
Badges (derive in UI from view columns)
| Badge | Rule |
|---|---|
| 🟢 Governed | registry_group_kind = 'domain' AND species_status = 'clean' AND composition_status = 'match' |
| 🔵 RP-local | classification_lane = 'rp_local' (cross-table / l2-drill / matrix / drill_overload) |
| 🟡 Needs decision | fac02_label_code IS NULL AND registry_group_kind IN ('domain','pivot_shape') AND species clean (Action B) |
| 🟠 Fix queued | composition_status = 'mismatch' (Action A) |
| ⚪ Meta | species_status = 'ambiguous_meta' (governed meta-species SPE-SPE) |
| 🔴 Retire | classification_lane = 'inactive_retire_candidate' |
JSON examples
Governed (PIV-007):
{
"pivot_code": "PIV-007", "source_object": "dot_tools", "is_active": true,
"raw_registry_group": "công_cụ", "registry_group_kind": "domain",
"fac02_label_code": "LBL-104", "fac02_label_name": "Công cụ", "pivot_kind": null,
"raw_species": "dot_tool", "species_governed_code": "SPE-DOT",
"species_display_name": "DOT Tool", "species_governed_composition": "atom",
"species_status": "clean", "raw_composition_level": "atom",
"composition_is_canonical": true, "composition_status": "match",
"classification_lane": "governance_home", "_badge": "governed"
}
RP-local drill (PIV-205):
{
"pivot_code": "PIV-205", "source_object": "meta_catalog", "is_active": true,
"raw_registry_group": "l2-drill", "registry_group_kind": "pivot_shape",
"fac02_label_code": null, "pivot_kind": "l2_drill",
"raw_species": "catalog", "species_governed_code": "SPE-CAT",
"species_governed_composition": "atom", "species_status": "clean",
"raw_composition_level": "product", "composition_is_canonical": false,
"composition_status": "drill_overload", "classification_lane": "rp_local",
"_badge": "rp-local", "_note": "composition_level used as drill axis, not entity composition"
}
Fix-queued (PIV-021):
{
"pivot_code": "PIV-021", "raw_species": "checkpoint_support",
"species_governed_composition": "molecule", "raw_composition_level": "atom",
"composition_status": "mismatch", "_badge": "fix-queued",
"_action": "dot-pivot-declare PIV-021 composition_level=molecule"
}
Hard UI rules
- Never write through these endpoints. Cleanup happens only via the DOT path (doc 04).
- Never render gap as 0 or claim L2 governance is active while
ospa=0. drill_overloadis informational, not an error — render as 🔵 RP-local with a tooltip, not a red flag.