KB-5BE5

P3D4C2W — User Visible Route 404 Investigation Report

18 min read Revision 1
p3d4c2wreportroute-404event_outboxinvestigationread-only2026-05-10

P3D4C2W — User Visible Route 404 Investigation Report

Date: 2026-05-10
Mode: READ-ONLY investigation
Hard boundaries honored: NO_DEPLOY=true, NO_RESTART=true, NO_CODE_CHANGE=true, NO_DIRECTUS_MUTATION=true, NO_PG_MUTATION=true, NO_TABLE_REGISTRY_MUTATION=true, NO_PERMISSION_CHANGE=true, NO_AUTO_FIX=true
Result: ROUTE_PUBLIC_200_BUT_TABLE_DISPLAY_BLOCKED
Root cause classification: GENERATED_MAP_STALE_BLOCKING_PUBLIC


Step 0 — Foundation

3 câu Tuyên ngôn

  1. Vĩnh viễn?
    Investigation does not patch a single URL. Root issue is that HTTP 200 smoke did not verify the user-visible table marker and event_outbox is absent from the Nuxt entityType -> table_id map. Permanent fix must make registry display driven by generated/metadata map and require table marker smoke, not just HTTP status.

  2. Nhầm được không?
    Current system can be mistaken: dynamic Nuxt route returns 200 fallback for unknown entity types. This allowed /knowledge/registries/event_outbox to pass status smoke while still showing "Chưa có bảng registry..." instead of DirectusTable. Blocking mechanism needed: smoke must assert tbl_event_outbox/known page content/table rendering, and generated map must include published table registry rows.

  3. 100% tự động?
    Not currently complete. The route exists automatically, but table resolution depends on a static tableIdMap; event_outbox is published in Directus but absent in runtime/source/build map. Next pack must refresh/fix generated map or route resolution so a published registry row appears without manual URL guessing.

Files/docs read

  • .claude/skills/incomex-rules.md
  • search_knowledge("operating rules SSOT") -> knowledge/dev/ssot/operating-rules.md, OR v7.58, 2026-05-01
  • search_knowledge("hiến pháp v4.0 constitution") -> knowledge/dev/laws/constitution.md, Constitution v4.6.3 enacted
  • search_knowledge("Điều 44 triển khai notification display event_outbox route registry user visible 404")
  • knowledge/dev/laws/dieu44-trien-khai/reports/p3d4c2u-resume-notification-display-report.md via Agent Data
  • knowledge/dev/laws/dieu44-trien-khai/prompts/p3d4c2u-resume-notification-display-prompt-review.md via Agent Data

Mission design checkpoint

  • Goal: investigate discrepancy between previous Agent smoke PASS and user-visible 404/non-display.
  • Method: read prior report/prompt, inspect nginx public base, curl public routes, browser-render safe marker check, inspect Nuxt source/build map, inspect Directus row id=21 read-only.
  • Preconditions: public nginx route reachable; Directus public proxy reachable; no mutation needed.
  • Roadmap: investigation only; next pack handles fix if needed.

Step 1 — Prior Report And Prompt Evidence

Agent Data prior report snippet:

route=/knowledge/registries/event_outbox
route_http_status=200
smoke_scan=PASS

Prior prompt/review state found through Agent Data:

live route smoke `/knowledge/registries/event_outbox`
tbl_event_outbox.status=draft
notification_display=paused

Interpretation: previous Agent smoke used the intended route path, but the accepted evidence was status-focused. It did not prove that SharedDirectusTable rendered with tbl_event_outbox.


Step 2 — Public Base URL Served By Nginx

Read-only source: infra/nginx/conf.d/default.conf.

Relevant nginx evidence:

server_name vps.incomexsaigoncorp.vn;

location / {
    proxy_pass http://nuxt_backend;
}

Public base URL:

https://vps.incomexsaigoncorp.vn

Directus public API is path-stripped under:

https://vps.incomexsaigoncorp.vn/directus/

No secret/token was printed.


Step 3 — Public Curl Matrix

Command surface:

curl -k -sS -L --max-time 30 https://vps.incomexsaigoncorp.vn/<path>
safe scan only: status, final URL, byte count, and markers

Curl from local public network:

