22-P3-P0 — IU Creation Gateway Inspection Report
22-P3-P0 — IU Creation Gateway Inspection Report
Date: 2026-05-06T05:33:37Z Executor: Claude (Opus 4.7) on VPS 38.242.240.89, container
postgres, dbdirectusPrompt: rev3 (knowledge/dev/laws/dieu44-trien-khai/prompts/22-p3-p0-iu-creation-gateway-inspection-prompt.md) inspection_status: PARTIAL (3/51 queries failed — all on optional auxiliary tables; core evidence COMPLETE) queries_run: 51 queries_failed: 3 (2.3, 5.5, 8.2 — all schema-mismatch on optional tables) log_path: /tmp/22-p3-p0.20260506-053337.log (VPS)
§1 PG Ownership and Privileges
Table owners (1.1):
public.information_unit→directuspublic.unit_version→directus(alsosandbox_tac.unit_version→workflow_admin)public.birth_registry→directuspublic.dot_config→directus
Granted table privileges on IU and UV (1.2, 1.3):
directushas full DML+TRIGGER+TRUNCATE+REFERENCES (with grant option)context_pack_readonlyhas SELECT only- No other roles directly granted.
EFFECTIVE table privileges (1.4):
| Role | iu_insert | iu_update | iu_delete | uv_insert | uv_update | uv_delete |
|---|---|---|---|---|---|---|
| context_pack_readonly | f | f | f | f | f | f |
| directus | t | t | t | t | t | t |
| incomex | f | f | f | f | f | f |
| workflow_admin | t | t | t | t | t | t |
EFFECTIVE function EXECUTE on fn_iu_create / fn_iu_create_plan (1.5):
- directus: t / t
- workflow_admin: t / t (superuser)
- context_pack_readonly, incomex: f / f
Function metadata (1.6):
fn_iu_create: SECURITY DEFINER, volatile, search_path=pg_catalog,public, owner=directusfn_iu_create_plan: SECURITY DEFINER, stable, search_path=pg_catalog,public- All other helpers (
fn_content_hash,fn_iu_classify_existing,fn_iu_create_preflight,fn_iu_resolve_default,fn_iu_verify_invariants): SECURITY INVOKER (prosecdef=f), stable/immutable fn_birth_registry_auto: SECURITY INVOKER, volatile
Granted EXECUTE on IU functions (1.7): only directus is grantee. No other explicit grants. workflow_admin execution comes from superuser bypass, not explicit grant.
PUBLIC grants on IU functions (1.8): none — clean.
All PG roles (1.9): context_pack_readonly, directus, incomex, workflow_admin (superuser).
Reading: Privileges are wide on directus (the writing role used by Directus itself, and the role that Directus container connects as per 2.5). Direct INSERT/UPDATE/DELETE on IU and UV is fully open to directus — there is currently NO role-level barrier between canonical (fn_iu_create) and direct-write paths. Option E (full role separation) would require introducing a new low-privilege Directus role and revoking direct DML from directus, which is high blast radius.
§2 Directus Dependency
Directus collection metadata (2.1):
birth_registryis registered indirectus_collections(accountability=all, versioning=f).information_unitandunit_versionare NOT present indirectus_collections— they are PG-native tables, not first-class Directus collections.
Directus permissions (2.2): 16 rows for IU/UV (create + read for Agent, AI Agent, plus read for Editor, Public, $t:public_label).
- Even though IU/UV are not in
directus_collections, Directus permissions reference them by name. createpermission on both IU and UV is granted toAgentandAI Agentpolicies (fields={"*"}). This is the only Directus-side write surface.
Directus roles admin/app flags (2.3): QUERY FAILED — column admin_access does not exist on directus_roles in this Directus 11.5.1 schema. (Note: in 11.x admin/app access moved to directus_policies.admin_access / directus_access.) Optional, does not block recommendation.
Directus fields for IU (2.4): 0 rows. No field-level Directus customisation.
PG role used by Directus (2.5): directus / directus / db directus. Confirmed.
Reading: Directus app-layer is a real but narrow dependency: it grants create on IU/UV to two policies (Agent, AI Agent). Removing/restricting these app-permissions would block the Directus REST/GraphQL write path without affecting fn_iu_create (which runs in PG). However, since IU/UV are NOT in directus_collections, the Directus admin UI does not natively render them — most agent writes likely come via REST/GraphQL or direct PG, not via Studio UI.
§3 Existing Triggers / Gates
Triggers on information_unit (3.1, 3.4):
| trigger | timing | events | function | enabled | constraint |
|---|---|---|---|---|---|
| trg_iu_birth_gate_layer1 | BEFORE | INSERT | fn_iu_birth_gate_layer1 | O | f |
| trg_birth_information_unit | AFTER | INSERT | fn_birth_registry_auto | O | f |
| trg_iu_birth_gate_layer2 | AFTER | INSERT|UPDATE | fn_iu_birth_gate_layer2 | O | t |
| trg_iu_updated_at | BEFORE | UPDATE | fn_iu_updated_at | O | f |
Triggers on unit_version (3.2): 0 rows. No triggers on UV. UV has neither a birth-gate nor a creation guard.
Birth trigger function (3.3): fn_birth_registry_auto reads _dot_origin from NEW, falls back to 'PG:trg_birth_' || TG_TABLE_NAME, then INSERTs into birth_registry ON CONFLICT DO NOTHING. Pure fire-and-forget — does not check canonical_writer.
Reading: A BEFORE INSERT slot on information_unit is already occupied by trg_iu_birth_gate_layer1. Any L2 trigger-guard would be an additional BEFORE INSERT trigger, alphabetically named to fire BEFORE or AFTER the existing layer1 (PG ordinary triggers fire alphabetically by name within same timing/event). UV has zero existing triggers — a UV-side guard is feasible without ordering conflicts.
§4 Existing IU Rows and Provenance
Counts (4.1):
- information_unit: 2
- unit_version: 2
- birth_registry total: 81,224 (this is multi-collection — the 2-row IU set covers the whole IU portion, see 4.2)
Birth coverage (4.2): total_iu=2, iu_with_birth=2, iu_missing_birth=0. All IUs have birth rows.
Duplicate births (4.3): 0 rows. No duplicates.
created_by (4.4):
agent:p2b-p1→ 1agent:p2-test→ 1
identity_profile keys (4.5): title, primary_section_type_ref, owner_lookup_ref (each 2/2 rows). No provenance keys.
Provenance markers (4.6): has_created_via=0, has_source=0, has_origin=0, has_pipeline=0 / total=2. No provenance markers stored in identity_profile.
P2 pilot rows (4.7): 1 row → pilot.p2.20260506-045033.e0ae7ec5.
Sample addresses (4.8):
pilot.iu0.test-001(agent:p2b-p1, design_doc_section)pilot.p2.20260506-045033.e0ae7ec5(agent:p2-test, design_doc_section)
Invariant check (4.9a — IU_COUNT=2, ALL rows checked, NOT a sample):
| canonical_address | all_pass | i1 | i2 | i3 | i4 | i5 |
|---|---|---|---|---|---|---|
| pilot.iu0.test-001 | true | t | t | t | t | t |
| pilot.p2.20260506-045033.e0ae7ec5 | true | t | t | t | t | t |
Reading: IU dataset is fully clean (2 pilot rows, both pass all 5 invariants, both have birth rows, no duplicates, no orphans). Both rows came from documented pilot prompts (P2B-P1 and P2-test). Production IU population is effectively zero — gateway can be enforced now with minimal blast radius.
§5 Policy Registry Location
dot_config columns (5.1): key text NOT NULL, value text NOT NULL, description text, updated_at timestamptz NOT NULL. Simple kv table.
Gateway-related keys (5.2): 0 rows — no iu_create.*, creation.*, gateway.*, canonical.* keys defined yet.
dot_config inventory (5.3, 5.3b): 35 total keys; sample shows context_pack_*, description_*, desc_template_*, hc_executor_*, etc. — all generic governance, no gateway policy yet.
collection_registry (5.4, 5.4b): EXISTS. Contains entries for both information_unit (id=184, COL-176) and unit_version (id=185, COL-177). Both have migration_state='pilot', governance_role='observed', storage_role='primary', source_kind='native'. Key field: birth_code_strategy is synthetic_id for IU and subordinate for UV.
dot_tools (5.5): QUERY FAILED — column tool_id does not exist on dot_tools. Schema differs from prompt's assumption. Optional.
Reading: dot_config is the natural home for gateway policy keys (e.g., iu_create.gateway.mode, iu_create.exempt_list, iu_create.canonical_writer_required) — schema is text/text and supports it. collection_registry already classifies IU/UV as pilot/observed, signalling they are not yet under enforced governance.
§6 TAC / P10 Direct Insert Inventory
SQL evidence (6.1, 6.2):
- All 2 IU rows fall under address pattern
pilot(creatorsagent:p2b-p1,agent:p2-test). - 0 non-pilot/test rows.
- No TAC-numeric or production rows present.
KB search for TAC IU creation INSERT information_unit:
19-p2b-p1-iu-pilot-insert-and-birth-fire-prompt.md— P2B-P1 pilot prompt (1 IU + 1 UV in a transaction). test-pilot.22-p2-iu-native-create-main-functions-prompt.md— definesfn_iu_createitself (canonical writer body). production / canonical.dieu44-trien-khai/design/22-p3-iu-creation-gateway-scope.md— design discussion of L0–L4. (no INSERT)dieu38-trien-khai/reports/p10b-2c-r2-d28-execute-artifacts-2026-04-30/*— P10B writes targettac_publication,tac_logical_unit,tac_unit_version(sandbox_tac schema), NOTpublic.information_unit/public.unit_version. Not in scope of IU/UV gateway.
KB search for P10A P10B direct INSERT:
- All hits target
tac_*tables. No P10 path inserts directly intopublic.information_unitorpublic.unit_version.
Classification:
| Source | Type | Target | Status |
|---|---|---|---|
| 22-p2 prompt | production / canonical | fn_iu_create body |
active canonical writer |
| 19-p2b-p1 prompt | test-pilot | direct INSERT into IU+UV (single transaction) | retired after pilot ran 2026-05-05 |
| P10A/P10B (Đ38) | TAC-import | sandbox_tac.tac_* only |
OUT OF SCOPE — different tables |
Reading: The only documented direct-write path into public.information_unit / public.unit_version is the P2B-P1 pilot prompt, which is one-shot test code (not a recurring production pipeline). All future production writes are expected to go through fn_iu_create. P10/TAC paths target separate sandbox_tac tables and do not touch the canonical IU/UV tables.
§7 Global Direct-write Inventory
Filesystem grep (7.1) — patterns searched against /home/, /opt/, /root/ (.sh, .sql, .md, .py, .js, .ts):
INSERT INTO public.information_unit— 0 hitsINSERT INTO information_unit— 0 hitsUPDATE public.information_unit— 0 hitsINSERT INTO public.unit_version— 0 hitsINSERT INTO unit_version— 0 hitsUPDATE public.unit_version— 0 hits
No timeouts. Note: search restricted to /home/, /opt/, /root/ and the listed extensions; ad-hoc CLI runs and content stored elsewhere are not covered.
KB search for INSERT INTO information_unit / INSERT INTO unit_version:
- Same 2 hits as §6: P2B-P1 pilot prompt (test) + 22-P2 fn_iu_create body (canonical).
dieu38-trien-khai/.../d28-insert-output.log— repeatedINSERT 0 1lines but they are output oftac_*inserts, not IU/UV.
Reading: No direct-write call sites for public.information_unit / public.unit_version exist on disk under the searched roots, and no production scripts in KB target those tables. Gateway enforcement would not break any known caller. Note this is a near-empty observation — the gateway is being built before production callers exist.
§8 Detector / Auxiliary Coverage
system_health_checks table (8.1): EXISTS.
IU/birth/orphan health checks (8.2): QUERY FAILED — column check_name does not exist on system_health_checks. Schema differs from prompt's assumption. Partial data — cannot enumerate existing health checks for IU.
Detector functions (8.3):
fn_refresh_orphan_col,fn_refresh_orphan_dot,fn_refresh_orphan_species(+ correspondingtrg_fn_refresh_orphan_*)fn_registry_health
These cover collection/DOT/species registry orphans — none are IU-specific.
pg_cron (8.4): NOT INSTALLED. Periodic detector would need an external scheduler (e.g., dot-hc-executor, cron host-side).
Reading: No IU-specific orphan / ghost / non-canonical-creation detector exists. Generic fn_registry_health and orphan-refresh functions cover registry tables, not IU rows. Building a Layer-3 detector for IU bypass would be greenfield, and would require a host-side scheduler since pg_cron is absent — likely re-using the dot-hc-executor framework already on VPS.
§9 Trigger-guard Feasibility
current_setting('app.canonical_writer') (9.1): (not set) — never written.
Existing BEFORE INSERT triggers on IU (9.2): 1 — trg_iu_birth_gate_layer1 (fn_iu_birth_gate_layer1, enabled).
fn_iu_create canonical_writer check (9.3): NO — does NOT set canonical_writer.
Trigger order context (9.4): same as §3 — 4 triggers, alphabetical order applies.
Key finding: fn_iu_create does not call set_config('app.canonical_writer', ...). Therefore an L2 trigger-guard that requires current_setting('app.canonical_writer')='fn_iu_create' (or similar) would BLOCK fn_iu_create itself.
Sequencing requirement: Patch fn_iu_create to set canonical_writer FIRST, then add the trigger guard. Two separate prompts/changes. Reverse order would brick the canonical path.
Additionally, alphabetical ordering: a guard named e.g. trg_iu_a_canonical_guard would fire before trg_iu_birth_gate_layer1, which is desirable (reject non-canonical INSERT before any side-effect computation).
UV has zero triggers — adding a guard there is unconstrained by ordering.
Query Errors / Partial Data
| id | label | error | impact |
|---|---|---|---|
| 2.3 | Directus roles admin/app | column admin_access does not exist |
Low — Directus 11.x moved this to directus_policies.admin_access; superuser/app-access can be re-derived if needed. |
| 5.5 | dot_tools registry IU-related | column tool_id does not exist |
Low — dot_tools schema differs from prompt's assumption. Recommendation does not depend on it (dot_tools is not the gateway policy store; dot_config is). |
| 8.2 | Health checks IU-related | column check_name does not exist |
Medium — cannot enumerate IU-specific health checks. Detector option (C) confidence reduced; would need a follow-up read of system_health_checks actual columns before designing L3. |
All 3 failures are on optional auxiliary inventory queries with stale column assumptions; none touch the core gateway design evidence (privileges, triggers, function bodies, IU rows, dot_config, file/KB writes).
§10 Risk / Recommendation Matrix (ADVISORY ONLY)
| Option | Breakage risk | Bypass reduction | Complexity | Hardcode risk | Rollback | Readiness |
|---|---|---|---|---|---|---|
| A Observe-only (log non-canonical writes; no enforcement) | Very low — no behavioural change. | None — bypass continues. | Low — add audit table + AFTER INSERT logger. | Low. | Trivial — drop trigger. | Ready immediately. |
B L0 policy only (publish iu_create.gateway.* keys in dot_config; document canonical path; no enforcement) |
None. | None directly; only normative. | Very low. | Low. | Trivial. | Ready immediately (dot_config schema supports it; gateway keys absent). |
| C L0 + L3 detector (policy + periodic IU-bypass detector via host scheduler, since pg_cron absent) | Low. | Detect-only, eventual reconcile. | Medium — needs detector design + scheduler hookup; need to re-inspect system_health_checks columns. |
Low. | Medium — disable scheduler, drop checks. | Partial — 8.2 failed, schema unknown. Need follow-up before designing. |
D L0 + L2 trigger guard (BEFORE INSERT trigger requires app.canonical_writer GUC; fn_iu_create patched to set it) |
Medium — must patch fn_iu_create FIRST or canonical writes break. Need 2-step ordered deploy. | High — blocks any direct PG INSERT (incl. ad-hoc DML by directus role) unless the GUC is set. | Medium. | Medium — requires hardcoding writer name in trigger. Use dot_config-driven allow-list to mitigate. |
Easy — drop trigger; revert fn_iu_create patch. | Ready for design (sequencing constraint clear, no callers to break per §7). |
E Full role separation (revoke direct DML from directus; grant only EXECUTE on fn_iu_create; create dedicated low-priv role) |
High — Directus REST/GraphQL create permissions for Agent/AI Agent policies (16 rows in 2.2) write directly via directus role; revoking would break them. Studio admin DML also affected. |
Highest. | High — must rewrite Directus permission layer to call fn via flow, or introduce a wrapper. | Low. | Hard — re-grant carefully. | Not ready — requires Directus permissions redesign first. |
Confidence note (PARTIAL inspection)
- Core evidence (privileges, triggers, function bodies, IU dataset, KB/FS direct-write search, dot_config) is complete and consistent. Recommendations C/D/E are well-grounded.
- Auxiliary evidence missing:
directus_rolesadmin/app flags (2.3),dot_toolsschema (5.5),system_health_checksIU rows (8.2). Of these, only 8.2 affects a recommendation (Option C — L3 detector design).
Recommendation (advisory)
Adopt Option B + D in two prompts, deferring C until the system_health_checks schema is re-read.
Sequencing:
- B first (P3-P1) — Land L0 policy keys (
iu_create.gateway.mode,iu_create.canonical_writer_id,iu_create.exempt_list, etc.) indot_config. No enforcement. Idempotent and reversible. Readiness: immediate. - D step 1 (P3-P2) — Patch
fn_iu_createto callPERFORM set_config('app.canonical_writer', 'fn_iu_create', true)at function entry (transaction-local). Verify pilot rows still create. No trigger yet. - D step 2 (P3-P3) — Add
trg_iu_a_canonical_guardBEFORE INSERT oninformation_unit(and a parallel guard onunit_version) that raises ifapp.canonical_writeris not in the allow-list fromdot_config(Option B keys). Alphabetically prefixed_a_to fire beforebirth_gate_layer1. Add the same guard to UV (zero existing triggers). - C deferred — Before designing L3 detector, run a follow-up read of
system_health_checkscolumns; then design IU-bypass detector to run viadot-hc-executor(no pg_cron). - E not now — defer until Directus app-layer is rewired to call fn_iu_create via flow. Otherwise the 16 Directus permissions in 2.2 break.
Rationale:
- §7 confirms zero on-disk direct-write callers → enforcement (D) has near-zero breakage.
- §4 shows IU dataset is 2 clean pilot rows → no migration burden.
- §9 confirms the canonical_writer GUC is unset and fn_iu_create does not set it → the sequencing trap (patch function first) is documented.
- §1 shows
directusrole still needs DML for now (Directus REST writes) → role-separation (E) is premature. - §8 partial → C deferred, reducing reliance on uncertain evidence.
Confidence: Medium-high for B/D, Medium for the detector deferral (need 8.2 re-read to confirm).
22-P3-P0 inspection report | 2026-05-06 | READ-ONLY | inspection_status=PARTIAL (3 optional failures, core evidence complete)