KB-A184

23-P3D4B — Directus DOT Notification Read-Only Exposure — Package Review Note

13 min read Revision 1
p3d4bdesignpackage-reviewdirectusdotnotificationreadonlyexposurenon-executable

23-P3D4B — Directus DOT Notification Read-Only Exposure — Package Review Note

Date: 2026-05-08 Status: PACKAGE REVIEW (non-executable). NO implementation. NO mutation. Source prompt: knowledge/dev/laws/dieu44-trien-khai/prompts/23-p3d4b-directus-dot-notification-readonly-exposure-prompt.md (rev2) Upstream decisions inherited from: 23-p3d4-directus-notification-exposure-review.md (Option C locked), 23-p3d4-directus-exposure-design-review-report.md (PASS).


A. Điều 43 / DOT Inventory Results

A.1 Inventory depth

  • dieu43_inventory_depth = KB_ONLY — bounded KB search only; no Directus item-level introspection performed (read-only API was already inventoried in P3D4 upstream, no need to re-fetch).
  • Sources consulted: constitution.md; KB search on dieu43, dot, directus, counter, relationship; existing DOT description-contract doc (du-thao-description-governance-package-fix27.md).

A.2 What Điều 43 actually is

  • Per constitution.md: Đ43 v1.2 FINAL = "Luật Bản đồ Hệ thống" (System Context Law). Domain: system-context bootstrap, context-pack/living-doc registration.
  • Điều 43 does not own notification, counting, or relationship semantics. It owns context-pack registration only.
  • Therefore: there is no Đ43 DOT format/template specifically for notifications that P3D4B must adopt. Đ43 only kicks in if P3D4C later registers new artifacts (e.g. a context pack pointing at the new view) — that registration is downstream and deferred.

A.3 Existing DOT conventions found (REUSABLE)

  • DOT registry table: dot_tools (PG). Columns observed in KB: name, trigger_type, cron_schedule, operation, paired_dot, file_path.
  • DOT description contract (Description Governance R2 FINAL / Fix 27): 3-part template
    • [MỤC ĐÍCH] = name
    • [TRIGGER→OUTPUT] = trigger_type + cron_schedule + operation
    • [PAIRED DOT] = paired_dot
  • Paired-DOT discipline (NT12, constitution): writer ↔ verifier. User MEMORY confirms current pattern: e.g. DOT-HC-EXECUTOR ↔ DOT-HC-EXECUTOR-VERIFY.
  • PG-Native First (NT13): new mechanisms must justify against built-in PG; a plain VIEW qualifies as native.
  • No notification-specific DOT template exists yet. P3D4B is the first of its kind, so we adopt the generic 3-part contract + paired pattern.

A.4 Counting / relationship pattern reuse

  • The candidate view's read_count is computed as COUNT(DISTINCT actor_ref) against iu_notification_read per event_id — i.e. reuses an existing PG-native aggregation pattern. No separate counter table, no rollup, no trigger-maintained denormalization.
  • This is consistent with PG-Native First and avoids inventing a new counting subsystem.

A.5 Đ43 blockers

  • None blocking P3D4B. Đ43 schema concerns (context-pack registration) are out of scope; a follow-up pack may register the view-cum-context-pack later, but P3D4B does not need that to land.

A.6 Verdict

  • dieu43_dot_inventory = PASS (bounded; depth = KB_ONLY)
  • existing_dot_conventions_checked = PASS
  • reusable_dot_template = FOUND (Description Governance R2 / 3-part contract + NT12 paired)
  • reusable_counting_pattern = FOUND (PG-native COUNT DISTINCT on iu_notification_read)

B. Candidate PG View — NON-EXECUTABLE SKETCH

⚠️ This is a sketch, not runnable SQL. Column types, exact LATERAL syntax, and ordering tie-breakers must be re-validated at P3D4C. Do not copy-paste-execute.

B.1 Identity

  • Candidate name: v_iu_notification_board (inherited from P3D4 review).
  • Confirmed (P3D4 inventory): no view matching %notif% exists yet.

B.2 Scope decision

  • view_scope = HISTORY
  • Justification: Phase 1 is human monitoring / oversight, not actor-specific actionable inbox. ACTIONABLE would require defining "resolved" (applied draft? read by all? acknowledged?) which is undefined in current schema. PAIR (current+history) doubles surface area without a Phase 1 caller. HISTORY is the minimum sufficient surface.
  • ACTIONABLE filter is deferred; a future view can be added without breaking HISTORY consumers.

