KB-46A4

23-P3D4C0Y — Universal Phase 2 PoC Scope Plan (Design Note rev1)

31 min read Revision 1
p3d4c0ydesignuniversalpocscopephase2eventsystem-issuesrev1

23-P3D4C0Y — Universal Phase 2 PoC Scope Plan (Design Note rev1)

Date: 2026-05-08 Status: DESIGN rev1 — PoC scope/design review only. NO implementation. NO mutation. Prompt: knowledge/dev/laws/dieu44-trien-khai/prompts/23-p3d4c0y-universal-phase2-poc-scope-plan-prompt.md (rev2) Report: knowledge/dev/laws/dieu44-trien-khai/reports/23-p3d4c0y-universal-phase2-poc-scope-plan-report.md Reviewer: Claude Opus 4.7 (1M context) Upstream: P3D4C0X design rev1 + report (PASS) + GPT directive 2026-05-08 + P3D4C1 rev3 (paused) Decisions locked: UNIVERSAL_WITH_IU_COMPAT (Option C) | C-i Phase 2 first | event_type_registry MANDATORY | LISTEN/NOTIFY defer Phase 3 | Đ45 defer | P3D4C1 absorbed (no resume)


§0. Executive summary

Phase 2 PoC chọn system_issues (Đ22) làm domain đầu tiên cho universal event substrate. Lý do (PG inventory-backed): 8548 rows tổng, lifecycle 3-state đơn giản (open → resolved/archived), severity catalog đã match event_severity (critical/warning/info), zero IU surface, governance owner rõ qua entity_type/entity_code.

Universal core schema sketches: 5 tables non-executable — event_outbox, event_read, event_subscription, event_type_registry (MANDATORY), event_pending (HYBRID strategy). Worker + log table reuse P3D4C1 patterns, generalized.

Config: REUSE dot_config với namespace event.* (verified: 47 rows, dotted-namespace pattern đã có sẵn iu_create.gateway.*, iu_edit.policy.*, vocab.*, context_pack_*, hc_* — hoàn toàn tương thích).

IU compat C-i locked: zero dual-write, zero row migration, fn_iu_* API surface bất biến.

Đ43 alignment: ADDITIVE_ONLY — không section mới, dùng existing pg_query source cho red_zones/recent_alerts feed.

Recommendation: next_required_pack=P3D4C1U_UNIVERSAL_CORE_IMPLEMENTATION_PROMPT_REVIEW.


§A. PoC Domain Selection — PG read-only inventory

A.1 Inventory (read-only, executed via docker exec postgres psql -U directus -d directus)

Candidate Schema verified Lifecycle Actor field Severity Row count Existing triggers Volume profile
system_issues (Đ22) ✅ Full status ∈ {open, resolved, archived} via CHECK source, source_system (no created_by) — resolved_by for closure severity ∈ {critical, warning, info} via CHECK 8 548 7 active triggers (birth/desc/guard/label) bursty per HC sweep
birth_registry (Đ0-G) ✅ Full status default 'born', certified bool, certified_at user_created, governance_role, dot_origin (no severity column) 105 227 2 triggers very high (every entity born)
admin_fallback_log (Đ35-FB) ✅ Full status ∈ {applied, retroactive_documented, audit_overdue, rolled_back} via CHECK approved_by, dot_code, session_code (implicit via audit_overdue) 22 1 trigger (trg_auto_apr_on_fallback) very low (rare)
dot_tools execution log ⚠️ NONE EXISTS only last_executed timestamptz, no per-run rows n/a n/a n/a n/a (no execution log table — would require new table, out of PoC scope)

A.2 PoC selection — system_issues

Verdict: poc_domain = system_issues

