KB-26A2

S146-M3 Codex Nuxt Investigation

25 min read Revision 1
reports146m3codexinvestigationnuxtregistries

S146-M3 — Codex Nuxt Registries Investigation

Agent: Codex CLI Date: 2026-03-29 Scope: Investigation only. No repo code edits. No merge.


Step 0 Checkpoint

  • OR SSOT read via search_knowledge("operating rules SSOT").
  • Required quote:

v7.5 | 2026-03-29 — S146-M2 DONE. v_registry_counts TABLE+trigger (Directus không serve VIEW). Data KHỚP. Chờ user verify UI.

  • .claude/skills/incomex-rules.md read in full.
  • Anti-patterns SSOT read. Relevant items:
    • AP-11: Sáng tác Nuxt page cho collection mới ... Fix: PG+Directus+meta_catalog → Nuxt TỰ HIỆN. §0-BA.
    • AP-12: Giám sát báo PASS nhưng số sai ... Fix: DOT verify accuracy PHẢI so COUNT(*) thật vs record_count...

Executive Summary

  1. Current source code DOES create phantom/synthetic rows in Nuxt. CAT-SPE, CAT-ORP, CAT-PHA, CAT-UNM, and CAT-017 are explicitly constructed in [web/pages/knowledge/registries/index.vue](file:line unavailable in Agent Data; see evidence tables below) and appended into tableRows.
  2. Current source code does NOT show evidence of “read 2 count sources then add them together” for CAT-ALL/MOL/CMP/MAT. The current bug path is different: the page reads meta_catalog, filters to status='active', recomputes summary rows client-side from detail rows, and hardcodes CAT-ALL -> atom.
  3. Current live SSR output matches current source code exactly. At 2026-03-29 09:30-09:34 ICT, the live page rendered CAT-ALL=33172, CAT-MOL=645, CAT-CMP=326, CAT-MAT=0, CAT-ORP=5, CAT-PHA=7, CAT-UNM=122, CAT-017=112, and 147 main-table rows. Those numbers are fully explained by the current code.
  4. The user-provided earlier snapshot (CAT-ALL=44413, UI 77729, orphan 21823) is stale relative to the current production state on 2026-03-29. During this investigation, CAT-ALL on Directus/PG changed to 34319, and health.totalGap changed to 21825.
  5. Foundation is not fully “KHỚP” anymore. As of 2026-03-29 09:33 ICT, v_registry_counts.cross_check='LỆCH' for CAT-ALL, even though meta_catalog.record_count and v_registry_counts.record_count are both 34319.

1. PG COUNT(*) vs Directus API Count

Answer: Yes, for the 7 base collections requested, PG COUNT(*) matches Directus API count at the time of investigation. collection_registry required Directus internal bearer-token access because the public proxy returned FORBIDDEN.

Collection PG COUNT(*) Directus API count 2 số khớp? Evidence
modules 5 5 Yes PG: docker exec postgres psql ...; Directus: /api/directus/items/modules?aggregate[count]=*
dot_tools 152 152 Yes same
tasks 10 10 Yes same
collection_registry 145 145 Yes PG: COUNT(*); Directus internal: Authorization: Bearer $NUXT_DIRECTUS_SERVICE_TOKEN
workflows 2 2 Yes same
meta_catalog 152 152 Yes same
v_registry_counts 152 152 Yes same

PG evidence output:

collection_registry|145
dot_tools|152
meta_catalog|152
modules|5
tasks|10
v_registry_counts|152
workflows|2

Directus API evidence output:

modules|5
dot_tools|152
tasks|10
workflows|2
meta_catalog|152
v_registry_counts|152
collection_registry|145   (via Directus internal bearer token)

2. meta_catalog.record_count vs PG Source-Equivalent Counts

Answer: For direct one-collection rows (CAT-006, CAT-008, CAT-009) they match PG COUNT(*). For virtual rows (CAT-ALL, CAT-MOL, CAT-CMP, CAT-MAT), the stored aggregate rows in PG currently match the summed managed detail rows in PG.