B.3 Payload strategy

  • payload_strategy = OMIT_RAW_PAYLOAD
  • Justification: payload jsonb NN default '{}' may carry future fields whose sensitivity is not yet classified. Phase 1 does not need any payload field. SANITIZED_REFS would require enumerating safe keys per event_type, which is design surface we don't need yet.
  • If a future caller demands refs (e.g. draft_id, comment_id), upgrade to SANITIZED_REFS in a separate review (not P3D4B, not P3D4C).

B.4 Column sketch (metadata-only, body-free)

Column Source Notes
event_id iu_notification_event.id uuid
event_type e.event_type constrained: `comment_added
event_stream e.event_stream constrained: `comment
unit_id e.unit_id uuid; FK to information_unit (no body join)
canonical_address e.canonical_address text; already-sanitised by construction
ref_id e.ref_id uuid; opaque ref, not joined
actor_ref e.actor_ref originating actor (text)
source e.source text
created_at e.created_at timestamptz
read_count COUNT(DISTINCT r.actor_ref) over iu_notification_read r WHERE r.event_id = e.id computed per row, no stored counter
latest_readers top-5 r.actor_ref ordered by r.read_at DESC, r.actor_ref ASC (tiebreak), JSON array computed via LATERAL

Forbidden columns (must NOT appear):

  • payload (raw jsonb) — OMIT_RAW_PAYLOAD
  • information_unit.body, unit_version.body — body-content boundary
  • Any token/secret/email/PII not already in iu_notification_event

B.5 Owner / GRANT sketch (descriptive, not a grant)

  • Owner: directus
  • Future GRANT (executed by P3D4C, not here): GRANT SELECT ON v_iu_notification_board TO directus;no PUBLIC, no anon.
  • Rollback shape: REVOKE SELECT ... ; DROP VIEW IF EXISTS v_iu_notification_board; (DOT-driven).

B.6 Non-executable fence

NON-EXECUTABLE SKETCH — DO NOT RUN
view: v_iu_notification_board
from: iu_notification_event e
left lateral: (
  select
    count(distinct r.actor_ref) as read_count,
    array(
      select r2.actor_ref
      from iu_notification_read r2
      where r2.event_id = e.id
      order by r2.read_at desc, r2.actor_ref asc
      limit 5
    ) as latest_readers
  from iu_notification_read r
  where r.event_id = e.id
) agg on true
select e.id as event_id, e.event_type, e.event_stream, e.unit_id,
       e.canonical_address, e.ref_id, e.actor_ref, e.source, e.created_at,
       coalesce(agg.read_count, 0) as read_count,
       coalesce(agg.latest_readers, '{}') as latest_readers
-- payload OMITTED. body OMITTED. no joins to information_unit/unit_version.

candidate_view_defined = PASS.


C. Candidate Directus DOT Outline — NON-EXECUTABLE

⚠️ Candidate outline only. No DOT package file is to be created in production by P3D4B.

C.1 DOT pair (NT12)

  • Primary (writer/applier): DOT-NOTIF-BOARD-EXPOSE — registers v_iu_notification_board as a Directus collection (read-only) and grants read to notif_board_reader.
  • Verifier (paired): DOT-NOTIF-BOARD-EXPOSE-VERIFY — checks: (i) view exists in PG, (ii) Directus collection registered, (iii) only notif_board_reader has read perm and no create/update/delete, (iv) no admin grant other than baseline, (v) no body column leaked.
  • Registry rows in dot_tools would carry paired_dot cross-link (NT12 satisfied).

C.2 Description (3-part contract per Description Governance R2)

  • [MỤC ĐÍCH]: Expose IU notification board (read-only) to Directus for human monitoring (Phase 1).
  • [TRIGGER→OUTPUT]: manual trigger → DIRECTUS_COLLECTION_EXPOSE operation; idempotent.
  • [PAIRED DOT]: DOT-NOTIF-BOARD-EXPOSE-VERIFY.