Path HTTP Final URL Content-Type Bytes event_outbox marker DirectusTable/fallback marker 404 marker Nuxt marker
/knowledge/registries/event_outbox 200 https://vps.incomexsaigoncorp.vn/knowledge/registries/event_outbox not present in response headers 256948 no no no yes
/knowledge/registries/event_outbox/ 200 https://vps.incomexsaigoncorp.vn/knowledge/registries/event_outbox/ not present in response headers 256949 no no no yes
/knowledge/registries/event-outbox 200 https://vps.incomexsaigoncorp.vn/knowledge/registries/event-outbox not present in response headers 256948 no no no yes
/knowledge/registries/event_outboxes 200 https://vps.incomexsaigoncorp.vn/knowledge/registries/event_outboxes not present in response headers 256976 no no no yes
/knowledge/registries/system_issue 200 https://vps.incomexsaigoncorp.vn/knowledge/registries/system_issue not present in response headers 271078 no no no yes
/knowledge/registries/system_issue/ 200 https://vps.incomexsaigoncorp.vn/knowledge/registries/system_issue/ not present in response headers 271080 no no no yes

Curl from VPS itself over public URL:

PATH=/knowledge/registries/event_outbox STATUS=200 FINAL=https://vps.incomexsaigoncorp.vn/knowledge/registries/event_outbox BYTES=256948 MARKER_404=no MARKER_NUXT=yes
PATH=/knowledge/registries/event_outbox/ STATUS=200 FINAL=https://vps.incomexsaigoncorp.vn/knowledge/registries/event_outbox/ BYTES=256949 MARKER_404=no MARKER_NUXT=yes
PATH=/knowledge/registries/event-outbox STATUS=200 FINAL=https://vps.incomexsaigoncorp.vn/knowledge/registries/event-outbox BYTES=256948 MARKER_404=no MARKER_NUXT=yes
PATH=/knowledge/registries/event_outboxes STATUS=200 FINAL=https://vps.incomexsaigoncorp.vn/knowledge/registries/event_outboxes BYTES=256976 MARKER_404=no MARKER_NUXT=yes
PATH=/knowledge/registries/system_issue STATUS=200 FINAL=https://vps.incomexsaigoncorp.vn/knowledge/registries/system_issue BYTES=271078 MARKER_404=no MARKER_NUXT=yes
PATH=/knowledge/registries/system_issue/ STATUS=200 FINAL=https://vps.incomexsaigoncorp.vn/knowledge/registries/system_issue/ BYTES=271080 MARKER_404=no MARKER_NUXT=yes

Header sample:

URL=https://vps.incomexsaigoncorp.vn/knowledge/registries/event_outbox
HTTP/1.1 200 OK
x-powered-by: Nuxt

URL=https://vps.incomexsaigoncorp.vn/knowledge/registries/system_issue
HTTP/1.1 200 OK
x-powered-by: Nuxt

Conclusion from curl: public route is not returning HTTP 404 on the correct nginx host. Curl alone does not show table rendering markers because the response is largely Nuxt app shell/SSR payload.


Step 4 — Browser User-Visible Marker Check

Read-only Playwright check against the same public host:

