10 — UI Contract — OS Agency Standard (Đ28 render-shell)
title: 10 — UI Contract — OS Agency Standard date: 2026-05-31
10 — UI Contract — OS Agency Standard
The surface is a render-shell over PG (Đ28). Nuxt reads a registered template + config + the doc-03 contract and renders. Nuxt does no business logic, no DB query, no counting, no hardcode, no components outside the template (Đ28 NT-D1). Config lives in PG JSONB, not text/arrays (NT-D3).
Template registration (Đ28)
Every display surface = an instance of a template registered in design_templates (status active via template_statuses + fn_template_lifecycle_guard(); 8-key birth checklist). Reuse existing templates:
- TabPivot — the L1 9-row summary + matrix tabs (Đ26 Tab Pivot).
- DirectusTable / DynamicEntityList — L2/L3 group & entity lists.
- DirectusMatrix (TPL-002) — L5 matrices (≤7 dims).
- (L4 detail) — entity "DB about itself" template (6 headings). Any route not backed by an active registered template renders "Not found" (Đ28 §VIII whitelist) — so the surface cannot show a fabricated/phantom screen.
OS-Agency node columns (rendered from the doc-03 contract)
Reflection row (per node/list): total · plus · minus · orphan · phantom · drift · verification · warning · next_action, plus drill affordance when has_children, plus pin_state, plus the child-summary chips (child_count, child_orphan/phantom/drift, missing_birth/pivot/iu/dot, top_child_groups). Numbers render exactly as served (pivot value or the literal PIVOT_MISSING).
Traffic-light + status semantics (reuse OS-Agency tokens)
- 🟢 green = verified, count_integrity ok, no warnings.
- 🟡 amber = drift OR unmonitored OR classification_required OR PIVOT_MISSING.
- 🔴 red = count_integrity failed OR orphan/phantom present OR unregistered.
- Tokens: primary
#639922, accent#ef9f27, alert#e24b4a(established OS-Agency palette). - Warning flags shown as chips;
next_actionas an explicit affordance (not a hidden state).
Drill interaction
Click any node with has_children → backend resolve_node returns the child layer (doc 04). Breadcrumb shows the dynamic path root→…→substrate. At a leaf, render L4 detail (the object's own DB view + relations/IU). The client never decides depth or layer kind — it renders next_layer_kind.
Pin / label affordances
- Pin: a pin toggle posts an event (design) →
registry_pin;pin_statere-rendered from PG (doc 08). No local pin state. - Grouping: when
requires_auto_label, the layer renders grouped bylabel_ref/suggested_next_grouping;LABEL_MISSINGrenders an explicit "needs classification" affordance (doc 07), not a flat 1000-row dump.
Accessibility / scale
- Long lists are grouped (doc 07), not just paginated — keeps each rendered layer ≤ the per-species ceiling.
- WCAG 2.1 AA contrast on the palette; traffic-light has text/icon, not color-only.
Boundaries (forbidden in the shell)
- No
reduce/Math.abs(gap)/Σ on counts (thehealth.get.tsanti-pattern — doc 11). - No literal category/species/label/threshold arrays.
- No direct PG from Nuxt; all data via Directus/API serving the doc-03 contract.
- No route or component outside the Đ28 registry (DOT-template-coverage scanner target: 0 outside registry).
Coverage / parity
- Đ28 Test-4 Truth Check: 100% of rendered cells == PG, 0 wrong.
- DOT-template-coverage: every registries-pivot route maps to a registered template; 0 outside registry.
- UI preview specs (when needed) live under
knowledge/dev/ui/registries-pivot/; runtime static preview under/ui-preview/registries-pivot/<version>/(per doc-governance, only when a preview is needed).