[CLAUDE] Docs: S191 closeout — vá 10 FLAG H24 + gotcha #92 đổi root-cause + RCA P0-BUNDLE + 3 action refine (A7 429→432) [skip ci]

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-08-13 01:58:29 +07:00
parent 305b573f5a
commit d6026b1436
38 changed files with 664 additions and 81 deletions

View File

@ -37,7 +37,7 @@ export type SupplierType = typeof SupplierType[keyof typeof SupplierType]
**Fix:**
- `package.json` engines: `">=20"` (min, không upper)
- `.nvmrc` = `20` cho CI
- `.nvmrc` = `20` (⚠️ CI **không đọc** — xem #92)
- GitHub/Gitea Actions: `actions/setup-node@v4` với `node-version: '20.x'`
## EF Core 10
@ -1459,13 +1459,22 @@ for h in resp.points: # ← .points không phải iterable trực tiếp
**Triệu chứng (2 ca cùng chữ ký):** deploy CI xanh trọn test gate pass, bundle rotate, **byte-match CI↔prod từng byte**, smoke HTTP 200 nhưng mở trang = **trắng màn**. Console: `Uncaught TypeError: Cannot read properties of null (reading 'useState')` ngay dòng đầu bundle. Ca-1 @S189 (P0 sau #486, cả 2 app, outage 15'). Ca-2 @S191 (sau **#600**: fe-user chết / fe-admin sống nhiễm KHÔNG đồng đều).
**Chữ ký đo được:** bundle CI **lệch size local đáng kể CÙNG commit** local build deterministic (2 lần build local ra đúng 1 hash): ca-2 fe-user CI 1.803.881B vs local 1.700.709B (**+103KB**) · fe-admin CI 1.819.492B vs local 1.780.807B (**+39KB**, đang sống nhưng cùng ). `useState null` = React bị resolve trùng/lệch bản trong graph dấu node_modules/npm-cache hỏng trên runner (di chứng đêm disk-0-byte #89, npm-cache SYSTEM 12,76GB ghi d).
**Chữ ký đo được:** bundle CI **lệch size local đáng kể CÙNG commit** local build deterministic (2 lần build local ra đúng 1 hash): ca-2 fe-user CI 1.803.881B vs local 1.700.709B (**+103KB**) · fe-admin CI 1.819.492B vs local 1.780.807B (**+39KB**, đang sống nhưng cùng ). `useState null` = React bị resolve **trùng/lệch bản** trong graph (component render bởi bản React khác bản đang gi dispatcher).
**Vì sao lọt 2 lần:** verify deploy đo **file-integrity** (hash CI-log prod) + **HTTP status** cả hai đều tính chất của FILE, không phải của CHƯƠNG TRÌNH. Bundle nhiễm vẫn thắng mọi phép đo đó. Giả thuyết "root-cause = disk, deploy.yml tự xóa node_modules fresh mỗi run nên #92 đóng được" (S190) bị ca-2 **bác**: xóa node_modules không cứu nếu **npm cache** nguồn nhiễm (`npm ci` kéo lại từ cache hỏng).
**🔴 ROOT-CAUSE THẬT (chốt @S191 bằng commit `305b573f` + run #488):** `.gitea/workflows/deploy.yml` **XOÁ `package-lock.json`** rồi chạy `npm install` **mỗi run resolve LẠI toàn bộ cây theo semver range** (`react ^19.2.5`, `vite ^8.0.9`…) **cùng một commit build ra bundle khác nhau tuỳ registry tại thời điểm chạy**. Lockfile (23/04) thiếu hẳn `@microsoft/signalr` + 9 transitive nên "xoá đi cho lành" từng **có vẻ** hợp thuốc đúng **cập nhật lockfile**, không phải bỏ .
**Fix chuẩn (đã chứng ×2):** build local từ HEAD (env lành) so hash 2 lần build (determinism = control dương) scp `dist/` đè app chết, **GIỮ bundle hỏng trên đĩa** (index.html = rollback 1 giây) verify bằng **console + read_page** (form render, load-mới sạch lỗi), KHÔNG chỉ curl.
🧊 **Giả thuyết đã BỊ BÁC — giữ vết để đừng ai đi lại (2 vòng):** *"root-cause = disk, `deploy.yml` tự xoá `node_modules` fresh mỗi run nên #92 đóng được"* (S190) ca-2 bác. *"npm-cache SYSTEM hỏng (di chứng #89), phải dọn cache rồi mới tin build"* (bản đầu của chính mục này, S191) **bác bằng đo**: run #488 ra **đúng 4/4 hash trùng local mà KHÔNG ai dọn cache lần nào**. Đòn bẩy **lockfile**, không phải cache.
**Luật:** (1) verify deploy FE phải **1 phép đo RUNTIME** (console error sau load / marker JS thực thi) file-integrity + HTTP 200 chưa đủ; (2) bundle CI lệch local >vài KB cùng commit khi local deterministic = **ĐỎ**, chặn trước khi ship; (3) sau sự cố hạ tầng (disk-0, kill-process giữa ghi): coi **mọi cache** của toolchain trên máy đó là nghi phạm — dọn `npm cache` + workspace RỒI MỚI tin build; (4) vá gốc phải **verify bằng dispatch-run + smoke runtime**, tới lúc đó **CẤM push code** (build mới sẽ đè hotfix bằng bundle nhiễm).
**Vì sao lọt 2 lần:** verify deploy đo **file-integrity** (hash CI-log prod) + **HTTP status** cả hai đều tính chất của FILE, không phải của CHƯƠNG TRÌNH. Bundle nhiễm vẫn thắng mọi phép đo đó. 🔴 Byte-match CIprod **vô giá trị khi cả hai đầu đều chưa ai chạy thử** hai file giống hệt nhau vẫn cùng crash.
**Fix chuẩn — 2 tầng:**
- *Cứu ngay:* build local từ HEAD (env lành) so hash 2 lần build (determinism = control dương) scp `dist/` đè app chết, **GIỮ bundle hỏng trên đĩa** (rollback 1 giây) verify bằng **console + read_page**, KHÔNG chỉ curl.
- * gốc (`305b573f`):* lockfile **tái sinh từ cây lành** + commit `npm install` **`npm ci`** + check exit-code từng bước **GUARD-92** chặn build khi React trùng bản (`npm ls react`, lookbehind `(?<![-\w./@])react@` thiếu lookbehind thì `lucide-react@1.8.0` khớp như một bản React cây lành cũng FLAG CI đỏ vĩnh viễn; fault-inject 4 ca bắt được trước khi push).
- *Chứng deterministic:* run #488 runner **node v24.15.0/npm 11.12.1** vs local **v22.13.0/npm 10.9.2** vẫn ra **đúng 4/4 hash + size**. Vite hash = hash nội dung cây resolve y hệt nghi vấn "chênh node/npm nguồn bất định thứ 2" **bị bác bằng dữ liệu**.
**Luật:** (1) verify deploy FE phải **1 phép đo RUNTIME** (console error sau load / marker JS thực thi) file-integrity + HTTP 200 chưa đủ; (2) bundle CI lệch local >vài KB cùng commit khi local deterministic = **ĐỎ**, chặn trước khi ship; (3) 🔴 **CI phải cài theo lockfile đã commit (`npm ci`), CẤM xoá lockfile rồi `npm install`** — "fresh resolve" nghe như sạch nhưng chính là bỏ tính tái lập; (4) vá gốc chỉ được coi là xong khi **dispatch-run + smoke runtime** cùng xanh — trước mốc đó, mọi push code sẽ đè hotfix bằng bundle chưa kiểm (mốc này **đã đạt @S191 run #488**, ràng buộc gỡ; câu này giữ làm **điều-kiện cho lần sau**, không phải lệnh cấm vô thời hạn).
🔸 **Nửa lỗ CÒN LẠI (khai thẳng):** `npm ci` + lockfile chặn drift **cây phụ-thuộc**; nó **KHÔNG** chặn node/npm của runner tự trôi. `.nvmrc` = `20` **CÓ tồn tại**`fe-admin/` + `fe-user/` nhưng `deploy.yml` **0 hit** `setup-node`/`node-version`**không tầng nào đọc nó** (ghost-wire class-2: defined-but-unread) — CI dùng bản Node cài sẵn trên runner. Trục này chưa nổ vì hash vẫn khớp, nhưng nó là biến chưa khoá.
## Checklist debug bug mới