KB-13C2

23-P3D4 — Directus Notification Exposure — Design Review Note (rev1)

15 min read Revision 1
p3d4design-reviewdirectusnotificationinventoryjurisdictionrev1

23-P3D4 — Directus Notification Exposure — Design Review Note (rev1)

Date: 2026-05-08 Scope: Design review + read-only inventory. NO implementation. Source prompt: knowledge/dev/laws/dieu44-trien-khai/prompts/23-p3d4-directus-exposure-design-review-prompt.md (rev4) Reviewer: Claude Opus 4.7 (1M)


0. Mandatory Law Pre-Read

# File Status
1 knowledge/dev/laws/constitution.md (HP v4.6.3) READ
2 knowledge/dev/laws/law-07-assembly-first.md (Đ7) READ
3 knowledge/dev/ssot/data-connection-law.md READ
4 Đ28 (Luật Kỹ thuật Hiển thị v2.0) REFERENCED via constitution table; must be consulted before any human-facing display work
5 knowledge/dev/laws/dieu44-trien-khai/design/23-p3d-ui-boundary-directus-nuxt-assembly-note.md READ

Plus P3D context files (Step 1): 23-p3d3-user-notification-board-directus-exposure-design.md, iu-agent-front-door-context.md, 23-p3d2-notification-triggers-report.md, 23-p3d3-notification-context-directus-exposure-report.md — all READ.


0B. Law Jurisdiction Map

Tầng Thẩm quyền luật Ví dụ vi phạm P3D4 stance
PG runtime (notification) PG-first (Đ7), Assembly First, IU laws, Đ33 v2.1 Code Nuxt để bù PG PG đã ACTIVE (P3D2); P3D4 không mutate PG
Directus exposure Directus/DOT (Đ35 v5.2), data-connection-law, Đ36 (Collection Protocol), Đ32 (Approval) Click UI sửa config thay vì DOT P3D4 review only; UI view-only
Nuxt display Đ28 (Luật Hiển thị v2.0), TreeView/Vue Flow read-only, data-connection-law Cổng Thêm business logic vào Nuxt P3D4 không commit Nuxt code; existing assembly chỉ
Human interaction UI/display + data-connection (Cổng → Não → Kho) User sửa content qua Directus UI Read-only metadata board only
Agent/Codex dispatch User-approval, agent-operation rules Agent tự dispatch Codex Không dispatch trong P3D4
Hermes automation Review riêng — hermes_readiness=BLOCKED_PENDING_REVIEW (P3D2) Trộn Hermes vào exposure design Không trộn

Hard rule (Constitution + Đ7 §0-H + UI Boundary): "No layer may solve another layer's problem by bypassing that layer's law/tooling." — P3D4 stays inside design + inventory.


A. Inventory Results

A1. Directus inventory — inventory_directus_access=FULL_READ_ONLY

Method: Directus MCP read-only API (directus_health, directus_list_collections, directus_get_schema, directus_get_items). No new token created. No UI mutation. URL: https://directus.incomexsaigoncorp.vn.

Findings:

Object Result
Health ok
Collections discovered 210 user / 237 total
iu_notification_event PRESENT — listed in directus_list_collections (whitelisted, no description)
iu_notification_read PRESENT — listed in directus_list_collections (whitelisted, no description)
iu_notification_event schema fetch HTTP 403 FORBIDDEN — token role lacks read permission
iu_notification_event items fetch HTTP 403 FORBIDDEN — same
Existing flows/endpoints for IU notification None observed (collection list does not surface a notification-specific flow; no custom endpoints attested in P3D2/P3D3 reports)
PG view auto-discovery N/A — no notification PG view exists yet (see A2)

Interpretation: Directus has auto-registered both iu_* tables (consistent with PG schema sync), but no role permission grant has been configured for them. This is the correct posture pre-DOT package: tables visible to admin only, not exposed.

A2. PG inventory — inventory_pg_access=FULL_READ_ONLY

Method: SSH alias contabodocker exec postgres psql -U directus -d directus, read-only \d, \df, pg_proc, pg_trigger, information_schema.views. No DDL/DML executed. No secret created.

Findings (verbatim PG state on 2026-05-08):

Tables:

  • public.iu_notification_event (owner directus) — columns: id uuid PK, event_type text NN, event_stream text NN, unit_id uuid NN, canonical_address text NN, ref_id uuid, actor_ref text NN, source text NN, payload jsonb NN default '{}', created_at timestamptz NN default now().
    • Indexes: iu_notification_event_pkey, idx_notif_event_created, idx_notif_event_stream_created, idx_notif_event_unit_created, uq_notif_event_type_ref (UNIQUE partial WHERE ref_id IS NOT NULL).
    • Check constraints: chk_notif_event_actor_ref_nonempty, chk_notif_event_canonical_address_nonempty, chk_notif_event_source_nonempty, chk_notif_event_stream (comment|review|update), chk_notif_event_type (comment_added|draft_created|version_applied), chk_notif_event_type_stream (compound).
    • FK: fk_notif_event_unit (unit_id) → information_unit(id).
  • public.iu_notification_read (owner directus) — columns: id uuid PK, event_id uuid NN, actor_ref text NN, read_at timestamptz NN default now().
    • Indexes: iu_notification_read_pkey, idx_notif_read_actor_event, uq_notif_read_event_actor UNIQUE (event_id, actor_ref).
    • FK: fk_notif_read_event (event_id) → iu_notification_event(id) ON DELETE CASCADE.

