From c03955405deb69c235c55105bd8e8591a1d170c7 Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Thu, 13 Aug 2026 01:01:29 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20Docs:=20P0-2=20eoffice=20tr=E1=BA=AF?= =?UTF-8?q?ng=20m=C3=A0n=20=E2=80=94=20hotfix=20bundle=20local=20D6lFjgKF?= =?UTF-8?q?=20+=20gotcha=20#92=20kh=E1=BA=AFc=20th=E1=BA=ADt=20(runner=20b?= =?UTF-8?q?uild-env=20nhi=E1=BB=85m,=20C=E1=BA=A4M=20push=20code=20t?= =?UTF-8?q?=E1=BB=9Bi=20khi=20v=C3=A1)=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- .claude/WAL.md | 22 +++++++++++++------ docs/HANDOFF.md | 2 +- docs/STATUS.md | 4 ++-- ...26-08-12-2252-S189-S191-yc028-yc030-l15.md | 13 +++++++++-- docs/gotchas.md | 12 ++++++++++ 5 files changed, 41 insertions(+), 12 deletions(-) diff --git a/.claude/WAL.md b/.claude/WAL.md index 37e5491e..3447302f 100644 --- a/.claude/WAL.md +++ b/.claude/WAL.md @@ -17,18 +17,26 @@ chain: (3) note báo VIETREPORT → VIETREPORT/docs/2026-08-13-canh-bao-ntfs-*.md (untracked, repo họ) [x] Finish NEXT-em BẰNG ĐO: nén-2-memory = SAI ĐÍCH (cờ-8, invest 23.148B + reviewer 25.469B DƯỚI trần 25.600 — không nén; ring2 27.458B vượt = {đang-cãi} giữ) · un-delete a79dd579 = - HẾT CẦN (Dev đồng hình prod 8 type-3, 0 mồ côi) · #92-candidate ĐÓNG (root-cause disk) + HẾT CẦN (Dev đồng hình prod 8 type-3, 0 mồ côi) [x] Vá bề mặt: STATUS:6 4-mặt · run.md S190 nửa-sau · sub-wq3-be on-behalf · stray-dir · session-log S189-S191 (S189 hết vô chủ) · HANDOFF segment L15 -[ ] chờ anh: KIỂM PROD GĐ3 (lệch-ý-thì-chỉnh; "ổn rồi" ⇒ mở W8 [carry:w8-delete-policy]) · - tồn (76)(77)(78)(79) · (89) jump-4-class · (74) rotate vrapp · value_protect · - lật ô YC-008↔YC-010 · tồn (22)(67)(72) +[x] 🔴 P0 LẦN 2 @00:50 (anh kiểm: "eOffice vào trắng màn hình"): bundle CI #600 BIN1bEGo + crash `useState null` (console đo thật) — HOTFIX build local HEAD (hash D6lFjgKF = + dist 21:54, deterministic) + scp đè fe-user → SỐNG (form login render + network load + D6lFjgKF + console sạch load-mới). BIN1bEGo giữ trên đĩa = rollback 1s. ⇒ GOTCHA #92 + KHẮC THẬT (2 ca: lệch CI-vs-local 103KB user / 39KB admin cùng commit; giả-thuyết + "root-cause=disk đã đóng" BÁC lần 2). Admin D2FHMRPX cùng lò nhiễm nhưng ĐANG SỐNG + → không đụng, chẩn ghi sổ (local KRJldwaN 1.780.807B sẵn nếu anh muốn đồng nhất) +[ ] chờ anh: KIỂM PROD GĐ3 tiếp (eoffice đã sống lại) · "ổn rồi" ⇒ mở W8 · chọn giờ VÁ GỐC + runner (dọn npm-cache SYSTEM + dispatch verify — SẼ rotate bundle prod) · tồn + (76)(77)(78)(79) · (89) · (74) · value_protect · YC-008↔YC-010 · (22)(67)(72) -next: anh phản hồi kiểm-prod / giao việc mới. Watch: reviewer/MEMORY.md dư 131B là vượt trần · - ring2-audit {đang-cãi} · [carry:w5-host-ondeleted] · OVERDUE:deep trả ở bookend kế. +next: 🔴 CẤM push CODE / dispatch CI cho tới khi runner được VÁ + VERIFY (build CI mới sẽ + đè hotfix bằng bundle nhiễm → trắng màn lại). Docs push = [skip ci]. Watch: reviewer + dư 131B · ring2 {đang-cãi} · [carry:w5-host-ondeleted] · OVERDUE:deep @bookend kế. verify: - prod: curl -s https://eoffice.solutions.com.vn/ | grep -o 'index-BIN1bEGo.js' + prod: curl -s https://eoffice.solutions.com.vn/ | grep -o 'index-D6lFjgKF.js' # HOTFIX local-build @S191 (BIN1bEGo = CI nhiễm, crash) admin: curl -s https://admin.solutions.com.vn/ | grep -o 'index-D2FHMRPX.js' mig: ssh vietreport-vps 'sqlcmd -E -S .\SQLEXPRESS -d SolutionErp -h -1 -Q "SELECT TOP 1 MigrationId FROM __EFMigrationsHistory ORDER BY MigrationId DESC"' # =20260812141501_AddContractEndedByLevelFinalize cron: ssh vietreport-vps 'powershell -NoProfile -Command "Select-String -Path C:\gitea\custom\conf\app.ini -Pattern archive_cleanup"' # co section diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 86753a12..da758712 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -2,7 +2,7 @@ > **Tiering rule (S40):** giữ **2-3 session gần nhất**. Cũ hơn → `docs/changelog/sessions/`. Full brief history pre-S40 → `docs/_archive/HANDOFF-preS40-fullhistory.md`. -**🆕 Last updated:** 2026-08-13 (**S189→S191, phiên-LOGIC L15 window 1-3** — GIỮ MỞ chờ anh kiểm prod): **YC-028 cứu prod 4 tầng** (disk 0-byte · orphan-sshd · Gitea câm #91 · RAM pin 1200MB; +#89/#90/#91, P0 hotfix scp outage 15') → **YC-029 5-wave B6 + YC-030 Q1-Q5 SHIP deploy #600 PASS 6/6** (test **697** · **Mig 73** prod-top · bundle eoffice `BIN1bEGo`/admin `D2FHMRPX` · Q5 type-3 = 7 `HD-*`) → **@S191 cửa ngoài-giờ đóng trọn** (cron `archive_cleanup` Gitea ON + **cứu `Vietreport_Master` khỏi RECOVERY_PENDING** #88 + note báo VIETREPORT) + finish-bằng-đo 2 mục NEXT-em (nén-2-memory = SAI ĐÍCH cờ-8, investigator/reviewer dưới trần — chỉ `ring2-audit` 27.458B vượt {đang-cãi}; un-delete `a79dd579` = HẾT CẦN, Dev đồng hình prod 8 type-3). Log: `docs/changelog/sessions/2026-08-12-2252-S189-S191-yc028-yc030-l15.md`. **Chờ anh: KIỂM PROD GĐ3** (eoffice 7 loại HĐ + admin 7 mục Designer) — lệch-ý-thì-chỉnh; "ổn rồi" ⇒ mở W8 + `[carry:w8-delete-policy]`. +**🆕 Last updated:** 2026-08-13 (**S189→S191, phiên-LOGIC L15 window 1-3** — GIỮ MỞ chờ anh kiểm prod): **YC-028 cứu prod 4 tầng** (disk 0-byte · orphan-sshd · Gitea câm #91 · RAM pin 1200MB; +#89/#90/#91, P0 hotfix scp outage 15') → **YC-029 5-wave B6 + YC-030 Q1-Q5 SHIP deploy #600 PASS 6/6** (test **697** · **Mig 73** prod-top · bundle eoffice `BIN1bEGo`/admin `D2FHMRPX` · Q5 type-3 = 7 `HD-*`) → **@S191 cửa ngoài-giờ đóng trọn** (cron `archive_cleanup` Gitea ON + **cứu `Vietreport_Master` khỏi RECOVERY_PENDING** #88 + note báo VIETREPORT) + finish-bằng-đo 2 mục NEXT-em (nén-2-memory = SAI ĐÍCH cờ-8, investigator/reviewer dưới trần — chỉ `ring2-audit` 27.458B vượt {đang-cãi}; un-delete `a79dd579` = HẾT CẦN, Dev đồng hình prod 8 type-3) + **P0-2 @00:50 anh kiểm ra: eoffice trắng màn — bundle CI #600 crash `useState null` → hotfix build-local scp đè, eoffice nay `D6lFjgKF` SỐNG (admin giữ `D2FHMRPX` — cùng lò nhiễm +39KB nhưng sống, dist local `KRJldwaN` sẵn) ⇒ gotcha #92 khắc thật (row 91→92)**. Log: `docs/changelog/sessions/2026-08-12-2252-S189-S191-yc028-yc030-l15.md`. **Chờ anh: KIỂM PROD GĐ3 tiếp** (eoffice đã sống lại) — lệch-ý-thì-chỉnh; "ổn rồi" ⇒ mở W8 + `[carry:w8-delete-policy]` · **chọn giờ VÁ GỐC runner** (dọn npm-cache SYSTEM + dispatch verify — SẼ rotate bundle prod). 🔴 **CẤM push CODE / dispatch CI tới khi runner vá+verify** (#92(4) — build mới đè hotfix bằng bundle nhiễm; docs push = `[skip ci]`). > 🧊 *(Segment cũ giữ nguyên bên dưới:)* 2026-08-10 (**S187→S188 ĐÓNG** — phiên-LOGIC **L14** window 2-3): **module Duyệt Hợp đồng GĐ3 từ KHKK SHIP PROD 2 đợt** — đợt-1 `0149f106` (W1/W2/W7) + cụm `b275893c` (W3/W4/W5/W6) + sổ `80febec9`; test **680** · 0-mig · Menu keys **120** / Policies **480** · cicd **4/4 + 5/5** · W10 dry-run Dev **PASS 5/5**. Owner: YC-024 9 đáp án (80)-(88) + YC-025 *"làm tiếp hết, review 1 lần"*. **W8 authz HOÃN** (chờ anh nói "ổn rồi") · **W9 HỦY**. > 🔺 **CARRY MỚI @S188 — ADDITIVE vào danh sách RE-STAMP dưới:** `[carry:w7b-inbox-param]` *(BE `/contracts/inbox` thêm `?type=` + bỏ `Take(100)` → rồi FE mới gỡ client-filter `InboxPage`; kèm `ContractCreatePage` ×2 app cùng lớp lọc-client-sau-phân-trang)* · `[carry:fe-contract-family-drift]` *(`fe-admin/src/lib/contractWorkflowFamily.ts` chép 7 mã `HD-*` — khớp BE hôm nay, cần drift-watch vì 2 nhà)* · `[carry:sentinel-truoc-bu]` *(guard SÓT-1 ring5 @S188: mọi lượt CHẠY-BÙ nghi-thức phải ĐẶT LẠI `closing:` sentinel trước khi bù — lượt bù S188 chạy không sentinel ⇒ cửa sổ mù Sàn-2 ~73 phút; wire vào `session-end.md` khối ONE-SHOT; RCA `ONESHOT-S188` error-ledger)*. 🔴 **Vì sao phải ghi TAY ở đây:** `lead-gap-auditor` DEEP FLAG-1 chứng nợ này **chỉ sống trong WAL**, mà `§6.4` xoá trắng WAL ở bước CUỐI, và nghi-thức re-stamp **chép danh-sách kỳ trước** nên slug sinh-trong-WAL **vô hình có cấu-trúc** ⇒ không lượt chép nào nhặt được. > 🔺 **CARRY MỚI @S190 (`/tiep` window-2 L15) — ADDITIVE, di-trú 2 ý khỏi bề mặt tự-hủy (ctx-verifier cờ-13: một ý WAL-only mà WAL tái sinh mỗi `/pause`, một ý `_mind`-only mà nén block là mất):** `[carry:gotcha-92-candidate]` *(runner-workspace corrupt — node_modules ghi dở đêm disk-0 + workspace giữ giữa run ⇒ CI build ra bundle crash `React null useState` CÙNG commit với local lành >100KB lệch [P0 @S189]; CHỐT thành gotcha #92 SAU khi dọn workspace runner + dispatch run verify xác nhận root-cause — việc nằm trong CỬA NGOÀI-GIỜ)* · `[carry:pin-max-server-memory-fleet]` *(S189 pin 1200MB mới cho SQLEXPRESS của SOLUTION_ERP/VIETREPORT; các máy SQL nhỏ KHÁC cùng hạ tầng còn default unbounded 2147483647 = bom chậm giằng RAM — rà + pin lúc setup, khắc runbook theo luật gotcha #91(3))*. diff --git a/docs/STATUS.md b/docs/STATUS.md index 723326d6..697ba8b4 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -3,7 +3,7 @@ > **Update rule:** trước khi bắt đầu 1 task → ghi row `🔥 In Progress`. Xong → `✅ Recently Done`. > **Tiering rule (S40):** chỉ giữ **state hiện tại + 3 session gần nhất** ở file này. Session cũ hơn → `docs/changelog/sessions/`. Full history pre-S40 → `docs/_archive/STATUS-preS40-fullhistory.md`. (Tránh over-context — xóa double, không cắt nội dung.) -> ✅ **S190→S191 ĐÓNG vế thi-công (2026-08-12→13, phiên-LOGIC L15 window 2-3)** — **YC-029 "SOLTUON-12-08-2026" 5 wave B6 + YC-030 Q1-Q5 SHIP PROD deploy #600 PASS 6/6** (cicd-monitor + lead re-verify độc-lập KHỚP): W4 `1cc16a2d` + W1 `09ebd20e` + W2 `7b291014` + W3 `ed3b39ff` + W5 `2ae62f3f` · YC-030 `69860362`(BE)+`59cb1217`(FE)+`091073f9`(docs) · test **680→697** (+13 `ContractDeleteGuardTests` test-before + 4 finalize; row canonical Tests bên dưới = 697 chép từ cicd #600) · **Mig 73** `AddContractEndedByLevelFinalize` prod-top · bundle rotate ×2 byte-match (eoffice `BIN1bEGo`/admin `D2FHMRPX`) · Q5 prod QT-HD-V2-001→0/0, type-3 selectable = 7 `HD-*`. **@S191 (13/08 00:34) CỬA NGOÀI-GIỜ đóng trọn:** bật `[cron.archive_cleanup]` Gitea (backup `app.ini.bak-20260813-003421`, restart verify HTTP 200 v1.24.2, cron fire ngay) + **cứu `Vietreport_Master` khỏi RECOVERY_PENDING** (compact /u hết nén NTFS, 3/3 DB ONLINE — gotcha #88 ứng nghiệm) + note báo VIETREPORT. Anh đang KIỂM prod GĐ3 — lệch-ý-thì-chỉnh. Mạch sống → `.claude/WAL.md`. +> ✅ **S190→S191 ĐÓNG vế thi-công (2026-08-12→13, phiên-LOGIC L15 window 2-3)** — **YC-029 "SOLTUON-12-08-2026" 5 wave B6 + YC-030 Q1-Q5 SHIP PROD deploy #600 PASS 6/6** (cicd-monitor + lead re-verify độc-lập KHỚP): W4 `1cc16a2d` + W1 `09ebd20e` + W2 `7b291014` + W3 `ed3b39ff` + W5 `2ae62f3f` · YC-030 `69860362`(BE)+`59cb1217`(FE)+`091073f9`(docs) · test **680→697** (+13 `ContractDeleteGuardTests` test-before + 4 finalize; row canonical Tests bên dưới = 697 chép từ cicd #600) · **Mig 73** `AddContractEndedByLevelFinalize` prod-top · bundle rotate ×2 byte-match (eoffice `BIN1bEGo`/admin `D2FHMRPX`) · Q5 prod QT-HD-V2-001→0/0, type-3 selectable = 7 `HD-*`. **@S191 (13/08 00:34) CỬA NGOÀI-GIỜ đóng trọn:** bật `[cron.archive_cleanup]` Gitea (backup `app.ini.bak-20260813-003421`, restart verify HTTP 200 v1.24.2, cron fire ngay) + **cứu `Vietreport_Master` khỏi RECOVERY_PENDING** (compact /u hết nén NTFS, 3/3 DB ONLINE — gotcha #88 ứng nghiệm) + note báo VIETREPORT. **@S191 00:50 P0-2 anh kiểm ra ("eOffice trắng màn"):** bundle CI #600 `BIN1bEGo` crash `useState null` → **hotfix build-local scp đè: eoffice nay chạy `index-D6lFjgKF.js`** (form login render, verify runtime browser; admin giữ `D2FHMRPX` — cùng lò nhiễm nhưng sống) ⇒ **gotcha #92 khắc thật** + 🔴 **CẤM push code/dispatch CI tới khi runner vá+verify** (bundle mới sẽ đè hotfix). Anh KIỂM prod GĐ3 tiếp — lệch-ý-thì-chỉnh. Mạch sống → `.claude/WAL.md`. > ✅ **S187→S188 ĐÓNG (2026-08-10, phiên-LOGIC L14 window 2-3)** — **module Duyệt Hợp đồng (GĐ3) từ KHKK SHIP PROD 2 đợt** (YC-023 mở việc · YC-024 9 đáp án (80)-(88) · YC-025 *"làm tiếp hết, review 1 lần"*). Đợt-1 `0149f106` (W1 gỡ 2 bom per-type · W2 BE-DTO nguồn-gốc+cây workflow · W7 server-filter) + cụm `b275893c` (W3 seed **7 quy trình `HD-*`** sao khuôn KHKK + **7 key `AwV2_Hd*`** · W4 guard 4-cửa khoá-cứng-theo-loại · W6-BE Đ2 đúng-trạm · W5 card Nguồn-gốc · W6-FE panel **2 nút** + Designer type-3 slice). **0 migration** · test **668→680** · Menu keys 113→**120** · Policies 452→**480** · cicd **4/4 + 5/5** (seeder prod 7 workflow + 7 menu-row, KHKK-8 control nguyên) · **W10 dry-run Dev PASS 5/5** toàn trình 2 nhánh tới `DaPhatHanh`. 🔴 W8 authz **HOÃN** theo (82) · W9 **HỦY** theo (85). #53 garble ~90% return, **đĩa cứu 100%**. Log: `docs/changelog/sessions/2026-08-10-2000-S187-S188-duyet-hop-dong-gd3.md` · run-trace `runs/2026-08-10-S187-duyet-hd-tu-khkk/` (19 file + `implement-synthesis.md`). > ✅ **S185 ĐÓNG (2026-08-10, phiên-LOGIC L13 window 1-4)** — closeout ONE-SHOT lượt đầu (YC-018). **10 mã YC đóng** (YC-013→022): KHKK 8-panel + guard tay-cầm-phiếu SHIP+verify+acceptance · pipeline email/adap **0/39 nợ per-id** · bookend @close **9 vai một mạch** · ring5-audit (roster 29) + `/day` day-wake · deep_every 15→1 · bảng thẩm-quyền owner chính thức. Tests **668** · counter **59** · over-cap 3. Log: `docs/changelog/sessions/2026-08-10-0400-S185-bookend-oneshot-yc017-022.md`. > 🔴 **mốc SỐNG = S185, phiên-LOGIC L13, window 4** (2026-08-10) — YC-013 KHKK 8-panel + YC-016 guard tay-cầm-phiếu SHIP+verify prod · YC-017 pipeline email/adap trọn · YC-018 ONE-SHOT bookend land · bookend @close MỘT MẠCH 9 vai (chi tiết `docs/HANDOFF.md` segment mới + `runs/2026-08-09-S185-bookend-close/`). 🧊 mốc cũ S179/L11/window-7 (chuỗi UAT KHKK 8 đợt + đội STYLE) → xem segment Recently-Done. **Dòng CURRENT ngay dưới đây advance @closeout nên tụt sau — giữa phiên đọc `.claude/WAL.md`.** @@ -475,7 +475,7 @@ monthly re-tier mega-line + ef-core SKILL Mig→59 + 3 vocab-fork (2026-07-01 du | Policies | **480** | 🔄 **480 @S187 W3** = All 120 × 4 (DELTA: **+28** = 7 `AwV2_Hd*` × 4 actions — 7 quy trình duyệt HĐ theo LOẠI, YC-023/YC-024). Đo RUNTIME, không suy từ số cũ: nạp `SolutionErp.Domain.dll` vừa build → `MenuKeys.All.Length × MenuKeys.Actions.Length` = 120 × 4 (`Distinct()` = 120 ⇒ 0 key trùng). 🧊 **452 @S166 K4a** = All 113 × 4 (máy khoá: test T2 `KhkkGroupMenuSeedTests` đỏ nếu drift; DELTA vá-2: +4 ContractCatalog K1 · +32 AwV2_KhkkN* K3 · +196 Khkk_G* K4a). Cũ: **220 @S161** (W1 KHKK: `All` +`KeHoachKyKet` ⇒ 55 × 4 — reviewer F-2 bắt cặp-row-đôi đúng luật dòng dưới) · 🆕 **row MỚI S121** (wave adap-6-broadcast W2 — H24 GAP-3: `governance-detectors.ps1` `Get-StatusValue` resolve canonical TỪ bảng này; **KHÔNG có row ⇒ token `policy` UNRESOLVED ⇒ nửa `policy` của GAP-3 không thể fire** ⇒ detector khai FAIL-LOUD + SKIP, KHÔNG im-lặng-giả-vờ-xanh). 🔴 **DERIVED, KHÔNG độc-lập:** `Policies = |MenuKeys.All| × |MenuKeys.Actions| = 55 × 4 = 220 (@S161; trước W1 = 54 × 4 = 216)` — sinh runtime ở `src/Backend/SolutionErp.Api/Program.cs:82-89` (`foreach menu → foreach action → opts.AddPolicy($"{menu}.{action}")`), `Actions = ["Read","Create","Update","Delete"]` (`MenuKeys.cs:167`). ⇒ **row này BUỘC đổi CÙNG row `Menu keys`**: thêm 1 menu key ⇒ 55 × 4 = 220; sửa `Menu keys` mà quên `Policies` ⇒ detector flag derived-doc thay vì flag chính chỗ sai. Ground S121 bằng đếm mã-nguồn (`MenuKeys.All` = 54 entry, `public const string` = 54, khớp) — KHÔNG chép số từ spec. ⚠️ `.claude/skills/permission-matrix/SKILL.md` ghi **~240 = STALE** ⇒ W4 vá (🔴 chuỗi H24 §2(4) BẮT BUỘC: detector fire → cổng chứng-kiến → RỒI MỚI vá; vá trước = giết chứng-nhân sống) | | Menu keys | **120** | 🔄 **120 @S187 W3** (đo RUNTIME `MenuKeys.All` bằng cách nạp DLL vừa build, KHÔNG đếm mắt: `ALL=120 · DISTINCT=120 · HD_KEYS=7`). DELTA **+7** = `AwV2_Hd{TP,GK,NCC,DV,MB,NTNCC,NTDV}` — 7 leaf Designer cho 7 quy trình duyệt HĐ theo LOẠI (YC-023/YC-024), sinh by-construction từ `ContractApprovalWorkflowFamily.Members` qua `MenuKeys.ContractWorkflowMenuKeys` ⇒ CÙNG bộ sinh với vòng seed menu-row trong `SeedMenuTreeAsync` (invariant T6: key vào `All` mà thiếu menu-row = Permission FK nổ lần boot đầu). 🧊 **113 @S166 K4a** (đo RUNTIME `MenuKeys.All` — test T2; +49 `Khkk_G{2-8}`+42-leaf bộ-sinh by-construction K4a · +8 `AwV2_KhkkN1..N8` K3 · +1 `ContractCatalog` K1 trên nền 55). Cũ: **+1 S161 `KeHoachKyKet` vào `All` (W1 KHKK — policy `KeHoachKyKet.*` sinh runtime; menu-row prod KHÔNG đổi, 142 giữ)** · +1 S69 `Off_Dashboard` (landing Văn phòng số). Prev re-ground S61 (`MenuKeys.cs` const — Mig 50 gỡ 4 `Bg_*` Budget menu cũ). Prev 57 (S58) | | Tests | **697 PASS** | **45 Domain + 652 Infra** · 🔄 **697 @S190 — cicd-verify CI run #600 `091073f9`** (deploy YC-029+YC-030 GĐ3; Δ+17 trọn ở Infra = YC-029 +13 [W1 menu-drift + W5 delete-guard 13-ca] + YC-030 +4 [`ContractApproveFinalizeTests` nhánh finalize]; 0 Failed/0 Skipped, đối chứng local `dotnet test SolutionErp.slnx` @gate-cụm). 🧊 **680 @S188 — ĐO ×2 nguồn độc-lập** (`dotnet test SolutionErp.slnx` local @gate-cụm + gate CI **run #482** `b275893c` tách-phần 45/635; đối chứng thứ 3 `sub-reviewer-cum.md`). **+12 vs S185** = 4 test-before W1 (`UpdateAwDefinitionTests` W1a/W1b bom per-type) + T2/T6 menu-key drift W3 + 3 security W6-BE (`ContractWorkflowServiceApproveV2Tests` đúng-trạm) + collateral. 🔴 **Ô này lệch 2 đời trong phiên S188** (668 → 672 đợt-1 → 680 cụm) — `lead-stale-auditor` DEEP FLAG-2 bắt, class `view-stale-count`, cùng ô đã 4 lần tái phát ⇒ **nguồn cập nhật = cicd-verify lượt kế, KHÔNG chép tay**. 🧊 **668 @S185 — ĐO BẰNG CÁCH CHẠY THẬT ×2 nguồn độc-lập** (`dotnet test SolutionErp.slnx` @/tiep-verify + `ring2-audit` tự chạy đối-chứng cùng ngày): Domain 45 · Infra 623 · 0 fail 0 skip. Lineage: +15 KHKK-mirror @S182 (14 spec + T15) + +8 `UploadDuyet_*/UploadCanCu_*` YC-016 @S183. 🧊 **645 @S179** (45D+600I — ô này từng kẹt 645 tới S185 trong khi suite đã 668: stale-F3 canonical-poison, máy lấy ô này làm thước in 6 flag khuyên vá về số SAI — vá @S185). 🔴 Không lấy số từ doc: `lead-stale-auditor` FLAG-4 bắt ô này còn **644**, mà 644 đang là **THƯỚC** cho 6 flag derived-stale của detector ⇒ canonical sai thì máy khuyên vá về số sai (class canonical-poison S148/S166). 🔸 Breakdown cũ ghi "599 Infra" cũng lệch — **600**. 🧊 **644 @S168 #444** (+5 Fact Lines-editor) · 🧊 **639 @S168-sáng** (đo tươi local 45D+594I sau K7 Δ+17; chuỗi K lineage: 597 K1 → 614 K3 → 622 K4c @S167 KHỚP CI #439 tách-phần → **639** K7 — gate `639=45+594` KHỚP cicd-K7 dòng-1) · 🧊 **+28 @S161** (5 wave KHKK: W1 566 → W2 574 → W3 580 → W6 587 → W7 590; đo tươi @S162 `dotnet test` 45+545, gate CI Run #432 khớp). ~~45 Domain + 517 Infra~~ · 0 fail / 0 skip · **S155 +30** (PE xóa-by-approver: `PeDeleteByApproverTests.cs` + T26/T27 — 🔴 **T27 khoá CẢ LỚP lỗi** *entity-có-cờ-mà-DTO-không-có* bằng reflection [mọi cờ `Allow*` trên entity phải có mặt trên DTO], **fault-inject xác nhận có răng**: tiêm bug ⇒ ĐỎ, khôi phục ⇒ xanh — không phải guard tuyên-bố suông; T26 giữ ĐỎ có chủ ý vì bug production thật, lead vá bằng đảo thứ tự chứ KHÔNG nới assert. Số 562 cross-check 2 nguồn độc-lập: log CI run #417 + `dotnet test` local @S156) · **S147 +4** (`ProjectCodeNormalizationTests` — Trim khi lưu + kiểm-trùng bỏ-dấu-cách; 🔴 **ô canonical này STALE 528/483I suốt S147→S148**, header `:6` và session-log S147 đều ghi 532/487I từ đầu — closeout S147 vá header mà quên ô canonical; H24 `lead-view-auditor` bắt @S148, lead verify 3-nguồn rồi vá) · **S134 +8** (`PePendingAccumulationTests` C1-C8 lũy-kế-TẠM-TÍNH: C1 ChoDuyet+winner vào delta-selected + PriorPes · C2 TuChoi/DaDuyet loại sạch · C3 Nháp list-only KHÔNG cộng tiền [D1] + TraLai vào delta-submitted [D2] · C4 peer-window CreatedAt · C5 delta âm signed · **C6 complementary-no-double-count exact-numbers [ComputeAsync bất động khi thêm peer non-terminal]** · C7 frozen→pending 0/null · C8 D4 changelog `[S134 pending-prior]` positive+contrast) · **S133 +11** (`PeBudgetFreezeTests` freeze-NS-at-DaDuyet: **T0 repro test-before RED→GREEN** [phiếu DaDuyet phải giữ số cũ sau `UpdatePeBudgetPro` — RED bắt 999tr @:184 pre-fix, phiếu đưa tới DaDuyet QUA service finalize mirror `PeApproverFinalizeTests` KHÔNG seed thẳng Phase] + **T1-T9** spec-coverage [snapshot các nhánh finalize + display-gate + 2 terminal-guard chặn sửa NS phiếu đã kết thúc (cả Admin) + backfill] + **site-5 AdminOverride** [reviewer catch — override thẳng → DaDuyet cũng chốt snapshot]) · **S118 +8** (PRO-perm scope-isolation **+4** `ProcurementMasterAccessSeedTests` [reflection no-prod-touch: upgrade-path Suppliers R+C+U + Master-Read · **scope-isolation** DeptManager-riêng vẫn revoke=false · insert-path no-Delete · idempotent] · ReportsController authz **+4** `AuthorizePolicyRegressionTests` [dashboard/export require Reports.Read · my-dashboard KHÔNG action-policy · class-level [Authorize] intact]) · **S117 +6** (PE endsBeforeCeo pointer-aware **+1** `PeFinalizeProjectionTests.ListPe_EndsBeforeCeo_ChoDuyet_PointerAware_PastFinalizeIsFalse` [ChoDuyet con-trỏ QUA cấp finalize → endsBeforeCeo=false, test-before RED→GREEN + EF-subquery translate smoke] · duyệt-theo-khoảng ProMinMax **+5** `PeApprovedPriceFinalizeTests` [happy set Min+Max · min==max boundary lock strict-`>` · thiếu-Max→Conflict not-finalized · min>max→Conflict · single-source+Max→Conflict wire-bug + regression Max=null] · harness `InvokeApply` 4→5 args fix TargetParameterCountException) · **S116 +9** (PE nhập giá âm: T1 ngân-sách-bất-biến khi quote-âm · T2 winnerQuoteTotal signed-sum · T3 submit-guard all-zero-blocked/pure-neg+net-zero-submittable · T4 CCM-net-threshold) · **S114 +9** (PE multi-NCC per hạng mục: `PeSelectWinnerClearTests` delete-invariant +4 [DeleteQuote/DeleteDetail re-derive IsWinner **test-before RED→GREEN** + guard-no-over-clear + UpsertQuote-update-branch coverage] + impl-lane +5 [per-hạng-mục-total · giaTri-single-partial · threshold-crossing · derive-invariant]) · **S113 +19** (Supplier import v2: `SupplierImportV2DedupTests` 13 [dedup MST-primary+Code-backstop T1-T7 · draft-on-import IsPublic · all-or-nothing · LayoutValid re-bake real-token · **null-Code→nháp FIXED** flipped-from-characterization] + `SupplierPublishAndListTests` 6 [publish-guard Code-required→409 · unpublish · list-filter published · DTO IsPublic]) · **S112 +18** (Supplier `SupplierExcelImportServiceTests` +10 [case-collation dedup có-RĂNG SQLite-BINARY-collation-nên-chứng-dedup-ở-SERVICE · fill-nulls-safe · all-or-nothing-abort · parser abs-cell-index/#REF!-null/header-fingerprint · Type-lạ→NCC · Status-emoji-strip] · PE `PeSubmitGuardAndBypassTests` 4-update→A1 [minOwn subordinate-only, pointer-stop] + 8-new [A1-happy · non-contiguous · EDGE-5 non-admin-throw/admin-pass/system-pass · opinion-history log/none]) · Prev 440: **S96 +6** (PE `PeApproverFinalizeTests` +2 [#6 finalize-opt-out→advance-CEO · #7 stale-noti-clear ExecuteUpdate] · `PeSuggestedPriceSetterAuthzTests` +4 [price-lock phase-guard after-submit — PRO/CCM ChoDuyet/DaDuyet→Conflict, Admin-override; +sửa SeedPeAsync default ChoDuyet→DangSoanThao spec-change]) · **S92 +3** (`AdminOnlyModulesRevokeTests` ẩn 5 nhóm menu chỉ Admin — non-Admin revoked-4-cờ / Admin untouched / Pe_* preserved) · **S91 +9** (ChangePassword self-service BE+test) · **S89 +3** (`PeDraftVisibilityTests` IDOR nháp riêng-tư) · **S88 +17** (D1 financials/CEO-notify/HoSoLink null-safe +11 · D2 create-contract multi-winner +6) · **S87 +25** (Mig 58 net +18: `PeMultiWinnerTests` sum + `PeSelectWinnerClearTests` multi-update + `PeSubmitGuardAndBypass` budget=0 + `PeApproverFinalizeTests` 5 auto-finalize [từng 7, −2 khi opt-in→auto rework] + `PeCcmThresholdFinalize`/`PeWorkItemBudget` collateral; Mig 59 +7 `PeCcmBudgetPeriodSetterAuthzTests` role-gate Admin|CCM/budget=0/null-clear) · **S86 +3** (`PeApproverNotifyExcludesDrafterTests` — notify "cần duyệt" loại NGƯỜI TẠO phiếu: drafter-excluded · co-approver-vẫn-nhận · drafter-vẫn-nhận-chuông-kết-quả) · **S85 +18** (PE CV PRO C5 hủy/đổi NCC winner +10 · urgent drafter-self-unset +5 · workflow-preserve bug-class S42 +3 `PeUpdateDraftWorkflowPreserveTests`) · **S83 +2** (PE require-workflow: `PeWorkItemGuardTests` ApprovalWorkflowId `NotEmpty` ×2 validator + submit-guard rewrite test 7/13 `PeSubmitGuardAndBypass` V1-submit-deprecated, no-count-change) · **S77 +10** (+7 `PeSuggestedPriceSetterAuthzTests` ghi chú PRO/CCM Mig 57 absolute-set/role-gate/null-clear/max-1000 · +3 `PeUrgentToggleAuthzTests` rewrite cờ-gấp **BẤT ĐỐI XỨNG** SET=function role / UNSET=function+DeptManager, plain-PRO-unset Forbidden) · **S76 +5** (`PeWorkItemBudgetTests` PRO split: set ProInitial+ProAdjust gồm âm · validator ProInitial≥0/ProAdjust-cho-âm · full=proFull khi CCM empty · full=CCM khi CCM present) · **S74 +5** (`PeWorkItemBudgetTests` §4b CcmNote: set CCM/Admin · null-clear absolute-set · non-priv Forbidden+no-mutate · all-3-persist) · **S73 +28** (PE opt-in finalize spec 6→11 + 10 giá-chốt `PeApprovedPriceFinalizeTests` + 13 setter-authz `PeSuggestedPriceSetterAuthzTests`) · **S69 +20** (Office-golive OfficeModulePermissionSeed 6 + PE PeCcmThresholdFinalize 5 + PeUrgentToggleAuthz 9) · **S67 +23 HRM test-after** (DepartmentTreeTests 8 cycle-guard/rollup + PeHoSoLinkTests 9 absolute-set + HrmProfilePermissionSeedTests 6 reflection-seed) → 286 · S61 +22 `PeWorkItemBudgetTests` −14 `BudgetPolicyTests` −1 → 263 · S60 +14 `PeSubmitGuardAndBypassTests` +2 spec → 256 (Domain 58→45 do drop Budget module tests) | -| Gotchas | **91** | 🔄 **91 @S189** — **+S189 #91** (push land trên ĐĨA nhưng tầng-app Gitea CÂM — 0 run/0 status dù MỌI tín hiệu quen XANH [runner Running · ENABLED · filter đúng · không push-đè]; tố cáo bằng **2 nguồn LỆCH cùng API**: `branches/main→commit.id` [git-đĩa] tiến ⟂ `repo→updated_at` [DB] đứng ⇒ CI **chưa từng được gọi**, không phải đỏ; kèm `gitea.log` 0-byte 11,5h [xoay không mở lại] ⇒ restart mở logger + `workflow_dispatch` API → run #486 ship trọn; cost-của-thuốc: restart kích re-index 25' tranh RAM → latency bimodal = số bẩn, khai chứ không giấu; vá RAM cùng lượt `max server memory` **unbounded→1200MB** [PLE 522→1123, A/059 0,58s]; luật verifier: `updated_at ≥ push-time` TRƯỚC khi tuyên "đang chờ CI") · **+S189 #89** (cache automation phình vô hình ăn sạch ổ C — `gitea repo-archive` 16,49GB + `npm-cache` SYSTEM 12,76GB → free **−20MB** → SQL `Error -2` không mở nổi connection → "ko duyệt được"/GET-500, FE gom mọi lỗi vào "Không tìm thấy phiếu" [#44] ⇒ nghi oan phiếu trong khi phiếu NGUYÊN; fix dọn 30GB + `AUTO_CLOSE OFF` ×3 DB [ON = mỗi connection mở lại ~5s] + FE tách 404⟂500 5-site ×2-app; guard runbook §Disk-free + cicd-monitor 1b; luật: prod chậm/mất/không-lưu ⇒ `fsutil diskfree` TRƯỚC khi nghi data) · **+S189 #90** (`ssh … \| head` cắt pipe → sshd Windows-OpenSSH MỒ CÔI spin full core — 3 orphan bão hòa máy 3-core, SQL `ASYNC_NETWORK_IO` CPU-66ms-elapsed-29s, client đói CPU không nhận data ⇒ A/059 47s/500 "task was canceled"; kill orphan [CHỪA ancestor-chain] → **0,38s** tức thì; luật: cắt output = `Select-Object -First N` phía REMOTE + đợi cold-start ~48s sau recycle mới đo + op đo nặng tự hỏi "ngắt rồi để lại gì") — sự cố YC-028 trọn: `runs/2026-08-10-S189-bookstart-open/run.md` + RCA `YC028-S189` error-ledger. 🧊 **88 @S185** — **+S185 #88** (sự cố prod 2 tầng: disk C: đầy → 2 DB RECOVERY_PENDING → Disk-Cleanup "free space" **NÉN NTFS đúng file .mdf/.ldf đang đóng** → SET ONLINE fail `Msg 5118`, login prod 500 "transient failure UseSqlServer" + Gitea push 502/unpacker CÙNG GỐC; fix OFFLINE-từng-DB → backup copy → restart-service-nhả-handle → `compact /U` 4 file + folder → ONLINE, `Recovery is complete` **0 mất data** [58 PE · 21 user · 97 bảng khớp]; luật: Msg 5118 ⇒ check `compact` TRƯỚC khi nghĩ restore + CẤM nén cây SQL DATA; kèm phát hiện **1433 phơi Internet, `sa` bị brute-force** — chờ anh vá firewall). 🧊 87 @S179 flush — đo đĩa `docs/gotchas.md` max anchor = **87** (`lead-stale-auditor` FLAG-5 + máy HIGH `canonical-itself-stale`; #87 sinh ở S177 vá lệch 2 tầng). 🧊 **+S168 #86** (2-push-sát → Gitea auto-CANCEL run đầu cùng-branch: CANCELLED ≠ fail — verifier đọc run CUỐI của range + bằng-chứng artifact-level; side-effect @startup chỉ 1-startup cho cả cụm push; ca thật `aaed699` ship qua #442) · **+S162 #85** (menu-key OR-gate FE ≠ policy per-action BE: cây 4-folder GĐ gate `['Khkk_List','Khkk_G1','KeHoachKyKet'].some(can)` NHƯNG endpoint gắn `[Authorize(Policy="KeHoachKyKet.Read")]` [`ContractSigningPlansController.cs:36`] + `MenuPermissionHandler.cs:40` so khớp **CHÍNH XÁC 1 key** — KHÔNG kế-thừa cha, KHÔNG OR key con ⇒ role `Khkk_List=1 ∧ KeHoachKyKet=0`: FE mở cổng → API **403** → folder GĐ2 hiện "—" dưới **MỌI** gói thầu, claim "gate trước fetch ⇒ 0 rác 403" **vỡ**. Bẫy: luật "OR nhiều key" **ĐÚNG ở tầng HIỂN-THỊ** [`GetMyMenuTreeQuery.cs:61-96` trả node khi con `CanRead`] nhưng **SAI ở tầng API-authz** — mặt thứ 2 của #82, lần này **menu-hiện RỘNG hơn API-cho** [#82 là chiều ngược]. Fix = gate đúng key policy CỦA endpoint; **mở controller đọc attribute TRƯỚC khi wire gate**, đừng suy từ cây menu. Reviewer bắt **trước ship**; Dev 13/13 role đều `Contracts=1 ∧ KeHoachKyKet=1` nên **chưa lộ** — lớp "chưa vỡ vì dữ-liệu hiện tại may mắn", vá ngay không đợi triệu chứng). **+S159 #84** (revoker-2-chiều: sửa data tay trên prod bị code-path ungated @restart ĐẢO NGƯỢC im lặng — grant menu SQL tay bị revoker [S92] lật 447/494 row; fix = code-first seeder + gỡ nhánh + guard 2-vế + khuôn 3-chân-kiềng; liên-quan #75/#76 chiều seeder). **+S133 #81-EXT** (instance 2 indirect-assignment, KHÔNG số mới: snapshot-NS-at-DaDuyet hook đặt ở finalize choke-point NHƯNG 2 site gán Phase TRỰC TIẾP bypass — admin-override `PurchaseEvaluationWorkflowService.cs:308` `= targetPhase` + seeder `DbInitializer:1323` `= current` → phiếu vào DaDuyet qua 2 đường này KHÔNG snapshot [frozen=0, display rơi về live = đúng bug đang vá]; reviewer adversarial bắt pre-commit, em-main + 3 invocation implement đều sót — cùng bài enumeration-writer #81; meta: field-đích có hook/derive/snapshot ⇒ grep MỌI assignment-site kể cả override/seeder). +1 S126 (**#83** số AGGREGATE session-cumulative tự-lão-hoá — audit COUNTS đổi bởi CHÍNH spawn review sau đó [2/2/0→4/4/0]; outward mời-reproduce = bảng per-lane theo run-id, KHÔNG số tổng; reviewer-gate bắt, em-main-solo sót). +1 S118 (**#82** menu-flag grant ≠ quyền API thật: display-layer [Permission `CanRead`→`GetMyMenuTree` FE-menu] ⟂ API-authz-layer [controller `[Authorize(Policy=)]`] = 2 tầng ĐỘC-LẬP; grant `Suppliers` R+C+U cho PRO nhưng `PUT/DELETE /suppliers` gate `[Authorize(Roles="Admin,CatalogManager")]` → PRO vẫn 403 sửa/xóa NCC [chỉ Publish/Import theo `Suppliers.Update` là ăn]; ngược lại `ReportsController` `[Authorize]` trần → mọi authed-user đọc `dashboard` tài-chính-HĐ ngược S92; fix gate `[Authorize(Policy="Reports.Read")]` per-method + grep controller authz TRƯỚC khi tin hiệu-lực grant; reviewer adversarial bắt, em-main-solo tin menu-flag=quyền thì sót; liên-quan #44). +1 S114 (**#81** derived-invariant re-derive ở write/select NHƯNG SÓT DELETE writer [DeleteQuote/DeleteDetail cascade] → xóa quote-selected cuối của NCC → `Supplier.IsWinner` kẹt stale [Any(IsSelected)=false] → `CreateContract Where(IsWinner)` gom NCC phantom → **HĐ giaTri=0**; fix helper `PeWinnerInvariant.ReSyncAfterQuoteRemovalAsync` gọi sau SaveChanges 2 handler; **enumeration-writer-trong-spec KHÔNG đáng tin** → grep MỌI `.Remove(`/`.Add(`/`=field` chạm entity chứa field-nguồn; chỉ **verify-adversarial-refute** bắt [invest/review/implement đều sót]; cardinality S87/S88 lên tầng mutation-path). +1 S113 (**#80** null-forgiving `!` che NRE trên external-parsed data: `SupplierExcelImportService.NewSupplier` gán `Code = r.Code!.Trim()` → NullReferenceException khi parser trả `null` cho ô cột-4 rỗng [ĐÚNG path R4 "thiếu Mã NCC → nháp"] → confirm nổ 500 CẢ batch dù preview classify New-draft KHÔNG lỗi; sibling `IsPublic=!IsNullOrWhiteSpace(row.Code)` null-safe nhưng NewSupplier thì KHÔNG; empty-string `""` chạy OK nhưng FE round-trip blank→JSON `null` → nổ. Fix `(r.Code ?? "").Trim()`. **Build 0/0 KHÔNG bắt** [`!` tắt nullable-warning]; chỉ test-before-critical bắt. Lesson: `!` null-forgiving trên dữ-liệu-parse-ngoài = mìn, dùng `?? default`). +1 S97 (**#79** backfill soi NHẦM bảng — transition-marker "[Duyệt KẾT THÚC]" nằm ở `PurchaseEvaluationChangelogs.ContextNote` KHÔNG phải `PurchaseEvaluationApprovals` → Mig 60 backfill match 0, phiếu finalize-CCM giữ flag=0 hiện sai "Đã được CEO duyệt"; fix Mig 61 query đúng bảng. PE 2 bảng lịch-sử tách vai [Approvals=ai-duyệt record :745 · Changelogs=chuyển-phase+marker LogTransition :1073]; verify marker-count prod trước khi tin backfill). +1 S97 (**#78** display-flag suy CONFIG thay RUNTIME → nói dối khi instance lệch config: `endsBeforeCeo` từ "workflow CÓ cấp finalize" thay "phiếu NÀY finalize" → phiếu lên CEO báo sai "không qua CEO"; nặng sau S97 default-flip opt-in; fix field runtime `EndedByLevelFinalize` Mig 60. Flag = "instance ĐÃ làm-gì" KHÔNG "config CÓ THỂ"). +1 S96 (**#77** SPA deploy bundle cache-lag: team thấy UI CŨ dù đã deploy + Ctrl+F5 — index.html no-cache ĐÚNG-chuẩn + JS hash-immutable nhưng browser giữ tab-SPA-mở/cache-local → verify bundle prod **BYTE-LEVEL** (curl + grep string-fix trong `/assets/index-.js`) phân-biệt cache-vs-deploy; **incognito = golden-test**; no-service-worker nên clear-site-data đủ). +2 S91 (**#76** deploy render config từ `.example` KHÔNG phải file-gitignored → prod thiếu setting tới khi điền tay · **#75** seed ungated per-code RESURRECT data đã-wipe mỗi restart → gate sau env-flag `DemoSeed:Disabled` + verify-by-restart-thật). +1 S90 (**#74** Chrome-MCP screenshot kẹt SPA-SignalR `document_idle` → PowerShell `CopyFromScreen` + Win32 lọc-process-`chrome.exe`). +2 S85 (**#72** lệch giờ +7h — DateTime lưu UTC nhưng serialize thiếu 'Z' → FE `new Date()`=local → `UtcDateTimeJsonConverter` global ép UTC-with-Z · **#73** UpdateDraft absolute-set CLEAR field SÓT khỏi pattern null-safe = bug-class S42 → guard `if (request.X is not null)`). +1 S78 **#71** (thêm enum value vào entity DÙNG-CHUNG → pollute UI/guard phân-loại theo PROXY-predicate `supplierId===null` thay vì enum tường minh; file-khi-duyệt purpose=5 lẫn "Bảng so sánh" + false-pass submit-guard → grep mọi field-proxy predicate loại value mới + build-verify TỪNG app; em-main grep no-leak bắt trước deploy). +1 S76 **#70** (FE absolute-set echo field anh-em từ server-snapshot + `invalidate()` fire-and-forget → cửa-sổ stale-echo mất-dữ-liệu khi lưu 2 ô liên-tiếp; guard `useIsFetching` gate nút Lưu tới khi refetch land; reviewer workflow bắt, impl self-review sót). +1 S72 **#69** FE bundle-hash non-deterministic + `deploy.yml` rebuild-FE-unconditional → bundle rotate kể cả commit governance (SPA-fallback-200 trap; cicd Run #312). +2 S69: **#67** Tailwind v4 accent palette thiếu stop (teal/violet/amberx/greenx chỉ 50/100/500/600/700; dùng -300 → teal/violet rơi DEFAULT Tailwind sai-hệ, amberx/greenx drop hẳn — build PASS, phải soi dist CSS) · **#68** IDE TS diagnostic giữa background-agent/workflow = snapshot dở-dang (TS server bắn mỗi save), chỉ tin build SẠCH sau-cùng (2× session này: re-skin + PE-FE đều false-alarm). Prev +1 S68 **#66** Tailwind v4 rule element thô `h1-h4{color:#0b1220}` viết NGOÀI `@layer` thắng utility `text-white` → heading render đen; fix ĐIỂM `text-white!` (important, grep dist confirm), KHÔNG move `@layer` (load-bearing ~30+ heading toàn app). Prev +1 S65 **#65** build csproj con (vd `SolutionErp.Api.csproj`) ≠ `dotnet build SolutionErp.slnx` (gồm tests) → miss test-compile khi đổi chữ ký record command (CreateDepartmentCommand +ParentId) → CI CS7036 FAIL-gated Run #291 (deploy chặn, prod nguyên — test-gate làm đúng việc). Fix: build full slnx trước push BE signature-change. Prev +2 S61: **#63** EF scaffold tự sinh `RenameColumn` SAI-semantics khi drop+add cùng type (test xanh không bắt — SQLite EnsureCreated không replay migration) · **#64** `dotnet ef database update` áp Design-DB 0-rows ≠ Dev-DB → data-migrate `Sql()` chạy thật lần đầu trên prod. Prev +2 S59 (#61 sqlcmd `-f 65001` · #62 rename natural-key UPDATE trước deploy) | +| Gotchas | **92** | 🔄 **92 @S191** — **+S191 #92** (runner build-env NHIỄM: CI exit-0 + **byte-match CI↔prod** + smoke 200 nhưng bundle **CRASH runtime** `useState null` → trắng màn; 2 ca S189+S191 [#600: fe-user CI 1.803.881B vs local 1.700.709B **+103KB** CÙNG commit, local deterministic 2-build-1-hash; fe-admin lệch +39KB nhưng sống — nhiễm KHÔNG đồng đều]; giả thuyết "root-cause=disk, deploy.yml xóa node_modules fresh nên đóng được" BÁC ca-2 — npm-cache SYSTEM [di chứng #89] vẫn là nguồn: `npm ci` kéo từ cache hỏng; fix chuẩn ×2 = build local HEAD → scp đè app chết GIỮ bundle hỏng làm rollback-1s → verify RUNTIME console+read_page; luật: verify deploy FE PHẢI có 1 phép đo runtime — file-integrity+HTTP-200 chưa đủ · bundle CI lệch local cùng-commit = ĐỎ · sau sự cố hạ tầng coi MỌI cache toolchain là nghi phạm · CẤM push code tới khi runner vá+verify) · 🧊 **91 @S189** — **+S189 #91** (push land trên ĐĨA nhưng tầng-app Gitea CÂM — 0 run/0 status dù MỌI tín hiệu quen XANH [runner Running · ENABLED · filter đúng · không push-đè]; tố cáo bằng **2 nguồn LỆCH cùng API**: `branches/main→commit.id` [git-đĩa] tiến ⟂ `repo→updated_at` [DB] đứng ⇒ CI **chưa từng được gọi**, không phải đỏ; kèm `gitea.log` 0-byte 11,5h [xoay không mở lại] ⇒ restart mở logger + `workflow_dispatch` API → run #486 ship trọn; cost-của-thuốc: restart kích re-index 25' tranh RAM → latency bimodal = số bẩn, khai chứ không giấu; vá RAM cùng lượt `max server memory` **unbounded→1200MB** [PLE 522→1123, A/059 0,58s]; luật verifier: `updated_at ≥ push-time` TRƯỚC khi tuyên "đang chờ CI") · **+S189 #89** (cache automation phình vô hình ăn sạch ổ C — `gitea repo-archive` 16,49GB + `npm-cache` SYSTEM 12,76GB → free **−20MB** → SQL `Error -2` không mở nổi connection → "ko duyệt được"/GET-500, FE gom mọi lỗi vào "Không tìm thấy phiếu" [#44] ⇒ nghi oan phiếu trong khi phiếu NGUYÊN; fix dọn 30GB + `AUTO_CLOSE OFF` ×3 DB [ON = mỗi connection mở lại ~5s] + FE tách 404⟂500 5-site ×2-app; guard runbook §Disk-free + cicd-monitor 1b; luật: prod chậm/mất/không-lưu ⇒ `fsutil diskfree` TRƯỚC khi nghi data) · **+S189 #90** (`ssh … \| head` cắt pipe → sshd Windows-OpenSSH MỒ CÔI spin full core — 3 orphan bão hòa máy 3-core, SQL `ASYNC_NETWORK_IO` CPU-66ms-elapsed-29s, client đói CPU không nhận data ⇒ A/059 47s/500 "task was canceled"; kill orphan [CHỪA ancestor-chain] → **0,38s** tức thì; luật: cắt output = `Select-Object -First N` phía REMOTE + đợi cold-start ~48s sau recycle mới đo + op đo nặng tự hỏi "ngắt rồi để lại gì") — sự cố YC-028 trọn: `runs/2026-08-10-S189-bookstart-open/run.md` + RCA `YC028-S189` error-ledger. 🧊 **88 @S185** — **+S185 #88** (sự cố prod 2 tầng: disk C: đầy → 2 DB RECOVERY_PENDING → Disk-Cleanup "free space" **NÉN NTFS đúng file .mdf/.ldf đang đóng** → SET ONLINE fail `Msg 5118`, login prod 500 "transient failure UseSqlServer" + Gitea push 502/unpacker CÙNG GỐC; fix OFFLINE-từng-DB → backup copy → restart-service-nhả-handle → `compact /U` 4 file + folder → ONLINE, `Recovery is complete` **0 mất data** [58 PE · 21 user · 97 bảng khớp]; luật: Msg 5118 ⇒ check `compact` TRƯỚC khi nghĩ restore + CẤM nén cây SQL DATA; kèm phát hiện **1433 phơi Internet, `sa` bị brute-force** — chờ anh vá firewall). 🧊 87 @S179 flush — đo đĩa `docs/gotchas.md` max anchor = **87** (`lead-stale-auditor` FLAG-5 + máy HIGH `canonical-itself-stale`; #87 sinh ở S177 vá lệch 2 tầng). 🧊 **+S168 #86** (2-push-sát → Gitea auto-CANCEL run đầu cùng-branch: CANCELLED ≠ fail — verifier đọc run CUỐI của range + bằng-chứng artifact-level; side-effect @startup chỉ 1-startup cho cả cụm push; ca thật `aaed699` ship qua #442) · **+S162 #85** (menu-key OR-gate FE ≠ policy per-action BE: cây 4-folder GĐ gate `['Khkk_List','Khkk_G1','KeHoachKyKet'].some(can)` NHƯNG endpoint gắn `[Authorize(Policy="KeHoachKyKet.Read")]` [`ContractSigningPlansController.cs:36`] + `MenuPermissionHandler.cs:40` so khớp **CHÍNH XÁC 1 key** — KHÔNG kế-thừa cha, KHÔNG OR key con ⇒ role `Khkk_List=1 ∧ KeHoachKyKet=0`: FE mở cổng → API **403** → folder GĐ2 hiện "—" dưới **MỌI** gói thầu, claim "gate trước fetch ⇒ 0 rác 403" **vỡ**. Bẫy: luật "OR nhiều key" **ĐÚNG ở tầng HIỂN-THỊ** [`GetMyMenuTreeQuery.cs:61-96` trả node khi con `CanRead`] nhưng **SAI ở tầng API-authz** — mặt thứ 2 của #82, lần này **menu-hiện RỘNG hơn API-cho** [#82 là chiều ngược]. Fix = gate đúng key policy CỦA endpoint; **mở controller đọc attribute TRƯỚC khi wire gate**, đừng suy từ cây menu. Reviewer bắt **trước ship**; Dev 13/13 role đều `Contracts=1 ∧ KeHoachKyKet=1` nên **chưa lộ** — lớp "chưa vỡ vì dữ-liệu hiện tại may mắn", vá ngay không đợi triệu chứng). **+S159 #84** (revoker-2-chiều: sửa data tay trên prod bị code-path ungated @restart ĐẢO NGƯỢC im lặng — grant menu SQL tay bị revoker [S92] lật 447/494 row; fix = code-first seeder + gỡ nhánh + guard 2-vế + khuôn 3-chân-kiềng; liên-quan #75/#76 chiều seeder). **+S133 #81-EXT** (instance 2 indirect-assignment, KHÔNG số mới: snapshot-NS-at-DaDuyet hook đặt ở finalize choke-point NHƯNG 2 site gán Phase TRỰC TIẾP bypass — admin-override `PurchaseEvaluationWorkflowService.cs:308` `= targetPhase` + seeder `DbInitializer:1323` `= current` → phiếu vào DaDuyet qua 2 đường này KHÔNG snapshot [frozen=0, display rơi về live = đúng bug đang vá]; reviewer adversarial bắt pre-commit, em-main + 3 invocation implement đều sót — cùng bài enumeration-writer #81; meta: field-đích có hook/derive/snapshot ⇒ grep MỌI assignment-site kể cả override/seeder). +1 S126 (**#83** số AGGREGATE session-cumulative tự-lão-hoá — audit COUNTS đổi bởi CHÍNH spawn review sau đó [2/2/0→4/4/0]; outward mời-reproduce = bảng per-lane theo run-id, KHÔNG số tổng; reviewer-gate bắt, em-main-solo sót). +1 S118 (**#82** menu-flag grant ≠ quyền API thật: display-layer [Permission `CanRead`→`GetMyMenuTree` FE-menu] ⟂ API-authz-layer [controller `[Authorize(Policy=)]`] = 2 tầng ĐỘC-LẬP; grant `Suppliers` R+C+U cho PRO nhưng `PUT/DELETE /suppliers` gate `[Authorize(Roles="Admin,CatalogManager")]` → PRO vẫn 403 sửa/xóa NCC [chỉ Publish/Import theo `Suppliers.Update` là ăn]; ngược lại `ReportsController` `[Authorize]` trần → mọi authed-user đọc `dashboard` tài-chính-HĐ ngược S92; fix gate `[Authorize(Policy="Reports.Read")]` per-method + grep controller authz TRƯỚC khi tin hiệu-lực grant; reviewer adversarial bắt, em-main-solo tin menu-flag=quyền thì sót; liên-quan #44). +1 S114 (**#81** derived-invariant re-derive ở write/select NHƯNG SÓT DELETE writer [DeleteQuote/DeleteDetail cascade] → xóa quote-selected cuối của NCC → `Supplier.IsWinner` kẹt stale [Any(IsSelected)=false] → `CreateContract Where(IsWinner)` gom NCC phantom → **HĐ giaTri=0**; fix helper `PeWinnerInvariant.ReSyncAfterQuoteRemovalAsync` gọi sau SaveChanges 2 handler; **enumeration-writer-trong-spec KHÔNG đáng tin** → grep MỌI `.Remove(`/`.Add(`/`=field` chạm entity chứa field-nguồn; chỉ **verify-adversarial-refute** bắt [invest/review/implement đều sót]; cardinality S87/S88 lên tầng mutation-path). +1 S113 (**#80** null-forgiving `!` che NRE trên external-parsed data: `SupplierExcelImportService.NewSupplier` gán `Code = r.Code!.Trim()` → NullReferenceException khi parser trả `null` cho ô cột-4 rỗng [ĐÚNG path R4 "thiếu Mã NCC → nháp"] → confirm nổ 500 CẢ batch dù preview classify New-draft KHÔNG lỗi; sibling `IsPublic=!IsNullOrWhiteSpace(row.Code)` null-safe nhưng NewSupplier thì KHÔNG; empty-string `""` chạy OK nhưng FE round-trip blank→JSON `null` → nổ. Fix `(r.Code ?? "").Trim()`. **Build 0/0 KHÔNG bắt** [`!` tắt nullable-warning]; chỉ test-before-critical bắt. Lesson: `!` null-forgiving trên dữ-liệu-parse-ngoài = mìn, dùng `?? default`). +1 S97 (**#79** backfill soi NHẦM bảng — transition-marker "[Duyệt KẾT THÚC]" nằm ở `PurchaseEvaluationChangelogs.ContextNote` KHÔNG phải `PurchaseEvaluationApprovals` → Mig 60 backfill match 0, phiếu finalize-CCM giữ flag=0 hiện sai "Đã được CEO duyệt"; fix Mig 61 query đúng bảng. PE 2 bảng lịch-sử tách vai [Approvals=ai-duyệt record :745 · Changelogs=chuyển-phase+marker LogTransition :1073]; verify marker-count prod trước khi tin backfill). +1 S97 (**#78** display-flag suy CONFIG thay RUNTIME → nói dối khi instance lệch config: `endsBeforeCeo` từ "workflow CÓ cấp finalize" thay "phiếu NÀY finalize" → phiếu lên CEO báo sai "không qua CEO"; nặng sau S97 default-flip opt-in; fix field runtime `EndedByLevelFinalize` Mig 60. Flag = "instance ĐÃ làm-gì" KHÔNG "config CÓ THỂ"). +1 S96 (**#77** SPA deploy bundle cache-lag: team thấy UI CŨ dù đã deploy + Ctrl+F5 — index.html no-cache ĐÚNG-chuẩn + JS hash-immutable nhưng browser giữ tab-SPA-mở/cache-local → verify bundle prod **BYTE-LEVEL** (curl + grep string-fix trong `/assets/index-.js`) phân-biệt cache-vs-deploy; **incognito = golden-test**; no-service-worker nên clear-site-data đủ). +2 S91 (**#76** deploy render config từ `.example` KHÔNG phải file-gitignored → prod thiếu setting tới khi điền tay · **#75** seed ungated per-code RESURRECT data đã-wipe mỗi restart → gate sau env-flag `DemoSeed:Disabled` + verify-by-restart-thật). +1 S90 (**#74** Chrome-MCP screenshot kẹt SPA-SignalR `document_idle` → PowerShell `CopyFromScreen` + Win32 lọc-process-`chrome.exe`). +2 S85 (**#72** lệch giờ +7h — DateTime lưu UTC nhưng serialize thiếu 'Z' → FE `new Date()`=local → `UtcDateTimeJsonConverter` global ép UTC-with-Z · **#73** UpdateDraft absolute-set CLEAR field SÓT khỏi pattern null-safe = bug-class S42 → guard `if (request.X is not null)`). +1 S78 **#71** (thêm enum value vào entity DÙNG-CHUNG → pollute UI/guard phân-loại theo PROXY-predicate `supplierId===null` thay vì enum tường minh; file-khi-duyệt purpose=5 lẫn "Bảng so sánh" + false-pass submit-guard → grep mọi field-proxy predicate loại value mới + build-verify TỪNG app; em-main grep no-leak bắt trước deploy). +1 S76 **#70** (FE absolute-set echo field anh-em từ server-snapshot + `invalidate()` fire-and-forget → cửa-sổ stale-echo mất-dữ-liệu khi lưu 2 ô liên-tiếp; guard `useIsFetching` gate nút Lưu tới khi refetch land; reviewer workflow bắt, impl self-review sót). +1 S72 **#69** FE bundle-hash non-deterministic + `deploy.yml` rebuild-FE-unconditional → bundle rotate kể cả commit governance (SPA-fallback-200 trap; cicd Run #312). +2 S69: **#67** Tailwind v4 accent palette thiếu stop (teal/violet/amberx/greenx chỉ 50/100/500/600/700; dùng -300 → teal/violet rơi DEFAULT Tailwind sai-hệ, amberx/greenx drop hẳn — build PASS, phải soi dist CSS) · **#68** IDE TS diagnostic giữa background-agent/workflow = snapshot dở-dang (TS server bắn mỗi save), chỉ tin build SẠCH sau-cùng (2× session này: re-skin + PE-FE đều false-alarm). Prev +1 S68 **#66** Tailwind v4 rule element thô `h1-h4{color:#0b1220}` viết NGOÀI `@layer` thắng utility `text-white` → heading render đen; fix ĐIỂM `text-white!` (important, grep dist confirm), KHÔNG move `@layer` (load-bearing ~30+ heading toàn app). Prev +1 S65 **#65** build csproj con (vd `SolutionErp.Api.csproj`) ≠ `dotnet build SolutionErp.slnx` (gồm tests) → miss test-compile khi đổi chữ ký record command (CreateDepartmentCommand +ParentId) → CI CS7036 FAIL-gated Run #291 (deploy chặn, prod nguyên — test-gate làm đúng việc). Fix: build full slnx trước push BE signature-change. Prev +2 S61: **#63** EF scaffold tự sinh `RenameColumn` SAI-semantics khi drop+add cùng type (test xanh không bắt — SQLite EnsureCreated không replay migration) · **#64** `dotnet ef database update` áp Design-DB 0-rows ≠ Dev-DB → data-migrate `Sql()` chạy thật lần đầu trên prod. Prev +2 S59 (#61 sqlcmd `-f 65001` · #62 rename natural-key UPDATE trước deploy) | | User memory | **60** | 🔄 **re-ground S179** — đo lại ĐÚNG phương-pháp dòng này tự khai: `ls .claude/auto-memory/*.md | wc -l` = **61**, trừ 1 index `MEMORY.md` = **60** (`lead-stale-auditor` FLAG-9; máy MÙ trục này — không có cross-check). 🔴 Ô này trôi lại chỉ sau **9 nhãn phiên** kể từ lần re-ground trước ⇒ đây là ô **rụng nhanh**, đừng tin số cũ. 🧊 re-ground S170 — đếm đĩa 60 file trừ 1 index = 59 (số "31" stale từ S84; lead-stale FLAG @S169, ring2 verify, vá @S170 SAU khi ring chấm — đúng thứ-tự H24 §2(4) fire→chứng-kiến→vá). +S84 `feedback_derived_flush_execution` (derived→canonical flush = em-main execution KHÔNG gate user-confirm; monitor INFORM-only ≠ cần-anh-duyệt; chỉ ĐỔI-con-số/policy = quyền anh). +S79 `feedback_canonical_spec_over_broadcast` (adap pull canonical-command/§P đừng phỏng-theo broadcast + report-before-stamp dogfood + residual own-diary OK). +S75 `feedback_harness11_engine` (engine governance tự-bảo-trì + gotcha #30-via-Edit + multi-workflow-verify + git-add-specific lesson). re-ground S71 (H1 disk-count: 25 `feedback_*` + 3 `project_*`; STATUS "21" stale từ S64). +S71 `feedback_harness10_run_trace` (run-trace + 3 lesson). Prev +1 S64 `feedback_harness7_outward_writing_quality`; re-grounded S54 base 19 | | Skills | 6 | 3 domain + 3 ops | | Sub-agents | **29** | **+S185 🔬 `ring5-audit`** (vòng-6 nghi-thức cửa phiên — slot (41) ĐÓNG, YC-021/W-2 anh chốt "đồng ý thêm 1 nghi thức"; KIỂM output `tien-kiem-cua` + bảng-nhãn `ritual-labels` 4-nhãn, quyền **đề-nghị-XOÁ-ÁN** khi nhãn nhầm lớp — khác 3 ring kia; wire `session-end §L.b(m)` + `hmw.js` VALID_ROLES 28→**29**; ✅ nấc = **verified-runtime @S185 CÙNG PHIÊN** — registry hot-reload agent-MỚI [data-point lần 2 sau S107, MỚI ⟂ EDIT] + spawn-probe ALIVE fail-closed đúng ngưỡng; KHÔNG cần đợi restart 73) **+S176 🎨 ĐỘI STYLE 3 vai** (`front-end-reviewer-style` · `back-end-reviewer-style` · `database-reviewer-style` — **anh chốt TRỰC TIẾP**, không qua adap). 🔴 **Luật lõi anh giao:** *"quy tắc thì các tính năng đã deploy thành production rồi thì sẽ là chuẩn, trừ khi tao có điều chỉnh lại lần nữa"* — lý-do sinh đội là câu *"mày cứ làm đi làm lại hoài"*. **Ca THẬT làm anh nêu:** S176 vòng-4b lead dựng thùng rác KHKK **thiếu banner + dấu `🗑 xóa ≈`** trong khi màn thùng rác Duyệt NCC **đã chạy prod** có đủ cả hai ⇒ anh phải tự chụp màn gửi sang bảo *"ảnh chỉ là mẫu"*. Trục = **ĐỒNG-NHẤT** (có giống bản đã chạy không) ⟂ `reviewer` = **ĐÚNG-SAI** (có bug không) ⇒ chạy SONG SONG, không giẫm. Địa-phận RỜI theo glob diff: `fe-*/src/**` · `src/Backend/**` trừ `Persistence/**` · `Persistence/**`; **gọi theo diff chạm đâu**, diff không chạm ⇒ KHÔNG gọi (không phải bỏ sót). 🧊 **Anh nêu ban đầu 4 con** (có `clean-code-reviewer-style`) **rồi tự chốt lại 3** (*"3 con tao nhầm"* + *"chốt theo ý mày"*): con clean-code phủ chồng cả 3 con kia nên **không viết nổi câu scope-rời** mà roster này bắt buộc ⇒ trục clean-code (đặt tên · trùng lặp · mật-độ comment · pattern drift) **gộp vào từng con trong địa phận của nó**. 🔸 **Máy trước, vai sau** (đo S176): FE có ESLint chạy trước (`npm run lint`) nhưng **CI KHÔNG gác** (`grep 'npm run lint'` trong workflows = **0 hit**) và đang tồn **89 vấn-đề / 78 lỗi** (fe-admin 42 · fe-user 47); **BE 0 máy style nào** (0 `.editorconfig` · 0 analyzer · 0 `dotnet format`) ⇒ con BE gánh cả lớp cơ-học, khác con FE — 🧊 **đây là SỐ ĐO NGÀY 2026-08-06 (S176), giữ làm LINEAGE có ngày, KHÔNG phải trạng-thái hiện-hành**; nguồn sống = phép-đo per-spawn trong `back-end-reviewer-style.md` §0 (SỬA-6a @S179: claim này từng chép ra **4 site** ⇒ cắm `.editorconfig` là 4 chỗ sai cùng lúc, detector chỉ so SỐ nên không bắt). Cắm được máy vào CI thì **PHẢI cập-nhật lại persona con BE** trả lớp đó về máy. ⚠️ **~~roster 26 > `hmw_width.cap` 20~~ ✅ **ĐÓNG @S181: cap 30, roster 23 ≤ 30 ⇒ 0 lane bị bỏ**** ⇒ full-roster-sweep LOUD-skip nhiều lane hơn trước — số cap = quyền anh (O-7 vẫn treo, nay lệch rộng hơn). 🔸 **Nấc = executed-file / verified-pending-restart** — 3 vai CHƯA spawn-probe; `hmw.js` VALID_ROLES 23→**26**. **+S153 bộ-ba vòng Ctx** (adopt thư `0df10df4` — 🔄 **OWNER-VETO @S153 hub-form 3-vai**: bản đầu (38)(a) 1-vai OWNER-DELEGATED→LEAD-DECIDED, **anh bác cùng ngày** chốt trực tiếp *"Build đủ 3 vai như hub"* qua AskUser = owner chốt THẬT cho quyết này; 🔄 **RENAME cùng phiên theo lệnh anh** *"đặt tên giống hub và sisters để dễ audit"* — tên khai-sinh `ctx-pause-check`/`ctx-reload` → **hub-form `ctx-curator`/`ctx-verifier`** [đo đĩa 3 nhà: hub = curator/verifier/audit · VIPIX = check/reload/chain · hub≠sister ⇒ SE theo HUB-form, tiền lệ rename S149 lead-*]): **`ctx-curator`** vai-1 KIỂM nháp @mỗi-`/pause` (carve-out `pause.md §1` mirror hub O-1(a)) · **`ctx-verifier`** vai-2 ĐỐI-CHIẾU+NẠP-LẠI @mỗi-`/tiep` (dòng `LEAD-PHAI-NHAC-LAI:` = bằng-chứng nội-dung) · **`ctx-audit`** vai-3 SOI-CHUỖI @2-bookend (kiểm-vết vai-1/2, hồi-tố = fallback phiên 1-cửa). Nấc: ctx-audit `file-land + registry-surfaced @S153` cho CẢ 3 vai ctx [🔄 vá @S153 FLAG-6: bản đầu chỉ cấp nấc này cho `ctx-audit` trong khi `hmw.js:42` + writeGuard + README `:27`/`:199`/`:200` chứng 2 vai mới ĐẠT CÙNG nấc — khai THIẾU vẫn là lệch] — spawn-probe ALIVE cả 3 = phiên sau restart T9; hmw VALID_ROLES 20→**23** + writeGuard :228/:235; ⚠️ **roster 23 > `hmw_width.cap` 20** ⇒ full-roster-sweep LOUD-skip 3 lane cuối — số cap = quyền anh, O-7 (câu (40) đang treo). **🔄 RENAME @S149 (anh ratify "OK đồng ý"): 5 vai đổi tên đồng-bộ sister — `tooling-harvest-audit`→`ring1-audit` · `h24-audit`→`ring2-audit` · `sleep-audit`→`ring4-audit` · `lead-view-auditor`→`lead-stale-auditor` · `lead-omission-auditor`→`lead-gap-auditor` (ring* = 2 sister TRÙNG tuyệt đối; lead-* = hub-form, VIPIX-form là prefix). Nấc **NÂNG @cùng-phiên: `registry-listed-in-session`** — harness list 5 tên MỚI + gỡ 5 tên CŨ NGAY TRONG PHIÊN S149 (không restart), **TRÁI phép đo S120/S121 no-hot-reload** (môi trường có thể đã đổi — đây là data-point ĐO ĐƯỢC, không suy). Spawn-probe vẫn CẦN để chứng instantiate (list ≠ spawn được — đúng 0.6b). Tên cũ trong narrative/runs/history = vết lịch-sử, CỐ Ý giữ.** **+S145 🔬 `ring1-audit` (18th sub — round-KIỂM vòng tooling/harvest, C4/C4b adap dot-11 khoản ① `harness-dong-du-vong:51`, owner-decision [2] TÁCH mỗi-vòng-1-con-KHÔNG-gộp). Vai-KIỂM ĐỘC-LẬP đọc OUTPUT H1 `tooling-auditor` (4-mặt freshness) + H2 `harvest-curator` (5-trục integrity), **KHÔNG re-chạy đo**: verdict per-claim ĐẠT/TRƯỢT + falsify ≥2 + **tự tái-dựng ≥1 số CÓ-THỂ-SAI** (đếm `.claude/agents/*.md` vs roster-count H1 · N-harvested/M-spawned) + **thách-CLEAN** (H1 PASS/H2 GATE-0 → mở 1 skill "fresh"+1 sub "harvested" xem thật). 🔴 no-self-exempt ≠ H1/H2 (m-3 S139). **AUTO fire-after-measure @session-end §L.b(g-bis)** — owner-chốt S145 KHÔNG consent-gate (KHÁC trio/H24), fail-safe NO-OP nếu H1/H2 không chạy phiên đó (CẤM ĐẠT-ảo); tuần-tự C4b (invocation SAU con-đo, mtime chứng). Propose-only, backstop = git-diff commit-gate (KHÔNG dựa thiếu-tool `[carry:tools-whitelist-no-teeth]`). `model: opus`+`effort: max`+`memory: project`+`maxTurns: 16`. **Vai #1/3 round-KIỂM** — `ring2-audit` (vòng soi-lead) + `ring4-audit` (vòng nén-ngủ) land dần incremental (§6 broadcast báo-nấc-per-vòng, owner S145). 🔴 **Nấc = executed-file / verified-pending-restart** — registry Agent-tool KHÔNG hot-reload (`agents/README` §Memory discipline — anchor không-số-dòng, vá cite-dòng-trống @S151 per H1 F-3), spawn-probe alive cần **restart CLI**; run-thử-thật @session-end phiên sau. Design-doc: `runs/2026-07-22-S144-adap-dot-11/C4-design-3-vai-kiem.md`.** **+S145 🔬 `ring2-audit` (19th sub — round-KIỂM vòng soi-lead H24, vai #2/3 C4/C4b). Đọc OUTPUT `lead-stale-auditor` + `lead-gap-auditor` (🔴 KIỂM 2 con-đo H24, **KHÔNG soi lead trực-tiếp**) → verdict per-FLAG + tự tái-dựng ≥1 số (flag-count · mở 1 FLAG đối-chứng source · `class_repeat` `.session-counter.json`) + **thách-CLEAN** (H24 báo ít/0 FLAG ⇒ tự soi 1 surface tìm FLAG sót — CLEAN "lead ổn" là ca nguy nhất, khớp H24 §2(1) phép-thử-nhanh). Verify vai H24 KHÔNG tự chế class ngoài enum ĐÓNG `flag_classes`. no-self-exempt ≠ lead-stale/lead-gap (m-3; tên mới @rename S149). AUTO wired `session-end §L.b(j)(ii-bis)` sau cặp H24; fail-safe NO-OP nếu H24 không chạy (nhịp/consent). `maxTurns: 16`.** **+S145 🔬 `ring4-audit` (20th sub — round-KIỂM vòng nén-ngủ, vai #3/3 C4/C4b). Đọc OUTPUT `/sleep-recovery-memory-l2` (`.gist.md` mới + Fidelity verdict + claim giảm-byte) → verdict per-claim + **tự ĐO LẠI byte** archive gốc vs gist (đối-chứng claim) + **thách-CLEAN** (Fidelity 5P/0F ⇒ mở 1 gist đối-chứng `.md` gốc xem có giữ 4-field load-bearing hay nén quá tay) + kiểm bất-biến gist-ADDITIVE (gốc byte-identical). no-self-exempt độc-lập lượt nén (m-3). AUTO wired `sleep-recovery-memory-l2.md Phase 4.5` sau nén; fail-safe NO-OP nếu 0 gist tươi. `maxTurns: 16`.** **+S141/S142 🧪🧭🧮 `harness-eval` + `harness-refine` + `harness-audit` (15th–17th sub — bộ-ba đồng-kiểm HỆ MEMORY, adopt AI_INFRA khuôn 5-câu `58e28bae` khối B, wave adap đợt-9/10; land S141 persona+seed+`hmw.js` VALID_ROLES 14→17, wiring nghi-thức S142 vòng-2 sau khi vòng-1 chết giữa chừng). Read-only INFORM-only propose-only, frontmatter **không khai Write/Edit** — 🔴 nhưng đo S143: **runtime VẪN cấp** (append cuối tool-list, 9/9 vai read-only; nhân-chứng `reviewer` đã Write+Edit thật) ⇒ đây là **tuyên-bố ý-định**, KHÔNG phải chặn-bằng-thiếu-tool; containment thật = single-writer + git-diff commit-gate · `model: opus`+`effort: max`+`memory: project`+`maxTurns: 18`. Trục = **CHÍNH HỆ MEMORY**, chạy **TUẦN TỰ** eval→refine→audit (🔴 CẤM `parallel()` — con KIỂM chạy song song sẽ chấm bản CŨ ⇒ verdict vô-nghĩa): `harness-eval` = con **ĐO** (chỉ trả số nguyên-văn từ instrument, CẤM số-từ-trí-nhớ) ⟂ `harness-refine` = con **ĐỀ-XUẤT** (4 lớp reinject/promote/archive/distill, bắt buộc có mục "đã cân-nhắc→BÁC") ⟂ `harness-audit` = con **KIỂM** (verdict ĐẠT/TRƯỢT per-claim + re-chạy ≥2 số load-bearing; PIN run-folder đích-danh, CẤM tự tìm "run mới nhất"). 🔴 **KHÔNG gộp** với H1 tooling-freshness / H2 harvest-integrity / H24 lead-surface / reviewer diff-code — 4 trục khác nhau. Nghi-thức 2-đầu-phiên **AUTO 1 vòng/phiên GIỮA 2 bookend** (`session-start §2.1.9(a)` canonical + `session-end §L.b(k)(i)` trỏ): owner chốt @S151 — em-main IN 1 dòng trạng-thái rồi CHẠY, KHÔNG chờ gật [🔄 vá @S153 FLAG-4 HIGH: ô này còn khai "consent-gated + anh gật mới chạy" sau khi owner (32) đã lật AUTO, và ngược chính `:466` cùng file; trio đã chạy THẬT 3 vòng S151/S152/S153]; skip → ghi vết WAL `trio: skipped S` (vết-2-lớp, chuỗi skip nhìn thấy được). Budget seed đo TƯƠI `measure-agent-memory.ps1`: eval 1547B · refine 1616B · audit 1513B (l2_*=0 = SEED-STATE đúng, chưa chạy lần nào). 🔸 **Nấc trung-thực = executed-file / verified-pending-restart** — 3 vai CHƯA spawn-probe lần nào (cần restart CLI + anh consent; KHÔNG được đọc thành verified-runtime).** **+S121 👁 `lead-stale-auditor` + `lead-gap-auditor` (13th+14th sub — H24 lead-self-audit, adopt broadcast `2026-07-14-Governance-harness-24`, anh chốt S119 `owner-decisions-15-07-2026`, land S121 wave-W2 `hmw.js` VALID_ROLES 12→14 CÙNG COMMIT roster+STATUS [R3-M2: lệch landing ⇒ C2/B3 flag mọi derived ghi "14" là stale-vs-12 ⇒ detector BÃO trên chính thay đổi của H24]). 2 monitor INFORM-only propose-only, cookie-cutter frontmatter monitor sẵn (`model: inherit` lúc-land [S124 re-pin → `opus`+`effort:max` cùng toàn roster] · `memory: project` · `maxTurns: 18` · 8 tool read-only, **NO Write/Edit**). Trục: `lead-stale-auditor` = **view LỆCH source** (5 class `view-*`) ⟂ `lead-gap-auditor` = **cái BỊ THIẾU** (6 class `gap-*` — +`gap-incident-unrecorded` @S143 owner-duyệt) — 🔴 **KHÔNG gộp** vào tooling-auditor(H1 tooling-freshness)/harvest-curator(H2 harvest-integrity): H24 §2(1) *"gộp là mất tiêu-điểm; cái ta cần là cặp mắt CHỈ dành cho lead"*. Class-flag = **enum ĐÓNG** `lead_self_audit.flag_classes` ở `.claude/agent-memory/memory-budget.json` (11 class = 5+6, vai CẤM tự chế — mở enum là quyền owner). Nhịp = `h24_cadence` cùng file (single-source H24 §2(3) — doc TRỎ key, KHÔNG chép số ⇒ 🔄 **đã GỠ digits `{6/15/3}` @S153** theo `ring2-audit` R-3: chép số ngay cạnh câu cấm-chép-số là **B1-copy** [số vẫn KHỚP canonical nên KHÔNG phải stale — F-6 chống vu-oan]; đọc số sống ở `memory-budget.json → h24_cadence`); 🔄 **HÌNH B @S149**: cặp soi-lead spawn **VÔ-ĐIỀU-KIỆN 2 bookend mỗi phiên** — `light_every` hạ thành **stats-only** (KHÔNG còn cổng bắn), `deep_every` GIỮ cổng nhịp [vá @S153 FLAG-5: ô này còn mô-tả bắn thuần-cadence; đối-chứng hành-vi = cặp chạy @counter 27 với light 1/6 CHƯA tới hạn]; counter `.claude/governance/.session-counter.json` (content-based `last_ticked_head`, KHÔNG blind-increment — hook `wal-flush.ps1` auto-commit `agent-memory/` mỗi turn sẽ làm hỏng đếm mù; đặt ngoài **4 path** hook CÓ CHỦ-ĐÍCH — *hook nới từ 3 lên 4 @S146 khi thêm `.claude/sessions/`; mệnh-đề "governance/ nằm ngoài" đã RE-VERIFY sau khi nới, vẫn đúng*). ✅ **verified-runtime S122** (spawn-probe toàn-roster-KHI-ĐÓ SỐNG post-restart — **N=14 tại thời-điểm S122**, chứng runtime, §Recently-Done S122; 🔸 +3 trio S141 CHƯA nằm trong phép đo này) **+ chạy THẬT S124** (dogfood 2-vai đầu-tiên, owner-triggered @counter 3) **+ S137** (light-audit consent OVERDUE 8/6 → 2 FLAG view-stale-status + 1 FLAG gap-decision-sunk). 🔸 Residual THẬT giữ: đường `hmw.js→agentType` cho 2 vai H24 chưa exercise (Agent-tool path đã chứng; workflow-path chưa) [S137 view-audit lật ⚠️→✅ khớp source, giữ residual]. 🔸 Từ-vựng "view"/"omission" chọn có chủ-đích tránh va-chạm: `governance-detectors.ps1:15` đã sở-hữu `derived-staleness`, và "GAP" quá-tải (`session-end` build-gap · `memory-selfimprove-audit.ps1` CRITERIA+GAP).** **+S107 📄 `office-document` (12th sub — Office document WRITE specialist: docx·xlsx·pptx·pdf + form-engine; floor OD1–OD10; VALID_ROLES hmw.js 9→10; anh giao trực tiếp S107 "Sub chính" Office; ✅ **verified-runtime S107 CÙNG PHIÊN** — registry hot-reload agent MỚI giữa phiên [S27 lesson thu hẹp còn EDIT-agent/command] + spawn-test PASS OD1/OD4/OD5 template thật, model self-report `claude-fable-5` [data-point #5 flip-8]; finding: 2 template `wwwroot/templates` 0-placeholder mẫu-blank).** **worker-tier-pin (owner-decision S124 2026-07-15 — thay all-inherit H8 S66, trước nữa two-tier H4; sub TOÀN-ROSTER pin cứng KHÔNG theo lead; H23-precedence ĐO S126: spawn-param THẮNG hard-pin ⇒ `/fable-real` SỐNG). Lead = **DEFAULT Opus 5 MAX** (thư `9c909007` @S149 (re-stamp 07-25 G-024a; pre `59ce1d0f`); **anh chốt ô canonical hiển-thị DEFAULT-fleet @S151** — *"cần gọi Fable tao chọn đầu mỗi phiên hoặc gọi bằng command fable-real"* ⇒ Fable = anh `/model` đầu phiên HOẶC per-invocation `/fable-real`; runtime thật = BƯỚC 0.6 probe mỗi phiên, KHÔNG ghi sổ mỗi flip — hết class stale-2-flip ring1 bắt @S150). 🧊 Lineage owner-choice/flip-chain FROZEN tới flip-8 (flip-9 S119 Opus-4.8 owner-choice + các lượt /model sau = per-session không enumerate, anti-churn S106):** em main Fable 5 (1M) Max (✅ **Fable RESTORED S98 2026-07-02** — lead self-report `claude-fable-5` + settings `claude-fable-5[1m]`; fallback-Opus H5 S62→S97 kết thúc, H5.6 hoàn tất) ⚠️ **S101 2026-07-03: runtime lead = Opus 4.8** (lead + 2 monitor + WF2 reviewer inherit đều self-report `claude-opus-4-8`; settings vẫn pin `claude-fable-5[1m]` → **Fable unavailable phiên này → H5-fallback tự-kích, revert-free, KHÔNG sửa frontmatter**; "Fable RESTORED S98" = point-in-time KHÔNG phải steady-state — inherit-chain resolve theo availability runtime; ✅ **S102 2026-07-06: Fable VỀ LẠI runtime** — lead + 2 monitor inherit self-report `claude-fable-5` → H5-fallback S101 kết thúc, pattern availability per-session xác nhận [S98 Fable → S101 Opus → S102 Fable → **S103 Opus** → **S104 Opus** (2026-07-06 Fable outage tiếp — S104 lead + 2 monitor spawn self-report `claude-opus-4-8` ×3, H5-fallback revert-free) → ✅ **S105 Fable** (2026-07-07 Fable VỀ lần 3 — lead + 2 monitor self-report `claude-fable-5` ×3, runtime-verified) → ⚠️ **S106 Opus** (2026-07-07 Fable outage lại — lead runtime self-report `claude-opus-4-8`, H5-fallback revert-free; flip-7) → ✅ **S107 Fable** (2026-07-10 Fable VỀ lần 4 — lead + 2 monitor self-report `claude-fable-5` ×3, runtime-verified; flip-8)], BƯỚC 0.6 probe mỗi phiên; per-session KHÔNG steady-state. 🔴 **[§Sub-agents = CANONICAL single-owner flip-chain — S106 gộp per anh-confirm; derived `agents/README §Architecture` · `session-start BƯỚC 0.6` · `session-end §commit-format` trỏ về đây [🔄 vá @S153 FLAG-1: 3 cite số-dòng cũ `:3`/`:37`/`:113` đo lại SAI cả 3 (đúng là `:5`/`:48`/`:315`) — vá bằng BỎ số theo luật đã áp @S151, không ĐỔI số], KHÔNG tự enumerate mỗi flip → chống churn 4-file/flip]**) · **TOÀN roster `model: opus` + `effort: max`** (worker-tier-pin S124; **A2 đo S126:** lane no-tier resolve `claude-opus-4-8` DÙ lead=Fable ⇒ pin ăn runtime, hết inherit-lead; số vai = cột giữa của chính row này — KHÔNG chép lại vào narrative, đó là cách row-14 và narrative-12 lệch nhau từ S121 tới S122; lineage: 7 demoted flip S66 → all-inherit S66–S123 [office-document S107 inherit-born] → pin S124) · effort Max. 10 product/quality + monitor INFORM-only (H1 · H2 · 2×H24 S121 · 3×trio-memory S141 · 3×round-KIỂM `ring1-audit`/`ring2-audit`/`ring4-audit` S145) — **liệt-kê NHÓM, số vai = cột giữa của chính row này**. Nấc H8 = **runtime-verified S98 (2026-07-02)** — spawn-test inherit-chain ×2 monitor self-report `claude-fable-5` (cả đội tự lên Fable khi lead restore, 0 sửa frontmatter — đúng thiết kế all-inherit). Prev two-tier H4 runtime-verified S57bis (spawn-test 2 chiều). | diff --git a/docs/changelog/sessions/2026-08-12-2252-S189-S191-yc028-yc030-l15.md b/docs/changelog/sessions/2026-08-12-2252-S189-S191-yc028-yc030-l15.md index 825be879..db6ce4e9 100644 --- a/docs/changelog/sessions/2026-08-12-2252-S189-S191-yc028-yc030-l15.md +++ b/docs/changelog/sessions/2026-08-12-2252-S189-S191-yc028-yc030-l15.md @@ -10,7 +10,7 @@ Anh Kiệt báo phiếu `PE/2026/A/059` "ko duyệt được / ko tìm thấy ph - **T2 orphan-sshd:** 3 con sshd mồ côi của chính lead (ssh|head cắt pipe — #90, tự khai RCA) ăn 3/3 core → kill → A/059 47s→0,38s. - **T3 Gitea tầng-app câm** nuốt push (#91): restart + workflow_dispatch → run #486 ship trọn (gate 680 EXACT · bundle rotate ×2 · marker 10/10). - **T4 RAM:** pin `max server memory` unbounded→**1200MB**, PLE 522→1123. -- **P0 chen giữa /pause:** bundle CI crash `React null useState` — build LỆCH local >100KB cùng commit ⇒ BUILD-ENV runner hỏng (workspace ghi dở đêm disk-0). **Hotfix scp dist local lành** đè fe-user+fe-admin → SỐNG, outage ~15'. Root-cause sau đó chốt = **disk, KHÔNG phải node_modules** (deploy.yml tự xóa fresh mỗi run) ⇒ #92-candidate đóng, không sinh gotcha mới. +- **P0 chen giữa /pause:** bundle CI crash `React null useState` — build LỆCH local >100KB cùng commit ⇒ BUILD-ENV runner hỏng (workspace ghi dở đêm disk-0). **Hotfix scp dist local lành** đè fe-user+fe-admin → SỐNG, outage ~15'. ~~Root-cause chốt = disk ⇒ #92-candidate đóng~~ → **BÁC @S191 ca-2** (xem dưới): thành **gotcha #92 chính thức**. - Gotcha **#89/#90/#91** (STATUS row 88→91) · sổ +YC-028 +PN-002 · FE tách "404 thật ⟂ lỗi hệ thống" 5 site ×2 app LIVE. ## S190 (2026-08-12) — YC-029 "SOLTUON-12-08-2026" + YC-030 Q1-Q5 + DEPLOY #600 @@ -30,10 +30,19 @@ Anh Kiệt báo phiếu `PE/2026/A/059` "ko duyệt được / ko tìm thấy ph - **Finish NEXT-em bằng ĐO (không làm mù theo claim):** - "Nén 2 agent-memory over-cap" = **SAI ĐÍCH** (ctx-verifier cờ-8, lead đối chứng từng byte): investigator 23.148B + reviewer 25.469B đều **DƯỚI** trần `autoinject_cap_bytes` 25.600 ⇒ KHÔNG nén (nén = phá thông tin vô cớ). File duy nhất vượt = `ring2-audit` 27.458B — vốn nhãn *đang-cãi* (60,1% bất-khả-drain), giữ nguyên chờ thiết kế. reviewer dư 131B = watch. - "Un-delete `a79dd579` Dev" = **HẾT CẦN** (đo `SolutionErp_Dev`): seeder tắt-từ-lúc-sinh đã tái tạo QT-HD-V2-001 (Active=0/UserSel=0, Id mới) — Dev đồng hình prod 8 type-3, 0 HĐ mồ côi. Un-delete giờ = row trùng Code, bẩn hơn. - - Gotcha #92-candidate **đóng** (root-cause = disk; #600 build sạch sau dọn đĩa là bằng chứng sống) — không sinh gotcha mới, lớp đã có #89. + - ~~Gotcha #92-candidate đóng (root-cause = disk)~~ → **BÁC ngay trong phiên bằng ca-2** (giữ dòng làm vết: claim "đóng được" sống đúng 40 phút — bài *đo-lại-tiền-đề* thắng, nhưng chính claim này cũng do em vừa ghi). - **Vá bề mặt theo cờ ctx-verifier:** STATUS `:6` (4 mặt chết: 693→697 · "PUSH GOM CHỜ"→#600 · nhãn IN-PROGRESS→ĐÓNG · con-trỏ `:476` chết) · `run.md` S190 bổ nửa sau (B5-kết-quả→B6→YC-030→deploy) · `sub-wq3-be.md` trám on-behalf (skeleton 593B → có đường tái dựng) · dọn stray dir rỗng · HANDOFF segment mới. Cờ-6 bác (dương giả — carry `pin-max-server-memory-fleet` đã có nhà HANDOFF @S190, vai grep từ khóa spaced). ## Carry/treo sau phiên - `[carry:w5-host-ondeleted]` (5 host clear-selection sau xoá) · `[carry:w8-delete-policy]` (policy DELETE khi anh nói "ổn rồi") · ring2-audit over-cap {đang-cãi} · session-log này gánh cả S189 (đã hết vô chủ). - Chờ anh: **KIỂM PROD GĐ3** (lệch-ý-thì-chỉnh) · tồn (76)(77)(78)(79) · (89) jump-dài · (74) rotate vrapp · value_protect · lật ô YC-008↔YC-010 · "ổn rồi" mở W8 · tồn (22)(67)(72). + +## S191 bổ sung 00:50 — P0 LẦN 2: eOffice trắng màn khi anh kiểm → hotfix + gotcha #92 khắc thật + +- Anh kiểm prod: *"Admin → tạm ổn. eOffice → vào trắng màn hình"*. Browser đo thật: console `Uncaught TypeError: Cannot read properties of null (reading 'useState')` @ `index-BIN1bEGo.js:8` — **bundle CI #600 crash runtime** dù byte-match + smoke 200 đều xanh @S190. +- ⇒ Giả thuyết "#92 đóng vì root-cause=disk" (em ghi 40 phút trước) **SAI**: file-integrity ≠ runtime; npm-cache runner (di chứng #89) vẫn là nghi phạm — `npm ci` kéo từ cache hỏng thì xóa node_modules cũng vô ích. +- **Hotfix (mirror S189, đã chứng):** build local HEAD (hash `D6lFjgKF` trùng dist 21:54 — determinism control-dương) → scp đè fe-user (GIỮ `BIN1bEGo` trên đĩa = rollback 1s) → verify **runtime**: form login render (`read_page`) + network load `D6lFjgKF` + console load-mới sạch. **eOffice SỐNG.** +- Chẩn admin cùng lò: CI `D2FHMRPX` 1.819.492B vs local `KRJldwaN` 1.780.807B (**lệch 39KB**) — nhiễm nhưng ĐANG SỐNG → không đụng (anh đang kiểm); dist local sẵn nếu anh muốn đồng nhất. +- **Gotcha #92 khắc chính thức** (2 ca đủ chín — docs/gotchas.md, STATUS row 91→92). Ràng buộc nổi ở WAL `next:`: 🔴 **CẤM push code / dispatch CI tới khi runner được vá + verify runtime** — build mới sẽ đè hotfix bằng bundle nhiễm. +- Vá gốc (chờ anh chọn giờ — sẽ rotate bundle prod): dọn `npm cache` SYSTEM + workspace runner → `workflow_dispatch` → cicd-monitor + **smoke runtime** (luật #92(1)) → khi đó mới mở lại đường push code. diff --git a/docs/gotchas.md b/docs/gotchas.md index 49bdf6f0..c968c39d 100644 --- a/docs/gotchas.md +++ b/docs/gotchas.md @@ -1455,6 +1455,18 @@ for h in resp.points: # ← .points không phải iterable trực tiếp **Luật:** (1) verifier tuyên "đang chờ CI" phải đo **`repo.updated_at ≥ thời-điểm-push`** trước — head khớp chưa đủ; (2) sau restart service có re-index/warm-up: **phép đo latency trong cửa sổ đó là số bẩn**, khai rõ hoặc đợi lắng; (3) SQLEXPRESS trên máy nhỏ: **pin `max server memory` ngay lúc setup** (ghi vào runbook), đừng để default unbounded. +### 92. Runner build-env NHIỄM — CI exit-0 + byte-match CI↔prod nhưng bundle CRASH runtime; "build thành công" ≠ "bundle sống" (Session 189 + 191) + +**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** dù 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 lò). `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ở). + +**Vì sao lọt 2 lần:** verify deploy đo **file-integrity** (hash CI-log ↔ prod) + **HTTP status** — cả hai đều là 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** là nguồn nhiễm (`npm ci` kéo lại từ cache hỏng). + +**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 cũ = rollback 1 giây) → verify bằng **console + read_page** (form render, load-mới sạch lỗi), KHÔNG chỉ curl. + +**Luật:** (1) verify deploy FE phải có **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). + ## Checklist debug bug mới 1. Build pass không? → fail → check using + package version compat