02 · Canonical Contract Design Review (_current architecture, version lifecycle, stale prevention)
02 · Canonical Contract Design Review
Area 1 verdict: MISSING (P0). No canonical view exists; UI has no version-stable binding target; the deployed-intended v1 is the stale/broken twin.
1. What the architecture must define (and current status)
| Design element | Status | Evidence |
|---|---|---|
Canonical _current view(s) |
MISSING | no *_current object in public. |
| Version lifecycle (v1/v2/v3/current/deprecated) | MISSING | three coexisting versioned views, no lifecycle metadata, no registry. |
| Which surface UI must consume | MISSING/AMBIGUOUS | UI package binds v1 (the stale twin); corrected logic only in undeployed v2/reliability. |
| How old views are kept for compatibility | PARTIAL (accidental) | v2/reliability depend on v1, so v1 cannot be dropped — but this is coincidence, not policy. |
| How stale contracts are prevented from use | MISSING | nothing stops a surface from binding v1 or a future _v3. |
2. Root problem
The three views are a thin-decorator stack on a single hardcoded base (01 §3):
v1 (8239c base) → v2 (+grouping/empty) → reliability (+8 reliability fields).
reliability is a strict superset of v2, which is a superset of v1. Yet:
- the deployed-intended contract is
v1(per the drill/grouping checkpoints, the UI package's pass-through routes were authored against v1 then "v2"), and - the most-correct contract is
reliability.
So "production-closeout / drill PROVEN" is true only for the undeployed superset; the bound surface is the stale base. This is the canonical-contract gap.
3. Design spec — canonical _current
3.1 Single canonical name
Create exactly one canonical view per consumer contract:
CREATE OR REPLACE VIEW v_rp_universal_node_ui_contract_current AS
SELECT * FROM v_rp_universal_node_ui_contract_reliability; -- today's best superset
- UI/API bind ONLY
…_current. Never a_vNname. (Lint rule in §6.) - When a better implementation lands (e.g. the registry-driven base, doc 03), repoint
_currentin oneCREATE OR REPLACE— zero UI change. - Because
reliabilityis already a strict superset, repointing UI from v1→_currentis additive (UI sees all old columns plus 8 reliability columns + corrected grouping/empty). No column is lost.
Note: a one-line
SELECT *alias is acceptable transitionally. The end state (doc 03/10) collapses the chain so_currentresolves to a single registry-driven view and v1/v2/reliability are retired — avoiding permanent 3-deep view nesting (a mild performance and reasoning cost).
3.2 Version lifecycle states
Define a controlled lifecycle for every RP contract object:
| State | Meaning | UI may bind? | Drop allowed? |
|---|---|---|---|
ACTIVE_CURRENT |
the implementation _current resolves to |
via _current only |
no |
SUPERSEDED |
replaced but kept one migration window (back-compat) | no | no (until window closes) |
DEPRECATED |
scheduled for removal; emits a deprecation marker | no | yes after grace |
FROZEN_COMPAT |
retained indefinitely for an external consumer | only that consumer | no |
Record this in a lifecycle registry (new, birth-free view or tiny table):
-- v_rp_contract_version_registry: one row per RP contract object
contract_name TEXT, kind TEXT('contract'|'proof'|'dashboard'|'reliability'),
resolves_from TEXT, lifecycle_state TEXT, superseded_by TEXT,
column_count INT, build_note TEXT, since DATE
Seed: v1=SUPERSEDED→by v_current, v2=SUPERSEDED→by v_current, reliability=ACTIVE_CURRENT (resolves_from of _current), _current=ALIAS.
3.3 Stale-contract prevention (4 mechanisms)
- Single binding target. UI/API reference
…_currentexclusively (enforced by lint, §6). - Lifecycle registry (
v_rp_contract_version_registry) is the SSOT for "which view is live"; dashboards read it, not hardcoded names. - Superset guard test (birth-free assertion view
v_rp_contract_current_guard):_currentrow-count ==reliabilityrow-count (== 87 today);_currentcolumn set ⊇ the documented consumer column contract (29 cols);- 0 rows where
next_route IS NULLordrill_action IS NULL(ties to the invariant, doc 04). Surface a singleguard_status = PASS|FAILrow; CI/operator reads it before deploy.
- Deprecation marker. A
DEPRECATEDview should not silently keep serving; emit a sentinel row (e.g.node_code='__DEPRECATED__') or a_deprecated=truecolumn so any accidental consumer is visibly wrong rather than silently stale.
4. Compatibility / migration window
- Keep
v1/v2asSUPERSEDED(they're load-bearing dependencies of the chain until the registry-driven base lands). - After doc-03 registry-driven base is built and
_currentrepoints to it, retire v1/v2/reliability toDEPRECATED(they collapse into the new base), keeping only_current+ the new base. - Never
DROPa contract view whilev_rp_contract_version_registryshows anyFROZEN_COMPAT/SUPERSEDEDconsumer.
5. Classification
| Issue | Class | Severity |
|---|---|---|
No _current canonical view |
ARCHITECTURE_GAP | P0 |
UI bound to stale v1 while corrected logic undeployed |
DESIGN_DRIFT | P0 |
| No version lifecycle / registry | ARCHITECTURE_GAP | P1 |
| 3-deep view nesting (transitional) | OPERATIONAL_DEBT | P2 |
Version sprawl (131 views, no _current convention) |
OPERATIONAL_DEBT | P2 |
6. Required technical spec for T1 (Area 1)
CREATE OR REPLACE VIEW v_rp_universal_node_ui_contract_current AS SELECT * FROM v_rp_universal_node_ui_contract_reliability;(birth-free).- Build
v_rp_contract_version_registry(lifecycle SSOT) + seed the 4 rows. - Build
v_rp_contract_current_guard(superset + non-null-route + rowcount assertion → single PASS/FAIL). - Add the same
_currentconvention for the proof matrix (v_rp_dynamic_drill_proof_matrix_current) and acceptance dashboard. - Lint/grep gate: no file under the Nuxt repo (
server/api/**,pages/**,composables/**) may referencev_rp_universal_node_ui_contractwith a_v1/_v2/_reliabilitysuffix — only_current. (UI source is UNVERIFIED this run, doc 09 — T1 must run this grep when source access is available.) - Acceptance:
v_rp_contract_current_guard.guard_status='PASS'AND_currentrow-count == 87 AND every node has non-nulldrill_action+next_route.