{"path":"/knowledge/registries/event_outbox","status":200,"final":"https://vps.incomexsaigoncorp.vn/knowledge/registries/event_outbox","title":"Registry - Incomex AI Portal","markers":{"event_outbox":true,"directusTableOrFallback":true,"notFound":false,"nuxtError":false},"textSampleSafe":"Incomex AI Portal Knowledge Modules Tasks Workflows Registries Pivot Laws Let's Talk Login ← Danh mục hệ thống LAYER 3 event_outbox Chưa có bảng registry cho loại \"event_outbox\". Vui lòng liên hệ admin. MENU Copyright © 1988 - 2026 Incomex AI Portal. All rights reserved. Site powered by Directus and"}
{"path":"/knowledge/registries/event_outbox/","status":200,"final":"https://vps.incomexsaigoncorp.vn/knowledge/registries/event_outbox/","title":"Registry - Incomex AI Portal","markers":{"event_outbox":true,"directusTableOrFallback":true,"notFound":false,"nuxtError":false},"textSampleSafe":"Incomex AI Portal Knowledge Modules Tasks Workflows Registries Pivot Laws Let's Talk Login ← Danh mục hệ thống LAYER 3 event_outbox Chưa có bảng registry cho loại \"event_outbox\". Vui lòng liên hệ admin. MENU Copyright © 1988 - 2026 Incomex AI Portal. All rights reserved. Site powered by Directus and"}
{"path":"/knowledge/registries/event-outbox","status":200,"final":"https://vps.incomexsaigoncorp.vn/knowledge/registries/event-outbox","title":"Registry - Incomex AI Portal","markers":{"event_outbox":false,"directusTableOrFallback":true,"notFound":false,"nuxtError":false},"textSampleSafe":"Incomex AI Portal Knowledge Modules Tasks Workflows Registries Pivot Laws Let's Talk Login ← Danh mục hệ thống LAYER 3 event-outbox Chưa có bảng registry cho loại \"event-outbox\". Vui lòng liên hệ admin. MENU Copyright © 1988 - 2026 Incomex AI Portal. All rights reserved. Site powered by Directus and"}
{"path":"/knowledge/registries/event_outboxes","status":200,"final":"https://vps.incomexsaigoncorp.vn/knowledge/registries/event_outboxes","title":"Registry - Incomex AI Portal","markers":{"event_outbox":true,"directusTableOrFallback":true,"notFound":false,"nuxtError":false},"textSampleSafe":"Incomex AI Portal Knowledge Modules Tasks Workflows Registries Pivot Laws Let's Talk Login ← Danh mục hệ thống LAYER 3 event_outboxes Chưa có bảng registry cho loại \"event_outboxes\". Vui lòng liên hệ admin. MENU Copyright © 1988 - 2026 Incomex AI Portal. All rights reserved. Site powered by Directus"}
{"path":"/knowledge/registries/system_issue","status":200,"final":"https://vps.incomexsaigoncorp.vn/knowledge/registries/system_issue","title":"Vấn đề Hệ thống - Incomex AI Portal","markers":{"event_outbox":false,"directusTableOrFallback":false,"notFound":false,"nuxtError":false},"textSampleSafe":"Incomex AI Portal Knowledge Modules Tasks Workflows Registries Pivot Laws Let's Talk Login ← Danh mục hệ thống LAYER 2 Vấn đề Hệ thống — Phân loại Gom nhóm theo phân loại nguyên nhân (issue_class — Điều 31 §IV.6). Tổng: 16266 issues trong 8 nhóm. 16266 Tổng issues 13744 CRITICAL 2520 WARNING 8 Nhóm "}
{"path":"/knowledge/registries/system_issue/","status":200,"final":"https://vps.incomexsaigoncorp.vn/knowledge/registries/system_issue/","title":"Vấn đề Hệ thống - Incomex AI Portal","markers":{"event_outbox":false,"directusTableOrFallback":false,"notFound":false,"nuxtError":false},"textSampleSafe":"Incomex AI Portal Knowledge Modules Tasks Workflows Registries Pivot Laws Let's Talk Login ← Danh mục hệ thống LAYER 2 Vấn đề Hệ thống — Phân loại Gom nhóm theo phân loại nguyên nhân (issue_class — Điều 31 §IV.6). Tổng: 16266 issues trong 8 nhóm. 16266 Tổng issues 13744 CRITICAL 2520 WARNING 8 Nhóm "}

User-visible conclusion:

  • Correct public URL is https://vps.incomexsaigoncorp.vn/knowledge/registries/event_outbox.
  • It does not 404 on the checked public surface.
  • It does not render the event_outbox table.
  • It renders the fallback: Chưa có bảng registry cho loại "event_outbox". Vui lòng liên hệ admin.

Step 5 — Nuxt Route Source And Build Artifact

Source file:

web/pages/knowledge/registries/[entityType]/index.vue

Relevant route logic:

const entityType = computed(() => route.params.entityType as string);

const tableIdMap: Record<string, string> = {
  catalog: 'tbl_meta_catalog',
  table: 'tbl_table_registry',
  ...
  changelog: 'tbl_registry_changelog',
  system_issue: 'tbl_system_issues',
};

const tableId = computed(() => tableIdMap[entityType.value] || '');

<SharedDirectusTable v-if="tableId" :table-id="tableId" ... />
<div v-else>Chưa có bảng registry cho loại "{{ entityType }}". Vui lòng liên hệ admin.</div>

Finding:

event_outbox is absent from tableIdMap in source.
tbl_event_outbox is absent from tableIdMap in source.
system_issue -> tbl_system_issues is present.

Build route manifest:

path: "/knowledge/registries/:entityType()/:id()"
path: "/knowledge/registries/:entityType()"
path: "/knowledge/registries/system_issue"

Build artifact:

web/.output/server/chunks/build/index-CE4tLNXH.mjs

Build artifact map:

const tableIdMap = {
  catalog: "tbl_meta_catalog",
  table: "tbl_table_registry",
  ...
  changelog: "tbl_registry_changelog",
  system_issue: "tbl_system_issues"
};
const tableId = computed(() => tableIdMap[entityType.value] || "");

Finding:

event_outbox is absent from production build artifact.
tbl_event_outbox is absent from production build artifact.

No fallback was found that could make internal route 200 but public route 404. The fallback makes unknown entity types HTTP 200 with a user-facing warning, which is exactly what the browser check shows.


Step 6 — Directus Table Registry Row id=21 Read-Only

Public Directus proxy read:

GET https://vps.incomexsaigoncorp.vn/directus/items/table_registry/21?fields=id,status,page_url,collection,table_id,name
HTTP=200
{"id":21,"status":"published","page_url":"/knowledge/registries/event_outbox","collection":"event_outbox","table_id":"tbl_event_outbox","name":"Hộp thư sự kiện"}