Code PG source-equivalent in PG meta_catalog.record_count Khớp? Evidence
CAT-006 152 152 Yes dot_tools COUNT(*)
CAT-008 145 145 Yes collection_registry COUNT(*)
CAT-009 10 10 Yes tasks COUNT(*)
CAT-CMP 326 326 Yes compound managed detail sum
CAT-MAT 55 55 Yes material managed detail sum
CAT-MOL 766 766 Yes molecule managed detail sum (645 active + 121 published)
CAT-ALL 34319 34319 Yes managed detail sum across all statuses

Current PG/meta output (2026-03-29 09:33 ICT):

CAT-006|152|151|0|active
CAT-008|145|145|0|active
CAT-009|10|9|0|active
CAT-ALL|34319|44044|0|active
CAT-CMP|326|321|0|active
CAT-MAT|55|55|0|active
CAT-MOL|766|755|0|active

Important clarification: this is not the same as the earlier stale snapshot CAT-ALL=44413. Current production data is different.


3. v_registry_counts.record_count vs meta_catalog.record_count

Answer: The numeric counts currently match for all 7 rows checked. However, v_registry_counts.cross_check is now LỆCH for CAT-ALL.

Code meta_catalog.record_count v_registry_counts.record_count v_registry_counts.cross_check Result
CAT-006 152 152 KHỚP OK
CAT-008 145 145 KHỚP OK
CAT-009 10 10 KHỚP OK
CAT-ALL 34319 34319 LỆCH Numeric match, status not OK
CAT-CMP 326 326 KHỚP OK
CAT-MAT 55 55 KHỚP OK
CAT-MOL 766 766 KHỚP OK

Evidence output:

CAT-006|152|0|KHỚP
CAT-008|145|0|KHỚP
CAT-009|10|0|KHỚP
CAT-ALL|34319|0|LỆCH
CAT-CMP|326|0|KHỚP
CAT-MAT|55|0|KHỚP
CAT-MOL|766|0|KHỚP

4. Nuxt registries/index.vue reads which collection?

Answer: The main page directly calls readItems('meta_catalog').

Bước File:Line Code (verbatim) Collection đọc Biến kết quả
Main fetch web/pages/knowledge/registries/index.vue:109-118 readItems('meta_catalog' as any, { ... filter: { identity_class: { _in: ['managed', 'log'] }, registry_collection: { _nnull: true }, status: { _eq: 'active' } }, limit: -1 }) meta_catalog catalog -> managedDetails / logDetails

Direct evidence:

const catalog = await $directus.request(
	readItems('meta_catalog' as any, {
		fields: ['code', 'name', 'entity_type', 'composition_level', 'identity_class', 'record_count', 'active_count', 'orphan_count', 'baseline_count'],
		filter: {
			identity_class: { _in: ['managed', 'log'] },
			registry_collection: { _nnull: true },
			status: { _eq: 'active' },
		},
		limit: -1,
	}),
);

5. Does Nuxt read more than 1 collection?

Answer: Yes. The registries page as a whole reads multiple collections, both directly and through Nuxt server endpoints.

Fetch point File:Line Code / endpoint Collections involved
Main table index.vue:109-118 readItems('meta_catalog') meta_catalog
Changelog index.vue:343-356 Promise.all([readItems('registry_changelog'), readItems('meta_catalog')]) registry_changelog, meta_catalog
DOT coverage index.vue:460-462 readItems('dot_tools') dot_tools
Species row index.vue:476-489 Promise.all([readItems('entity_species'), readItems('birth_registry')]) entity_species, birth_registry
Health row index.vue:418, health.get.ts:51-123 $fetch('/api/registry/health') species_collection_map, collection_registry, birth_registry, then each governed collection via items/${cn}
Unmanaged row index.vue:432, unmanaged.get.ts:38-58 $fetch('/api/registry/unmanaged') collection_registry
System issues row index.vue:446, system-issues.get.ts:37-66 $fetch('/api/registry/system-issues') system_issues
Composition column index.vue:190, composition.get.ts:32-95 $fetch('/api/registry/composition') entity_species, species_collection_map

