KB-5BC2

RS4A-PATCH1-03 — Canonical Inert State Resolution — 2026-06-21

9 min read Revision 1
rs4a-patch1inert-statusdraftcanonical-stateactivation-fencedesign-only2026-06-21

RS4A-PATCH1-03 — Canonical Inert State Resolution — 2026-06-21

Macro: RS4A-PATCH1 · Mục tiêu C (closes Codex C2 canonical inert persisted status) Deliverable: 03 of 10 · design-only · correction addendum (does NOT overwrite RS4A-02/04/09; does NOT write any row, does NOT add any constraint) Builds on / corrects: RS4A-02 §3 (status: "<inert/non-active>" placeholder) and RS4A-04 Phase 3/Phase 6. Gate: REGISTRATION_HOLD · REGISTRATION_CAN_PROCEED = NO Status: CANONICAL_INERT_STATE_RESOLVED = "draft" (Option 1) + one carried hardening backstop (status-domain CHECK) — the placeholder is replaced by an exact, governed, live-proven value.


0. The Codex defect this file closes

C2 (Codex §4.1 / §17): "The output uses status: \"<inert/non-active>\". A testable contract must select or govern an exact persisted value and prove it is accepted by current metadata/constraints and ignored by activation consumers. A placeholder cannot support deterministic validation or readback." Codex §17: "REJECT <inert/non-active> as a final contract value."

The brief allowed two outcomes: Option 1 (find an existing allowed inert value, prove it) or Option 2 (CANONICAL_INERT_STATE_NOT_AVAILABLE + INERT_STATUS_VALUE_NOT_PROVEN, fail-closed). Live evidence supports Option 1. No value is invented.


1. Live evidence (Claude read-only query_pg, db directus, 2026-06-21)

1.1 The status field is governed at the Directus-metadata layer

directus_fields for (collection='dot_tools', field='status'):

options.choices = [ {Draft: "draft"}, {Active: "active"}, {Deprecated: "deprecated"}, {Retired: "retired"} ]
validation = null
required = false
note = "Trạng thái: draft/active/deprecated/retired"

The field's governed vocabulary (its SSOT) is {draft, active, deprecated, retired}, with a declared lifecycle draft → active → deprecated → retired. The registrar writes through the Directus REST item layer (POST /items/dot_tools, source L156), so this field metadata is part of the effective constraint surface, not merely a UI hint.

1.2 The status column is NOT hard-enforced at the DB layer

  • dot_tools constraints (LIVE): dot_tools_pkey PRIMARY KEY (id), chk_dot_tier, chk_dot_coverage, chk_dot_trigger, fk_dot_tools_domain. No CHECK touches status.
  • dot_tools.status is character varying, nullable.
  • Live data distribution: active = 291, published = 16, null = 2. Note published is out-of-vocabulary (not one of the four governed choices) — proof that the choice set is declared but not DB-enforced (legacy/drift rows exist).

1.3 The activation producer fires only on status='active'

fn_context_pack_on_dot_register (RS4A-09, live body): pg_notify('context_pack_event', …) fires iff NEW.tier ∈ watch_tiers (["A","B","C"]) AND NEW.status='active'.


2. Resolution: canonical inert persisted status = draft

draft is selected as the canonical inert registration state. Proof against each C2 criterion:

C2 criterion Proof for draft Evidence tier
Exact value (not a placeholder) status = "draft" — a single literal, supports deterministic validation + readback
Governed, not invented draft is an explicit declared choice in directus_fields.dot_tools.status.options.choices (value "draft", label "Draft"); the field note names it first in the lifecycle LIVE
Accepted by current metadata/constraints DB: no CHECK rejects it (status unconstrained at PG). Directus: validation=null, required=false, and draft IS in the declared choices ⇒ accepted by the REST item write path LIVE (PG pg_constraint + directus_fields)
Ignored by the activation producer 'draft' ≠ 'active'fn_context_pack_on_dot_register notify condition NEW.status='active' is falseno context_pack_event emitted at a draft insert LIVE (function body, RS4A-09)
Lifecycle-coherent (pre-activation) the field's own lifecycle is draft → active → …; draft = registered-but-not-activated, exactly the inert registration state RS4A requires; activation is a separate Owner-gated UPDATE draft → active (RS4A-04 Phase 6) LIVE (field note)

