KB-400E

02 · Canonical Contract Design Review (_current architecture, version lifecycle, stale prevention)

7 min read Revision 1
terminal2canonical-contractversion-lifecyclecurrent-viewstale-preventionP02026-06-05

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 _vN name. (Lint rule in §6.)
  • When a better implementation lands (e.g. the registry-driven base, doc 03), repoint _current in one CREATE OR REPLACE — zero UI change.
  • Because reliability is already a strict superset, repointing UI from v1→_current is 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 _current resolves 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)

  1. Single binding target. UI/API reference …_current exclusively (enforced by lint, §6).
  2. Lifecycle registry (v_rp_contract_version_registry) is the SSOT for "which view is live"; dashboards read it, not hardcoded names.
  3. Superset guard test (birth-free assertion view v_rp_contract_current_guard):
    • _current row-count == reliability row-count (== 87 today);
    • _current column set ⊇ the documented consumer column contract (29 cols);
    • 0 rows where next_route IS NULL or drill_action IS NULL (ties to the invariant, doc 04). Surface a single guard_status = PASS|FAIL row; CI/operator reads it before deploy.
  4. Deprecation marker. A DEPRECATED view should not silently keep serving; emit a sentinel row (e.g. node_code='__DEPRECATED__') or a _deprecated=true column so any accidental consumer is visibly wrong rather than silently stale.

4. Compatibility / migration window

  • Keep v1/v2 as SUPERSEDED (they're load-bearing dependencies of the chain until the registry-driven base lands).
  • After doc-03 registry-driven base is built and _current repoints to it, retire v1/v2/reliability to DEPRECATED (they collapse into the new base), keeping only _current + the new base.
  • Never DROP a contract view while v_rp_contract_version_registry shows any FROZEN_COMPAT/SUPERSEDED consumer.

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)

  1. CREATE OR REPLACE VIEW v_rp_universal_node_ui_contract_current AS SELECT * FROM v_rp_universal_node_ui_contract_reliability; (birth-free).
  2. Build v_rp_contract_version_registry (lifecycle SSOT) + seed the 4 rows.
  3. Build v_rp_contract_current_guard (superset + non-null-route + rowcount assertion → single PASS/FAIL).
  4. Add the same _current convention for the proof matrix (v_rp_dynamic_drill_proof_matrix_current) and acceptance dashboard.
  5. Lint/grep gate: no file under the Nuxt repo (server/api/**, pages/**, composables/**) may reference v_rp_universal_node_ui_contract with a _v1/_v2/_reliability suffix — only _current. (UI source is UNVERIFIED this run, doc 09 — T1 must run this grep when source access is available.)
  6. Acceptance: v_rp_contract_current_guard.guard_status='PASS' AND _current row-count == 87 AND every node has non-null drill_action+next_route.
Back to Knowledge Hub knowledge/dev/reports/architecture/parallel-terminal2-rp-canonical-contract-design-alignment-technical-spec-2026-06-05/02-canonical-contract-design-review.md