08 — Nuxt Render-Shell Implementation Pack (Branch G)
title: 08 — Nuxt Render-Shell Implementation Pack (Branch G) date: 2026-05-31 status: implementation PACK only — NO production Nuxt change
08 — Nuxt Render-Shell Implementation Pack (Branch G)
Production-ready spec for the converged surface. Not implemented — no Nuxt change this macro. Nuxt renders only registered templates; no business logic, no hardcode, no local count/label/pin math (Đ28 NT-D1/NT-D3, Test-4).
Route plan
- Canonical:
/knowledge/registries-pivot(ONE page; no 3rd page). - Keep
/knowledge/pivot(clean render-shell overpivot_results) as the structural basis. - Converge
/knowledge/registries(the legacy Đ28 page) onto the canonical route at cutover (doc 09); until then/knowledge/registriesstays as-is.
Component structure (all dumb/presentational; data via fetch)
pages/knowledge/registries-pivot/index.vue // shell: layout + section slots only
├─ RootSummaryCards.vue ← /grand-total (PIV-500) + /integrity summary
├─ DisagreeingTotals.vue ← the 6 totals, each tagged source/trusted
├─ CountIntegrityPanel.vue ← /integrity (status FAILED honest)
├─ DriftTable.vue ← /drift (source_model classification)
├─ PivotTreeDrill.vue ← /tree + recursive drill (count>1 ⇒ fetch child layer)
├─ LeafSubstratePanel.vue ← /substrate?code= (file path vs PG table)
├─ LabelGroupingPanel.vue ← /labels (PIV-311) + grouping_required flag
├─ PinColumn.vue ← /pins?scope=
└─ OrphanPhantomWarnings.vue ← /integrity + /drift (candidate, never "phantom")
Props / data contract
Each component receives a typed object matching the doc-06 endpoint shapes. No component
computes a total, gap, classification, or grouping decision — those arrive pre-computed from
PG. Drill state (is_root, has_children, child rows) comes from v_registries_pivot_tree +
backend, never a frontend tree.
Fetch strategy
useFetch/$fetchto the doc-06 read endpoints (which themselves read Directus, thepivot-query.get.tspattern).- SSR-friendly; cache per endpoint (mirror
health.get.ts60s cache but over pivot data). - Drilldown is lazy: a node with
has_childrenfetches/tree?parent=on expand; a leaf fetches/substrate?code=.
Forbidden in the shell (CI-enforced, doc 10/12)
- No hardcoded arrays of CAT-/PIV- / categories / species / layers / labels / pins / thresholds.
- No
reduce(... + gap), noMath.abs(gap), no local SUM of counts. - No JS KHOP/ORPHAN/PHANTOM classification (that's
v_count_drift's job). - No
localStoragepins.
States
- loading: skeleton cards per section.
- error: per-section error card (one failed endpoint never blanks the page).
- empty: valid empty list rendered as "0 items", not an error.
- PIVOT_MISSING: explicit badge, never silent 0.
- stale: show
refreshed_atfrom pivot_results; net_gap flagged "live moving target".
Accessibility
Semantic table markup, scope on th, aria-labels on drill toggles and pin buttons, focus
management on drill expand, color is never the only signal (status text + icon alongside the
green/amber/red token).
Rollback
New route is additive; rollback = remove the route + components. Convergence/cutover of
/knowledge/registries is a separate gated step (doc 09) with its own snapshot rollback.
Acceptance tests (Đ28 Test-4) — see doc 10
100% of rendered counts trace to a pivot or PIVOT_MISSING; 0 counts computed client-side;
count_integrity_status shown honestly; CI lint blocks reduce(/Math.abs(gap)/hardcoded
CAT-* in the shell.
Verdict
Nuxt render-shell pack COMPLETE. Route, components, contract, fetch, states, a11y, rollback, tests specified. No production Nuxt touched (gated to RG8, after views + API live).