Functions (all SECURITY DEFINER = t):

  • fn_iu_notif_comment()trigger
  • fn_iu_notif_draft()trigger
  • fn_iu_notif_version()trigger
  • fn_iu_unread(p_actor text, p_stream text, p_include_self boolean, p_limit integer)SETOF jsonb
  • fn_iu_mark_read(p_event_ids uuid[], p_actor text)jsonb
  • fn_iu_notification_board(p_actor text DEFAULT NULL, p_stream text DEFAULT NULL, p_limit integer DEFAULT 50)SETOF jsonb

Triggers (all AFTER INSERT FOR EACH ROW, tgtype=5):

  • trg_aa_iu_notif_comment on unit_edit_comment
  • trg_aa_iu_notif_draft on unit_edit_draft
  • trg_aa_iu_notif_version on unit_version

Views: SELECT … FROM information_schema.views WHERE table_name ILIKE '%notif%'0 rows. No PG view exists yet for notification board overview.

A3. Inventory assumptions documented

  • Directus token in MCP belongs to a non-admin role (item access denied). Schema/permission view of these collections at admin level was NOT performed in this session — admin-level audit deferred to package P3D4B unless surfaced earlier.
  • Hermes production daemon NOT inspected (out of scope; remains BLOCKED_PENDING_REVIEW).
  • No auth/identity mapping table (Directus user → actor_ref) was searched; the convention user:huyen is documented in the P3D3 design and front-door context only.

B. 8 Design Questions Answered

Q1. Directus native capability — what can be exposed without custom code? Both iu_notification_event and iu_notification_read are already auto-registered as Directus collections (confirmed in A1). With a DOT-applied permissions grant, Directus can natively expose them as read-only REST collections — no custom code. Filtering by actor_ref/event_stream/created_at is supported by Directus query language. Limitation: Directus collection access cannot invoke PG functions (fn_iu_unread, fn_iu_notification_board) because they take parameters; collection-level filters operate over base tables only.

Q2. PG primitive most appropriate.

  • Phase 1 board overview (no per-actor): a read-only PG VIEW (v_iu_notification_board or similar, NOT YET CREATED) joining iu_notification_eventiu_notification_read to surface latest_readers + per-event aggregate. Directus auto-discovers views as collections; adding a permissions-grant via DOT is sufficient.
  • Per-actor unread/board: requires PG function invocation with parameters → either (a) Directus custom endpoint/flow that calls fn_iu_* (DOT-managed), or (b) keep agent path (direct PG SQL) and provide humans only the overview view. (a) needs DOT support.
  • Materialized view: not justified now (write volume is small; freshness matters).
  • Custom Directus endpoint: only if assembly fails — see Q3.

Q3. fn_iu_notification_board via Directus without custom code? Not directly: Directus collections cannot call parameterised SETOF jsonb functions natively. Two assembly paths remain:

  • Directus Flow (operation: exec PostgreSQL/SQL or webhook to a small endpoint) — but Directus 11.5.1 lacks isolated-vm (per memory note), so script ops are constrained.
  • DOT-managed custom endpoint extension registered via Đ35 v5.2 (DOT Governance) — DOT-supported only. Alternative (recommended for human Phase 1): expose the view version (no per-actor parameter) to humans; keep fn_iu_* reserved for AI/Agent direct PG path (already working — front-door context).

Q4. Phase-1 human monitoring — is a PG read-only view sufficient? Yes. Required surface:

  • All events (id, type, stream, unit_id, canonical_address, actor_ref, created_at, payload meta keys);
  • Latest readers (top 5 actors by read_at desc) — joinable from iu_notification_read;
  • Read-state overview (count of distinct readers per event);
  • Originating actor (actor_ref of event). This fits a single view definition. Per-actor unread/inbox is deferred to a controlled second package (matches P3D2 boundary "no global read flag", and P3D3 staged plan).

Q5. Human actor identity. Convention from front-door context (verbatim): gpt, opus, agent:codex, reviewer:gpt, user:huyen. For Directus exposure, the mapping Directus user → actor_ref must be defined (e.g. via a Directus user-extra field or a deterministic rule). This mapping is NOT in scope for P3D4 — flagged as a P3D4B prerequisite. Until then, the read-only overview is actor-agnostic (no per-user filter), which is safe.

Q6. Human mark-read — defer Phase 1? Defer. Per P3D2 (no global read flag, per-actor only) and front-door context (Per-actor, implicit_self, explicit_read). Mark-read is a write action; allowing humans to write through Directus collides with the "user no content edit" boundary unless executed via a controlled, reviewed Directus custom endpoint that calls fn_iu_mark_read. That endpoint is a separate reviewed package (not P3D4).