Key point: current source is not a simple meta_catalog -> render page.


6. Merge / concat / spread arrays?

Answer: Yes, but not in the specific “read 2 count sources and add them together” pattern previously alleged. The current merge is:

File:Line Code merge/concat (verbatim) Arrays merged Hậu quả
web/pages/knowledge/registries/index.vue:323-326 return [...data.summaries, speciesRow, orphanRow, phantomRow, unmanagedRow, systemIssuesRow, ...data.details, coverageRow].map((row, idx) => ({ ...row, stt: idx + 1 })) summaries + 5 synthetic rows + details + coverageRow Main table contains synthetic rows and recomputed summaries
web/pages/knowledge/registries/index.vue:151-166 levelDetails.reduce(...) Detail rows only Summary counts are recomputed in Nuxt instead of using virtual rows from Directus

Direct evidence for recomputation:

const levelDetails = managedDetails.filter((d) => d.composition_level === level);
...
record_count: levelDetails.reduce((s: number, d: any) => s + d.record_count, 0),
orphan_count: levelDetails.reduce((s: number, d: any) => s + d.orphan_count, 0),

Important conclusion: I found no current-source evidence of concatenating meta_catalog counts with v_registry_counts counts for CAT-ALL/MOL/CMP/MAT.


7. Where are CAT-ORP / CAT-PHA / CAT-SPE / CAT-UNM created?

Answer: They are explicitly created in web/pages/knowledge/registries/index.vue. Current source actually creates 5 synthetic rows: the 4 user named, plus CAT-017.

Dòng ảo File:Line Code tạo (verbatim) Logic
CAT-SPE index.vue:244-258 code: 'CAT-SPE' row 7 species classification
CAT-ORP index.vue:262-275 code: 'CAT-ORP' health/orphan summary row
CAT-PHA index.vue:276-289 code: 'CAT-PHA' health/phantom summary row
CAT-UNM index.vue:290-304 code: 'CAT-UNM' unmanaged summary row
CAT-017 index.vue:308-322 code: 'CAT-017' system issues summary row

Verbatim evidence:

code: 'CAT-SPE'
code: 'CAT-ORP'
code: 'CAT-PHA'
code: 'CAT-UNM'
code: 'CAT-017'

This means the “phantom rows” are not mysterious, not build-cache-only, and not hidden middleware injection. They are directly in the page source.


8. Where does UI orphan=21825 come from? What formula?

Answer: It comes from /api/registry/health, specifically totals.totalGap, and the page writes that into the orphan_count column of CAT-ORP.

Bước File:Line Code (verbatim) Meaning
Gap formula web/server/api/registry/health.get.ts:98-104 const gap = Number(sourceCount) - birthCount; ... status: gap === 0 ? 'KHOP' : gap > 0 ? 'ORPHAN' : 'PHANTOM' per-collection gap
Total gap health.get.ts:119-123 totalGap: collections.reduce((s, c) => s + Math.abs(c.gap), 0) summed absolute gap
Row assignment index.vue:269-271 record_count: hd.orphan, orphan_count: hd.totalGap CAT-ORP shows orphan collection count + total gap
Render column index.vue:621-625 {{ row.orphan_count }} table renders 21825 in orphan column

Live endpoint evidence (2026-03-29 09:29:57 ICT):

{"totals":{"khop":11,"orphan":5,"phantom":7,"totalGap":21825}}

So current live UI should show:

  • CAT-ORP.record_count = 5
  • CAT-ORP.orphan_count = 21825

This is not a count of orphan records. It is a summed absolute gap across health rows.


9. Any direct pg/postgres/SQL imports in Nuxt?

Answer: No direct pg / postgres runtime imports found in web/.

grep result:

rg -n "from 'pg'|from \"pg\"|from 'postgres'|from \"postgres\"|require\('pg'\)|require\(\"pg\"\)" web --glob '!web/.nuxt/**' --glob '!web/.output/**'
# no output, exit code 1