Therefore the RS4A-02 §3 output is corrected:

registered_row_intent.columns.status = "draft"     # was "<inert/non-active>"

and the no-activation invariant (RS4A-02 §5.2, RS4A-09 §3.1) is now concrete: register with status='draft'; never status='active' at registration.


3. Why this is Option 1, not Option 2 (and not a HOLD)

Codex C2 asked for "select or govern an exact persisted value." draft is selected from the field's governed vocabulary and proven accepted + inert. The placeholder is gone. There is no invention: draft is the field's declared first-lifecycle state. Hence the inert-state axis is CLOSED, and the PATCH1 verdict is not RS4A_PATCH1_HOLD_INERT_STATE_UNPROVEN.

(If the field metadata had instead constrained status to a set with no inert member, or had been empty, Option 2 / INERT_STATUS_VALUE_NOT_PROVEN would have been the honest outcome. It is not the case here.)


4. Carried residual hardening (NOT a blocker on the value)

Two honesty caveats are carried so the resolution is not overclaimed. Neither blocks selecting draft; both are CONTRACT_BACKSTOP / future-surface items, like D13's missing UNIQUE.

  1. STATUS_DOMAIN_NOT_DB_ENFORCED (CONTRACT_BACKSTOP). Because there is no PG CHECK on status and out-of-vocab rows already exist (published ×16), the governed vocabulary {draft, active, deprecated, retired} is advisory at the DB layer — a future/buggy writer could persist active (or any string) and bypass the inert intent. The replacement contract must therefore also require a governed CHECK/enum that locks status to the declared vocabulary, so the inert value cannot be bypassed and active cannot be set at registration. RS4A does not author this constraint (no DDL); it is carried as a backstop the governed registration depends on, Owner/design-gated. Until it exists, the inert-status invariant is enforced by the registrar's own write discipline + the Phase-4 post-commit readback (verify status='draft'), not by the DB.
  2. G7-consumer (carried from RS4A-09). "Ignored by activation consumers" is proven at the producer (no notify on a draft insert). For the registration path this is structurally sufficient: a draft insert emits no context_pack_event, so the consumer is never invoked at registration. The consumer body remains unread, so "consumer is inert" is not asserted for the separate activation UPDATE path (Phase 6) — that path stays fail-closed (context_pack_mode='warn' live, but unproven). This does not affect the inert-registration claim.

5. Added acceptance tests (feed PATCH1-07)

New case Input/state Expected Layer
T-PX-1 registrar writes status='draft' for a watch-tier (A/B) row ACCEPT as inert; no context_pack_event; post-commit readback confirms status='draft' R/SN
T-PX-2 registrar attempts status='active' (the source D05 behavior) ACTIVATION_AT_REGISTRATION (reject) R
T-PX-3 a non-vocabulary status (e.g. published/arbitrary) is written at registration HOLD STATUS_DOMAIN_NOT_DB_ENFORCED until a governed status CHECK exists R/SCHEMA
T-PX-4 Phase-4 verifier reads back the committed row PASS only if status='draft' and notify-not-emitted proof holds R

6. Status

  • Canonical inert state: CANONICAL_INERT_STATE_RESOLVED = "draft" — governed Directus choice, live-proven accepted + non-activating; placeholder retired.
  • Carried: STATUS_DOMAIN_NOT_DB_ENFORCED (add status CHECK backstop) + G7-consumer (activation-UPDATE path only). Neither blocks the value.
  • No row written, no constraint added, no DDL. Gate REGISTRATION_HOLD · CAN_PROCEED = NO.
Back to Knowledge Hub knowledge/dev/laws-new/reports/rs4a-patch1/03-canonical-inert-state-resolution-2026-06-21.md