KB-131A rev 2

Codex — Reports refresh + VPS SSOT pre-deploy report

11 min read Revision 2
codexreportslive-refreshvps-ssotpredeployowner-gate2026-07-15da89657

Codex — Reports refresh + VPS SSOT pre-deploy report

Ngày: 2026-07-15
Phạm vi: chỉ Reports; không sửa/ghi dữ liệu Balo.
Trạng thái: READY_FOR_OWNER_GATE — CHƯA DEPLOY / CHƯA RESTART / CHƯA RELOAD.

1. Kết luận ngắn cho Owner

  1. Nút Làm mới trên production hiện chạy thật, không phải chỉ SSR/HTTP 200:
    • Report PG phát request GET /api/reports/live?table_id=tbl_report_pg, HTTP 200, Cache-Control: no-store, max-age=0; UI nhận refreshed_at=2026-07-15T03:32:25.368Z, dòng table=385.
    • Reports Home phát request GET /api/reports/live?table_id=tbl_report_home, HTTP 200/no-store; API có 6 dòng và UI vẫn hiển thị đúng 6 báo cáo. Số Tổng trên UI sau click: Function 654, Table 383, Trigger 410, View 699, Loài 42, PG Census 12.
  2. Production runtime vẫn là artifact GitHub cũ ngày 2026-07-11, nhưng nó đang chứa bản vá Reports và đang hoạt động. Source VPS thật trước nhiệm vụ vẫn ở 2f92ecb và thiếu một phần bản vá; vì vậy lần build tương lai từ source VPS sẽ làm lùi Reports nếu không sửa source.
  3. Đã ghép bản vá tối thiểu vào source VPS SSOT, build/typecheck PASS và commit:
    • Nuxt /opt/incomex/docker/nuxt-repo: da89657cf2d3a4751fe01ed95485d0dbad49eb26.
    • DOT /opt/incomex/dot: 5a2e1bf998d89813729d6f10a0d87849075738b9.
  4. Chưa thể tuyên bố toàn bộ hậu quả deploy cũ đã được dọn: compose, nginx và 41 file DOT ngoài phạm vi Reports còn drift. Không tự phục hồi mù.
  5. Không có deployment production nào trong nhiệm vụ này. Runtime mtime vẫn 2026-07-11 04:49:53 +0200; container Nuxt vẫn started 2026-07-11T02:50:33Z.

2. Nền tảng và rào chắn đã đọc

  • .claude/skills/incomex-rules.md: đủ 36 mục/8 bước, quy trình 2 mũ, evidence thật.
  • knowledge/dev/ssot/operating-rules.md v7.58: VPS là SSOT code/runtime; không chắc phải dừng/verify; AP-CLOSE cần evidence và commit.
  • knowledge/dev/ssot/vps/vps-architecture.md v2.0: production duy nhất trên VPS, PG16 là DB duy nhất.
  • knowledge/dev/ssot/vps/vps-operating-rules.md v1.0: HTTP 200 không chứng minh end-user đúng; deploy cần review/gate.
  • knowledge/dev/laws/constitution.md v4.6.3: Assembly First, DOT theo cặp, PG First/Native/Driven, metadata hơn code.
  • Báo cáo sự cố codex-vps-ssot-overwrite-incident-2026-07-11.md và runbook Reports MVP.

3 câu Tuyên ngôn

  1. Vĩnh viễn: bản vá nằm trong source VPS và khuôn Reports dùng chung; báo cáo có view_sql tương lai tự dùng đường live/no-store.
  2. Nhầm được không: nguồn build phải là /opt/incomex/docker/nuxt-repo; GitHub/local không được phủ ngược.
  3. 100% tự động: publisher gọi verifier ghép cặp; verifier fail-closed khi thời gian không tiến, API khác PG-live, hoặc SSR thiếu số/timestamp theo contract.

3. ĐÃ CHỨNG MINH TRÊN PRODUCTION

3.1 API sống, no-store và chỉ nhận table_id

Hai lần gọi Report PG cách nhau khoảng 2 giây:

A generated_at=2026-07-15T03:30:55.909Z refreshed_at=2026-07-15T03:30:55.686Z table=385 count=12
B generated_at=2026-07-15T03:30:58.060Z refreshed_at=2026-07-15T03:30:58.017Z table=385 count=12
Cache-Control: no-store, max-age=0
page_url_only_http=400
no_identifier_http=400