Field config read:

HTTP=200
{"fieldKeys":["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"],"default_sort":"-occurred_at","rows_per_page":50,"enable_search":true}

Public Directus read with registry allowlisted fields:

GET /directus/items/event_outbox?limit=1&fields=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&sort=-occurred_at
HTTP=200
{"data_is_array":true,"count":1,"first_keys":["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"]}

Control probe showing permission is field-restricted as expected:

GET /directus/items/event_outbox?limit=1&fields=id,occurred_at,event_domain,event_type,event_stream,status
HTTP=403
{"error":["You don't have permission to access field \"status\" in collection \"event_outbox\" or it does not exist. Queried in root."]}

Interpretation:

  • table_registry id=21 is published.
  • page_url points to /knowledge/registries/event_outbox.
  • collection=event_outbox.
  • table_id=tbl_event_outbox.
  • Public Directus permission allows the registry-configured event_outbox fields.
  • Permission is not the blocker for table display.

Step 7 — Comparison

URL Agent smoke used

/knowledge/registries/event_outbox

Prior report only shows route path and HTTP 200; no evidence found that it asserted user-visible SharedDirectusTable, tbl_event_outbox, or the Vietnamese table name.

URL user-facing public should use

https://vps.incomexsaigoncorp.vn/knowledge/registries/event_outbox

Difference analysis

Dimension Finding
Base host Correct public host is https://vps.incomexsaigoncorp.vn; nginx serves it to nuxt_backend.
Nginx path /knowledge/... goes through location / to Nuxt. No path stripping issue found.
Trailing slash Both /event_outbox and /event_outbox/ return 200 and same fallback.
Auth Page is public. Directus row and allowlisted fields are public-readable.
Redirect No redirect for tested URLs; final URL equals requested URL.
Previous smoke Used correct route path but wrong acceptance surface/criteria: HTTP 200 can mean fallback, not table displayed.

Step 8 — Root Cause And Next Pack

Root cause classification

Primary:

GENERATED_MAP_STALE_BLOCKING_PUBLIC

Supporting:

AGENT_PREVIOUS_SMOKE_USED_WRONG_SURFACE = false as a host/path claim, but true as an acceptance-criteria problem.

Precise root cause:

table_registry id=21 is published and points to /knowledge/registries/event_outbox, but Nuxt route resolution still uses a static tableIdMap that does not include event_outbox -> tbl_event_outbox in either source or production build artifact. Dynamic route therefore resolves HTTP 200 but renders fallback instead of SharedDirectusTable.

Not supported by evidence:

ROUTE_URL_WRONG=false
PUBLIC_NGINX_PATH_MISMATCH=false
NUXT_ROUTE_RESOLUTION_FAIL=false for HTTP route matching; true only for entityType -> table id resolution
AUTH_OR_REDIRECT_SURFACE=false
AGENT_PREVIOUS_SMOKE_USED_WRONG_SURFACE=false for base URL/path

Smallest proposed fix, not executed

Do not create a custom page. Smallest durable fix should be in the existing generated/dynamic table map path:

Ensure generated table map includes event_outbox -> tbl_event_outbox for published table_registry rows, rebuild/deploy, then smoke must assert user-visible table marker.

Acceptance criteria for next pack:

curl/browser public URL -> HTTP 200
final URL unchanged
browser text/table marker includes Hộp thư sự kiện or tbl_event_outbox-backed columns
fallback marker "Chưa có bảng registry" absent
Directus allowlisted event_outbox fields return HTTP 200
unsafe/denied fields remain 403

next_required_pack

D28_GENERATED_MAP_REFRESH_PACK

If the refresh pack discovers no separate generated-map tool exists and the static map is the only implementation, then escalate to:

D28_ROUTE_RESOLUTION_FIX

Final Status

phase_status=PASS_INVESTIGATION_COMPLETE
public_base_url=https://vps.incomexsaigoncorp.vn
correct_public_url=https://vps.incomexsaigoncorp.vn/knowledge/registries/event_outbox
public_route_404=false
user_visible_table_rendered=false
user_visible_fallback_rendered=true
directus_table_registry_id_21=published
directus_event_outbox_allowlisted_read=PASS
nuxt_source_has_event_outbox_mapping=false
nuxt_build_has_event_outbox_mapping=false
root_cause=GENERATED_MAP_STALE_BLOCKING_PUBLIC
next_required_pack=D28_GENERATED_MAP_REFRESH_PACK
mutation_performed=false
deploy_performed=false
restart_performed=false
code_change_performed=false
Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/reports/p3d4c2w-user-visible-route-404-investigation-report.md