P3D4C2U — Resume Notification Display — Agent Prompt (REVIEW DRAFT Rev4)
P3D4C2U — Resume Notification Display — Agent Prompt (REVIEW DRAFT Rev4)
Date: 2026-05-10 | Rev: 4 | 0F generated-map gate → informational (chicken-and-egg: draft excluded from map, route works independently). 0G/0H route evidence → hard gates. Field-security rev3 giữ nguyên. Status: DRAFT — chờ GPT/User review trước khi dispatch Audience: Agent claude-go (1 row publish + read-only smoke + optional API deny smoke) Predecessor: D28_DEPLOY_PARTIAL_FIX_PACK PASS (2026-05-10, prompt rev6, image d2db418) Critical: Mutation duy nhất = 1 row status (
draft → published). No deploy/restart/build.
⚠️ Cảnh báo
- Mutation duy nhất:
table_registryrow 21 status:draft → published. - KHÔNG: deploy, restart, build, compose edit, Nuxt code, PG schema, widen permission, event core trigger, UI component, bespoke notification page, Điều 43, TAC resume, auto-rollback.
- Sau pack:
notification_display_checkpoint=unblocked. TAC resume = pack tiếp theo.
Tham chiếu KB
Predecessor: knowledge/dev/laws/dieu28-trien-khai/reports/d28-deploy-partial-fix-pack-report.md
Option D report (field classification SSOT): search KB "Option D" or "P3D4C2U Option D"
This report: knowledge/dev/laws/dieu44-trien-khai/reports/p3d4c2u-resume-notification-display-report.md
CHECKPOINT đầu prompt
- Đọc Fix Pack report →
phase_status=PASS. - Đọc Stage 2 report →
production_running_under_d2db418=true,page_routes=18/18. - search_knowledge DOT tool ("DOT table_registry publish").
- Verify Directus admin token available (no print).
Predecessor mismatch → STOP PREDECESSOR_DRIFT.
Field classification (rev3 — UNCHANGED in rev4)
Permission #1483 ALLOWLIST — 14 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
Registry visible — 13 fields (DirectusTable auto-adds id)
occurred_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, created_at
DENYLIST (rev3 corrected — safe_payload/correlation_id/causation_id are DENIED)
safe_payload, correlation_id, causation_id,
payload, body, raw_payload, payload_raw, vector, embedding,
secret, token, password, ssn, personal_data, internal_*, admin_only
Phase 0 — Preflight (12 gates: 10 hard + 1 informational + 1 optional)
0A. D28 chain state (HARD)
ssh contabo "docker compose -f /opt/incomex/docker/docker-compose.yml ps nuxt --format '{{.Image}}'"
ssh contabo "grep -E 'image:.*nuxt-ssr-local' /opt/incomex/docker/docker-compose.yml"
Expected: nuxt-ssr-local:d2db418, count=1.
0B. Relations endpoint (HARD)
TS_LOG=$(date +%s); RESPFILE="/tmp/p3d-relations-$TS_LOG.txt"
STATUS=$(ssh contabo "curl -s -o $RESPFILE -w '%{http_code}' '<base_url>/api/discovery/relations?collection=workflow_steps' --max-time 10")
ssh contabo "chmod 600 $RESPFILE"; ssh contabo "grep -qi 'token\|secret\|bearer\|password\|authorization' $RESPFILE && echo SCAN=FAIL || echo SCAN=PASS"
ssh contabo "rm -f $RESPFILE"
Expected HTTP 200. FAIL → STOP.
0C. tbl_event_outbox row state (HARD)
TS_LOG=$(date +%s); PRESTATE="/tmp/p3d-prestate-$TS_LOG.json"
ssh contabo "set -a; source /opt/incomex/docker/.env 2>/dev/null; set +a; curl -s -H \"Authorization: Bearer \$DIRECTUS_ADMIN_TOKEN\" \"\$DIRECTUS_PUBLIC_URL/items/table_registry/21\" > $PRESTATE 2>&1"
ssh contabo "chmod 600 $PRESTATE"
# scan + parse id=21, collection, status=draft|published
ssh contabo "rm -f $PRESTATE"
Status already published → IDEMPOTENT_ALREADY_PUBLISHED (Phase 1D). Other unexpected → STOP.
0D. Permission #1483 (HARD — rev3 field-security)
PERMFILE="/tmp/p3d-perm-$TS_LOG.json"
ssh contabo "set -a; source /opt/incomex/docker/.env 2>/dev/null; set +a; curl -s -H \"Authorization: Bearer \$DIRECTUS_ADMIN_TOKEN\" \"\$DIRECTUS_PUBLIC_URL/permissions/1483\" > $PERMFILE 2>&1"
ssh contabo "chmod 600 $PERMFILE"
# scan + verify: id=1483, collection=event_outbox, action=read
# MUST match 14-field allowlist. MUST NOT contain denylist.
# MUST exclude safe_payload, correlation_id, causation_id (rev3)
ssh contabo "rm -f $PERMFILE"
Report: permission_matches_option_d_allowlist, permission_excludes_safe_payload/correlation_id/causation_id, permission_contains_any_denylist_field=false.
0E. Registry visible fields (HARD — rev3 P6)
REGFIELDS="/tmp/p3d-regfields-$TS_LOG.json"
ssh contabo "set -a; source /opt/incomex/docker/.env 2>/dev/null; set +a; curl -s -H \"Authorization: Bearer \$DIRECTUS_ADMIN_TOKEN\" \"\$DIRECTUS_PUBLIC_URL/items/table_registry/21?fields=*\" > $REGFIELDS 2>&1"
ssh contabo "chmod 600 $REGFIELDS"
# scan + verify no denylist fields in registry configuration
ssh contabo "rm -f $REGFIELDS"
Contains safe_payload/correlation_id/causation_id → STOP REGISTRY_FIELDS_UNSAFE_DRIFT.
0F. Generated table-map — INFORMATIONAL (rev4 — was hard gate in rev3)
ssh contabo "grep -E 'event_outbox|tbl_event_outbox' /opt/incomex/docker/nuxt-repo/web/generated/table-maps.generated.ts | head -5"
Report (rev4 — informational, does NOT block):
generated_map_has_event_outbox=true|false
generated_map_entry_expected_absent_for_draft=true (if status=draft pre-mutation)
generated_map_status_filter=active,published (generator behavior: excludes draft)
generated_map_missing_blocks_publish=false (rev4 — informational gate, NOT blocking)
Vì sao informational (rev4): Generator excludes draft rows. event_outbox status=draft → absence expected. Pack này publish row, không regenerate/rebuild. Route works independently (0G/0H evidence). Next deploy cycle sẽ regenerate map với event_outbox included.
0G. /knowledge/registries route (HARD — rev4 promoted)
TS_LOG=$(date +%s); RESPFILE="/tmp/p3d-regroute-$TS_LOG.txt"
STATUS=$(ssh contabo "curl -s -o $RESPFILE -w '%{http_code}' '<base_url>/knowledge/registries' --max-time 10")
ssh contabo "chmod 600 $RESPFILE"; ssh contabo "rm -f $RESPFILE"
HARD GATE rev4: HTTP 200 REQUIRED. Non-200 → STOP REGISTRIES_ROUTE_REGRESSION.
0H. event_outbox route pre-mutation (HARD — rev4 promoted, includes security check)
TS_LOG=$(date +%s); SMOKEFILE="/tmp/p3d-eventoutbox-pre-$TS_LOG.html"
PRE_STATUS=$(ssh contabo "curl -s -o $SMOKEFILE -w '%{http_code}' '<base_url>/knowledge/registries/event_outbox' --max-time 15")
ssh contabo "chmod 600 $SMOKEFILE"
ssh contabo "grep -qi 'token\|secret\|bearer\|password\|authorization' $SMOKEFILE && echo SCAN=FAIL || echo SCAN=PASS"
IF SCAN=PASS:
# Denylist check (rev3 corrected list)
ssh contabo "grep -qE 'safe_payload|correlation_id|causation_id|payload_raw|raw_payload|\"payload\"|\"body\"|vector|embedding|\"secret\"|\"token\"|\"password\"|ssn|personal_data|internal_|admin_only' $SMOKEFILE && echo PRE_UNSAFE=true || echo PRE_UNSAFE=false"
ssh contabo "rm -f $SMOKEFILE"
HARD GATE rev4:
PRE_STATUS=200REQUIRED (route must already work pre-mutation)PRE_UNSAFE=falseREQUIRED (no denylist fields visible even pre-mutation)- Non-200 → STOP
EVENT_OUTBOX_ROUTE_NOT_FUNCTIONAL_PRE_MUTATION - PRE_UNSAFE=true → STOP
UNSAFE_FIELDS_LEAKED_PRE_MUTATION(CRITICAL — permission leak exists regardless of publish status)
Report: pre_mutation_event_outbox_status, pre_mutation_unsafe_fields_absent.
0I. No pending D28 rollback (HARD)
search_knowledge "D28 rollback_recommended". Must be false.
0J. PG event_outbox accessible (OPTIONAL per rev2 P5)
ssh contabo "docker exec postgres psql -U directus -d directus -c \"SELECT 1 FROM event_outbox LIMIT 1;\" 2>&1 || echo PG_CHECK_FAILED"
event_outbox_data_access_check=PASS|SKIPPED_NO_SAFE_METHOD.
0K. DOT discovery (INFORMATIONAL per rev2 P6)
search_knowledge "DOT table_registry publish". dot_tool_for_publish_found=true|false. false ≠ failure.
0L. D28 chain verified (HARD)
Read 3 reports: Stage 1 PASS + Stage 2 PARTIAL_PROMPT_DRIFT + Fix Pack PASS.
0M. Preflight consolidate (rev4)
PASS requires:
- 0A, 0B, 0C, 0D, 0E, 0G, 0H (hard gates), 0I, 0L = ALL PASS
- 0F = INFORMATIONAL (any value acceptable,
INFO_EXPECTED_ABSENT_FOR_DRAFTnormal) - 0J = PASS or SKIPPED_NO_SAFE_METHOD (optional)
- 0K = informational (false acceptable)
ALL hard gates PASS → preflight_status=PASS.
Phase 1 — Mutation (same as rev3)
1A. Method priority: DOT > Directus API > PG direct
1B. Mutation execution (PATCH /items/table_registry/21, status: published)
Log safety. KHÔNG print body.
1C. Post-mutation verify (no auto rollback per rev2 P7)
Side effect detected → STOP + recommend rollback (NOT execute).
1D. Idempotency (status already published → skip, Phase 2)
1E. Mutation report fields (same as rev3)
Phase 2 — Smoke (same as rev3, with rev4 notes)
2A. SSR warmup: sleep 2
2B. HTTP smoke /knowledge/registries/event_outbox
Log safety. SCAN=FAIL_LEAK → STOP CRITICAL.
2C. Body checks (rev3 corrected denylist + rev2 informational markers)
CRITICAL (Tier 1): UNSAFE_FIELDS_DETECTED_IN_HTML (denylist includes safe_payload/correlation_id/causation_id) INFORMATIONAL (Tier 2): render signal, event_outbox token, empty/data state
2D. Runtime field-level API deny smoke (rev2 P8, rev3 corrected probes)
Safe request: fields=id,occurred_at,event_domain,event_type,payload_classification (allowlist fields)
Deny probes: fields=safe_payload → 403, fields=correlation_id → 403, fields=causation_id → 403
SKIPPED_NO_PUBLIC_TOKEN acceptable.
2E. Cleanup
2F. Pass criteria (rev3 — unchanged)
Tier 1 ALL met → PASS (or PASS_HTTP_ONLY_WITH_INCONCLUSIVE_MARKERS if Tier 2 inconclusive).
Phase 3 — Decision & report (same as rev3)
3A. Result decision (8 outcomes unchanged)
3B. Routing matrix (unchanged)
| phase_status | next | rollback |
|---|---|---|
| PASS / PASS_IDEMPOTENT / PASS_HTTP_ONLY | P3D_INFORMATION_UNIT_TEXT_AS_CODE_RESUME |
No |
| FAIL_ROUTE | D28_ROUTE_RESOLUTION_FIX |
No |
| FAIL_PERMISSION | P3D4C2U_DIRECTUS_PERMISSION_FIX |
Optional |
| FAIL_EVENT_CORE | P3D4C1U_EVENT_CORE_FIX |
Optional |
| FAIL_CRITICAL_SECURITY | P3D4C2U_UNSAFE_FIELDS_LEAK_INVESTIGATION |
YES (no auto) |
| FAIL_MUTATION_SIDE_EFFECT | P3D4C2U_SIDE_EFFECT_INVESTIGATION |
YES (no auto) |
3C. Rollback: APPROVE P3D4C2U ROLLBACK: I authorize reverting tbl_event_outbox status from published back to draft.
3D. Report template (rev4 — 0F fields updated)
## Preflight
0a-0e = same as rev3
0f_generated_map_has_event_outbox=true|false (INFORMATIONAL rev4)
0f_generated_map_entry_expected_absent_for_draft=true|false
0f_generated_map_missing_blocks_publish=false (rev4)
0g_registries_route=PASS (HARD rev4)
0h_pre_mutation_event_outbox_status=200 (HARD rev4)
0h_pre_mutation_unsafe_fields_absent=true (HARD rev4)
0i-0l = same as rev3
preflight_status=PASS
All other report sections same as rev3.
Hard boundaries (13 NO_* — same as rev3)
NO_DEPLOY, NO_CONTAINER_RESTART, NO_IMAGE_BUILD, NO_COMPOSE_MODIFICATION,
NO_PG_SCHEMA_CHANGE, NO_NUXT_CODE_CHANGE, NO_WIDEN_DIRECTUS_PERMISSION,
NO_EVENT_CORE_TRIGGER_CHANGE, NO_UI_COMPONENT_CREATION,
NO_BESPOKE_NOTIFICATION_PAGE, NO_DIEU43_MODIFICATION,
NO_TAC_RESUME_IN_THIS_PACK, NO_AUTO_ROLLBACK
Mutations allowed (7 — same as rev3)
- PATCH 1 row table_registry id=21 status (DOT > API > PG)
- upload_document report
- ssh curl read-only (preflight + smoke + optional deny probes)
- ssh temp file /tmp/ (chmod 600 + remove)
- ssh psql read-only (0J, optional)
- search_knowledge
- (Optional) ssh curl public token (Phase 2D)
Self-check (14 items — rev4 updated)
| # | Phải đạt |
|---|---|
| 1 | D28 chain PASS verified |
| 2 | Image vẫn d2db418 |
| 3 | Permission #1483 = 14-field allowlist, EXCLUDES safe_payload/correlation_id/causation_id (rev3) |
| 4 | Registry fields no denylist (rev3 P6) |
| 5 | 0G registries route 200 (HARD rev4) |
| 6 | 0H event_outbox route 200 + unsafe absent PRE-mutation (HARD rev4) |
| 7 | 0F generated map = informational (not blocking rev4) |
| 8 | Mutation method declared, status changed, no side effect |
| 9 | Idempotency handled |
| 10 | Post-mutation smoke UNSAFE_FIELDS_DETECTED_IN_HTML=false |
| 11 | Runtime deny probes: safe_payload/correlation_id/causation_id → 403 (or SKIPPED) |
| 12 | NO auto rollback |
| 13 | Hard boundaries 13 TRUE |
| 14 | Report uploaded KB |
Future improvement note (rev4)
After this pack publishes tbl_event_outbox, the static generated map (web/generated/table-maps.generated.ts) still won't include event_outbox until the next deploy cycle runs generate:table-maps + build + deploy. This is non-blocking because event_outbox route works independently (proven by 0H pre-mutation evidence). Next D28 maintenance or feature deploy will naturally include it.
P3D4C2U Resume Notification Display | Rev4 DRAFT | 0F informational (chicken-and-egg resolved), 0G/0H hard gates (route functional evidence), field-security rev3 preserved | 2026-05-10