3.2 Cú bấm thật — Report PG

Playwright headless trên VPS, bấm đúng button Làm mới:

request=https://vps.incomexsaigoncorp.vn/api/reports/live?table_id=tbl_report_pg
http=200
cache-control=no-store, max-age=0
generated_at=2026-07-15T03:32:25.394Z
refreshed_at=2026-07-15T03:32:25.368Z
api_count=12
api_table_total=385
request_count=1
ui_contains_refreshed_at=true
ui_changed=true

3.3 Cú bấm thật — Reports Home

request=https://vps.incomexsaigoncorp.vn/api/reports/live?table_id=tbl_report_home
http=200
cache-control=no-store, max-age=0
generated_at=2026-07-15T03:52:07.741Z
api_count=6
UI totals after click: 654, 383, 410, 699, 42, 12

Home không hiển thị timestamp theo contract hiện tại; vì vậy verifier bắt generated_at tiến và đối chiếu toàn bộ rows/totals với PG/SSR, nhưng chỉ bắt timestamp SSR ở report có trường refreshed_at (như PG Census).

3.4 Verifier chạy với PG thật

PASS source=live_view_sql table_id=tbl_report_pg api_count=12 pg_count=12 generated_at=2026-07-15T03:51:13.405Z ssr_refreshed_at=2026-07-15T03:51:14.239Z
PASS source=live_view_sql table_id=tbl_report_home api_count=6 pg_count=6 generated_at=2026-07-15T03:51:18.905Z ssr_refreshed_at=not_exposed_by_contract