Q7. Security / permissions.

  • Read-only at all surfaces.
  • Metadata-only default: surface event metadata + read-state aggregates; do not join information_unit.body / unit_version.body.
  • User MUST NOT be able to create/update/delete IU rows via Directus UI for these collections — permissions grant must be read only on iu_notification_event, iu_notification_read, and the future v_iu_notification_board.
  • PG function ownership already correct: SECURITY DEFINER, owner directus, no PUBLIC EXECUTE (P3D2 evidence).
  • No new Directus token; reuse existing approved access.

Q8. Nuxt boundary. No new Nuxt code. Notification surface, when displayed, must reuse existing Nuxt assembled screens reading from Directus REST/SDK. TreeView / Vue Flow read-only mode (data-connection-law) applies if a graph view is desired. Đ28 (Luật Hiển thị v2.0) governs any human-facing render and must be checked before display work commences. Đ33 §13 exception is not sought here.


C. Recommendation

Option C — Staged read-only board first via reviewed DOT package; mark-read later as a separate controlled action.

Rationale:

  • Aligns with P3D2 boundary (Phase-1 no global read flag, no human write) and P3D3 staged plan.
  • Limits initial blast radius to a single PG VIEW + a single Directus read permissions-grant (both DOT-managed).
  • Defers the harder problem (Directus user → actor_ref mapping, parameterised function invocation, write action fn_iu_mark_read) to a second review with a clear gate.
  • Preserves AI/Agent path unchanged (direct PG fn_iu_*).

(Options A and B are subsumed: A = the read-only piece of C; B = the mark-read piece, deferred.)


D. Body Content Exposure Decision

body_content_exposure=NOT_INCLUDED
metadata_only_exposure_default=PASS

Rationale: iu_notification_event already references the originating IU/draft/version/comment by unit_id + ref_id + canonical_address (text reference) without embedding body. The recommended view + collection exposure surfaces only event metadata and per-actor read-state aggregates. Any join to information_unit / unit_version body content requires a separate review (OUT_OF_SCOPE_REQUIRES_SEPARATE_REVIEW if requested later).


E. DOT Package Outline for Next Pack (P3D4B)

High-level; NO implementation here.

  1. PG view (read-only):
    • Candidate name: v_iu_notification_board.
    • Definition (sketch): iu_notification_event e LEFT JOIN LATERAL (SELECT array_agg(actor_ref ORDER BY read_at DESC) FILTER (...) AS latest_readers, count(*) AS read_count FROM iu_notification_read r WHERE r.event_id = e.id LIMIT 5) ...
    • Owner directus, GRANT SELECT TO directus only; no PUBLIC.
    • Indexed via underlying tables.
  2. Directus exposure plan:
    • Apply DOT to grant a NEW read-only role (e.g. notif_board_reader) read permission on v_iu_notification_board (and minimal projection of iu_notification_event/iu_notification_read if needed).
    • Do NOT grant create, update, delete on any of these collections to any non-admin role.
    • No custom endpoint, no flow, no extension in P3D4B.
  3. Role/permission intent:
    • Human read-only: limited columns; no body fields.
    • AI/Agent: unchanged (PG SQL).
  4. Read-only vs mark-read boundary:
    • P3D4B: read-only board view exposure only.
    • Mark-read (write action via fn_iu_mark_read) and per-actor parameterised endpoints: separate package with its own review (Directus user→actor_ref mapping is a prerequisite).
  5. Rollback / review:
    • Rollback = REVOKE + DROP VIEW (DOT-driven), no external dependency.
    • Quiet-period after grant (24–48h) before stacking the mark-read package.
    • Đ28 consult required if any new Nuxt rendering is proposed.
  6. Goal: every step DOT-driven, no ad-hoc UI clicking, no Nuxt code, no PG function exposed by parameter in P3D4B.

F. Constitution / Law Compliance Checklist

PG source of truth: PASS
Directus DOT-only: PASS
Directus UI view-only: PASS
User no content editing in Directus: PASS
Nuxt no-code/no-business-logic: PASS
Existing TreeView assembly path respected: PASS
No direct PG from Nuxt: PASS
No Codex dispatch: PASS

G. Law Jurisdiction Compliance Checklist

PG work stays in PG domain: PASS
Directus exposure stays in Directus/DOT domain: PASS
Nuxt display stays in display-only domain: PASS
User interaction does not bypass Directus: PASS
Directus does not become content-edit workflow: PASS
Nuxt does not become workflow/business logic: PASS
Hermes/agents not mixed into exposure design: PASS
No duplicate implementation across PG/Directus/Nuxt: PASS

P3D4 design review note rev1 | Inventory-driven | No implementation | Recommendation: C (staged) | Next: P3D4B_DIRECTUS_DOT_EXPOSURE_PACKAGE_REVIEW

Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/design/23-p3d4-directus-notification-exposure-review.md