So the violation is not direct PG import in Nuxt runtime code. The problem is logic placement and client/server aggregation inside Nuxt.


10. Hardcoded CAT codes / collection names / fixed maps?

Answer: Yes, extensively.

File:Line Hardcode Evidence
web/pages/knowledge/registries/index.vue:10-17 Virtual code map CAT-ALL, CAT-MOL, CAT-CMP, CAT-MAT, CAT-PRD, CAT-BLD
web/pages/knowledge/registries/index.vue:248,266,280,294,312 Synthetic codes CAT-SPE, CAT-ORP, CAT-PHA, CAT-UNM, CAT-017
web/pages/knowledge/registries/[entityType]/index.vue:8-15 Duplicate virtual map same CAT codes
web/pages/knowledge/registries/[entityType]/index.vue:39-58 tableIdMap hardcoded entity_type -> table_id
web/pages/knowledge/registries/all/index.vue:15-33 COLLECTION_MAP hardcoded collection -> codeField/nameField/entityType
web/pages/knowledge/registries/matrix/index.vue:75-91 COLLECTION_ENTITY_MAP hardcoded collection/entity mapping
web/config/detail-sections.ts:228-244 collection maps hardcoded source collections

Conclusion: Current registries implementation is not metadata-driven end-to-end.


11. Client-side / Nuxt-side logic that should be in PG/Directus

Answer: Yes. The following logic currently lives in Nuxt and violates the “Nuxt = màn hình” direction.

File:Line Logic currently in Nuxt Why it should not live here
index.vue:147-166 recompute summary counts by composition level summary rows should come from Directus/PG, not Nuxt reduce()
index.vue:10-17 CAT-ALL -> atom mapping virtual summary semantics should not be hardcoded in page
index.vue:242-323 synthetic registries rows health/species/unmanaged/system rows are injected by page logic
health.get.ts:45-123 health aggregation across many collections this is platform/business logic, not screen logic
unmanaged.get.ts:38-58 governance-role counting belongs in data layer
composition.get.ts:32-95 composition matrix building belongs in metadata/data layer
all/index.vue:75-117 fetch every mapped collection and build atom inventory in Nuxt N+1 / scale risk; should be pre-assembled data
[entityType]/index.vue:39-58 tableIdMap duplicates metadata already available elsewhere

12. Verify: predict UI output from code, then compare with live UI

Answer: Current code predicts the current live SSR page exactly.

Prediction from current source

  • managedDetails source = active managed meta_catalog rows only.
  • Summary rows are recomputed from those details.
  • CAT-ALL is mapped to atom via VIRTUAL_CODE_LEVEL.
  • Published rows are excluded by status: { _eq: 'active' }.
  • Synthetic rows are appended after summaries.
  • tableRows length = 6 summaries + 5 synthetic + 135 details + 1 coverage = 147.

Numeric prediction inputs

Input PG evidence
active managed atom sum 33172
active managed molecule sum 645
active managed compound sum 326
active managed material sum 0
governed birth_registry count 2099
entity_species count 35
health.totals orphan=5, phantom=7, totalGap=21825
unmanaged.totals.total 122
system_issues.totals.all 112
dot_tools count 152

