KB-3252

dot-iu-cutter v0.4 — Credential Dry-Run Verification Plan

15 min read Revision 1
dot-iu-cutterv0.4credential-authoringverification-plandieu44

dot-iu-cutter v0.4 — Credential Dry-Run Verification Plan

document_path: knowledge/dev/laws/dieu44-trien-khai/v0.4-credential-authoring/dot-iu-cutter-v0.4-credential-dry-run-verification-plan-2026-05-16.md
revision: r1
date_authored: 2026-05-17
cycle_date_label: 2026-05-16
author: Agent (Claude Code CLI, Opus 4.7 1M)
phase: v0.4 — credential-cycle DRY-RUN AUTHORING (verification plan)
status: authoring_only_not_executed_pending_gpt_review

⛔ AUTHORING ONLY. No query here is run. This is the harness the SEPARATE, GPT-gated dry-run cycle will run inside the isolated env only. Every check is structural / catalog-set based — NOT a fragile rendered-string comparison. (Carries the feedback memory lesson: pg_get_constraintdef schema-qualifier false-negative forced a needless prod rollback in P0-6/P0-5. We compare privilege SETS via aclexplode, never bare strings.)


§0 — Verification Doctrine

method: build the ACTUAL (grantee, object, privilege[, column]) tuple set
  from system catalogs via aclexplode(); build the EXPECTED set from the
  matrix; assert symmetric difference == ∅. PASS iff EVERY expected tuple is
  present AND no unexpected tuple exists.
never: parse pg_get_*def() text, never substring-match a rendered ACL, never
  compare a psql \dp dump as a string. Structural sets only.
PASS_iff: all V-* PASS, all allow-probes succeed, all deny-probes are
  REFUSED by PG, baseline/rollback checks clean, prod provably untouched.
any_fail: => matrix/role spec revised => re-design => GPT re-review. NO
  production credential until clean PASS + GPT PASS.

§1 — Expected Privilege Set (the contract being asserted)

schema_usage:
  cutter_governance -> {cutter_exec: USAGE, cutter_verify: USAGE}
  cutter_governance CREATE -> {} (neither principal)

cutter_exec table tuples:
  decision_backlog_entry      : SELECT, INSERT ; UPDATE col=status
  decision_backlog_history    : SELECT, INSERT
  decision_backlog_dependency : SELECT, INSERT
  decision_backlog_sweep_log  : INSERT            (NO SELECT)
  manifest_envelope           : SELECT, INSERT
  manifest_unit_block         : SELECT, INSERT
  review_decision             : SELECT, INSERT ; UPDATE col=superseded_by_review_decision_id
  cut_change_set              : SELECT, INSERT
  cut_change_set_affected_row : INSERT            (NO SELECT)
  dot_pair_signature          : SELECT, INSERT
  verify_result               : {} (none)
  canonical_address_alias     : {} (none)

cutter_verify table tuples:
  decision_backlog_entry      : SELECT, INSERT ; UPDATE col=status
  decision_backlog_history    : SELECT, INSERT
  dot_pair_signature          : SELECT, INSERT
  cut_change_set              : SELECT, INSERT
  cut_change_set_affected_row : SELECT, INSERT
  verify_result               : SELECT, INSERT
  manifest_envelope           : SELECT
  manifest_unit_block         : SELECT
  review_decision             : SELECT
  decision_backlog_dependency : {} (none)
  decision_backlog_sweep_log  : {} (none)
  canonical_address_alias     : {} (none)

global_denied (both, all objects): DELETE, TRUNCATE, REFERENCES, TRIGGER,
  any GRANT OPTION, any UPDATE column other than the 2 above.
total_expected_update_columns: exactly 2 (entry.status, review_decision.superseded_by_review_decision_id) — one per principal-eligible table, granted to BOTH principals only where matrix says so.

§2 — Catalog Checks (structural; set-equality)

