KB-1648

dot-iu-cutter v0.5 WS-Q5 — Seed + Privilege Verification Plan (catalog/data; no rendered-string equality; authoring)

10 min read Revision 1
dot-iu-cutterv0.5ws-q5verification-planseedprivilegecatalog-levelauthoring-onlyno-executiondieu442026-05-18

dot-iu-cutter v0.5 WS-Q5 — Seed + Privilege Verification Plan

Phase: v0_5_WS_Q5_seed_and_privilege_command_authoring · Nature: authoring_only / no_execution · Date: 2026-05-18 Method: data + pg_catalog/information_schema assertions; catalog checks preferred over rendered-string equality where catalog is better (carry C-07 / v0.3 false-negative lesson). This is the PLAN; nothing is executed.

verification_executed: false ; checks_run: 0   # QG2
outcome_vocabulary: PASS | FAIL(detail)
run_when: ONLY in a future separately-authorized seed+grant execution phase,
          immediately after seed COMMIT and after GRANT, same production DB.

0. Pre-seed gate (run BEFORE seed — emptiness, replaces ON CONFLICT)

PSG-1  each of the 12 WS-Q5 tables has 0 rows pre-seed (production-apply
       verified empty; re-confirm) -> guarantees plain INSERT cannot collide.
PSG-2  system_identifier = 7611578671664259111 (correct production target).
PSG-3  schema owner cutter_governance = workflow_admin (unchanged).
any PSG fail -> STOP, do NOT seed.

1. Seed verification (data-level)

1.1 Expected row counts (exact; any deviation = FAIL)

matcher_config_registry           : 8
address_template_registry         : 2
grammar_profile                   : 2
grammar_profile_level             : 8     # Profile A=3 + Profile B=5
grammar_profile_status_marker     : 2
entity_kind_registry              : 5
source_family_registry            : 3     # READY subset (OD-SF1)
metadata_key_registry             : 1
entity_reference_registry         : 0     # intentionally not seeded
source_document_registry          : 0     # intentionally not seeded (OD-SEQ1)
source_document_version_registry  : 0     # intentionally not seeded
authority_override                : 0     # intentionally not seeded (WS-2 D4)
TOTAL seeded rows                 : 31
check: SELECT count(*) per table = expected; SUM = 31; the 4 zero-tables = 0
       (RSV-6: no row beyond authorized seed — "no extra seed rows").

1.2 Expected key values (exact PK set per table)

SV-K1  matcher_config_registry.matcher_ref = {mc.icx.nguyen_tac, mc.icx.kien_truc,
        mc.icx.dieu, mc.vn.chuong, mc.vn.dieu, mc.vn.khoan, mc.vn.diem, mc.vn.doan}
SV-K2  address_template_registry.address_template_ref = {at.icx.const.v4, at.vn.law}
SV-K3  grammar_profile.grammar_profile_ref = {incomex-architecture-constitution-v4, vn-national-law}
SV-K4  entity_kind_registry.entity_kind = {sql_entity, code_module, git_file, directus_item, report_path}
SV-K5  source_family_registry.source_family = {internal_incomex_constitution, internal_incomex_law, external_government_law}
SV-K6  metadata_key_registry.metadata_key = {idempotency_key}
        (exact-set compare via EXCEPT both directions — no extra/missing key)

1.3 FK integrity (every seeded FK resolves; catalog join, NOT string)

FKV-1  grammar_profile.address_template_ref ∈ address_template_registry.address_template_ref  (2/2)
FKV-2  grammar_profile_level.grammar_profile_ref ∈ grammar_profile.grammar_profile_ref        (8/8)
FKV-3  grammar_profile_level.matcher_ref ∈ matcher_config_registry.matcher_ref                (8/8)
FKV-4  grammar_profile_status_marker.grammar_profile_ref ∈ grammar_profile.grammar_profile_ref(2/2)
FKV-5  source_family_registry.grammar_profile_ref ∈ grammar_profile.grammar_profile_ref       (3/3)
       (internal_incomex_constitution+internal_incomex_law -> ...constitution-v4;
        external_government_law -> vn-national-law)
FKV-6  zero orphan: anti-join each child to parent returns 0 rows.
method: LEFT JOIN / NOT EXISTS row counts — never pg_get_constraintdef text.

1.4 Canonical address template separator check (BR-A1 locked scheme — QG7)

CAV-S1  address_template_registry.docprefix_separator = '/' for both rows
CAV-S2  address_template_registry.level_separator     = '-' for both rows
CAV-S3  address_template_registry.encodes_status       = false for both rows
        (status is metadata, NEVER in address — WS-2 D6 / master-plan CAV-3)
CAV-S4  template_pattern = '<DOCPREFIX>/<L1>-<L2>-...-<Lk>' (literal data
        compare of the stored value — this IS the authoritative scheme value,
        a data assertion not a DDL rendered-string)
note: BR-A1 final lock is GPT/User-OPEN; this asserts the WS-2 D6 scheme used.

1.5 Grammar profile level counts + ordering

GLV-1  count(grammar_profile_level WHERE grammar_profile_ref='incomex-architecture-constitution-v4') = 3
GLV-2  count(... WHERE grammar_profile_ref='vn-national-law') = 5
GLV-3  level_seq is gap-free 1..N per profile; UQ(grammar_profile_ref,level)
       holds (no duplicate level name within a profile)
