KB-5E48

05 — Phantom Policy & Law Patch Pack (Branch D, RG4)

6 min read Revision 1
registries-pivotphantomLAW_DEFINITION_GAPRG4source-modelsystem_issuesevent_outboxdieu45no-enactment2026-05-31

title: 05 — Phantom Policy & Law Patch Pack (Branch D, RG4) date: 2026-05-31 status: decision package + law-patch DRAFT; NO enactment, NO self-approval

05 — Phantom Policy & Law Patch Pack (Branch D)

Resolves the LAW_DEFINITION_GAP: there is no authoritative phantom definition and no phantom_count column anywhere. This is a council/RG4 decision; this doc packages it.

The core finding (why blind record > actual is wrong)

code model record actual gap truth
CAT-023 A 990,720 985,471 +5,249 write-race / stale scan — NOT phantom
CAT-006 B 309 163 +146 phantom candidate
CAT-007 B 37 52 −15 unregistered (source-over-registry)

CAT-023 and CAT-006 have the identical sign (record > actual) yet opposite meaning. Only source_model distinguishes them. The legacy /api/registry/health classifies KHOP/ORPHAN/PHANTOM by blind gap sign in JS (health.get.ts:104-110) — source-model-blind, therefore wrong.

Proposed source_model-aware definition (for RG4 ratification)

model-A (live PG table, trigger-counted): record > actual  ⇒ model_a_surplus_recheck
        = live-write race or stale full-scan. NEVER a phantom. Fix = refresh scan / live actual.
model-B (registry-vs-source: file/page):  record > actual  ⇒ model_b_phantom_candidate
        = registered objects with no backing source artifact = PHANTOM CANDIDATE.
        record < actual ⇒ model_b_unregistered_candidate (source exists, not registered).
phantom_confirmed ⇒ requires (a) source_model='B', (b) a verification scan of the source,
        (c) council ratification. The pivot/view counts CANDIDATES only; never asserts "phantom".

This is exactly what the Macro-1 v_count_integrity.drift_classification already computes (source_model-aware, labelled "candidate"). RG4 ratifies it as law + authorizes a phantom_count column / PIV-302 promotion from "candidate count" to "phantom count".

Severity model

classification severity rationale
model_a_surplus_recheck info/warning self-heals on next scan
model_b_phantom_candidate warning → critical if confirmed registered ghost objects
model_b_unregistered_candidate warning coverage gap, registrable
unverified (NULL counts) warning unmeasured (CAT-1006..1010)

Scanner

Reuse the Macro-1 view layer: v_count_drift IS the scanner output (3 rows live). PIV-303 counts drift total; PIV-302 counts model_b_phantom_candidate. No new scanner code — the classification is a view column. A periodic refresh of actual_count (model-A) and a source scan (model-B) feed it (Macro 2 scan cadence; CAT-1006..1010 need a first count).

system_issues mapping (live distribution — reuse + 3 NEW types)

Existing reusable issue types: thiếu_quan_hệ(606, data_fault) / thiếu_mã_định_danh(9) = orphan halves; sai_lệch_dữ_liệu(2, sync_fault) = drift; hardcode_violation(11) / hc_finding_* = Đ28; lone apr_phantom_applied(1, info). NEW issue types required (none exist today): count_integrity_failed, phantom_candidate, phantom_confirmed, label_grouping_required. Use existing rich columns: coalesce_key (idempotent), issue_class/sub_class, severity, occurrence_count, verification_contract_id.

event_outbox / notification contract (Đ45)

event_type_registry (40 rows) has zero count_integrity.* / phantom / pin / label event types (verified). NEW event types needed, then event_outbox (event_domain, event_type, event_severity, event_subject_table, event_subject_ref, safe_payload jsonb, delivery_lane, correlation_id) is the emit substrate:

registries_pivot.count_integrity_failed   (warning)  subject=meta_catalog ref=CAT-xxx
registries_pivot.phantom_candidate_found  (warning)  subject=meta_catalog ref=CAT-006
registries_pivot.label_grouping_required  (info)     subject=meta_catalog ref=CAT-xxx
registries_pivot.pin_changed              (info)     subject=registry_pin

No event emitted, no notification sent this macro (forbidden). Contract only.

Cleanup workflow trigger

On phantom_confirmed (post-RG4): a gated repair workflow (reuse the DOT/APR repair pattern from S178) proposes deregistration of confirmed ghosts — propose-only, Đ32 approval per action, never auto-delete (Đ0 Atom soft-retire).

Output

  • Phantom decision pack: the source_model-aware definition above.
  • Law-patch DRAFT: amend the phantom/count-integrity law to (a) forbid blind record>actual as a phantom signal, (b) mandate source_model discrimination, (c) define phantom_candidate vs phantom_confirmed, (d) authorize phantom_count + PIV-302.
  • Approval wording (for RG4): "Council ratifies the source_model-aware phantom definition (model-A surplus = recheck, model-B surplus = phantom_candidate), authorizes a phantom_count column and PIV-302, and approves NEW system_issues/event types count_integrity_failed, phantom_candidate, phantom_confirmed, label_grouping_required."
  • Implementation gates: law ratified → add phantom_count + issue/event types (gated DDL) → PIV-302 promotion → cleanup workflow (propose-only).

Verdict

Phantom pack COMPLETE. Definition is source_model-aware and proven against live drift; no law enacted, no self-approval, no event/notification emitted. Awaits RG4.

Back to Knowledge Hub knowledge/dev/reports/architecture/registries-pivot-macro2-3-combined-ui-api-legacy-acceptance-2026-05-31/05-phantom-policy-law-patch.md