V-01 role_attributes:
  source: pg_roles WHERE rolname IN ('cutter_exec','cutter_verify')
  assert per role: rolcanlogin=t, rolsuper=f, rolcreatedb=f,
    rolcreaterole=f, rolreplication=f, rolbypassrls=f, rolinherit (record),
    rolconnlimit=2.
  PASS iff both rows exactly match; 0 extra cutter_* roles created.

V-02 connection_limit:
  pg_roles.rolconnlimit = 2 for BOTH (explicit, not -1/0). FAIL on any other value.

V-03 no_membership:
  pg_auth_members: 0 rows where member = cutter_exec/cutter_verify OR
  roleid = cutter_exec/cutter_verify (they neither inherit nor are inherited).
  Also assert neither is a member of cutter_ro / workflow_admin / directus /
  postgres / pg_* default roles.

V-04 no_object_ownership:
  scan pg_class.relowner, pg_namespace.nspowner, pg_proc.proowner,
  pg_type.typowner, pg_authid via pg_shdepend WHERE refobjid = roleoid
  AND deptype='o'. Expected count = 0 for both principals.

V-05 schema_usage_exact:
  has_schema_privilege('cutter_exec','cutter_governance','USAGE')=t,
  has_schema_privilege('cutter_verify',...,'USAGE')=t,
  has_schema_privilege(...,'CREATE')=f for both.
  Cross-check via aclexplode(nspacl) of cutter_governance: privilege set for
  each principal == {USAGE} exactly.

V-06 table_privileges_set_equality (THE core structural check):
  WITH actual AS (
    SELECT r.rolname AS grantee, c.relname AS tbl, a.privilege_type
    FROM pg_class c
    JOIN pg_namespace n ON n.oid=c.relnamespace AND n.nspname='cutter_governance'
    CROSS JOIN LATERAL aclexplode(c.relacl) a
    JOIN pg_roles r ON r.oid=a.grantee
    WHERE r.rolname IN ('cutter_exec','cutter_verify')
      AND c.relkind='r')
  Compare `actual` to the §1 expected table tuple set. Assert:
    (expected EXCEPT actual) = ∅  AND  (actual EXCEPT expected) = ∅.
  Also assert a.is_grantable = false for EVERY actual row (no GRANT OPTION).
  NOTE: table-level set must NOT contain UPDATE for entry/review_decision
  (those are column-scoped — verified in V-07), and must NOT contain
  SELECT for decision_backlog_sweep_log (exec) or cut_change_set_affected_row (exec).

V-07 column_privileges_set_equality:
  WITH actual_col AS (
    SELECT r.rolname grantee, c.relname tbl, att.attname col, ac.privilege_type
    FROM pg_class c
    JOIN pg_namespace n ON n.oid=c.relnamespace AND n.nspname='cutter_governance'
    JOIN pg_attribute att ON att.attrelid=c.oid AND att.attnum>0 AND NOT att.attisdropped
    CROSS JOIN LATERAL aclexplode(att.attacl) ac
    JOIN pg_roles r ON r.oid=ac.grantee
    WHERE r.rolname IN ('cutter_exec','cutter_verify'))
  Assert actual_col == EXACTLY:
    {(cutter_exec, decision_backlog_entry, status, UPDATE),
     (cutter_verify, decision_backlog_entry, status, UPDATE),
     (cutter_exec, review_decision, superseded_by_review_decision_id, UPDATE)}
  and NOTHING else. is_grantable=false on all. (Symmetric-difference = ∅.)

V-08 no_extra_update:
  derived from V-06+V-07: total distinct (grantee,tbl,col) UPDATE tuples = 3
  exactly (2 on entry — both principals, 1 on review_decision — exec only);
  0 table-level UPDATE; 0 UPDATE on any other column of any table.

V-09 no_destructive_privs:
  from V-06 actual set: 0 rows with privilege_type IN
  ('DELETE','TRUNCATE','REFERENCES','TRIGGER') for either principal on ANY
  cutter_governance object (tables AND views).

