KB-29AA

22-P3-P0 — IU Creation Gateway Inspection Report

20 min read Revision 1
pack-22p3p0inspectionreportread-onlygateway

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, db directus Prompt: 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_unitdirectus
  • public.unit_versiondirectus (also sandbox_tac.unit_versionworkflow_admin)
  • public.birth_registrydirectus
  • public.dot_configdirectus

Granted table privileges on IU and UV (1.2, 1.3):

  • directus has full DML+TRIGGER+TRUNCATE+REFERENCES (with grant option)
  • context_pack_readonly has 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=directus
  • fn_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_registry is registered in directus_collections (accountability=all, versioning=f).
  • information_unit and unit_version are NOT present in directus_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.
  • create permission on both IU and UV is granted to Agent and AI Agent policies (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 → 1
  • agent: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 (creators agent: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 — defines fn_iu_create itself (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 target tac_publication, tac_logical_unit, tac_unit_version (sandbox_tac schema), NOT public.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 into public.information_unit or public.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_unit0 hits
  • INSERT INTO information_unit0 hits
  • UPDATE public.information_unit0 hits
  • INSERT INTO public.unit_version0 hits
  • INSERT INTO unit_version0 hits
  • UPDATE public.unit_version0 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 — repeated INSERT 0 1 lines but they are output of tac_* 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 (+ corresponding trg_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_roles admin/app flags (2.3), dot_tools schema (5.5), system_health_checks IU 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:

  1. B first (P3-P1) — Land L0 policy keys (iu_create.gateway.mode, iu_create.canonical_writer_id, iu_create.exempt_list, etc.) in dot_config. No enforcement. Idempotent and reversible. Readiness: immediate.
  2. D step 1 (P3-P2) — Patch fn_iu_create to call PERFORM set_config('app.canonical_writer', 'fn_iu_create', true) at function entry (transaction-local). Verify pilot rows still create. No trigger yet.
  3. D step 2 (P3-P3) — Add trg_iu_a_canonical_guard BEFORE INSERT on information_unit (and a parallel guard on unit_version) that raises if app.canonical_writer is not in the allow-list from dot_config (Option B keys). Alphabetically prefixed _a_ to fire before birth_gate_layer1. Add the same guard to UV (zero existing triggers).
  4. C deferred — Before designing L3 detector, run a follow-up read of system_health_checks columns; then design IU-bypass detector to run via dot-hc-executor (no pg_cron).
  5. 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 directus role 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)

Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/reports/22-p3-p0-iu-creation-gateway-inspection-report.md