Live SSR verification (https://vps.incomexsaigoncorp.vn/knowledge/registries, Date: Sun, 29 Mar 2026 02:30:12 GMT)

Metric Dự đoán từ code UI thật (SSR HTML) Khớp?
Main-table row count 147 147 Yes
CAT-ALL 33172 33172 Yes
CAT-MOL 645 645 Yes
CAT-CMP 326 326 Yes
CAT-MAT 0 0 Yes
CAT-ORP.record_count 5 5 Yes
CAT-PHA.record_count 7 7 Yes
CAT-UNM.record_count 122 122 Yes
CAT-017.record_count 112 112 Yes

Live HTML evidence snippets:

CAT-ALL ... <span class="font-bold text-gray-900 dark:text-white">33172</span>
CAT-MOL ... <span class="font-bold text-gray-900 dark:text-white">645</span>
CAT-CMP ... <span class="font-bold text-gray-900 dark:text-white">326</span>
CAT-MAT ... <span class="font-bold text-gray-900 dark:text-white">0</span>
CAT-ORP ... <span class="font-bold text-gray-900 dark:text-white">5</span>
CAT-PHA ... <span class="font-bold text-gray-900 dark:text-white">7</span>
CAT-UNM ... <span class="font-bold text-gray-900 dark:text-white">122</span>
CAT-017 ... <span class="font-bold text-gray-900 dark:text-white">112</span>

Conclusion: current investigation is verified. The current live UI is explained by current source.


File Inventory

File Dòng code Fetch gì Logic gì SẠCH/BẨN
web/pages/knowledge/registries/index.vue 717 meta_catalog, registry_changelog, dot_tools, entity_species, birth_registry, 4 server endpoints recompute summaries, synthetic rows, render main table BẨN
web/pages/knowledge/registries/[entityType]/index.vue 375 meta_catalog, system_issues hardcoded virtual/detail routing BẨN
web/pages/knowledge/registries/all/index.vue 237 meta_catalog + every collection in COLLECTION_MAP client-side global atom inventory BẨN
web/server/api/registry/health.get.ts 130 species_collection_map, collection_registry, birth_registry, governed collections orphan/phantom math BẨN
web/server/api/registry/unmanaged.get.ts 64 collection_registry observed/excluded totals BẨN
web/server/api/registry/composition.get.ts 100 entity_species, species_collection_map composition totals BẨN
web/server/api/registry/system-issues.get.ts 76 system_issues row 11 totals BẨN
web/pages/knowledge/registries/[entityType]/[id].vue 387 meta_catalog + collection fallback detail rendering Mixed
web/pages/knowledge/registries/matrix/index.vue 233 server matrix API hardcoded collection/entity map BẨN
web/config/detail-sections.ts 264 none (config) hardcoded collection + section maps BẨN

Git Diff

Commit Date Files Mô tả
a3d5aaa 2026-03-06 registries foundation first live catalog system commit
3d5fd30 2026-03-21 index.vue, species/index.vue, DirectusMatrix.vue added CAT-SPE row 7
63c3338 2026-03-21 index.vue, health.get.ts, unmanaged.get.ts, pages added CAT-ORP, CAT-PHA, CAT-UNM
2725952 2026-03-23 index.vue, system-issues.get.ts added CAT-017 row 11
7c4d382 2026-03-28 registries page/comments switched comments to pivot_count()/meta_catalog path

Diff-stat from first registries commit to HEAD:

34 files changed, 5318 insertions(+), 32 deletions(-)

Principle Violations

File:Line Nguyên tắc Code / behavior Mức
index.vue:147-166 §0-BA Nuxt = màn hình page recomputes summary rows with reduce() High
index.vue:10-17 §0-AU Không hardcode hardcoded virtual code map High
index.vue:242-323 §0-BA page injects 5 synthetic rows High
all/index.vue:75-117 Scale N+1 fetch across all collections Medium
[entityType]/index.vue:39-58 §0-AU hardcoded tableIdMap Medium
health.get.ts:45-123 §0-BA business aggregation moved into Nuxt API layer Medium
composition.get.ts:32-95 §0-BA metadata composition assembled in Nuxt Medium
system-issues.get.ts:37-66 §0-BA summary aggregation in Nuxt endpoint Medium

Flow GỐC vs Flow HIỆN TẠI

Bước Flow chuẩn Flow hiện tại Sai ở đâu
1 PG/Directus prepares summary rows page reads active meta_catalog detail rows page does not trust/render virtual rows directly
2 Nuxt renders what Directus gives page recomputes summaries with reduce() summary semantics shifted into Nuxt
3 No synthetic registry rows page creates CAT-SPE/ORP/PHA/UNM/017 phantom/synthetic rows are in source
4 One data path page + 4 Nuxt APIs + extra reads multi-source page assembly
5 CAT-ALL means “all” page maps CAT-ALL -> atom wrong semantic mapping
6 Render direct list tableRows = [...summaries, synthetic, details, coverage] UI shape is heavily manufactured in page code

Directus ↔ Agent Data

Answer: Agent Data currently contains stale documents that still describe the older 44413 snapshot.

Data Directus / production current Agent Data search result Khớp?
CAT-ALL current count 34319 (meta_catalog) handoff-s142-session.md and s146-m2-registries-display-fix-report still mention 44413 No
CAT-ALL status in v_registry_counts 34319, cross_check=LỆCH prior docs still describe “data khớp 44413” No
CAT-008 record_count 145 registries/meta_catalog/CAT-008 in Agent Data also says 145 Yes

Relevant Agent Data search evidence:

  • search_knowledge("meta_catalog count") returned stale narrative: meta_catalog.record_count can drift and prior documents still describe old states.
  • search_knowledge("s146 m3 nuxt registries investigation cat-all 44413 34319") surfaced handoff-s142-session.md and s146-m2-registries-display-fix-report still anchored on 44413.

Additional Findings

  1. Current production numbers changed during the investigation.
    • Earlier stale docs: CAT-ALL=44413, orphan gap 21823.
    • Current production at investigation end: CAT-ALL=34319 in Directus/PG, live SSR page CAT-ALL=33172, health.totalGap=21825.
  2. Local build cache exists but is not the explanation.
    • web/.nuxt and web/.output both exist locally (Mar 28 11:42).
    • Current live SSR page matches current source logic, so build cache is not needed to explain synthetic rows.
  3. Automation is actively changing counts.
    • VPS cron contains:
*/10 * * * * ... SELECT refresh_meta_catalog_from_pivot();
*/10 * * * * ... SELECT refresh_pivot_results();
0 */3 * * * ... scanner-counts.sh

This explains why counts changed during the session. 4. meta_catalog contains active rows whose registry_collection does not exist in PG:

CAT-030|billing|molecule|active|0
CAT-050|blocks|atom|active|0
CAT-073|help|molecule|active|0
CAT-123|sales|molecule|active|0
CAT-137|website|molecule|active|0

These do not explain the current visible inflation because their record_count is 0, but they are catalog drift.


Conclusion + Estimate

Root cause

Current main-page logic is the root cause of the current UI output: it filters meta_catalog to active managed rows, recomputes summary rows in Nuxt, hardcodes CAT-ALL -> atom, and appends 5 synthetic rows. That is why the live page shows 33172/645/326/0 plus explicit synthetic rows.

Not supported by current source

The earlier hypothesis “Nuxt reads 2 count sources then cộng” is not supported by the current checked source code. The current problem is client-side/page-side aggregation + hardcoded semantic mapping + synthetic row injection.

If a future fix is requested

  • Minimum files that definitely need change: 1 primary file: web/pages/knowledge/registries/index.vue.
  • If removing synthetic-row system cleanly: likely 5-7 files (index.vue plus health.get.ts, unmanaged.get.ts, composition.get.ts, system-issues.get.ts, maybe [entityType]/index.vue and all/index.vue).
  • Delete-only footprint estimate: roughly 90-130 lines in index.vue for summary/synthetic-row logic; 250-400 lines if the supporting synthetic-row endpoints are also removed from the registries root flow.

If reverted to first registries commit a3d5aaa

  • Giữ: initial live-catalog foundation.
  • Mất: all later registries expansion added since 2026-03-06, including species row/page, health rows/pages, unmanaged page, system issues row/page, matrix page, taxonomy pages, all-atoms page, extended detail/config system.

Risk assessment

A future fix that removes Nuxt-side aggregation and synthetic rows should not inherently break enforcement layers 1/2/4. The main risk is product/UX regression for rows/pages that were added as Nuxt-only features. The architectural risk of not fixing is continued drift between Directus summary rows and live UI.