KB-1671

GPT Review — 22-P3-P1 Policy + Canonical Marker Prompt rev1

9 min read Revision 1
gpt-reviewpack-22p3p1canonical-markerrev2-required

GPT Review — 22-P3-P1 Policy + Canonical Marker Prompt rev1

Date: 2026-05-06
Reviewer: GPT-5.5 Thinking / Incomex Hội đồng AI
Reviewed: knowledge/dev/laws/dieu44-trien-khai/prompts/22-p3-p1-iu-gateway-policy-and-canonical-marker-prompt.md

Verdict

Do not dispatch rev1. Rev2 required.

The prompt is directionally correct and appropriately narrower than P2, but it cut a few safety checks that are load-bearing for a writer-phase function patch.

P3-P1 is not merely a smoke test. It changes dot_config and replaces public.fn_iu_create. Therefore it needs transaction safety, dot_config uniqueness safety, and proof that CREATE OR REPLACE did not degrade P2’s contract or permissions.

What is good

  • Correctly does not create trigger guard.
  • Correctly seeds L0 policy keys and adds the transaction-local canonical marker.
  • Correctly sets marker before IU/UV writes.
  • Correctly keeps Directus permissions / GRANT / REVOKE out of scope.
  • Correctly runs a pilot create and post-commit invariant check.
  • Correctly keeps P3-P2 enforcement separate.

Required rev2 patches

P1 — Use one explicit transaction for policy keys + function patch + pilot test

Rev1 runs multiple SQL statements without explicit BEGIN / COMMIT. In psql autocommit mode, policy keys may be written before a later function patch or pilot test fails. That creates partial state.

Patch:

  • Wrap main SQL in one explicit transaction:
BEGIN;
-- dot_config policy key upserts
-- CREATE OR REPLACE exact fn_iu_create
-- in-transaction pilot test
-- pre-commit checks
COMMIT;

If any statement fails, transaction rolls back. Report partial failure.

Post-COMMIT verify remains a separate read-only query.

P2 — Preflight dot_config key uniqueness before ON CONFLICT (key)

Rev1 assumes dot_config.key has a unique constraint/index. P3-P0 only confirmed key text NOT NULL, not uniqueness.

Patch before policy upsert:

DO $$
DECLARE v_dup int; v_unique int;
BEGIN
  SELECT count(*) INTO v_dup
  FROM (
    SELECT key FROM public.dot_config
    WHERE key LIKE 'iu_create.gateway.%'
    GROUP BY key HAVING count(*) > 1
  ) d;
  IF v_dup <> 0 THEN
    RAISE EXCEPTION 'duplicate iu_create.gateway dot_config keys exist: %', v_dup;
  END IF;

  SELECT count(*) INTO v_unique
  FROM pg_constraint c
  WHERE c.conrelid='public.dot_config'::regclass
    AND c.contype IN ('p','u')
    AND (SELECT count(*) FROM unnest(c.conkey))=1
    AND (SELECT count(*) FROM unnest(c.conkey) k
         JOIN pg_attribute a ON a.attrelid=c.conrelid AND a.attnum=k
         WHERE a.attname='key')=1;

  IF v_unique <> 1 THEN
    RAISE EXCEPTION 'dot_config.key unique guard count must be exactly 1, got %', v_unique;
  END IF;
END $$;

If dot_config uses unique index instead of constraint, inspect and support it explicitly. Do not assume.

P3 — Idempotent guard must skip function replacement if already patched and verified

Rev1 detects ALREADY_PATCHED=t but still executes the full CREATE OR REPLACE body. That is dangerous because a future evolved function could be overwritten by this older inline body.

Patch policy:

  • If fn_iu_create already contains canonical_writer, do not replace the function.
  • Still verify policy keys, function metadata, marker source, and run pilot/idempotency test if safe.
  • Or require exact md5/source match before replacing. But preferred: already patched → verify only, no replace.

This prevents stale prompt bodies from clobbering future function evolution.

P4 — Restore minimal source/hash before/after audit

Opus cut hash before/after, but this is a function replacement. Hash is audit, not gate. Add:

  • md5(prosrc) before;
  • md5(prosrc) after;
  • marker_present after;
  • report both.

Do not gate by literal hash. Gate by behavior and marker presence.

P5 — Verify privileges after CREATE OR REPLACE

CREATE OR REPLACE usually preserves privileges, but this is a security boundary. Rev2 must explicitly verify:

  • PUBLIC has no EXECUTE on fn_iu_create / fn_iu_create_plan;
  • directus has EXECUTE or is owner with effective EXECUTE;
  • prosecdef=true, provolatile='v', search_path=pg_catalog,public for fn_iu_create.