C.3 Role strategy

  • role_strategy = REUSE_EXISTING if a generic read-only board role already exists, otherwise NEW_ROLE_RECOMMENDED for notif_board_reader.
  • P3D4 review concretely proposed notif_board_reader. Inventory shows no current grant (MCP 403 on item-level), suggesting no analogous role exists. Recommendation for this package: NEW_ROLE_RECOMMENDEDnotif_board_reader, scoped to read on the new view (and minimal projection of iu_notification_event / iu_notification_read only if Directus auto-discovery cannot be confined to the view).
  • Justification for new role: existing roles (admin, public, MCP service) are either too broad or unrelated to monitoring. A dedicated role keeps blast radius minimal and revocation atomic.
  • This recommendation is to be finalised in P3D4C, not committed in P3D4B.

C.4 Permission grant description (NOT executed)

  • read ON v_iu_notification_board TO notif_board_reader.
  • Optional read on iu_notification_event, iu_notification_read (only if needed; prefer view-only).
  • Forbidden: create | update | delete on any of the three to non-admin roles. Forbidden: any grant to public / anonymous.

C.5 Phase boundaries (deferred items, NOT in P3D4B/P3D4C)

  • Mark-read flow (fn_iu_mark_read) — DEFERRED.
  • Per-actor unread/inbox (fn_iu_unread) — DEFERRED.
  • Directus user → actor_ref mapping — DEFERRED.
  • Nuxt page wiring / Đ28 display review — DEFERRED.
  • Hermes — DEFERRED (BLOCKED_PENDING_REVIEW).

C.6 Quiet period

  • 24–48h between successful P3D4C grant and any stacking package (e.g. mark-read), per upstream P3D4 directive.

candidate_dot_outline_defined = PASS.


D. Reuse / Overlap Assessment

Concern Verdict
Existing DOT description contract (3-part) REUSED
NT12 paired-DOT pattern REUSED
PG-Native counting (COUNT DISTINCT on iu_notification_read) REUSED
Existing role for read-only board NOT FOUND → new role justified
Đ43 (System Context Law) overlap NONE — different jurisdiction
Đ43 schema blockers NONE relevant to P3D4B
Counting subsystem duplication NONE (no separate counter table)
Body-content exposure risk MITIGATED by OMIT_RAW_PAYLOAD + no body join
Law jurisdiction overlap NONE (Đ7 Assembly First + Đ35 DOT Governance only)

overlap_risk = LOW.

dieu43_blockers_summary = "No Đ43 jurisdiction over notification view; Đ43 covers context-pack registration which is out of P3D4B scope. No schema repair needed."


E. Phase Boundaries (restated)

  • Phase 1 (P3D4C): create view + register Directus collection + grant notif_board_reader.read. Read-only.
  • Phase 2 (separate review): mark-read + actor mapping.
  • Phase 3 (separate review): Nuxt assembly via Đ28.
  • Hermes: out of scope across all phases until separately unblocked.

F. Go / No-Go Gate for P3D4C

Gate Status
overlap_risk = LOW
DOT convention/template identified ✅ (3-part description contract + NT12 pair)
view_scope decided ✅ HISTORY
payload_strategy decided ✅ OMIT_RAW_PAYLOAD
No hard-boundary violation in candidate design

go_nogo_gate = GO.


G. Recommendation

recommendation = READY_FOR_IMPLEMENTATION_PROMPT

Next pack: P3D4C_PG_VIEW_AND_DIRECTUS_DOT_IMPLEMENTATION_PROMPT_REVIEW.

P3D4C must:

  1. Author the executable CREATE VIEW v_iu_notification_board ... (still review, not yet run).
  2. Author the paired DOT package (DOT-NOTIF-BOARD-EXPOSE + -VERIFY) targeting Directus collection registration + role grant.
  3. Confirm role decision (REUSE vs NEW) against current Directus inventory at execution time.
  4. Carry rollback (REVOKE + DROP VIEW) and quiet-period clause (24–48h).

H. Compliance Checklist

PG source of truth:                 PASS
Directus DOT-only (no UI clicks):   PASS
Directus UI view-only (read):       PASS
Metadata-only (no body):            PASS
No Điều 43 DOT overlap:             PASS
No duplicate counting:              PASS
No law jurisdiction overlap:        PASS
Assembly First respected:           PASS
No Nuxt code:                       PASS
Non-executable artifacts only:      PASS

P3D4B rev2 — Package review note. Non-executable. No mutation. Awaits final user/GPT review before P3D4C is dispatched.

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