Justification (multi-factor):

  1. Volume sane — 8 548 total; ~10s open/resolve daily → safe for trigger-based fact capture without overload.
  2. Lifecycle simplest — 3-state CHECK constraint already enforced (open → resolved | archived). Maps cleanly to event_types: issue_opened, issue_resolved, issue_archived.
  3. Severity nativeseverity already classified (critical/warning/info), 1:1 với event_outbox.event_severity.
  4. Zero IU surface — no IU table/function/trigger touched; IU C-i compatibility requirement honored automatically.
  5. Governance owner derivableentity_type + entity_code resolve target IU/Birth row → recipient routing via existing governance graph.
  6. Existing trigger ecosystem mature — 7 active triggers (birth, description guard, label assign) prove AFTER INSERT trigger surface is well-exercised; adding 1 more append-only INSERT trigger is additive-only.
  7. Aligns with rev2 prompt §1 preferred candidate + GPT directive §1 ("Prefer system_issues / Đ22").
  8. Stream coverage — 3 of 7 streams exercised (alert, health, update) → tests universal taxonomy without forcing all streams.

Rejected alternatives (with reason):

  • birth_registry — 105K rows + every entity births = trigger fan-out unacceptable for first PoC.
  • admin_fallback_log — only 22 rows; insufficient signal for routing/grouping/lifecycle tests; tightly coupled với existing trg_auto_apr_on_fallback.
  • dot_tools execution log — table doesn't exist; out of PoC scope (would require new infra).

A.3 Event types proposed for PoC domain

event_domain event_type event_stream event_severity Trigger source Subject
system issue_opened alert (mirror system_issues.severity) AFTER INSERT ON system_issues system_issues row
system issue_resolved update info AFTER UPDATE OF status WHEN status='resolved' system_issues row
system issue_archived update info AFTER UPDATE OF status WHEN status='archived' system_issues row
system red_zone_violation alert critical AFTER INSERT/UPDATE WHEN severity='critical' system_issues row

PoC fires events idempotently — same (event_domain, event_type, event_subject_ref) collapses via UNIQUE partial index.


§B. Universal Core Schema (NON-EXECUTABLE sketches)

Fence: tất cả DDL trong §B là DESIGN ONLY. Không apply trên PG. P3D4C1U sẽ là pack thực thi.

B.1 event_outbox — durable event SoT

-- NON-EXECUTABLE SKETCH (P3D4C0X §C envelope, refined)
CREATE TABLE event_outbox (
  -- Identity
  event_id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),

  -- Taxonomy
  event_domain      text NOT NULL,
  event_type        text NOT NULL,
  event_stream      text NOT NULL,
  event_severity    text,

  -- Subject
  event_subject_type   text NOT NULL,
  event_subject_ref    uuid,
  event_subject_table  text NOT NULL,
  canonical_address    text NOT NULL CHECK (btrim(canonical_address) != ''),

  -- Actor (producer)
  actor_ref         text NOT NULL CHECK (btrim(actor_ref) != ''),
  source_system     text NOT NULL,
  source_function   text,

  -- Correlation / causation
  correlation_id    text,
  causation_id      uuid,

  -- Provenance
  source_document_ref  text,
  import_batch_ref     text,

  -- Payload — METADATA ONLY (denylist enforced)
  payload_classification text NOT NULL DEFAULT 'safe_metadata'
    CHECK (payload_classification IN ('safe_metadata','restricted')),
  safe_payload      jsonb NOT NULL DEFAULT '{}'::jsonb,

  -- Timing
  occurred_at       timestamptz NOT NULL DEFAULT now(),
  created_at        timestamptz NOT NULL DEFAULT now(),
  processed_at      timestamptz,

  -- Domain enum
  CHECK (event_domain IN ('iu','birth_registry','governance','tac','kg','system','dot','health')),
  -- Stream enum
  CHECK (event_stream IN ('comment','review','update','birth','task','alert','health')),
  -- Severity enum
  CHECK (event_severity IS NULL OR event_severity IN ('info','warning','critical')),

  -- Payload safety denylist (B.5)
  CHECK (
    NOT (safe_payload ? 'body')        AND
    NOT (safe_payload ? 'content')     AND
    NOT (safe_payload ? 'raw')         AND
    NOT (safe_payload ? 'vector')      AND
    NOT (safe_payload ? 'embedding')   AND
    NOT (safe_payload ? 'secret')      AND
    NOT (safe_payload ? 'token')       AND
    NOT (safe_payload ? 'password')    AND
    NOT (safe_payload ? 'ssn')         AND
    NOT (safe_payload ? 'personal_data')
  )
);