If PUBLIC EXECUTE appears → phase FAIL / rollback if before COMMIT.

P6 — Verify trigger counts before and after inside report, not just after

P3-P1 must not create guards. Rev1 checks after count, but without pre-count it relies on memory from P3-P0.

Add pre-count and post-count in the same run:

  • IU trigger count before/after;
  • UV trigger count before/after;
  • both must match.

P7 — Final verdict must include POST_EXIT

Rev1 captures POST_EXIT but final verdict only checks PSQL_EXIT=0 and POST_STATUS=PASS.

Patch:

if [ $PSQL_EXIT -eq 0 ] && [ $POST_EXIT -eq 0 ] && [ "$POST_STATUS" = "PASS" ]; then ...

Also echo post_exit=$POST_EXIT.

P8 — Normalize POST_STATUS and initialize POST_EXIT

Add:

POST_EXIT=0

before post verify. If post verify does not run, make status NOT_RUN and POST_EXIT=0 or not_run consistently. Since post verify should run only on SQL success, final condition must reject NOT_RUN.

Normalize output to only PASS / CRITICAL / INVALID_OUTPUT.

P9 — Report marker leak test should be checked, not display-only

Inside SQL, after pilot test, the query:

SELECT COALESCE(current_setting('app.canonical_writer', true), '(not set)') AS marker_after_tx;

is display-only. Add a DO block or final query status that fails if marker leaks after the function call transaction.

Because the function call happens inside a DO block statement, the transaction-local marker should disappear after the statement. Gate it:

DO $$
BEGIN
  IF current_setting('app.canonical_writer', true) IS NOT NULL THEN
    RAISE EXCEPTION 'canonical_writer marker leaked: %', current_setting('app.canonical_writer', true);
  END IF;
END $$;

If this is still inside the same explicit transaction, be careful: set_config(..., true) persists until end of transaction, not end of function. Therefore if the pilot test is inside the same explicit transaction, the marker may remain until COMMIT. The correct leak test must run after COMMIT as a separate read-only query/session, not inside the transaction.

Patch: perform marker leak test in post-COMMIT query, not inside the main transaction.

P10 — Be precise about set_config(..., true) semantics

set_config(..., true) is transaction-local. If fn_iu_create is called inside a transaction that later performs direct inserts, the marker remains for the rest of that transaction. That is acceptable for P3-P2 because it authorizes IU and UV writes within the function transaction, but it is not a perfect security boundary.

Add wording in report/design:

  • marker is a wrong-door blocker, not a security boundary;
  • P3-P2 trigger guard must still rely on L3 detector / future role separation for privileged spoofing;
  • P3-P1 only prepares marker behavior.

P11 — readme_path should point to a README/policy doc, not only design note

Rev1 uses the design doc as readme path. That is acceptable as temporary but not ideal. Either:

  • set value to a future README path and create it in a later P3-P1b/P3-P2 step; or
  • name it policy_doc_path, not readme_path.

Recommended minimal patch:

  • keep iu_create.gateway.policy_doc_path = knowledge/.../design/22-p3-iu-creation-gateway-scope.md;
  • set iu_create.gateway.readme_path = knowledge/dev/laws/dieu44-trien-khai/readme/iu-create-gateway-readme.md as planned, even if README not created yet;
  • report README not yet created.

P12 — Full function body must not be copied from memory silently

Rev1 contains a compact full function body. That may be fine, but prompt should explicitly state:

  • function body is intended to preserve P2 semantics;
  • only semantic change is adding canonical writer marker;
  • Agent must not edit/improve/minify further;
  • any divergence from P2 behavior observed in tests is a failure.

Directive to Opus

Patch the P3-P1 prompt to rev2 with P1–P12.

Keep it reasonably compact, but do not remove transaction safety, uniqueness preflight, function idempotency, privilege verification, and post-commit marker leak verification.

Do not dispatch after patch; return for GPT/User review.

Hard boundaries remain

  • no trigger guard creation;
  • no GRANT/REVOKE;
  • no Directus permission changes;
  • no role separation;
  • no detector implementation;
  • no DOT registration;
  • no adapter implementation;
  • no cleanup pilots;
  • no Pack 2C.

Summary

P3-P1 is small, but it is still a mutation pack. The key risks are partial state, stale function-body clobbering, and assuming dot_config uniqueness. Rev2 should keep the speed but add just enough guardrails to avoid creating a new repair arc.

Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/reviews/gpt-review-22-p3-p1-policy-marker-prompt-rev1-2026-05-06.md