V-10 no_observe_view_grants:
  aclexplode over relkind='v' (the 12 v_*_observe) in cutter_governance:
  0 rows where grantee IN (cutter_exec,cutter_verify). Views remain
  cutter_ro-only.

V-11 no_canonical_address_alias_grant:
  aclexplode(relacl) of cutter_governance.canonical_address_alias AND its
  attacl: 0 tuples for cutter_exec OR cutter_verify (table and column level).

V-12 no_out_of_schema_priv:
  scan pg_class/pg_namespace for any object NOT in cutter_governance
  (esp. schema public): 0 relacl/attacl tuple grants to either principal.
  has_schema_privilege(principal,'public','CREATE')=f. (public USAGE is the
  PG cluster default, note N-1, NOT introduced here — record as NOTE, not FAIL.)

V-13 cutter_ro_unchanged (byte-state):
  pg_roles row for cutter_ro identical to v0.3 inventory §4 (NOLOGIN,
  NOSUPERUSER/NOCREATE*/NOBYPASSRLS, 0 membership);
  aclexplode set for cutter_ro == exactly {schema cutter_governance USAGE}
  ∪ {SELECT on each of the 12 v_*_observe} = 13 grant tuples, 0 base-table
  grant, 0 write privilege. Symmetric difference vs the v0.3 expected set = ∅.

V-14 directus_unchanged:
  counts: directus_collections=164, directus_permissions=1173,
  directus_roles=9, directus_policies=8, directus_access=9 (== v0.3 §5
  baseline). cutter_governance still UNregistered in Directus.

V-15 rls_unchanged:
  SELECT count(*) FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace
  WHERE n.nspname='cutter_governance' AND c.relrowsecurity; expected 0.
  0 pg_policy rows on any cutter_governance relation.

V-16 schema_creation_denied:
  has_schema_privilege(principal,'cutter_governance','CREATE')=f for both
  (re-assert; ensures USAGE without CREATE).

V-17 password_encryption_scram (CD-5):
  pg_authid.rolpassword for both begins with 'SCRAM-SHA-256$' (structural
  prefix test, not value disclosure — never SELECT/print the hash body).

§3 — Behavioural Allow-Probes (must SUCCEED; rolled back in-txn)

note: each probe runs as the named principal in the isolated env, inside a
  SAVEPOINT, asserting success, then ROLLBACK TO SAVEPOINT so no row persists
  (empty-schema fidelity preserved). FK-satisfying minimal parent rows are
  staged within the same outer txn as needed.

A-EXEC:
  AE-1 INSERT decision_backlog_entry          -> success
  AE-2 UPDATE decision_backlog_entry SET status=... (granted col) -> success
  AE-3 SELECT decision_backlog_entry          -> success
  AE-4 INSERT decision_backlog_history / _dependency -> success
  AE-5 INSERT decision_backlog_sweep_log      -> success
  AE-6 INSERT manifest_envelope / manifest_unit_block ; SELECT both -> success
  AE-7 INSERT review_decision ; UPDATE review_decision SET
       superseded_by_review_decision_id=... -> success
  AE-8 INSERT cut_change_set ; SELECT cut_change_set -> success
  AE-9 INSERT cut_change_set_affected_row     -> success
  AE-10 INSERT/SELECT dot_pair_signature      -> success

A-VERIFY:
  AV-1 INSERT/SELECT verify_result            -> success
  AV-2 INSERT/SELECT dot_pair_signature       -> success
  AV-3 INSERT cut_change_set (fwd compensation, CD-8/10) -> success
  AV-4 INSERT/SELECT cut_change_set_affected_row -> success
  AV-5 INSERT decision_backlog_entry (escalation) ; UPDATE status ;
       INSERT decision_backlog_history -> success
  AV-6 SELECT manifest_envelope / manifest_unit_block / review_decision -> success

§4 — Behavioural Deny-Probes (must be REFUSED by PG: SQLSTATE 42501)