-- Idempotency: same (domain, type, subject) at most once
CREATE UNIQUE INDEX uq_event_outbox_subject_type
  ON event_outbox (event_domain, event_type, event_subject_table, event_subject_ref)
  WHERE event_subject_ref IS NOT NULL;

-- Correlated idempotency (for events that can fire multiple times with different correlation)
CREATE UNIQUE INDEX uq_event_outbox_correlation
  ON event_outbox (event_domain, event_type, event_subject_ref, correlation_id)
  WHERE correlation_id IS NOT NULL;

-- Hot indexes
CREATE INDEX idx_event_outbox_domain_stream_created
  ON event_outbox (event_domain, event_stream, created_at DESC);
CREATE INDEX idx_event_outbox_severity_created
  ON event_outbox (event_severity, created_at DESC) WHERE event_severity IN ('warning','critical');
CREATE INDEX idx_event_outbox_subject
  ON event_outbox (event_subject_table, event_subject_ref);

B.2 event_read — per-actor read state (universal)

-- NON-EXECUTABLE SKETCH
CREATE TABLE event_read (
  event_id     uuid NOT NULL REFERENCES event_outbox(event_id) ON DELETE CASCADE,
  actor_ref    text NOT NULL CHECK (btrim(actor_ref) != ''),
  read_at      timestamptz NOT NULL DEFAULT now(),
  ack_at       timestamptz,                          -- Phase 2 future (stream='task','alert')
  PRIMARY KEY (event_id, actor_ref)
);
CREATE INDEX idx_event_read_actor ON event_read (actor_ref, read_at DESC);

Generalized từ iu_notification_read (P3D2). Universal constraint: UNIQUE (event_id, actor_ref) (PK đảm bảo). Implicit self-read computed (no row inserted) — thống nhất với P3D4C0X §D.4.

B.3 event_subscription — config-driven routing