GLV-4  each level.matcher_ref present in matcher_config_registry (≡ FKV-3)
GLV-5  Profile A levels = {NGUYEN_TAC,KIEN_TRUC_SECTION,DIEU};
       Profile B levels = {CHUONG,DIEU,KHOAN,DIEM,DOAN} (exact set per profile)

1.6 Status-marker mapping (exact UTF-8 codepoint — OD-SM1, NOT rendered glyph)

SMV-1  grammar_profile_status_marker has exactly 2 rows, all for
       grammar_profile_ref='incomex-architecture-constitution-v4'
SMV-2  mapping exact:
         encode(convert_to(marker,'UTF8'),'hex') = 'e29c85'   => maps_to='enacted'           (U+2705 ✅)
         encode(convert_to(marker,'UTF8'),'hex') = 'f09f938b'  => maps_to='controlled_draft'  (U+1F4CB 📋)
       (hex codepoint assertion — defends against ASCII normalization /
        rendering ambiguity; catalog/data check superior to glyph compare)
SMV-3  vn-national-law has 0 status_marker rows (WS-2 D3 Profile B: none)

1.7 Lifecycle / no-orphan-state + no-extra-rows

LCV-1  every seeded row in lifecycle-bearing tables has lifecycle='active'
       (no proposed/deprecated/orphan state in the READY bootstrap)
LCV-2  metadata_key_registry.idempotency_key: key_type='text',
       cardinality_policy='single', mutability_policy='immutable',
       index_policy='promoted_index' (v0.4 §4 graduate-first)
LCV-3  RSV-6: total rows across all 12 = exactly 31; the 4 zero-tables = 0;
       NO row with registered_by/created_by != 'ws-q5-seed-bootstrap'
       (detects any unauthorized extra seed row).

2. Privilege verification (catalog-level)

2.1 Grants present for selected roles

PV-1  cutter_ro   has exactly SELECT on each of the 12 new tables (12 rows in
      information_schema.role_table_grants; privilege_type set = {SELECT})
PV-2  cutter_exec has SELECT+INSERT on each of the 12 new tables
      (table grants); AND UPDATE on column 'lifecycle' for the 8
      lifecycle-bearing tables ONLY (information_schema.role_column_grants,
      privilege_type='UPDATE', column_name='lifecycle')
PV-3  cutter_verify has exactly SELECT on each of the 12 new tables
PV-4  exact-match: derived grant set == privilege-grant-draft §4 matrix
      (EXCEPT both directions over (grantee,table,privilege[,column]))

2.2 No unintended broad privileges (negative — any TRUE = FAIL)

NPV-1  any of DELETE / TRUNCATE / REFERENCES / TRIGGER granted to
       cutter_ro|cutter_exec|cutter_verify on any of the 12 -> FAIL
NPV-2  any table-wide UPDATE (no column scope) to cutter_exec -> FAIL
       (UPDATE must be column-scoped to 'lifecycle' only)
NPV-3  any grant to PUBLIC on the 12 -> FAIL
NPV-4  any grant carrying WITH GRANT OPTION (is_grantable='YES') -> FAIL
NPV-5  any privilege change on a baseline (non-WS-Q5) cutter_governance
       table or on the existing 12 -> FAIL (scope leak)
NPV-6  cutter_exec UPDATE present on any NON-lifecycle column -> FAIL

2.3 Owner / pre-existing state unchanged

OWN-1  pg_namespace.nspowner(cutter_governance) = workflow_admin (unchanged)
OWN-2  pg_class.relowner for all 12 new tables = workflow_admin (unchanged)
OWN-3  directus retains exactly its PRE-package SELECT on the 12 (not
       widened, not revoked) — diff vs the read-only baseline snapshot = 0
OWN-4  no new role created; no role membership (pg_auth_members) change
OWN-5  system_identifier 7611578671664259111 unchanged before==after

3. Pass criterion

seed_PASS  iff: PSG-1..3 PASS AND §1.1 counts exact AND SV-K1..6 exact-set
  AND FKV-1..6 PASS AND CAV-S1..4 PASS AND GLV-1..5 PASS AND SMV-1..3 PASS
  AND LCV-1..3 PASS
privilege_PASS iff: PV-1..4 PASS AND every NPV-* FALSE AND OWN-1..5 PASS
fail_action: ANY FAIL or negative TRUE -> DO NOT proceed; apply the matching
  rollback (seed-rollback-compensation §1 probe -> R-A/R-B; privilege-rollback)
  ONLY under the standing sovereign rollback rule; route GPT/User. No
  schema/data self-fix, no rendered-string re-interpretation.

4. Statements

  • QG6: row-count + FK checks included (§1.1, §1.3/FKV); plus key-set, separator, level-count, marker-mapping, no-extra-row, full privilege matrix + negatives + owner. Catalog/data assertions; codepoint hex instead of glyph; no pg_get_*def() string equality.
  • QG2: nothing executed (plan only). No DML/GRANT, no Directus, no CUT/VERIFY, no deploy, no git commit.
  • Self-advance PROHIBITED — doc 5 of 6; STOP → route GPT/User.

Companion: seed-data-draft, seed-rollback-compensation-draft, privilege-grant-draft, privilege-rollback-draft, seed-privilege-authoring-report.

Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.5-ws-q5-seed-privilege-authoring/dot-iu-cutter-v0.5-WS-Q5-seed-privilege-verification-plan-2026-05-18.md