D-1  ANY DELETE on ANY cutter_governance table (both principals)  -> denied
D-2  ANY TRUNCATE on ANY table (both)                              -> denied
D-3  UPDATE decision_backlog_entry SET payload=... (non-granted col, exec & verify) -> denied
D-4  UPDATE decision_backlog_entry SET entry_id=... (PK / non-granted) -> denied
D-5  UPDATE review_decision SET <any col != superseded_by_review_decision_id> (exec) -> denied
D-6  UPDATE review_decision (any) by cutter_verify                 -> denied (verify read-only here)
D-7  ANY UPDATE on cut_change_set (either principal; OD-6 keeps
     verifier_signature_id NULL forever)                           -> denied
D-8  cutter_exec any verb on verify_result                         -> denied (strong SoD)
D-9  cutter_exec SELECT decision_backlog_sweep_log                 -> denied (CD-7 write-only)
D-10 cutter_exec SELECT cut_change_set_affected_row                -> denied (INSERT-only)
D-11 cutter_verify INSERT/UPDATE manifest_envelope/manifest_unit_block/review_decision -> denied
D-12 cutter_verify any verb on decision_backlog_dependency / decision_backlog_sweep_log -> denied
D-13 EITHER principal any verb on canonical_address_alias          -> denied
D-14 EITHER principal SELECT/INSERT any v_*_observe view           -> denied
D-15 EITHER principal any access to a schema public object / any
     non-cutter_governance object                                  -> denied
D-16 EITHER principal CREATE TABLE in cutter_governance / any DDL  -> denied
D-17 EITHER principal GRANT/REVOKE / ALTER ROLE / CREATE ROLE      -> denied
D-18 EITHER principal REFERENCES or CREATE TRIGGER on any table    -> denied
D-19 connect as cutter_ro, attempt any write on view/base table    -> denied (cutter_ro unchanged, SELECT-on-views-only)
D-20 open a 3rd concurrent session per writer (CONNECTION LIMIT 2) -> refused (too many connections for role)

§5 — Rollback Verification (after the companion rollback draft runs)

RBV-1 pg_roles: 0 rows for cutter_exec / cutter_verify (both DROPped).
RBV-2 aclexplode over all cutter_governance objects: 0 tuple for either
      principal (REVOKE was exact inverse; no residue).
RBV-3 cutter_ro byte-state == V-13 expected (UNCHANGED by rollback).
RBV-4 12 base tables / 12 views intact, 0 rows, schema present.
RBV-5 RLS still 0; Directus counts still 164/1173/9/8/9.
RBV-6 NO CASCADE side effect: object/owner counts identical to pre-dry-run.

§6 — Prod-Untouched Gate

PU-1 dry-run cluster system identifier  !=  prod 7611578671664259111
     (asserted before AND after; recorded both times).
PU-2 prod reached read-only ONLY for the pg_dump source (command plan);
     never connected by §3 SQL or any probe.
PU-3 every catalog/probe query confined to the ephemeral container; the
     env is torn down after RBV; only KB result docs persist.

§7 — Harness Discipline (false-negative guard)

- all privilege assertions are SET operations on aclexplode() output, not
  string comparisons of \dp / pg_get_*def() (feedback memory: schema-
  qualifier mismatch caused a false-negative + needless prod rollback in
  dot-iu-cutter P0-6/P0-5 2026-05-16).
- expected sets are declared as explicit (grantee,object[,column],priv)
  literals in §1; the harness computes symmetric difference and FAILS only
  on a genuine structural delta.
- a deny-probe is PASS iff PG raises insufficient_privilege (SQLSTATE
  42501) — not iff a generic error occurs (distinguish 42501 from 42P01
  etc. so a missing object is not mistaken for a denial).
- on CONNECTION LIMIT probe, PASS iff PG refuses the 3rd login with the
  too-many-connections-for-role error specifically.

End of credential dry-run verification plan (authoring only; nothing executed).

Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.4-credential-authoring/dot-iu-cutter-v0.4-credential-dry-run-verification-plan-2026-05-16.md