GPT Review — 22-P3-P1 Policy + Canonical Marker Prompt rev2
GPT Review — 22-P3-P1 Policy + Canonical Marker Prompt rev2
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.mdrev2
Verdict
Do not dispatch rev2. Rev3 required.
Rev2 added the right guardrails in concept, but one blocker remains: the idempotent guard still does not work. The prompt says it will skip CREATE OR REPLACE if already patched, but the SQL still always executes CREATE OR REPLACE FUNCTION unconditionally.
This is exactly the stale-body clobber risk we need to avoid.
What is good
- Main mutation is wrapped in
BEGIN/COMMIT. - Policy keys are explicit.
policy_doc_pathandreadme_pathare split.- Post-COMMIT marker leak test exists.
- Final verdict checks
POST_EXIT. - Marker as speed bump, not security boundary, is documented.
- Function body includes marker before IU/UV writes.
Required rev3 patches
P1 — Make idempotent guard real: do not replace if already patched
Current rev2 says:
IF prosrc LIKE '%canonical_writer%' THEN ... RETURN; END IF;
...
CREATE OR REPLACE FUNCTION public.fn_iu_create(...)
But PostgreSQL cannot conditionally skip a top-level CREATE OR REPLACE using a prior DO block. The CREATE OR REPLACE still runs every time.
Patch using two script branches at the shell level:
-
If
ALREADY_PATCHED=t: run a verify-only SQL path.- seed/verify policy keys;
- do NOT
CREATE OR REPLACE FUNCTION; - run pilot test;
- run post-COMMIT verify;
- report
function_replace=SKIPPED_ALREADY_PATCHED.
-
If
ALREADY_PATCHED!=t: run patch SQL path.- seed policy keys;
CREATE OR REPLACE FUNCTIONexact signature;- run pilot test;
- run post-COMMIT verify;
- report
function_replace=EXECUTED.
This can be implemented as two heredocs or by generating a temporary SQL file. Keep it simple but explicit.
P2 — LEAK_RESULT must be initialized
Rev2 echoes marker_leak=$LEAK_RESULT. If PSQL_EXIT != 0, LEAK_RESULT may be unset under set -u, causing shell failure before report.
Patch:
LEAK_RESULT="NOT_RUN"
at initialization.
P3 — Final verdict should require marker leak test result
If PSQL_EXIT=0, marker leak test should return (not set). Include it in final verdict:
if [ $PSQL_EXIT -eq 0 ] && [ $POST_EXIT -eq 0 ] && [ "$POST_STATUS" = "PASS" ] && [ "$LEAK_RESULT" = "(notset)" ]; then ...
Depending on whitespace removal, use the exact normalized value. If query returns (not set) and spaces are stripped, the expected value is (notset).
Better: make SQL return a machine status:
SELECT CASE WHEN current_setting('app.canonical_writer', true) IS NULL THEN 'PASS' ELSE 'LEAK' END;
Then use LEAK_STATUS=PASS/LEAK/INVALID_OUTPUT in final verdict. Preferred.
P4 — dot_config.key unique check should require exactly one guard, not at least one
Rev2 accepts v_c < 1. If there are multiple unique constraints/indexes involving key, ON CONFLICT may still work, but for governance consistency we should avoid ambiguous schema.
Patch to exactly one recognized guard. If support for unique index is needed, explicitly count it separately. Current system likely has one constraint.
P5 — Duplicate gateway keys check should actually check duplicates
Rev2 duplicate check just counts rows:
SELECT count(*) FROM public.dot_config WHERE key LIKE 'iu_create.gateway.%';
That does not detect duplicates.
Patch:
SELECT count(*) INTO v_c
FROM (
SELECT key
FROM public.dot_config
WHERE key LIKE 'iu_create.gateway.%'
GROUP BY key
HAVING count(*) > 1
) d;
IF v_c <> 0 THEN RAISE EXCEPTION 'duplicate gateway keys: %', v_c; END IF;
P6 — already_patched should not use broad %canonical_writer% alone
Broad source grep can false-positive on comments or old text. At minimum, check exact marker call:
prosrc LIKE '%set_config(''app.canonical_writer'', ''fn_iu_create'', true)%'
Because formatting may vary, also accept equivalent whitespace lightly, but do not use only generic canonical_writer.
For rev3, exact call string is acceptable because this prompt controls the function body. If already patched by future evolved function with different formatting, it should avoid replace and report UNKNOWN_PATCH_STATE rather than clobber.
Recommended shell preflight query returns one of:
PATCHED_EXACTUNPATCHEDUNKNOWN_PATCH_STATE
If UNKNOWN_PATCH_STATE, STOP and report; do not replace.
P7 — Verify-only branch must still create a new pilot carefully
If already patched, pilot creation remains okay, but the prompt should make clear this creates another P3-P1 pilot row. If repeated many times, it will accumulate pilots. That is acceptable only with user approval for rerun.
Add report field:
idempotent_branch=verify_only/create_patchpilot_created=true/false
Optionally, in verify-only branch, use fn_iu_create_plan only unless user explicitly wants another pilot. Simpler for rev3: keep pilot creation, but report it.
P8 — Post-COMMIT verify should also confirm policy keys exist
Since policy keys are part of P3-P1, post success should verify the 9 keys exist after COMMIT. Add a read-only post check or include in main SQL and report. If skipped function branch still seeds keys, same check applies.
P9 — Permissions check should machine-fail if PUBLIC appears
Rev2 displays routine_privileges but does not assert. Add DO block before COMMIT:
DO $$
BEGIN
IF EXISTS (... grantee='PUBLIC' ... fn_iu_create/fn_iu_create_plan ...) THEN
RAISE EXCEPTION 'PUBLIC EXECUTE present on IU functions';
END IF;
END $$;
Also verify directus effective execute.
Directive to Opus
Patch P3-P1 prompt to rev3 with P1–P9.
The critical patch is P1: branch at shell level so already-patched state cannot still run CREATE OR REPLACE.
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
Rev2 is closer but still violates the stated idempotency guarantee. If the function is already patched or has evolved, rerunning the prompt would overwrite it with the inline body. Rev3 must split execution paths and avoid CREATE OR REPLACE in the already-patched branch.