Lỗi thật đã phát hiện và sửa: verifier cũ tự FAIL Home vì chỉ cho /reports/*, không cho route gốc /reports; sau vá hai contract cùng PASS.

4. Forensic: thứ đã bị phủ/lùi

4.1 Nuxt source và runtime lệch nhau

  • Source VPS trước vá: 2f92ecbfb8894432b03efb1edd217957f30f8587, main...origin/main [ahead 28, behind 13].
  • Runtime: image Artifact Registry .../web-test/nuxt-ssr:latest, mounted /opt/incomex/deploys/nuxt-output, started 2026-07-11T02:50:33Z.
  • Runtime không được build từ source VPS hiện hành. Sau build thử, source .output/nitro.json có mtime 2026-07-15 05:49:32 +0200, còn live runtime vẫn 2026-07-11 04:49:53 +0200 — chứng minh chưa deploy.

4.2 Compose còn drift nguy hiểm

So với VPS root Git HEAD:

docker/docker-compose.yml | 80 insertions, 61 deletions

Thay đổi hành vi chính:

  • Agent Data image local → Artifact Registry cũ.
  • Memory mặc định 2560m → cố định 1536m.
  • Mất biến kết nối Directus DB và PG của Agent Data.
  • Nuxt image local → Artifact Registry cũ.
  • Có thêm RP_PG env cho Reports.

Các container Agent Data/Directus/Postgres chưa bị recreate bởi nhiệm vụ này; Agent Data live vẫn chạy agent-data-local:latest. Tuy nhiên compose hiện tại là “mìn” cho lần recreate sau.

4.3 Nginx còn drift

docker/nginx/conf.d/default.conf | 26 insertions, 25 deletions
mtime current: 2026-07-11 04:50:30 +0200

Route Reports production đang chạy, nhưng file đã bị phủ cùng vòng deploy và còn thay đổi ở .well-known, UI preview và comment/route sections. Chưa restore vì cần đối chiếu từng route với backup/live inventory.

4.4 DOT còn drift ngoài Reports

166 file trong /opt/incomex/dot/bin có mtime quanh 2026-07-11 04:47:37
41 tracked file còn modified
diff còn lại: 491 insertions, 1171 deletions

Ba file Reports đã được review/commit riêng. 41 file còn lại không được tự khôi phục vì working tree vốn có thay đổi trước sự cố và deploy không tạo backup DOT đầy đủ.

5. Bản vá đã đưa vào SOURCE VPS SSOT

5.1 Nuxt commit da89657

web/components/shared/DirectusTable.vue | nút gọi handleRefresh thật + emit refreshed
web/composables/useDirectusTable.ts      | client fetch cache: no-store
web/pages/reports/index.vue              | Home refresh cả rows và Tổng theo route metadata
web/server/api/reports/live.get.ts       | no-store + table_id-only + one-statement SELECT/WITH
web/tests/e2e/reports-live.spec.ts       | 3 contract tests (Home, PG, base-table)
151 insertions, 48 deletions

SHA-256 chính:

live.get.ts             537040f5e6c9f024d822bfb90ca778f360045f87f6368104c036cdd572eb1053
useDirectusTable.ts     92c4c71a4bda72b4cfed6a1627365aae89273f292913eace996d4a1fb70dbb07
DirectusTable.vue       b5c86d6fdb52597b00c462e6db76bca59b28b4d654018135e3f07d35241088ae
reports/index.vue       29ff46e109d80522e5963dc2850529ef132aff529a211dff5cd1d44bb482bf02

5.2 DOT commit 5a2e1bf

  • dot-report-publish: computed report dùng view_sql live, probe transaction READ ONLY, không tạo/truncate snapshot; gọi verifier.
  • dot-list-publish: base-table path và gọi verifier.
  • dot-table-ui-verify: so API rows với PG-live, bắt no-store/time progression/SSR totals; hiểu cả /reports/reports/*.
3 files changed, 288 insertions, 262 deletions
dot-table-ui-verify sha256=e55b9372af2be6a3e20712a6bd1e0066479b4f340b848da4ecec324dfe5b8d1e

Backup trước vá: /opt/incomex/evidence/reports-refresh-predeploy-20260715T033700Z.

6. Verify bản source — CHƯA PHẢI production deploy

git diff --check: PASS
eslint changed/new route + Home + E2E: PASS
nuxt typecheck: PASS
nuxt build: PASS — Build complete
dot-report-publish report_pg --dry-run: PASS, zero writes
dot-report-publish report_home --dry-run: PASS, zero writes
bash -n verifier/publishers: PASS

Lưu ý minh bạch: lượt ESLint đầu trên toàn bộ ba file core báo 330 vấn đề formatting/lint trong source hiện hữu (đa số ngoài hunk vá), nên không dùng nó làm PASS. Các file/hunk mới trọng yếu được lint riêng và build/typecheck toàn dự án đã PASS. Build có warning cũ về browserslist, SVG data URI và chunk size; không có build error.

7. Kế hoạch khôi phục an toàn

  1. Giữ đóng băng GitHub/local → VPS.
  2. Trước deploy: snapshot/hash runtime, source commits, compose, nginx, DOT và container inspect; backup rollback mới, không dùng nuxt-output.prev mù.
  3. Nếu Owner duyệt Reports deploy: dùng build từ commit VPS da89657, chỉ thay Nuxt output và recreate duy nhất Nuxt; không chạy migration/DB write, không recreate Agent Data/Directus/Postgres, không rsync DOT/nginx/compose từ GitHub.
  4. Verify hai mũ trên production: click PG + Home, request URL, no-store, generated/refreshed time, UI totals, verifier PG, smoke nội dung các bề mặt liên quan.
  5. Compose/nginx/41 DOT drift là pha phục hồi riêng, forensic từng file và Owner gate; không bulk rollback.
  6. Sau production PASS mới sao ngược VPS → GitHub archival bằng cơ chế không kích hoạt auto-deploy.

8. Owner gate — câu hỏi gom một lượt

  1. Owner có duyệt deploy chỉ Nuxt Reports từ VPS commit da89657, với quy trình backup mới → thay volume output → recreate duy nhất Nuxt → verify nội dung/cú bấm/verifier không?
  2. Owner có duyệt mở lệnh riêng để forensic và phục hồi compose + nginx + 41 DOT còn drift trước bất kỳ lần recreate service nào không? Khuyến nghị: , nhưng tách khỏi deploy Reports.
  3. Sau production PASS, Owner chọn cho phép sao ngược GitHub bằng bundle/archive không kích auto-deploy trước, hay cho phép push sau khi auto-deploy đã bị khóa có bằng chứng?

9. Ranh giới đã giữ

  • Không DDL/DML, không ghi Balo, không sửa trang/data Balo.
  • Không deploy/restart/reload production.
  • Không pull/reset/checkout/rsync mã GitHub cũ lên VPS.
  • Không khôi phục hàng loạt compose/nginx/DOT khi chưa có Owner gate.
  • PIDX council review đã dừng hoàn toàn theo lệnh.