GPT Review — P3D4C2U BLOCKED and Option D Directive
GPT Review — P3D4C2U Implementation BLOCKED and Option D Directive
Date: 2026-05-08
Reviewer: GPT-5.5 Thinking / Incomex Hội đồng AI
Reviewed:
knowledge/dev/laws/dieu44-trien-khai/reports/23-p3d4c2u-dot-table-registration-implementation-report.mdknowledge/dev/laws/dieu44-trien-khai/reviews/opus-review-23-p3d4c2u-implementation-blocked-2026-05-08.md
Verdict
P3D4C2U execution BLOCKED confirmed.
Agent did the correct thing: hit the DIRECTUS_VIEW_PK_BLOCKED condition, stopped, rolled back cleanly, and left no residual state.
Opus review is accepted with one additional GPT safety requirement for Option D.
What happened
The implementation attempted the previously approved path:
PG view v_event_outbox_table
→ Directus collection/fields/permission
→ table_registry row
→ DirectusTable generic render
PG side worked:
- view created with 13 metadata columns;
payload_classificationincluded;- unsafe fields omitted;
- grants applied to
directus,incomex,context_pack_readonly.
Directus API metadata calls partially worked:
- collection metadata created;
- read permission created;
- 13 fields seeded.
But runtime data access failed:
GET /items/v_event_outbox_table → 403
Root cause:
Directus/knex-schema-inspector does not recognise the uuid `id` projected through a PG view as a primary key.
This is a Directus + PG view introspection limitation, not an Agent implementation bug.
Rollback was clean:
- view dropped;
- collection deleted;
- permission deleted;
- 13 field rows deleted;
- registry row was never inserted;
- residual probe = 0;
- P3D4C1U event core untouched;
- IU runtime untouched.
Historical precedent accepted
Opus found prior history: the same class of issue happened around v_registry_counts.
The working convention became:
Do not force Directus to treat a PG view as a table when PK introspection fails.
Use a real table with a real PK, or expose the base table when it already has the correct PK.
For event_outbox, the base table already has a real id uuid PRIMARY KEY, so the simplest convention-compliant path is Option D:
Directus collection = event_outbox base table
Directus fields permission = explicit allowlist of metadata columns only
Table Module registry = tbl_event_outbox points to event_outbox
No PG view
No materialized view
No worker/cron/refresh
No custom Directus extension
No Nuxt code
Decision
Approve Option D as the next direction for prompt drafting.
Do not implement immediately. Opus should draft a new implementation prompt for GPT/User review.
Why Option D is preferred
Option D follows the operating lesson from P3D4C1U:
Choose the simplest sufficient reliable mechanism.
Option A materialized view is safer at schema-masking level but introduces refresh semantics, possible staleness, and likely worker/cron pressure.
Option E custom Directus extension is too heavy and creates runtime extension burden.
Option C companion table adds a new write path.
Option B surrogate serial key is unstable and unsuitable.
Option D uses the existing base table and Directus field-level allowlist. It is the smallest viable path and aligns with prior Directus PK convention.
GPT safety addition for Option D
Option D is acceptable only if field exposure is guarded at both layers:
- Directus permission field allowlist must include only approved metadata fields.
- Table Module/table_registry field config must request only approved metadata fields.
Additionally, implementation must verify how DirectusTable fetches data:
- If it uses end-user/Public Access Directus permissions, field-level permission is the main protection.
- If it uses an elevated server/admin token, table_registry field config becomes the active fetch allowlist, and that must be proven safe.
- If DirectusTable fetch path cannot be determined, STOP and report
DIRECTUS_TABLE_FETCH_PATH_UNCLEAR.
No unsafe fields may appear in API or table response:
payload
safe_payload
body
raw_payload
vector
embedding
secret
token
password
correlation_id
causation_id
ssn
personal_data
Directive to Opus — draft Option D implementation prompt, do not dispatch
Create new prompt:
knowledge/dev/laws/dieu44-trien-khai/prompts/23-p3d4c2u-option-d-base-table-field-allowlist-implementation-prompt.md
Future report:
knowledge/dev/laws/dieu44-trien-khai/reports/23-p3d4c2u-option-d-base-table-field-allowlist-implementation-report.md
Do not reuse the PG view implementation prompt as-is. The new prompt must explicitly abandon the view path for this pack.
Required prompt scope
Step 0 — Preflight / history / residual check
- Read P3D4C2U BLOCKED report.
- Read Opus BLOCKED review.
- Confirm rollback residuals still zero:
- no
v_event_outbox_table; - no
tbl_event_outboxrow; - no Directus collection/field/permission residue for the view.
- no
- Confirm
event_outboxbase table exists and has real PKid. - Confirm Directus already introspects
event_outboxcollection. - Confirm no PG view will be created.
Step 1 — Table Module and fetch path safety
- Smoke
tbl_system_issues/ system_issues as before. - Determine how
DirectusTablefetches data:- user/Public Access token;
- Directus app role;
- server/admin token;
- unknown.
- If unknown, STOP.
- Verify table_registry fields are used as
fields=selector in DirectusTable request. - If DirectusTable does not constrain fields, STOP until Table Module is fixed.
Step 2 — Directus permission allowlist for base table
Use existing Directus app role convention from system_issues / registry tables.
Create/update read permission for collection event_outbox with fields allowlist only.
Approved fields:
id
occurred_at
created_at
event_domain
event_type
event_stream
delivery_lane
event_severity
event_subject_table
event_subject_ref
canonical_address
actor_ref
source_system
payload_classification
Do not expose:
safe_payload
payload
correlation_id
causation_id
plus the broader unsafe denylist above.
No create/update/delete permissions.
Step 3 — Directus field metadata
Patch labels for approved fields only. Preserve unrelated settings. Do not touch unsafe fields except to ensure hidden/omitted if convention allows.
Step 4 — table_registry row
Create/update tbl_event_outbox using exact live table_registry schema and tbl_system_issues precedent.
Collection should be:
event_outbox
not v_event_outbox_table.
Fields config must include only approved fields. It must not include safe_payload, payload, correlation_id, causation_id.
Initial status = draft unless route smoke is verified.
Step 5 — Smoke tests
GET /items/event_outbox?limit=5&fields=<approved_fields>returns 200 or empty array.- Same request without explicit unsafe fields does not include unsafe fields.
- Attempt
GET /items/event_outbox?fields=safe_payloadunder intended role must be denied or omit field. - Write attempts to
event_outboxdenied. table_registryrow exists and referencesevent_outbox.system_issuesstill works.- No Nuxt code changed.
- IU runtime unchanged.
- Event core tables/triggers/functions unchanged.
Step 6 — Publish gate
Publish tbl_event_outbox only if:
- system_issues smoke passes;
- base table read smoke passes;
- unsafe field deny smoke passes;
- route smoke is verified.
If route smoke cannot be automated:
registry_final_status=draft
recommendation=READONLY_EXPOSURE_DRAFT_PENDING_ROUTE_SMOKE
Step 7 — Rollback
Rollback only objects changed by this pack:
- remove/restore
event_outboxread permission if created/changed by pack; - remove table_registry row if created by pack;
- revert field metadata only if changed by pack and safe to do;
- do not touch
event_outboxdata; - do not touch P3D4C1U triggers/functions;
- do not touch IU runtime.
Required report fields for future execution
phase_status=PASS|FAIL|BLOCKED
previous_view_blocked_confirmed=PASS|FAIL
rollback_residual_zero=PASS|FAIL
display_source=BASE_TABLE_FIELD_ALLOWLIST
event_outbox_pk_detected=PASS|FAIL
directus_event_outbox_collection_visible=PASS|FAIL
directus_table_fetch_path=USER_ROLE|PUBLIC_ACCESS|SERVER_ADMIN|UNKNOWN
directus_table_uses_fields_selector=PASS|FAIL|UNKNOWN
field_allowlist_count=<number>
unsafe_fields_in_permission=false
unsafe_fields_in_table_registry=false
unsafe_fields_denied_by_directus=PASS|FAIL
system_issues_smoke=PASS|FAIL
route_smoke=PASS|CANNOT_VERIFY|FAIL
directus_read_permission_status=CREATED|ALREADY_PRESENT_COMPATIBLE|UPDATED_COMPATIBLE|CONFLICT_STOP
directus_write_denied=PASS|FAIL
table_registry_status=CREATED|ALREADY_PRESENT_COMPATIBLE|CONFLICT_STOP
registry_initial_status=draft
registry_final_status=draft|published|removed
published=YES|NO
publish_block_reason=NONE|ROUTE_SMOKE_CANNOT_VERIFY|SMOKE_FAIL|FIELD_LEAK|CONFLICT
no_pg_view_created=true
no_materialized_view_created=true
no_worker_cron=true
no_nuxt_code=true
no_bespoke_ui=true
no_iu_change=true
recommendation=READONLY_EXPOSURE_PUBLISHED|READONLY_EXPOSURE_DRAFT_PENDING_ROUTE_SMOKE|TABLE_MODULE_FETCH_PATH_REPAIR_REQUIRED|FIELD_LEAK_BLOCKED|BLOCKED
next_required_pack=<based_on_result>
Hard boundaries
- No PG view for this pack.
- No materialized view.
- No worker/cron/refresh.
- No new table/write path.
- No custom Directus extension.
- No Nuxt code.
- No bespoke notification UI.
- No Directus click-config.
- No Directus write/mark-read.
- No unsafe field exposure.
- No IU runtime change.
- No P3D4C1U event core mutation except Directus permission/field metadata/table_registry exposure.
- No Điều 43 machinery change.
Final directive
Opus should draft the Option D implementation prompt and return for GPT/User review. Do not dispatch implementation yet.