-- NON-EXECUTABLE SKETCH
CREATE TABLE event_subscription (
  id              uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  recipient_ref   text NOT NULL CHECK (btrim(recipient_ref) != ''),
  event_domain    text,                              -- NULL = any
  event_type      text,                              -- NULL = any
  event_stream    text,                              -- NULL = any
  scope_subject_table text,                          -- NULL = any
  scope_filter    jsonb NOT NULL DEFAULT '{}'::jsonb,
  mute            boolean NOT NULL DEFAULT false,
  created_at      timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX idx_event_sub_recipient ON event_subscription (recipient_ref);
CREATE INDEX idx_event_sub_match ON event_subscription (event_domain, event_type, event_stream);

Phase 2 PoC seed:

  • ('agency:sysop', 'system', NULL, 'alert', NULL, '{}', false) — sysop nhận tất cả system alerts.
  • ('role:health_owner', 'system', 'red_zone_violation', NULL, 'system_issues', '{}', false).
  • Default broadcast (no row matches) → fallback "all known actors except creator" — giữ behavior P3D2.

B.4 event_type_registry — MANDATORY (chống free-form)

-- NON-EXECUTABLE SKETCH
CREATE TABLE event_type_registry (
  event_domain      text NOT NULL,
  event_type        text NOT NULL,
  event_stream      text NOT NULL,
  default_severity  text,                            -- NULL = derive from producer
  description       text NOT NULL,
  active            boolean NOT NULL DEFAULT true,
  created_at        timestamptz NOT NULL DEFAULT now(),
  PRIMARY KEY (event_domain, event_type),
  CHECK (event_stream IN ('comment','review','update','birth','task','alert','health')),
  CHECK (default_severity IS NULL OR default_severity IN ('info','warning','critical'))
);

PoC seed (4 rows):

event_domain event_type event_stream default_severity description
system issue_opened alert (NULL — mirror row) A new system_issues row was created.
system issue_resolved update info system_issues.status transitioned to resolved.
system issue_archived update info system_issues.status transitioned to archived.
system red_zone_violation alert critical system_issues with severity='critical' opened or persisted.

B.4.1 Enforcement mechanism — choice with justification

Option Mechanism Verdict
A Compound FK event_outbox(event_domain, event_type) → event_type_registry(event_domain, event_type) Acceptable but rigid — adding a new event type requires registry INSERT before first emission, otherwise FK fail kills emitter. Plus FK on text-pair has slightly higher cost than UUID FK.
B BEFORE INSERT trigger on event_outbox validates (event_domain, event_type) exists in registry AND active=true. Mismatched stream → RAISE EXCEPTION with custom message. CHOSEN — flexibility for migration windows (deactivate vs delete), custom error text aids debugging, trigger overhead ~µs (single PK lookup). Allows runtime registry refresh without DDL.
C Static CHECK constraint regenerated from registry on every change Brittle — requires DDL re-issue on registry change; stale risk.
D Registry only, no enforcement Unsafe — turns universal event into free-form activity log. Rejected by GPT directive §4 hardening requirement.

Decision: registry_enforcement = B (BEFORE INSERT trigger). Trigger sketch:

-- NON-EXECUTABLE SKETCH
CREATE FUNCTION fn_event_type_validate() RETURNS trigger LANGUAGE plpgsql AS $$
DECLARE r record;
BEGIN
  SELECT event_stream, active INTO r FROM event_type_registry
   WHERE event_domain = NEW.event_domain AND event_type = NEW.event_type;
  IF NOT FOUND THEN
    RAISE EXCEPTION 'event_type_registry: unknown (%, %)', NEW.event_domain, NEW.event_type;
  END IF;
  IF NOT r.active THEN
    RAISE EXCEPTION 'event_type_registry: (% , %) is inactive', NEW.event_domain, NEW.event_type;
  END IF;
  IF r.event_stream <> NEW.event_stream THEN
    RAISE EXCEPTION 'event_type_registry: stream mismatch — registry has %, got %', r.event_stream, NEW.event_stream;
  END IF;
  RETURN NEW;
END $$;

B.5 event_pending — staging (HYBRID strategy)

-- NON-EXECUTABLE SKETCH
CREATE TABLE event_pending (
  id                  uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  event_domain        text NOT NULL,
  event_type_hint     text NOT NULL,                 -- final type resolved by worker
  event_subject_table text NOT NULL,
  event_subject_ref   uuid,
  canonical_address   text NOT NULL,
  actor_ref           text NOT NULL,
  correlation_id      text,
  source_document_ref text,
  import_batch_ref    text,
  capture_payload     jsonb NOT NULL DEFAULT '{}'::jsonb,
  created_at          timestamptz NOT NULL DEFAULT now(),
  processed_at        timestamptz,
  error_count         int NOT NULL DEFAULT 0,
  last_error          text
);

-- HYBRID: universal table + per-domain partial indexes for query plan tightness
CREATE INDEX idx_event_pending_unprocessed
  ON event_pending (created_at) WHERE processed_at IS NULL;
CREATE INDEX idx_event_pending_system_unprocessed
  ON event_pending (created_at) WHERE event_domain = 'system' AND processed_at IS NULL;
CREATE INDEX idx_event_pending_correlation
  ON event_pending (correlation_id) WHERE correlation_id IS NOT NULL AND processed_at IS NULL;

B.5.1 Strategy choice — HYBRID

Option Pros Cons Verdict
UNIVERSAL (1 table) Simplest ops, 1 worker, 1 advisory lock All domains share index; large WHERE plans Acceptable but slow at scale
PER_DOMAIN (N tables) Tight indexes, permission isolation per role N triggers/workers/lock to maintain; cross-domain ops hard Overhead too early
HYBRID 1 table + per-domain partial index — best of both. Permission isolation via RLS or domain-scoped roles. Slight DDL complexity (need to add partial index when new domain ships) CHOSEN

Decision: pending_strategy = HYBRID.

B.6 event_worker_log (universal)

-- NON-EXECUTABLE SKETCH
CREATE TABLE event_worker_log (
  id              uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  run_at          timestamptz NOT NULL DEFAULT now(),
  domain_filter   text,                              -- NULL = all-domain tick
  pending_pre     int NOT NULL DEFAULT 0,
  pending_post    int NOT NULL DEFAULT 0,
  events_emitted  int NOT NULL DEFAULT 0,
  rollups_emitted int NOT NULL DEFAULT 0,
  conflicts_skipped int NOT NULL DEFAULT 0,
  rows_marked     int NOT NULL DEFAULT 0,
  duration_ms     numeric,
  error_count     int NOT NULL DEFAULT 0,
  error_text      text
);

Generalized từ P3D4C1 rev3 §2B.

B.7 Config — REUSE dot_config

Inventory verified (read-only):

psql> SELECT count(*) FROM dot_config;  → 47
psql> SELECT key FROM dot_config ORDER BY key;
   → dotted-namespace pattern in use:
     iu_create.gateway.*  (8 keys)
     iu_edit.policy.*, iu_edit.schema.*
     vocab.*
     desc_template_*, hc_*, context_pack_*
   → schema (key text PK, value text NOT NULL, description text, updated_at timestamptz)

config_strategy = REUSE_DOT_CONFIG ✅ — schema fits, namespace pattern already standard.

PoC keys to seed (P3D4C1U scope, NOT this pack):

Key Default Description
event.global.debounce_seconds 90 Universal debounce (clamped 60–300 by worker).
event.global.batch_threshold 2 Group rollup threshold.
event.global.worker_lock_name event_worker hashtext(this) → advisory lock id.
event.system.debounce_seconds 30 Per-domain override (alerts faster).

Justification: zero new infra; consistent với P3D4C1 rev3 §5 pattern; future domains chỉ thêm key, không thêm bảng.


§C. IU Compatibility C-i — LOCKED

C.1 Hard rules (non-negotiable, locked by P3D4C0X recommendation + GPT directive)

  • ❌ KHÔNG dual-write IU triggers — trg_aa_iu_notif_* continue writing only to iu_notification_event.
  • ❌ KHÔNG migrate IU rows to event_outbox trong Phase 2.
  • ❌ KHÔNG modify fn_iu_unread, fn_iu_mark_read, fn_iu_notification_board signature hay body.
  • ❌ KHÔNG modify iu_notification_event, iu_notification_read schema.
  • event_outbox co-exists riêng biệt; IU table vẫn là SoT cho IU domain.
  • ✅ Optional convenience view v_event_unified (UNION ALL) cho cross-domain board query — defer creation đến Phase 2 tests need it; không phải blocker.
  • ✅ Optional new function fn_event_unread(p_actor, p_domain text DEFAULT NULL) for cross-domain query — does NOT replace fn_iu_*.

C.2 Convergence (Phase 3, future, OUT-OF-SCOPE for P3D4C0Y)

C-ii roadmap (P3D4C0X §I.3) — one-time copy + view-redirect — chỉ kích hoạt sau khi Phase 2 PoC chứng minh stable + GPT/User approval. KHÔNG plan trong P3D4C0Y.

C.3 Verification gates

  • iu_no_dual_write = true (no trigger writes to both tables).
  • iu_no_row_migration = true (no script copies rows in Phase 2).
  • iu_fn_api_unchanged = true (front-door functions untouched).
  • fn_iu_unread / fn_iu_mark_read / fn_iu_notification_board hashes snapshot before P3D4C1U; verified equal after.

§D. P3D4C1 Lessons Absorption

P3D4C1 rev3 (paused) is design material; concepts mapped to universal:

P3D4C1 (IU-specific) concept Universal P3D4C0Y mapping
iu_notification_pending table event_pending (B.5) — adds event_domain, event_type_hint, correlation_id.
fn_iu_notif_birth() AFTER INSERT trigger O(1) fn_event_capture_system_issues() AFTER INSERT/UPDATE — same O(1) shape: 1 INSERT into pending + 1 PK lookup. No COUNT/JOIN.
fn_iu_notification_worker_tick() exception-safe with advisory lock fn_event_worker_tick(p_domain text DEFAULT NULL) — universal worker, group by (event_domain, COALESCE(source_document_ref, import_batch_ref, correlation_id)). Same EXCEPTION block + lock release.
Debounce clamp LEAST(300, GREATEST(60, value)) Same — universal default 90s, per-domain override via event.{domain}.debounce_seconds.
Rollback policy "no-execute on success" Same — rollback script written but not run; verification gate.
Backdated test rows (UPDATE pending SET created_at = now()-interval '120s') Same test pattern — deterministic, no wall-clock wait.
Worker log diagnostic columns Same — event_worker_log (B.6).
Constraint name verify (Step 0E) Same — preflight inventory required in P3D4C1U.

p3d4c1_lessons_absorbed = PASS (8/8 lessons reused, generalized).

P3D4C1 status: PAUSED_ABSORBED_INTO_UNIVERSAL — không resume IU-specific implementation. Future IU pending table chỉ thêm vào event_pending với event_domain='iu' ở Phase 3 convergence.


§E. Payload Safety (schema-level)

E.1 Classification field

event_outbox.payload_classification text NOT NULL DEFAULT 'safe_metadata' ∈ {safe_metadata, restricted}.

Phase 2 PoC: only safe_metadata accepted (CHECK enforced). restricted reserved for Phase 3 (encrypted-at-rest payload, separate access control).

E.2 Denylist enforcement (CHECK constraint, B.1)

Forbidden top-level keys in safe_payload: body, content, raw, vector, embedding, secret, token, password, ssn, personal_data.

Implementation: top-level key denylist via CHECK (sufficient for PoC). Phase 2 hardening seam: BEFORE INSERT trigger that recursively scans nested keys (deferred — adds overhead, not needed for system_issues PoC payload shape).

E.3 Allowlist guidance (documented, not enforced)

PoC safe_payload for system_issues:

{
  "issue_code": "ISS-12345",
  "severity": "warning",
  "occurrence_count": 3,
  "source_system": "self_healing_v1.2",
  "issue_class": "schema_drift",
  "coalesce_key": "schema_drift:tac_publication"
}

ALLOWED: counts, status, codes, refs, classification keys. NEVER: free-text body, error stack traces (those go to event_worker_log.error_text separately), business descriptions.

E.4 Idempotency rules

Two unique partial indexes (B.1):

  • (event_domain, event_type, event_subject_table, event_subject_ref) WHERE event_subject_ref IS NOT NULL — single-shot events per subject.
  • (event_domain, event_type, event_subject_ref, correlation_id) WHERE correlation_id IS NOT NULL — correlated events (re-fire allowed once per correlation).

Producer uses INSERT … ON CONFLICT DO NOTHING semantics where re-fire is expected (worker rollups). Triggers may RAISE NOTICE on conflict for diagnostics.

payload_safety_schema = PASS, payload_denylist_enforced = PASS.


§F. Stream Taxonomy — task vs review

F.1 Resolution rule

Stream Definition Trigger condition
review Content needs evaluation/approval by recipient. Subject is a piece of content (draft, doc, publication). Producer = author; recipient = reviewer pool; outcome = approve/reject content.
task Imperative action to perform a procedure. Subject is a procedure invocation (DOT run, health check, workflow step). Producer = system; recipient = role/agency; outcome = action executed.

F.2 Overlap analysis

Borderline cases reviewed:

  • dot_apr_required — recipient must review the fallback evidence AND act (create retroactive APR). → Stream = review (content evaluation precedes procedure). Procedure tracking via correlation_id chain.
  • checkpoint_due — pure procedural reminder. → Stream = task.
  • approval_requested — content review. → Stream = review.

F.3 Verdict

task_review_resolved = SEPARATED

Rule documented above; PoC system_issues uses {alert, update, health} only — task/review not exercised by this PoC, so resolution is forward-looking but not deferred.

GPT directive §3 ("test whether task and review overlap in practice, but do not change now") — honored: rule stated, no taxonomy change, PoC doesn't trigger any task/review event.


§G. Đ43 Alignment Gate

G.1 Specific checks

Check Result
dot_config reuse — schema/contract change? ❌ NO — only adds keys with event.* namespace, schema unchanged.
system_health_checks overlap with universal events? ❌ NO — Đ43 health checks remain SoT; universal event only emits notification about state transitions in system_issues (the consumer of HC).
New Đ43 section needed? ❌ NO — red_zones section can use existing pg_query source mode (Đ43 §6) to render top-N recent universal alerts.
Context-pack registration for new tables? ✅ YES — 5 new tables (event_outbox, event_read, event_subscription, event_type_registry, event_pending, event_worker_log) registered via existing table_registry infra (no Đ43 contract change).
dot-context-pack-build machinery duplicated? ❌ NO — Đ43 builder produces events into event_outbox (event_domain='system', event_type='context_pack_built/failed') — additive, doesn't duplicate.

G.2 Verdict

dieu43_gate = ADDITIVE_ONLY

GPT directive §7 confirms: "Additive-only if using existing Đ43 pg_query source mode and no section/schema change. If P3D4C0Y proposes a new Đ43 section or changes context-pack contract, require separate Đ43 review/amend."

P3D4C0Y proposes neither new section nor contract change. PASS additive-only path.


§H. Rollback / Failure-Mode Design

H.1 PoC failure scenarios + responses

Failure Response Recovery time
Schema DDL fails partway through P3D4C1U apply Inverse-order DROP TABLE for the 5+1 new tables (no IU touch). FK cascade handles event_read → event_outbox. Minutes
Birth trigger on system_issues causes hot-path slowdown DROP TRIGGER + DROP FUNCTION; system_issues writes resume normal. Pending rows orphaned (truncate when ready). Seconds
Worker hangs / stuck advisory lock pg_try_advisory_lock returns false → next tick skips; manual SELECT pg_advisory_unlock(hashtext('event_worker')) if needed; cron unschedule via cron.unschedule('event-worker'). Minutes
event_outbox grows unbounded Phase 2 retention seam: daily cron DELETE FROM event_outbox WHERE event_stream='update' AND processed_at IS NOT NULL AND created_at < now() - interval '90 days'. Severity=critical retained 1y. (Design only — implement when volume warrants.) N/A (preventive)
event_type_registry validate trigger blocks legitimate emission INSERT row in registry first; trigger re-validates next emission. Worst case: temporary active=true flip in registry, no DDL. Seconds
Routing wrong recipient event_subscription.mute=true row; or DELETE subscription. No event_outbox change needed. Seconds

H.2 IU regression risk assessment

Asset Touched? Risk
iu_notification_event NO ZERO
iu_notification_read NO ZERO
fn_iu_unread, fn_iu_mark_read, fn_iu_notification_board NO ZERO
fn_iu_notif_comment/draft/version triggers NO ZERO
fn_iu_save, fn_iu_apply_edit_draft NO ZERO
unit_edit_comment, unit_edit_draft, unit_version, information_unit NO ZERO

iu_regression_risk = ZERO ✅. Verified: PoC domain is system_issues, not IU.

H.3 Test rollback protocol

rollback_executed = NO_ON_SUCCESS | YES_ON_FAIL | USER_APPROVED_DRILL — script verified syntactically + dependency-checked, executed only on failure or explicit approval (P3D4C1 rev3 pattern).

rollback_design = PASS.


§I. Routing model — Phase 2 minimal

Recipient kind actor_ref format PoC seed
Sysop role agency:sysop broadcast for event_domain='system' AND event_stream='alert'
Health owner role:health_owner filter event_type='red_zone_violation'
Specific user user:{login} none seeded for PoC (kept generic)

Resolution view (P3D4C0X §D.2):

-- NON-EXECUTABLE SKETCH
CREATE VIEW v_event_recipient AS
SELECT e.event_id, s.recipient_ref, s.mute
FROM event_outbox e
JOIN event_subscription s
  ON  (s.event_domain  IS NULL OR s.event_domain  = e.event_domain)
  AND (s.event_type    IS NULL OR s.event_type    = e.event_type)
  AND (s.event_stream  IS NULL OR s.event_stream  = e.event_stream)
  AND (s.scope_subject_table IS NULL OR s.scope_subject_table = e.event_subject_table)
WHERE NOT s.mute;

Phase 1 fallback (no subscription matches → broadcast minus self) implemented in fn_event_unread(), mirroring P3D2 IU behavior.


§J. Implementation Pack Recommendation

next_required_pack=P3D4C1U_UNIVERSAL_CORE_IMPLEMENTATION_PROMPT_REVIEW
poc_domain=system_issues
poc_domain_pg_evidence=row_count=8548; lifecycle=open|resolved|archived (CHECK); severity=critical|warning|info (CHECK); 7 active triggers; zero IU surface
core_tables_count=6  # event_outbox, event_read, event_subscription, event_type_registry, event_pending, event_worker_log
registry_enforcement=B  # BEFORE INSERT trigger validating against event_type_registry
pending_strategy=HYBRID  # 1 universal table + per-domain partial indexes
config_strategy=REUSE_DOT_CONFIG  # 47-row inventory confirms namespace + schema fit
iu_compat=C-i (no dual-write, no row migration, fn_iu_* unchanged)
dieu43_gate=ADDITIVE_ONLY
estimated_implementation_effort=MEDIUM
listen_notify_status=DEFERRED_PHASE3
worker_strategy=PG_CRON_ONLY  # 1 worker, multi-domain via group by event_domain
p3d4c1_status=PAUSED_ABSORBED_INTO_UNIVERSAL

P3D4C1U scope (proposal — separate prompt, requires GPT/User review):

  • §1 Apply DDL §B (6 tables) — IDEMPOTENT.
  • §2 Seed event_type_registry (4 rows) + event_subscription (2 rows) + dot_config (4 keys).
  • §3 BEFORE INSERT trigger fn_event_type_validate on event_outbox.
  • §4 Capture trigger fn_event_capture_system_issues AFTER INSERT/UPDATE on system_issues.
  • §5 Universal worker fn_event_worker_tick + pg_cron schedule.
  • §6 Tests: 12+ deterministic backdated tests (parity với P3D4C1 rev3 §7 + cross-domain board test + subscription routing test + registry enforcement test + IU compat snapshot test).
  • §7 Rollback script (data-safe, conditional drops).

§K. Verification Block

phase_status=PASS
poc_domain_selected=system_issues
poc_domain_pg_inventory=PASS
core_schema_designed=PASS
event_type_registry_included=PASS
registry_enforcement_mechanism=B
pending_strategy=HYBRID
config_strategy=REUSE_DOT_CONFIG
iu_compat_ci_locked=PASS
iu_no_dual_write=true
iu_no_row_migration=true
iu_fn_api_unchanged=true
p3d4c1_lessons_absorbed=PASS
payload_safety_schema=PASS
payload_denylist_enforced=PASS
task_review_resolved=SEPARATED
dieu43_gate=ADDITIVE_ONLY
rollback_design=PASS
not_activity_log=PASS  # 4-event PoC type registry + 3-question rubric (P3D4C0X §M.3)
no_pg_mutation=true
no_directus_mutation=true
no_nuxt_code=true
no_iu_runtime_change=true
no_p3d4c1_resume=true
listen_notify_status=DEFERRED_PHASE3
hot_path_contract=O1_APPEND_ONLY
worker_strategy=PG_CRON_ONLY
recommendation=P3D4C1U_UNIVERSAL_CORE_IMPLEMENTATION_PROMPT_REVIEW
next_required_pack=P3D4C1U_UNIVERSAL_CORE_IMPLEMENTATION_PROMPT_REVIEW

P3D4C0Y rev1 design note | PoC scope plan | PG inventory-backed | Non-executable | KB-only | All hard boundaries honored | Awaiting GPT/User review