From f8f2ef964cc723149a98df87e25897d13b9f04cb Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Tue, 11 Aug 2026 12:13:20 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20Docs:=20YC-028=20=C4=91=C3=B3ng=20tr?= =?UTF-8?q?=E1=BB=8Dn=20=E2=80=94=20gotcha=20#89/#90/#91=20+=20RCA=20YC028?= =?UTF-8?q?-S189=20+=20STATUS=20row=2088=E2=86=9291=20+=20run-trace=20cicd?= =?UTF-8?q?=202=20v=C3=B2ng?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sự cố prod 3 tầng (A/059 "ko tìm thấy phiếu") giải trọn trong phiên, chi tiết error-ledger RCA YC028-S189: - T1 disk 0-byte (cache automation 30GB) → #89 · T2 orphan-sshd bão hòa 3 core (lỗi lead, tự khai) → #90 - T3 lộ khi ship: Gitea tầng-app câm nuốt push (0 run dù mọi tín hiệu xanh) → #91; restart + dispatch → run #486 ship trọn (gate 680 EXACT · bundle rotate ×2 · marker 10/10 — cicd-monitor 2 vòng PARTIAL 8/9) - Vá RAM cùng lượt: sp_configure max server memory unbounded→1200MB (PLE 522→1123 · A/059 0,58s) Sổ: +YC-028 4 tầng xử · run-trace sub-cicd-monitor-yc028.md (FAIL vòng-1 giữ nguyên làm vết, vòng-2 append) · AS-10 cicd-monitor tự-curate memory qua Bash (persona 0 Write) = verify-KEPT (27,4→17KB + archive/_INDEX đúng khuôn + sửa stale Mig 71→72 đúng canonical) · squash wal: theo §5.0. Docs-only ⇒ CI skip (đã đối chứng #486 xong trước khi push — không đụng #86). Co-Authored-By: Claude Opus 5 --- .claude/WAL.md | 5 + .claude/agent-memory/cicd-monitor/MEMORY.md | 50 ++-- .../cicd-monitor/archive/2026-08.md | 29 ++ .../cicd-monitor/archive/_INDEX.md | 2 + .../sub-cicd-monitor-yc028.md | 281 ++++++++++++++++++ docs/STATUS.md | 2 +- docs/gotchas.md | 34 +++ docs/governance/error-ledger.md | 14 + 8 files changed, 390 insertions(+), 27 deletions(-) create mode 100644 .claude/workflows/runs/2026-08-10-S189-bookstart-open/sub-cicd-monitor-yc028.md diff --git a/.claude/WAL.md b/.claude/WAL.md index 97eb1bd..a365df7 100644 --- a/.claude/WAL.md +++ b/.claude/WAL.md @@ -15,6 +15,11 @@ chain: 3 sshd orphan 6,3 core-s/5s trên máy 3-core → kill (chừa chain mình) → 0,17-0,41s ×2 lượt. 🔴 BÀI HỌC MỚI: `ssh ... | head` = ĐẺ ORPHAN SPIN phía Windows-OpenSSH — cấm head trên ssh output dài; recycle pool + đợi cold-start ~48s trước khi đo +[!] SỰ CỐ PHỤ lộ khi ship (5): Gitea nhận push `92d8215a` vào ĐĨA nhưng tầng app KHÔNG xử lý + (0 run, `updated_at` đứng mốc cũ — cicd vòng-1 FAIL-8/11 chẩn đúng "CI chưa từng được gọi"; + kèm `gitea.log` 0-byte 11,5h). ĐÃ XỬ: Restart-Service gitea (log sống lại 31,9MB) + + workflow_dispatch API 204 → run #486 head=92d8215a RUNNING — cicd đang gác vòng-2 trọn. + Nợ ghi sau verdict: gotcha #91 (phép đo updated_at ⟂ branch-head, 2 nguồn cùng API) [!] Hàng (1)-(6) YC-028 còn: (3B) bật cron archive_cleanup Gitea + decompress Vietreport_Master [ngoài giờ — restart gitea vài giây] · (4) disk-watch vào cicd-monitor persona + runbook · (5) FE tách "lỗi hệ thống"⟂"không tìm thấy" ×2 app + CÂN NHẮC AsSplitQuery cho diff --git a/.claude/agent-memory/cicd-monitor/MEMORY.md b/.claude/agent-memory/cicd-monitor/MEMORY.md index ec370ee..c330057 100644 --- a/.claude/agent-memory/cicd-monitor/MEMORY.md +++ b/.claude/agent-memory/cicd-monitor/MEMORY.md @@ -1,14 +1,16 @@ # CI/CD Monitor Agent — Persistent Memory -- **S159 (2026-07-29) đợt-1 `b5799fc` + đợt-4 `a2bbcb9` — CẢ HAI `VERDICT = PASS 9/9` `[LEAD HARVEST HỒI-TỐ @S172 — nợ MỞ 3 PHIÊN (S162→S172), do CHÍNH vai này bắt @S162 F-02 rồi không ai đóng; owner slot (71) @S172 chốt 'gom và sleep thì auto, tự quyết'. Nguồn = `runs/2026-07-29-S159-tong-quan-pipeline-menu/cicd-verify-{b5799fc,a2bbcb9}.md`, KHÔNG suy diễn]`:** 🔴 **Vì sao món này nằm lại lâu:** `MEMORY.md:9` từng khai *'verbatim→archive'* cho cụm này mà `git log -S` **rỗng toàn lịch sử** ⇒ vai tự đính chính @S162 là *'không mất hẳn, nguyên liệu còn ở run-folder'* — **đúng, nhưng rồi không ai gom**. Đo @S172: **0 hit ở CẢ L1 lẫn archive** cho cả 2 sha, control-dương = 3 sha anh em cùng folder **có** hit ⇒ phép grep không rỗng. ⇒ **'không mất hẳn' KHÔNG bằng 'đã về nhà'**; nợ-harvest cần một dòng ghi, không cần một lời trấn an. **Nội dung cứu được:** đợt-1 `b5799fc` (11:44) — M2b test-gate **562 EXACT** · M4 bundle rotate user `DptYR4wL`→`0GO8mgrG` (**điều kiện PASS bắt buộc**, đo chứ không giả định) · M5 byte-marker **3 dương + 2 âm** · M6 smoke 4/4. đợt-4 `a2bbcb9` (13:52) — M3 byte-marker **3 dương/3 âm** · **M6 RESTART-DURABILITY lần 2 (chứng bền qua ≥2 restart)** · M7 smoke 4/4. 🔴 **Arc 4 đợt là bài học hoàn chỉnh, đừng đọc lẻ:** đợt-1 PASS 9/9 → **đợt-2 `1a47a61` PARTIAL 8/9 bắt regression revoker ăn 447/494 row** → đợt-3 `2a72695` PASS 9/9 (đóng regression, chứng bằng **3 chân kiềng**) → đợt-4 PASS 9/9 (tái xác nhận durability qua restart **thứ hai**). ⇒ *một PASS lẻ ở đợt-1 KHÔNG chứng được thứ mà chuỗi 4 đợt chứng*: bền qua restart phải đo **≥2 lần restart**, không phải 1. 🔑 **Nghề giữ lại (`a2bbcb9` M3):** *'đọc grep-source thô sẽ báo FAIL oan cho một control vốn ĐÚNG'* — control âm phải đọc ở tầng **render/bundle**, không đọc ở tầng source. Cùng họ với bẫy `cut` cắt UTF-8 và glob-sai mà các vòng kiểm @S172 dính. Tag `[s159, no-mo-3-phien, khong-mat-han-khac-da-ve-nha, arc-4-dot, durability-can-2-restart, control-am-doc-tang-bundle]` +- **S189 (08-11) `92d8215a` YC-028 FE tách 404⟂lỗi-hệ-thống (8 FE, **0 BE**, 0-mig) — V1 FAIL 8/11 → V2 PARTIAL 8/9.** 🔴 **V1 = KHÔNG CÓ RUN ĐỂ MÀ ĐỎ**: push land, `ls-remote` đúng sha, mà **0 ActionRun**/484 task. 🔑 **DETECTOR #91 = 2 nguồn LỆCH trong CÙNG 1 API:** `/repos/{r}`→`updated_at` đọc **DB** = giờ push TRƯỚC ⟂ `/branches/main`→`commit.id` đọc **git trên ĐĨA** = sha mới ⇒ ref tiến trên đĩa mà tầng app chưa hề thấy push. **Stage 2 từ nay ĐO `updated_at ≥ giờ push` TRƯỚC khi kết luận 'đang chờ CI'.** Terminal push SẠCH ⇒ chết ở **notifier**, không ở receive-pack. Sửa = `workflow_dispatch` (204). 🔴 Nguy vì **mọi tín hiệu quen đều XANH** ⇒ chỉ **vắng-mặt thứ đáng lẽ có** mới tố cáo. 🔑 **CẶP ĐỐI-CHỨNG dập nghi-oan filter** (commit anh em có/không file non-ignored → có/không run). 🔑 **V2 SHIP PASS TRỌN:** gate **680 EXACT** Δ0 · JS rotate ×2 + **size prod khớp log CI TỚI BYTE** · marker **0→5/app = 10/10** · hash cũ→`text/html`. 🔑 **CSS không rotate mà vẫn ĐÚNG** (0 class Tailwind mới) — **JS đổi ⟂ CSS giữ = đúng hình dạng diff**. 🔴 **V2 FAIL: A/059 6/16 mẫu >3s (max 8,75s).** **Tự bác cold-start của chính mình vì burst-2 XẤU HƠN burst-1** ⇒ *warm-up curve đẹp KHÔNG đủ, phải đo burst THỨ HAI*. Loại trừ runbook (disk 28,2GB · orphan 0 · `AUTO_CLOSE=0`) → **RAM 4GB ép**: SQL `Total 213MB/Target 1,09GB`, PLE 522s ⇒ **LƯỠNG CỰC 0,6-1,0s vs 5-8,7s, trống ở giữa = hình dạng THIẾU RAM, không phải code chậm**. 🔑 **Logic rẻ hơn đo: commit 0 file BE ⇒ latency không thể do commit này.** 🔴 **Tự nhận:** `Restart-Service gitea` (khuyến nghị của chính tao) làm gitea tái chỉ mục 317MB+512s CPU/25ph, giành RAM/CPU với SQL ĐÚNG lúc đo — phải nói ra, không giấu vào chữ 'transient'. Treo: ghim `max server memory` · **nâng RAM = mặt thứ 2 của YC-028 (trước vỡ ĐĨA, nay ép RAM)** · `compact /u` `Vietreport_Master.mdf` (#88). Evidence `runs/2026-08-10-S189-bookstart-open/sub-cicd-monitor-yc028.md`. Tag `[s189, gotcha-91, detector-updatedat-vs-branchhead, burst2-bac-coldstart, luong-cuc-thieu-RAM, tu-nhan]` -- **S168 (08-01) `a2b37dc..0bfadc2` GĐ3-D Panel-3 + Lines-editor KHKK (7 FE + 2 BE + 1 test, 0-mig) — PASS 6/6** (#444 6m28s · gate **644=45+599** Δ+5 = đúng 5 Fact mới · Mig GIỮ 71 · tables 97 · bundle `RgvHZkPH→DLRf9TeA` + `D65FJPGd→C7TGA4Kn`). 🔑 **BÀI TỐT NHẤT — WIRE-PROOF BẰNG THÔNG-ĐIỆP-LỖI GỌI TÊN FIELD**: PUT route mới + bearer + GUID toàn-0 trả **400 liệt kê ĐỦ 3 field `PlanId`/`LineId`/`CatalogEntryId`** ⇒ command+validator MỚI chạy end-to-end Api→Application — **mạnh hơn hẳn 401** (401 chỉ chứng route tồn tại). Kèm miễn phí: đây là chứng **pool đang phục vụ BINARY MỚI**, mạnh hơn đo `w3wp` StartTime (StartTime chỉ nói 'có restart', không nói 'code nào'). 🔑 **CHỤP PRE KỊP GIỜ = tài sản rẻ nhất**: spawn 17:10 / push 17:08 ⇒ đo PRE (bundle + marker 0-hit + route **404**) TRƯỚC khi deploy land 17:13 ⇒ có differential THẬT 404→401 và 0→1. **Vào việc là chụp PRE ngay, đừng poll trước.** 🔑 **MARKER BẤT-ĐỐI-XỨNG > marker đối-xứng**: `lg:max-h-none` = **1 (admin) : 2 (eoffice)** đúng bằng số trang Panel-3 mỗi app ⇒ xác suất trùng-hợp ~0; comment cùng chuỗi bị Vite strip nên không nhiễu. 🔑 **CSS = nhân chứng ở ARTIFACT KHÁC** (Tailwind gen `.lg\:max-h-none` 0→1 cả 2 file .css) ⟂ JS. ⚠️ `grep -P` CHẾT trên Git-Bash locale ⇒ trích byte quanh marker bằng **python `str.find`+slice**. Evidence `runs/2026-07-31-S164-4gd-khkk-fanout/sub-cicd-verify-gd3d-lineeditor.md`. Tag `[run-444, wire-proof-400-ten-field, chup-PRE-kip-gio, marker-bat-doi-xung, css-nhan-chung]` +- _(**S159 (07-29) — 4 đợt PASS/PARTIAL** [harvest hồi-tố @S172]: đợt-2 `1a47a61` PARTIAL bắt regression revoker 447/494 row → đợt-3 đóng bằng **3 chân kiềng** → đợt-4 tái xác nhận ⇒ **durability phải đo ≥2 restart**. 🔑 **control ÂM đọc ở tầng BUNDLE, không tầng source**. verbatim → `archive/2026-08.md`)_ -- _(**08-01 `aaed699` S167 K7 bridge KHKK→HĐ — PARTIAL 4/6+1**: #442 (run #441 CANCELLED), gate 639. 🔴 **PUSH-ĐÈ NUỐT DEPLOY = gotcha #86** (mục 🚨 dưới). 🔑 kiểm-tra-TIỀN-ĐỀ marker trước khi tin 0-hit (nghi unicode-escape/code-split → **bác bằng ĐO**). 🔑 control-dương = chuỗi bản TIỀN-commit của CHÍNH file đang sửa. 🔑 seeder báo **2/3 upgraded** = chứng idempotent tầng-row miễn phí (log lần-2 IM LẶNG do guard). ⚠️ `appcmd list wp` map PID→pool BẮT BUỘC. ⚠️ CẤM lấy Admin-qua-được tuyên grant đã ăn. verbatim → `archive/2026-08.md`)_ +- _(**S168 (08-01) `0bfadc2` — PASS 6/6**: #444, gate 644 Δ+5 đúng 5 Fact mới. 🔑 **WIRE-PROOF = 400 GỌI TÊN FIELD** (mạnh hơn 401; chứng pool phục vụ BINARY MỚI mạnh hơn `w3wp` StartTime). 🔑 **CHỤP PRE NGAY khi vào việc** → differential thật 404→401, 0→1. 🔑 marker **BẤT-ĐỐI-XỨNG** · CSS = nhân chứng ARTIFACT KHÁC. verbatim → `archive/2026-08.md`)_ -- _(**08-01 `1c6d77a` S167 K6 admin thu-gọn menu 12-key — PASS 5/5**: #440, gate 622 delta 0. 🔴 **0-hit marker SAI 2 LẦN do CƠ-CHẾ REGEX** (minify→BACKTICK · pattern-chứa-backtick qua biến làm `grep -cE` gãy im lặng) ⇒ **LUẬT `grep -F` + trích byte thật TRƯỚC khi kết luận vắng-mặt**. 🔑 **COUNT-DELTA** khi literal đã tồn tại (12/12 +1). 🔑 **log CI in `dist/assets/index-.js `** = nhân chứng thứ 4, tách FROZEN-lành-tính khỏi bundle-kẹt. 🔑 menu ẩn 12/12 mà route 68→68 ⇒ ẩn client KHÔNG thu quyền (#82). verbatim → `archive/2026-08.md`)_ +- _(**`aaed699` S167 K7 — PARTIAL 4/6+1**: #442 (run #441 CANCELLED). 🔴 **PUSH-ĐÈ NUỐT DEPLOY = gotcha #86**. 🔑 control-dương = chuỗi bản TIỀN-commit của CHÍNH file đang sửa. 🔑 seeder báo **2/3 upgraded** = chứng idempotent tầng-row miễn phí. ⚠️ `appcmd list wp` map PID→pool. verbatim → `archive/2026-08.md`)_ -- _(**Arc S155→S166 (#416 · #419 · #422-#427 · #432 · #434-#439)** — gate 551→622. 🔑 **#439** marker differential **element-wise** (K5 vào CẢ 2 app · K4c CHỈ eoffice) + Δsize cùng chiều; ⚠️ bar 'FE route 200' KHÔNG CÓ RĂNG với SPA-fallback. 🔑 **#438** PHỦ 2 RUN 1 LƯỢT ⇒ delta khép kín; **mốc PRE đề bài SAI, bắt bằng ĐO** (mốc PRE lão hoá sau MỖI deploy); PERM row ≠ policy. 🔑 **#436** dự-đoán-TRƯỚC-khi-đo; marker = tên biến local thì 0 bit (minifier đổi). 🔴 **#435 PARTIAL** ACCEPTANCE CHÂN-LÝ-RỖNG (0 dòng cả 2 phía ⇒ `0==0` xanh mà 0 bit) → `INFORMATION_SCHEMA` · liệt-kê-TRỌN-HỌ index · **wire-proof bind-error 400**. 🔑 **#434** TAM-GIÁC 3 NGUỒN (source-parse ⟂ sqlcmd ⟂ API-materialize-qua-EF). 🔴 **#432** LUẬT CẤM `HEAD~1` → neo SHA `X^..X`; control ÂM phải ĐỘC-QUYỀN. 🔴 **#423 PARTIAL** revoker UNGATED lật 447/494 row, sót vì **đo TRƯỚC recycle** → luật 3-CHÂN-KIỀNG; #427 ĐO-CỜ không đếm ROW; #419 count = SỐ FILE trong diff. verbatim → `archive/2026-08.md` + `2026-07.md` via `_INDEX`)_ +- _(**`1c6d77a` S167 K6 menu 12-key — PASS 5/5**: #440, gate 622 Δ0. 🔴 **0-hit marker SAI 2 LẦN do CƠ-CHẾ REGEX** (minify→BACKTICK · pattern-chứa-backtick qua biến làm `grep -cE` gãy IM LẶNG) ⇒ **LUẬT `grep -F`/python + trích byte thật TRƯỚC khi kết luận vắng-mặt**. 🔑 **COUNT-DELTA** khi literal đã tồn tại. 🔑 menu ẩn 12/12 mà route 68→68 ⇒ **ẩn client KHÔNG thu quyền** (#82). verbatim → `archive/2026-08.md`)_ + +- _(**Arc S155→S166 (#416→#439)** gate 551→622. 🔑 #438 **mốc PRE trong đề bài LÃO HOÁ sau MỖI deploy — bắt bằng ĐO**. 🔑 #436 marker = tên biến local thì 0 bit. 🔴 **#435 ACCEPTANCE CHÂN-LÝ-RỖNG** (`0==0` xanh mà 0 bit). 🔑 #434 TAM-GIÁC 3 nguồn. 🔴 #432 CẤM `HEAD~1` → neo `X^..X`. 🔴 #423 revoker lật 447/494 vì **đo TRƯỚC recycle**. verbatim → `archive/2026-08.md`)_ > **Persistent diary cross-session.** Auto-injected first ~200 lines at spawn (L1 HOT). > Update BEFORE every stop. Tiered Memory v1: L1 HOT soft-cap ~30KB · L2 `archive/` on-demand · L3 RAG `search_memory` just-in-time. Keep entry ≤ 1.5K chars (gotcha #53). @@ -24,7 +26,7 @@ Read-only CI/CD + post-deploy verifier SOLUTION_ERP. Poll Gitea Actions API, ver ## ⚠️ cicd-lane return-garble tracker (#53) -cicd-lane trả nhầm slice transcript thay verdict — **tao TÁI PHẠM dai nhất roster** (×5/6 qua S114; S117/S118 ×3 → [[feedback_agent_return_garble_recover]]). ⚠️ Từng tự dán nhãn 'CLEAN-return' SAI ⇒ **KHÔNG tự phong return sạch**. 🔒 **Đối-sách: ghi-đĩa TRONG LÚC LÀM** — prompt KHÔNG chặn được, artifact mới chặn ⇒ file trong run-folder là SẢN PHẨM CHÍNH, ghi từng mục ngay khi có bằng chứng, kết bằng `END … VERDICT=`; return chỉ là tóm tắt. +cicd-lane trả nhầm slice transcript thay verdict — **tao TÁI PHẠM dai nhất roster** (×5/6 qua S114; S117/S118 ×3 → [[feedback_agent_return_garble_recover]]). ⚠️ Từng tự dán nhãn 'CLEAN-return' SAI ⇒ **KHÔNG tự phong return sạch**. 🔒 **Đối-sách: ghi-đĩa TRONG LÚC LÀM** — prompt KHÔNG chặn được, artifact mới chặn ⇒ file trong run-folder là SẢN PHẨM CHÍNH, kết bằng `END … VERDICT=`; return chỉ là tóm tắt. --- @@ -32,13 +34,13 @@ cicd-lane trả nhầm slice transcript thay verdict — **tao TÁI PHẠM dai n - **#39 act_runner github.com TCP timeout** — run hang "Set up job" 21s. Log `dial tcp github.com:443 i/o timeout`. Fix: manual checkout bypass hardcoded `.gitea/workflows/deploy.yml`. KHÔNG revert. - **#40 npm cache `tsc not found`** — `build_fe_admin` fail post `cache: npm`. DISABLED rolled back `a21790d`. KHÔNG re-enable. -- **#41 paths-ignore docs-only skip** — code commit không trigger CI? Check `git diff --name-only ..` vs **7-glob** (D10 S111 `8aa3869`): `['docs/**','**/*.md','.claude/skills/**','.claude/agent-memory/**','.claude/workflows/runs/**','.gitignore','scripts/**.md']`. Gitea evaluate push *range* — ≥1 commit có file non-ignored → toàn range build. ⚠️ **AS-6: memory-push/run-trace HẾT trigger CI.** +- **#41 paths-ignore docs-only skip** — check `git diff --name-only ..` vs **7-glob** `['docs/**','**/*.md','.claude/skills/**','.claude/agent-memory/**','.claude/workflows/runs/**','.gitignore','scripts/**.md']`. Gitea đọc *range* — ≥1 file non-ignored → build. ⚠️ **AS-6: memory-push/run-trace HẾT trigger CI.** ⚠️ @S189: filter ĐÚNG mà vẫn 0 run ⇒ xem **#91** trước khi đổ tại filter. - **#25 IIS WebSocket** — `notification-hub/negotiate` 401/404 prod. Fix: WebSocket module enable `web.config` site api (skill `iis-deploy-runbook`). - **#48 SQLite tie-break** — `OrderByDescending(CreatedAt).First()` pick wrong khi 2+ `.Add()` cùng frozen-clock. Fix: discriminator filter BEFORE OrderBy. -- **Bundle hash unchanged = ship FAIL** — push+action success nhưng prod không đổi. Verify via INDEX.HTML ref (`curl -s https://admin.solutions.com.vn/ | grep -oE '/assets/index-[A-Za-z0-9_-]+\.(js|css)'`), NOT by GETting a hash-named asset directly. ⚠️ verify MUST sau status=success (Run #242 false-positive: check khi "running" → stale hash). - - **🔴 #69 TWO-MODE — FE hash vừa có thể ROTATE vừa có thể FROZEN.** deploy.yml copy `dist\*` UNCONDITIONALLY mọi run ⇒ 0-FE-change vẫn rotate (mode-A); src y hệt ⇒ Vite byte-identical ⇒ FROZEN (mode-B). **Cả hai lành tính ⟹ hash-delta KHÔNG phải ship-proof.** Ship-proof THẬT = `Last-Modified` in deploy-window + real-size + byte-marker. 🔑 FROZEN-lành-tính vs bundle-cũ-kẹt: **xem LM** — in-window = re-ship byte-identical (OK) · LM **cũ** = deploy không chạm file (cờ đỏ). - - **⚠️ SPA-fallback 200 trap (S72):** server rewrite `/*`→`index.html` ⇒ GET `/assets/index-.js` vẫn **200**. Discriminator sạch = **`Content-Type`** (`text/html` ~876-919B = trap · `application/javascript` ~1,6MB = thật); size drift per-build nên đừng neo số byte. **Hash CŨ rơi về text/html = bundle cũ biến mất khỏi đĩa = chứng ghi-đè (control âm tầng-2).** -- **🔴 Durability: đo SAU recycle + 3 CHÂN KIỀNG** — pool restart ở CUỐI deploy-window ⇒ query DB lúc `running` = trạng thái CŨ (tao sai đúng chỗ này ở #423). Khuôn: (i) **chứng minh restart thật** (`w3wp` StartTime in-window + dòng ĐẦU log `Applying migrations...`), (ii) đo chỉ-số đích SAU mốc đó, (iii) **control ÂM chứng checker còn sống** (thiếu (iii) thì 'fix' kiểu xoá checker cũng xanh). Data-change ngoài CI luôn re-verify SAU status=success. +- **Bundle hash unchanged ≠ ship FAIL (đọc #69 bên dưới trước).** Đọc hash qua INDEX.HTML (`curl -s https://admin… | grep -oE '/assets/index-[A-Za-z0-9_-]+\.(js|css)'`), KHÔNG GET thẳng asset. ⚠️ verify PHẢI sau `status=success` (Run #242 false-positive: đo lúc 'running' → hash stale). + - **🔴 #69 TWO-MODE — FE hash vừa có thể ROTATE vừa có thể FROZEN.** deploy.yml copy `dist\*` UNCONDITIONALLY ⇒ 0-FE-change vẫn rotate (mode-A); src y hệt ⇒ Vite byte-identical ⇒ FROZEN (mode-B). **Cả hai lành tính ⟹ hash-delta KHÔNG phải ship-proof.** Ship-proof THẬT = `Last-Modified` in-window + real-size khớp log CI + byte-marker. LM **cũ** = deploy không chạm file (cờ đỏ). + - **⚠️ SPA-fallback 200 trap (S72):** server rewrite `/*`→`index.html` ⇒ GET `/assets/index-.js` vẫn **200**. Discriminator sạch = **`Content-Type`** (`text/html` ~876-900B = trap · `application/javascript` ~1,8MB = thật). **Hash CŨ rơi về text/html = bundle cũ đã bị ghi đè = control âm tầng-2.** +- **🔴 Durability: đo SAU recycle + 3 CHÂN KIỀNG** — pool restart ở CUỐI deploy-window ⇒ query DB lúc `running` = trạng thái CŨ (tao sai đúng chỗ này @#423). Khuôn: (i) chứng minh restart thật (`w3wp` StartTime in-window), (ii) đo chỉ-số đích SAU mốc đó, (iii) **control ÂM chứng checker còn sống**. ⚠️ @S189: w3wp mới sinh ⇒ **vài request đầu là cold-start, đừng lấy làm số perf**. - **Migration drift prod vs repo** — compare `ls .../Persistence/Migrations/*.cs` vs `sqlcmd __EFMigrationsHistory`. Fix: check `Program.cs`/DbInitializer `app.MigrateDatabase()` + app pool recycle. Mig-applied proof = `__EFMigrationsHistory` top==repo-HEAD; stuck-old ⟹ pool didn't recycle ⟹ FAIL even if status=success+bundle-rotated. --- @@ -48,8 +50,8 @@ cicd-lane trả nhầm slice transcript thay verdict — **tao TÁI PHẠM dai n - **Stage 0 RAG infra:** `Get-Service Qdrant` Running + `http://localhost:6333/healthz`. Collection `proj_solution_erp`. - **Stage 1 Push+filter:** `git log -1 --format='%H %s'` + `git log origin/main..HEAD` empty + diff vs paths-ignore (docs-only → SKIPPED-DOCS return). - **Stage 2 Gitea poll** (max 10 iter × 60s): API `.../actions/tasks?limit=5` (NOT `/runs` 404). Match `head_sha` (NOT run_number — cancelled runs shift numbering). ⚠️ `updated_at` stale ~2min (gotcha #46) → cross-check VPS mtime. Foreground-sleep BLOCKED Windows → Monitor-style busy-wait-curl-spin ~30s/iter. -- **Stage 3 Test gate:** baseline **644 PASS** (45 Domain + 599 Infra; run #444 `0bfadc2` 08-01; climb 644/639/622/621/614/609/598/590/566/551/532/509). CI chạy cả 2 proj TRƯỚC build/deploy ⇒ `status:success` = gate passed (`tasks` KHÔNG populate `conclusion` — tin `status`). Local grep undercount (Theory/InlineData). Phase 9 UAT skip OK. -- **Stage 4 Post-deploy** (if SUCCESS): auth login bearer (admin + nv.test gotcha #44; token=`accessToken` route `/api/auth/login`) → 3-5 endpoint smoke 2XX (incl new — NEW-endpoint bodyless-POST returns **411 IIS Length-Required pre-auth NOT 404**; re-probe WITH body → 401 confirms wired) → FE bundle hash 2 app (per #69 = real-size-vs-fake not hash-delta) → SignalR negotiate (gotcha #25) → EF mig prod==repo. +- **Stage 3 Test gate:** baseline **680 PASS** (45 Domain + 635 Infra; run #486 `92d8215a` 08-11; climb 680/645/644/639/622/614/609/598/590/566/551/532/509). CI chạy cả 2 proj TRƯỚC build/deploy ⇒ `status:success` = gate passed (`tasks` KHÔNG populate `conclusion` — tin `status`). Local grep undercount (Theory/InlineData). +- **Stage 4 Post-deploy** (if SUCCESS): login bearer (admin + nv.test #44; token=`accessToken`) → smoke 2XX (**NEW-endpoint bodyless-POST = 411 pre-auth NOT 404**; re-probe WITH body → 401 = đã wire; 415 cùng lớp) → bundle 2 app (#69: real-size, KHÔNG hash-delta) → SignalR (#25) → mig prod==repo. - **Stage 4.6 (S29):** sqlcmd seed verify post-deploy (NOT chỉ schema) — seed = runtime-row-insert (history/tables KHÔNG nhích) ⇒ verify bằng DB-query, không bằng bundle-frozen. - enum cần input SỐ (no `JsonStringEnumConverter`). INFRASTRUCTURE seed ungated / DEMO seed gated → #51. - **Stage 5 Report** PASS/FAIL + evidence + MEMORY update. @@ -64,11 +66,11 @@ cicd-lane trả nhầm slice transcript thay verdict — **tao TÁI PHẠM dai n ## 🧠 SOLUTION_ERP CI/CD essentials (S80 verified — re-ground từ docs/STATUS.md canonical) - **Gitea:** `git.baocaogiaoduc.vn/vietreport-admin/solution-erp` · workflow `.gitea/workflows/deploy.yml` · paths-ignore 7-entry (D10 S111 #495): `['docs/**','**/*.md','.claude/skills/**','.claude/agent-memory/**','.claude/workflows/runs/**','.gitignore','scripts/**.md']`. Anon API works (public repo) when GITEA_TOKEN absent. -- **Prod:** api/admin/eoffice `.solutions.com.vn` · SSH `ssh vietreport-vps` (Administrator, id_ed25519) · IIS phys paths: API `C:\inetpub\solution-erp\api` · admin `\fe-admin` · user `\fe-user`. DB `.\SQLEXPRESS`/`SolutionErp`/`vrapp` SQL-auth. **Conn key = `ConnectionStrings.Default` (NOT `DefaultConnection`)**. DB pw = `buKL3TGBkD0wDDbYVw65QeX9` (User Id=`vrapp`; ⚠️ Password field has NO `vrapp/` prefix — S89 login-failed when prefix included; read verbatim from prod `appsettings.Production.json` ConnectionStrings.Default when `$env:PROD_DB_PASSWORD` empty). +- **Prod:** `.solutions.com.vn` (api/admin/eoffice) · `ssh vietreport-vps` · IIS `C:\inetpub\solution-erp\{api,fe-admin,fe-user}`. DB `.\SQLEXPRESS`/`SolutionErp`, key **`ConnectionStrings.Default`**. ⚠️ **VPS chỉ 4GB RAM/3 core, chung với VIETREPORT** (@S189 SQL buffer 213MB/1,09GB → API lưỡng-cực). **SSH→PS: viết `.ps1` + `scp` + `powershell -File`**, đừng inline. 🔴 **CẤM `ssh … | head`** → `Select-Object -First N` phía REMOTE. - **SSH→PS quoting (S42):** bash→ssh→powershell nuốt `$var`/`\"` ⇒ viết `.sql`/`.ps1` rồi `scp` + `sqlcmd -i` / `powershell -File` (đừng nhét inline). sqlcmd `-W -h -1` pw-inline. sys-catalog concat → `COLLATE DATABASE_DEFAULT`. -- **Tests baseline:** **680 PASS** (45 Domain + **635** Infra · 0 fail/skip; run **#482** `b275893c` 08-10; prev 644 = 45+599 @#444). 🔑 Số lấy ở web-UI log `https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp/actions/runs/{run_number}/jobs/0/logs` (~18KB, KHÔNG có `/api/v1/` — api/v1 logs 404) → mỗi project 1 dòng `Passed! - Failed: 0, Passed: N, Skipped: 0`; đối chiếu **TÁCH-PHẦN** (45/599) chứ đừng chỉ khớp tổng, và **so delta với số Fact mới khai trong commit-msg**. ⚠️ Flaky đã khai `DeleteContractSigningPlan_SoftDeletes` — xanh liên tục #439→#444. -- **Mig latest repo: Mig 71 `AddKhkkApprovalGroupCatalogAndFinalizeRuntime`** (`20260731085624`, S164 K2) — applied-prod #435, GIỮ qua #436-#444. Path `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/`. 🔴 Đếm phải **NEO ĐUÔI** `grep -vE 'Designer\.cs$|ModelSnapshot\.cs$'` (recipe cũ `grep -v Snapshot` ăn nhầm Mig 67 `AddPeApprovedBudgetSnapshot`). Mạnh hơn khớp-tổng = **set-diff repo↔prod 2 chiều** (`comm -23`/`-13`, cả 2 phải rỗng). Prod: `ssh vietreport-vps` + `sqlcmd -S .\SQLEXPRESS -d SolutionErp -E` (⚠️ **`$env:PROD_DB_PASSWORD` KHÔNG tồn tại trên VPS ⇒ dùng `-E` Windows-auth**). ⚠️ `sys.tables`(is_ms_shipped=0)=**97**; AddColumn/data-only KHÔNG đổi số. **NO-Mig proof = history NOT-advance + empty `git diff .. -- '*Migrations*'`.** Bảng mig đầy đủ → skill `ef-core-migration`. -- **Bundle hash live:** baseline #444 (08-01) admin `DLRf9TeA`/`IuRRquXK` · eoffice `C7TGA4Kn`/`DesmJ6-_`. 🔑 Ship-proof rẻ nhất = so **LM prod** với dòng `dist/assets/index-.js ` IN TRONG LOG CI (size khớp tới từng byte: `1,767.41 kB`→1767413). ⚠️ marker chuỗi: rolldown/Vite 8 minify ra **BACKTICK** → `grep -F`. ⚠️ **SNAPSHOT-per-run** (#69) → LUÔN re-snapshot LIVE sau status=success; ship-proof = LM-in-window + real-size + marker, KHÔNG phải hash-delta. **Control-âm tầng-2 = hash CŨ phải rơi về `text/html` ~876-900B** (SPA-fallback ⇒ bundle cũ đã bị ghi đè). ⚠️ id ≠ run_number (key theo head_sha). +- **Tests baseline: 680 PASS** (45 Domain + 635 Infra · 0 fail/skip) — xác nhận lại run **#486** `92d8215a` 08-11 (prev #482). 🔑 Số lấy ở web-UI log `…/actions/runs/{run_number}/jobs/0/logs` (~18KB, KHÔNG có `/api/v1/`) → mỗi project 1 dòng `Passed! - Failed: 0, Passed: N, Skipped: 0`; đối chiếu **TÁCH-PHẦN** (45/635) chứ đừng chỉ khớp tổng, và **so delta với số Fact mới khai trong commit-msg**. +- **Mig latest repo: Mig 72 `AddMaHangMucToContractSigningPlanLines`** (`20260806070242`) — đo đĩa @S189 = **72** (🧊 ô này từng kẹt **71** tới S189). 🔴 Đếm phải **NEO ĐUÔI** `grep -vE 'Designer\.cs$|ModelSnapshot\.cs$'`. Mạnh hơn khớp-tổng = **set-diff repo↔prod 2 chiều** (`comm -23`/`-13` cùng rỗng). Prod: `sqlcmd -S .\SQLEXPRESS -d SolutionErp -E` (⚠️ `$env:PROD_DB_PASSWORD` KHÔNG có trên VPS). `sys.tables`=**97**. **NO-Mig proof = history NOT-advance + empty `git diff .. -- '*Migrations*'`.** +- **Bundle hash live:** baseline **#486 (08-11)** admin `DxSej4Da`/`DVHtHLex` · eoffice `BcfxaUBO`/`Ibv3QD_1` (JS 1.815.258/1.794.780 B). 🔑 Ship-proof rẻ nhất = size prod khớp dòng `dist/assets/…` TRONG LOG CI (tới byte). ⚠️ marker minify ra **BACKTICK** → `grep -F`/python. ⚠️ **re-snapshot LIVE sau status=success** (#69). ⚠️ id ≠ run_number (key theo head_sha). - **Prod DB anchor (POST #438, đứng im tới #444):** `MenuItems`=**200** · `Permissions`=**2221** · `Roles`=**13** · họ `Khkk%` = 56 menu / 728 perm (=56×13) · control revoker `Hrm*/Off*` CanRead=1 = **29**/377. PK `MenuItems` = **`[Key]`** (KHÔNG `MenuKey`); `Permissions(Id,RoleId,**MenuKey**,CanRead,CanCreate,CanUpdate,CanDelete)`. - **Bearer:** admin `admin@solutions.com.vn/Admin@123456` (full) · UAT `nv.test@solutions.com.vn/TestUser@123456` (Drafter CCM, gotcha #44 check). - **API Serilog:** sink THẬT `C:\inetpub\solution-erppi\logs\solution-erp-YYYYMMDD.log` (skill-runbook `…\Logs\log-*.txt` STALE). Level=Information (seed/revoke lines CÓ ghi). ⚠️ deploy xoá `logs/` ⇒ chỉ còn từ deploy cuối. @@ -87,13 +89,9 @@ BE ~90s · FE ×2 ~60s/app · deploy ~30s · **total ~3min code / 0s docs-only** ## 📅 Recent runs (compressed — verbatim → archive via `_INDEX.md`) -- **#447 `998ea55` S171→S172 KHKK 3-panel — GO/PASS `[LEAD SEED ON-BEHALF @closeout S172; mày CHẠY THẬT 2 lượt nhưng KHÔNG tự ghi — `grep -c S172`=0, mtime kẹt 08-01 ⇒ carry `sub-no-self-append` nay **KHÔNG còn riêng ctx-verifier**: mày CÓ Write/Edit trong tools mà vẫn 0 đường ghi ⇒ giả-thuyết "bệnh riêng 1 vai" **YẾU ĐI**]`:** gate **644** (45D+599I) Failed 0 Skipped 0 — khớp B1 **tách-phần** cả 2 project, Δ0 đúng kỳ vọng (commit chạm **0 file test**). Bundle 4/4 rotate, size **khớp log CI tới byte**, LM trong cửa-sổ, hash cũ rơi `text/html`. Mig repo 71 = prod 71, **set-diff `comm -23`/`comm -13` = 0/0 HAI CHIỀU** (mạnh hơn khớp-tổng), tables 97. Smoke 8/8. - 🔑 **3 nghề mới, đều tái dùng được:** (a) **Stage 3b cặp đối-chứng MIỄN PHÍ** — #446 (cây tiền-S171) vs #447 khác **đúng 1 commit** ⇒ cô-lập nhân-quả **thay được `git worktree` đã fail**; (b) **bẫy 415 = họ hàng bẫy 411** — POST `application/json` vào endpoint **multipart** bị loại ở **action-selection, TRƯỚC authz** ⇒ 415 chỉ chứng route tồn tại, **vô nghĩa cho câu hỏi authz**; re-probe đúng multipart → 401. ⇒ **luật: mọi mã pre-auth (411/415) phải re-probe đúng dạng**; (c) **wire-proof MẠNH HƠN 401 = thông-điệp-lỗi GỌI TÊN FIELD** — bearer + multipart ⇒ 400 liệt `ContractSigningPlanId` + whitelist đuôi file ⇒ chứng **binary MỚI đang phục vụ**, mạnh hơn đo `w3wp` StartTime. - 🔴 **2 vế PHẢI tách, cấm nén:** ✅ *không hồi-quy do S171* (chứng bằng cặp #446/#447) ⟂ ❌ *flake `Create_DifferentEvaluationType_DoesNotAffect_Other` pre-exist* **VẪN chưa chứng** — nay **0/4 log CI** tái hiện. CI xanh **không rửa sạch** flake và cũng **không xác nhận** nó (vắng-mặt bằng-chứng ≠ bằng-chứng vắng-mặt). - 🔴 **Bài đắt nhất — "KHÔNG ĐO ĐƯỢC" là kết luận PHẢI TỰ NGHI:** mày kết `nv.test@` login 401 ⇒ *"credential UAT chết trên prod"* ⇒ bỏ ngỏ chân #44/#85. **SAI** — không phải credential chết mà **tra nhầm sổ**: bộ đang sống nằm ở `docs/HANDOFF.md` slot (64) (`test.drafter@`/`test.approver@`, `Test@12345678`). Lead đo lại: login 200/200 · list **200** · changelogs **404 KHÔNG 403** · attach **400 gọi tên field** · CTRL-ÂM 404 ⇒ role thường **có đủ** `KeHoachKyKet.Read`+`Update`, **gotcha #85 KHÔNG tái phát**. ⇒ **LUẬT: trước khi khai "KHÔNG ĐO ĐƯỢC", phải trả lời "đã tra hết sổ có sẵn chưa?"** (HANDOFF · STATUS · run-folder). Cùng lớp `feedback_mechanism_right_data_absent`. - 🔴 **Return #53-garble:** trả câu giữa chừng *"Writing Stage 5 + Verdict"* — **KHÔNG mất việc**: đĩa 10.156→**24.096 B**, 0 ô `_(chờ đo)_`. Đối-chứng lần nữa cho luật S158: **chống-#53 = thiết-kế artifact đổ-ruột-liên-tục, KHÔNG phải prompt.** Tag `[s171-s172, #447, cap-doi-chung-mien-phi, bay-415-pre-auth, wire-proof-goi-ten-field, khong-do-duoc-phai-tu-nghi, 2-ve-cam-nen, doi-ruot-lien-tuc, seed-on-behalf]` +- _(**#447 `998ea55` KHKK 3-panel — GO/PASS**: gate 644 Δ0, **set-diff Mig 2 CHIỀU = 0/0**. 🔑 **cặp đối-chứng MIỄN PHÍ** #446/#447 khác đúng 1 commit ⇒ cô-lập nhân-quả. 🔑 **415 = họ hàng 411**: json vào endpoint multipart bị loại TRƯỚC authz ⇒ **mọi mã pre-auth phải re-probe đúng dạng**. 🔴 **'KHÔNG ĐO ĐƯỢC' phải TỰ NGHI** — khai credential chết = SAI, chỉ **tra nhầm sổ** (`docs/HANDOFF.md`). verbatim → `archive/2026-08.md`)_ -- _(**S98-S132 #383-#397 + PASS-pattern library S69b→S104 #307-#494** all GO (gate 440→509): AS-6 #41 soi TỪNG file vs 7-glob · `conclusion:None` dù success ⇒ tin `status` · **411-refine** (endpoint MỚI bodyless-POST = 411 IIS pre-auth; re-probe CÓ body → 401/415 = đã wire) · **LM mới hơn cửa-sổ đã verify = detector 'có run bỏ sót'** · recipe SCHEMA-ADD / DATA-BACKFILL / DATA-SEED · #472 PROD Identity table NON-prefix `Roles`. verbatim → `archive/2026-07.md` + `2026-06.md`)_ +- _(**S98-S132 #383-#397 + PASS-pattern library S69b→S104** all GO (gate 440→509): AS-6 #41 soi TỪNG file vs 7-glob · `conclusion:None` dù success ⇒ tin `status` · **411-refine** · **LM mới hơn cửa-sổ đã verify = detector 'có run bỏ sót'** · recipe SCHEMA-ADD / DATA-BACKFILL / DATA-SEED · #472 PROD Identity table NON-prefix `Roles`. verbatim → `archive/2026-07.md`+`2026-06.md`)_ - **Older (S83 #325 ← S29 #232) → git `d2f52ba` + `archive/2026-06.md`** (incl #291 06-16 FAIL [gotcha #65]); pre-S38 → `archive/2026-05-*`. @@ -101,12 +99,12 @@ BE ~90s · FE ×2 ~60s/app · deploy ~30s · **total ~3min code / 0s docs-only** ## 🔄 Curate trigger - >~30KB → archive recent runs → L2 `archive/.md` (byte-exact append) + `_INDEX.md` substring pointer. Dup failure patterns → merge. Stale >3mo → remove. -- **Last curate: 2026-08-01 S168 (self #444-run: 21.3KB→~16.6KB — K7/K6/K5-K4c/K4ab verbatim→`archive/2026-08.md` NEW + 4 `_INDEX` pointer; #416→#439 gộp 1 arc-line; baselines gate **644** + bundle #444 + Mig 71 + `-E` Windows-auth).** ⚠️ giữ luật: script sửa MEMORY phải build chuỗi XONG → temp → `os.replace` → verify byte>0. +- **Last curate: 2026-08-11 S189 (self, 27.4KB→~16.6KB — S159/S168/ARC-S155-S166/#447/S182 verbatim→`archive/2026-08.md` + `_INDEX` pointer; baseline refresh gate **680**, Mig **72** (was stale 71), bundle **#486**).** ⚠️ giữ luật: script sửa MEMORY phải build chuỗi XONG → temp → `os.replace` → verify byte>0. - **Prev:** S166 08-01 (27.3→16.5) · S162 07-30 (26.9→17) · S118 · S111 · S107 · S104 · S98 (`2026-07.md` NEW) · S80 38.8→16 · S70 65.2→23.2 · S66 · S40. -- **S182 YC-013 W0.4 PRECHECK (lead-seed từ memoryDelta workflow-return VERIFY khớp artifact, C2 /pause):** `LỆCH — Q2 sai tiền-đề nhưng W1 GO 0-blocker` — 12 phiếu prod (3 DaDuyet + 8 Nháp + 1 TraLai; memory-pack "001-003 Phase=3" KHÔNG SAI — thế giới đẻ thêm 9; ký-ức đóng-băng-thời-điểm khác ký-ức hỏng, SPEC phổ-quát-hoá mốc cũ mới là chỗ gãy) · pin-lệch 0 row VỚI control: chạy CHÍNH câu audit bỏ vế phủ-định ra 12 row element-wise + bịt điểm-mù LIKE bằng đẳng-thức-đếm IS-NOT-NULL=12=JOIN-in-family · hiệu chuẩn thước bằng 2 ANCHOR đã biết (MenuItems=200, Permissions=2221) mạnh hơn count>0 · ĐO bác acceptance wave-sau: 6 ô cờ đều 0/88 trên prod ⇒ ép-false vs giữ-thật render Y HỆT, A2 phải seed local 1 level cờ=true mới có răng · #8 Finalize=8/88 = CỜ CHỊU LỰC cấm đụng. → `runs/2026-08-08-S182-khkk-awf-mirror-ncc/sub-cicd-monitor-0.md` +- _(**S182 YC-013 W0.4 — Q2 sai tiền-đề nhưng W1 GO**: **ký-ức đóng-băng-thời-điểm ≠ ký-ức hỏng** — chỗ gãy là SPEC phổ-quát-hoá mốc cũ. 🔑 pin-lệch 0 row **VỚI control** (bỏ vế phủ-định ra 12 row). 🔑 **hiệu chuẩn thước bằng 2 ANCHOR đã biết** mạnh hơn `count>0`. verbatim → `archive/2026-08.md`)_ ## S188 (2026-08-10) — [LEAD SEED ON-BEHALF] -- 2 luot verify: dot-1 (run #481, lead tu do POST vi trace dut) + cum PASS 5/5 (run #482: bundle rotate x2, seeder prod 7 HD-* + 7 menu-row, KHKK-8 control nguyen, DLL marker ContractWorkflowGuards 0->4, smoke 9/9). Bai: tu-dinh-chinh trong luot — phep do dau ra 'HD-*=0' la ARTIFACT PARSE cua chinh minh (shape {types:[{active,activeOthers,history}]}), suyt bao FAIL oan; moc PRO bundle trong de bai LAO HOA sau moi deploy, phai do live. +- _(**S188 (08-10)** #481 + cụm PASS 5/5 #482: bundle rotate ×2, seeder prod 7 HD-*, DLL marker `ContractWorkflowGuards` 0→4, smoke 9/9. 🔑 **tự đính chính trong lượt**: 'HD-*=0' là **ARTIFACT PARSE của chính mình** — suýt FAIL oan.)_ diff --git a/.claude/agent-memory/cicd-monitor/archive/2026-08.md b/.claude/agent-memory/cicd-monitor/archive/2026-08.md index 612bf25..6766ad3 100644 --- a/.claude/agent-memory/cicd-monitor/archive/2026-08.md +++ b/.claude/agent-memory/cicd-monitor/archive/2026-08.md @@ -12,3 +12,32 @@ - _(**08-01 `88e7ecf` S166 K4a+K4b — PASS 7/7**: #438, gate 621=45+576, menu 151→**200**, perm 1584→**2221**. 🔑 **PHỦ 2 RUN 1 LƯỢT ⇒ delta khép kín**. 🔑 **MỐC PRE ĐỀ BÀI SAI, bắt bằng ĐO** (lead giao 143⇒192, đo LIVE ra 151 → truy phạm-vi thay vì hô drift) — **mốc PRE lão hoá sau MỖI deploy**. 🔑 phân rã 49+588 khớp cơ-chế 2-site skip-existing (588=49×12 role). ⚠️ **PERM row ≠ policy**. 🔑 A5 restart ×2 thật + **soi ĐIỀU KIỆN log trong source TRƯỚC khi đọc im-lặng** (`if (added>0)`). 🔴 **marker lead gợi ý BỊ BÁC BẰNG ĐO** (`Kết cấu` có sẵn ⇒ 1/1 = 0 bit). verbatim → `runs/…/sub-cicd-verify-k4ab.md`)_ + + +--- + +## [ARCHIVED @S189 2026-08-11] verbatim L1 blocks (curate 27,4KB → target <17,1KB) + +### S159 (verbatim) + +- **S159 (2026-07-29) đợt-1 `b5799fc` + đợt-4 `a2bbcb9` — CẢ HAI `VERDICT = PASS 9/9` `[LEAD HARVEST HỒI-TỐ @S172 — nợ MỞ 3 PHIÊN (S162→S172), do CHÍNH vai này bắt @S162 F-02 rồi không ai đóng; owner slot (71) @S172 chốt 'gom và sleep thì auto, tự quyết'. Nguồn = `runs/2026-07-29-S159-tong-quan-pipeline-menu/cicd-verify-{b5799fc,a2bbcb9}.md`, KHÔNG suy diễn]`:** 🔴 **Vì sao món này nằm lại lâu:** `MEMORY.md:9` từng khai *'verbatim→archive'* cho cụm này mà `git log -S` **rỗng toàn lịch sử** ⇒ vai tự đính chính @S162 là *'không mất hẳn, nguyên liệu còn ở run-folder'* — **đúng, nhưng rồi không ai gom**. Đo @S172: **0 hit ở CẢ L1 lẫn archive** cho cả 2 sha, control-dương = 3 sha anh em cùng folder **có** hit ⇒ phép grep không rỗng. ⇒ **'không mất hẳn' KHÔNG bằng 'đã về nhà'**; nợ-harvest cần một dòng ghi, không cần một lời trấn an. **Nội dung cứu được:** đợt-1 `b5799fc` (11:44) — M2b test-gate **562 EXACT** · M4 bundle rotate user `DptYR4wL`→`0GO8mgrG` (**điều kiện PASS bắt buộc**, đo chứ không giả định) · M5 byte-marker **3 dương + 2 âm** · M6 smoke 4/4. đợt-4 `a2bbcb9` (13:52) — M3 byte-marker **3 dương/3 âm** · **M6 RESTART-DURABILITY lần 2 (chứng bền qua ≥2 restart)** · M7 smoke 4/4. 🔴 **Arc 4 đợt là bài học hoàn chỉnh, đừng đọc lẻ:** đợt-1 PASS 9/9 → **đợt-2 `1a47a61` PARTIAL 8/9 bắt regression revoker ăn 447/494 row** → đợt-3 `2a72695` PASS 9/9 (đóng regression, chứng bằng **3 chân kiềng**) → đợt-4 PASS 9/9 (tái xác nhận durability qua restart **thứ hai**). ⇒ *một PASS lẻ ở đợt-1 KHÔNG chứng được thứ mà chuỗi 4 đợt chứng*: bền qua restart phải đo **≥2 lần restart**, không phải 1. 🔑 **Nghề giữ lại (`a2bbcb9` M3):** *'đọc grep-source thô sẽ báo FAIL oan cho một control vốn ĐÚNG'* — control âm phải đọc ở tầng **render/bundle**, không đọc ở tầng source. Cùng họ với bẫy `cut` cắt UTF-8 và glob-sai mà các vòng kiểm @S172 dính. Tag `[s159, no-mo-3-phien, khong-mat-han-khac-da-ve-nha, arc-4-dot, durability-can-2-restart, control-am-doc-tang-bundle]` + +### S168 (verbatim) + +- **S168 (08-01) `a2b37dc..0bfadc2` GĐ3-D Panel-3 + Lines-editor KHKK (7 FE + 2 BE + 1 test, 0-mig) — PASS 6/6** (#444 6m28s · gate **644=45+599** Δ+5 = đúng 5 Fact mới · Mig GIỮ 71 · tables 97 · bundle `RgvHZkPH→DLRf9TeA` + `D65FJPGd→C7TGA4Kn`). 🔑 **BÀI TỐT NHẤT — WIRE-PROOF BẰNG THÔNG-ĐIỆP-LỖI GỌI TÊN FIELD**: PUT route mới + bearer + GUID toàn-0 trả **400 liệt kê ĐỦ 3 field `PlanId`/`LineId`/`CatalogEntryId`** ⇒ command+validator MỚI chạy end-to-end Api→Application — **mạnh hơn hẳn 401** (401 chỉ chứng route tồn tại). Kèm miễn phí: đây là chứng **pool đang phục vụ BINARY MỚI**, mạnh hơn đo `w3wp` StartTime (StartTime chỉ nói 'có restart', không nói 'code nào'). 🔑 **CHỤP PRE KỊP GIỜ = tài sản rẻ nhất**: spawn 17:10 / push 17:08 ⇒ đo PRE (bundle + marker 0-hit + route **404**) TRƯỚC khi deploy land 17:13 ⇒ có differential THẬT 404→401 và 0→1. **Vào việc là chụp PRE ngay, đừng poll trước.** 🔑 **MARKER BẤT-ĐỐI-XỨNG > marker đối-xứng**: `lg:max-h-none` = **1 (admin) : 2 (eoffice)** đúng bằng số trang Panel-3 mỗi app ⇒ xác suất trùng-hợp ~0; comment cùng chuỗi bị Vite strip nên không nhiễu. 🔑 **CSS = nhân chứng ở ARTIFACT KHÁC** (Tailwind gen `.lg\:max-h-none` 0→1 cả 2 file .css) ⟂ JS. ⚠️ `grep -P` CHẾT trên Git-Bash locale ⇒ trích byte quanh marker bằng **python `str.find`+slice**. Evidence `runs/2026-07-31-S164-4gd-khkk-fanout/sub-cicd-verify-gd3d-lineeditor.md`. Tag `[run-444, wire-proof-400-ten-field, chup-PRE-kip-gio, marker-bat-doi-xung, css-nhan-chung]` + +### ARC (verbatim) + +- _(**Arc S155→S166 (#416 · #419 · #422-#427 · #432 · #434-#439)** — gate 551→622. 🔑 **#439** marker differential **element-wise** (K5 vào CẢ 2 app · K4c CHỈ eoffice) + Δsize cùng chiều; ⚠️ bar 'FE route 200' KHÔNG CÓ RĂNG với SPA-fallback. 🔑 **#438** PHỦ 2 RUN 1 LƯỢT ⇒ delta khép kín; **mốc PRE đề bài SAI, bắt bằng ĐO** (mốc PRE lão hoá sau MỖI deploy); PERM row ≠ policy. 🔑 **#436** dự-đoán-TRƯỚC-khi-đo; marker = tên biến local thì 0 bit (minifier đổi). 🔴 **#435 PARTIAL** ACCEPTANCE CHÂN-LÝ-RỖNG (0 dòng cả 2 phía ⇒ `0==0` xanh mà 0 bit) → `INFORMATION_SCHEMA` · liệt-kê-TRỌN-HỌ index · **wire-proof bind-error 400**. 🔑 **#434** TAM-GIÁC 3 NGUỒN (source-parse ⟂ sqlcmd ⟂ API-materialize-qua-EF). 🔴 **#432** LUẬT CẤM `HEAD~1` → neo SHA `X^..X`; control ÂM phải ĐỘC-QUYỀN. 🔴 **#423 PARTIAL** revoker UNGATED lật 447/494 row, sót vì **đo TRƯỚC recycle** → luật 3-CHÂN-KIỀNG; #427 ĐO-CỜ không đếm ROW; #419 count = SỐ FILE trong diff. verbatim → `archive/2026-08.md` + `2026-07.md` via `_INDEX`)_ + +### 447 (verbatim) + +- **#447 `998ea55` S171→S172 KHKK 3-panel — GO/PASS `[LEAD SEED ON-BEHALF @closeout S172; mày CHẠY THẬT 2 lượt nhưng KHÔNG tự ghi — `grep -c S172`=0, mtime kẹt 08-01 ⇒ carry `sub-no-self-append` nay **KHÔNG còn riêng ctx-verifier**: mày CÓ Write/Edit trong tools mà vẫn 0 đường ghi ⇒ giả-thuyết "bệnh riêng 1 vai" **YẾU ĐI**]`:** gate **644** (45D+599I) Failed 0 Skipped 0 — khớp B1 **tách-phần** cả 2 project, Δ0 đúng kỳ vọng (commit chạm **0 file test**). Bundle 4/4 rotate, size **khớp log CI tới byte**, LM trong cửa-sổ, hash cũ rơi `text/html`. Mig repo 71 = prod 71, **set-diff `comm -23`/`comm -13` = 0/0 HAI CHIỀU** (mạnh hơn khớp-tổng), tables 97. Smoke 8/8. + 🔑 **3 nghề mới, đều tái dùng được:** (a) **Stage 3b cặp đối-chứng MIỄN PHÍ** — #446 (cây tiền-S171) vs #447 khác **đúng 1 commit** ⇒ cô-lập nhân-quả **thay được `git worktree` đã fail**; (b) **bẫy 415 = họ hàng bẫy 411** — POST `application/json` vào endpoint **multipart** bị loại ở **action-selection, TRƯỚC authz** ⇒ 415 chỉ chứng route tồn tại, **vô nghĩa cho câu hỏi authz**; re-probe đúng multipart → 401. ⇒ **luật: mọi mã pre-auth (411/415) phải re-probe đúng dạng**; (c) **wire-proof MẠNH HƠN 401 = thông-điệp-lỗi GỌI TÊN FIELD** — bearer + multipart ⇒ 400 liệt `ContractSigningPlanId` + whitelist đuôi file ⇒ chứng **binary MỚI đang phục vụ**, mạnh hơn đo `w3wp` StartTime. + 🔴 **2 vế PHẢI tách, cấm nén:** ✅ *không hồi-quy do S171* (chứng bằng cặp #446/#447) ⟂ ❌ *flake `Create_DifferentEvaluationType_DoesNotAffect_Other` pre-exist* **VẪN chưa chứng** — nay **0/4 log CI** tái hiện. CI xanh **không rửa sạch** flake và cũng **không xác nhận** nó (vắng-mặt bằng-chứng ≠ bằng-chứng vắng-mặt). + 🔴 **Bài đắt nhất — "KHÔNG ĐO ĐƯỢC" là kết luận PHẢI TỰ NGHI:** mày kết `nv.test@` login 401 ⇒ *"credential UAT chết trên prod"* ⇒ bỏ ngỏ chân #44/#85. **SAI** — không phải credential chết mà **tra nhầm sổ**: bộ đang sống nằm ở `docs/HANDOFF.md` slot (64) (`test.drafter@`/`test.approver@`, `Test@12345678`). Lead đo lại: login 200/200 · list **200** · changelogs **404 KHÔNG 403** · attach **400 gọi tên field** · CTRL-ÂM 404 ⇒ role thường **có đủ** `KeHoachKyKet.Read`+`Update`, **gotcha #85 KHÔNG tái phát**. ⇒ **LUẬT: trước khi khai "KHÔNG ĐO ĐƯỢC", phải trả lời "đã tra hết sổ có sẵn chưa?"** (HANDOFF · STATUS · run-folder). Cùng lớp `feedback_mechanism_right_data_absent`. + 🔴 **Return #53-garble:** trả câu giữa chừng *"Writing Stage 5 + Verdict"* — **KHÔNG mất việc**: đĩa 10.156→**24.096 B**, 0 ô `_(chờ đo)_`. Đối-chứng lần nữa cho luật S158: **chống-#53 = thiết-kế artifact đổ-ruột-liên-tục, KHÔNG phải prompt.** Tag `[s171-s172, #447, cap-doi-chung-mien-phi, bay-415-pre-auth, wire-proof-goi-ten-field, khong-do-duoc-phai-tu-nghi, 2-ve-cam-nen, doi-ruot-lien-tuc, seed-on-behalf]` + +### S182 (verbatim) + +- **S182 YC-013 W0.4 PRECHECK (lead-seed từ memoryDelta workflow-return VERIFY khớp artifact, C2 /pause):** `LỆCH — Q2 sai tiền-đề nhưng W1 GO 0-blocker` — 12 phiếu prod (3 DaDuyet + 8 Nháp + 1 TraLai; memory-pack "001-003 Phase=3" KHÔNG SAI — thế giới đẻ thêm 9; ký-ức đóng-băng-thời-điểm khác ký-ức hỏng, SPEC phổ-quát-hoá mốc cũ mới là chỗ gãy) · pin-lệch 0 row VỚI control: chạy CHÍNH câu audit bỏ vế phủ-định ra 12 row element-wise + bịt điểm-mù LIKE bằng đẳng-thức-đếm IS-NOT-NULL=12=JOIN-in-family · hiệu chuẩn thước bằng 2 ANCHOR đã biết (MenuItems=200, Permissions=2221) mạnh hơn count>0 · ĐO bác acceptance wave-sau: 6 ô cờ đều 0/88 trên prod ⇒ ép-false vs giữ-thật render Y HỆT, A2 phải seed local 1 level cờ=true mới có răng · #8 Finalize=8/88 = CỜ CHỊU LỰC cấm đụng. → `runs/2026-08-08-S182-khkk-awf-mirror-ncc/sub-cicd-monitor-0.md` diff --git a/.claude/agent-memory/cicd-monitor/archive/_INDEX.md b/.claude/agent-memory/cicd-monitor/archive/_INDEX.md index 659d082..33abc79 100644 --- a/.claude/agent-memory/cicd-monitor/archive/_INDEX.md +++ b/.claude/agent-memory/cicd-monitor/archive/_INDEX.md @@ -56,3 +56,5 @@ - 2026-08-01 · S167 #440 `1c6d77a` K6 admin thu-gon menu 12-key — PASS 5/5, 0-hit marker SAI 2 lan do co-che regex (grep -F) + COUNT-DELTA + log-CI nhan-chung-4 · `2026-08.md` · substring:"S167 (08-01) `1c6d77a` K6" - 2026-08-01 · S167 #442 `aaed699` K7 bridge KHKK->HD — PARTIAL 4/6+1, PUSH-DE NUOT DEPLOY (gotcha #86) + ancestry-proof + control-duong tien-commit · `2026-08.md` · substring:"S167 (08-01) `aaed699` K7" - 2026-08-01 · S168 #444 `0bfadc2` GD3-D Panel-3 + Lines-editor KHKK (0-mig) — PASS 6/6, wire-proof 400 goi-ten-3-field + chup-PRE-kip-gio + marker bat-doi-xung 1:2 + CSS nhan-chung · `runs/2026-07-31-S164-4gd-khkk-fanout/sub-cicd-verify-gd3d-lineeditor.md` + +- **@S189 curate → `2026-08.md` §[ARCHIVED @S189]** — verbatim 5 khối L1: `S159` (arc 4 đợt `b5799fc`/`1a47a61`/`2a72695`/`a2bbcb9`, durability-≥2-restart, control-âm-tầng-bundle) · `S168` (#444 `0bfadc2`, wire-proof-400-tên-field, chụp-PRE, marker bất-đối-xứng) · `ARC S155→S166` (#416→#439, chân-lý-rỗng #435, luật cấm `HEAD~1` #432, 3-chân-kiềng #423) · `#447` (`998ea55`, cặp-đối-chứng #446/#447, bẫy-415-pre-auth, không-đo-được-phải-tự-nghi) · `S182` (YC-013 W0.4, hiệu-chuẩn-thước-2-anchor, acceptance ép-false). diff --git a/.claude/workflows/runs/2026-08-10-S189-bookstart-open/sub-cicd-monitor-yc028.md b/.claude/workflows/runs/2026-08-10-S189-bookstart-open/sub-cicd-monitor-yc028.md new file mode 100644 index 0000000..6480d3a --- /dev/null +++ b/.claude/workflows/runs/2026-08-10-S189-bookstart-open/sub-cicd-monitor-yc028.md @@ -0,0 +1,281 @@ +CICD: **PARTIAL — vòng 2: 8/9 check** — SHIP THÀNH CÔNG TRỌN (run #486 success · gate **680 EXACT** · bundle rotate ×2 · marker **10/10**) ⟂ **1 FAIL: A/059 latency 6/16 mẫu > 3 s (max 8,75 s)**, gốc đo được = **RAM 4 GB bị ép / buffer pool SQL 213 MB**, KHÔNG phải code (commit 0 file BE). _(vòng 1 = FAIL 8/11: Gitea không tạo run)_ + +# sub-cicd-monitor — YC-028 `92d8215a` (S189) + +> ARTIFACT-FIRST. Ghi từng mục NGAY khi có bằng chứng. Dòng-1 lật thành `PASS|FAIL — n/n` ở cuối. + +## Stage 1 — Push + path filter + +- HEAD = `92d8215a6253d749794ca7551b5f0bc453ab08e9` +- Subject: `[CLAUDE] PurchaseEvaluation: YC-028 sự cố prod A/059 — FE tách "404 thật ⟂ lỗi hệ thống" 5 site ×2 app + guard disk/orphan vào runbook+cicd-monitor` +- Commit time: `2026-08-11 11:25:51 +0700` +- `git log origin/main..HEAD` = **RỖNG** ⇒ push đã land. +- `git diff --name-only 9b3c0edb..92d8215a` = **12 file**: + - FE deploy được (8): `fe-{admin,user}/src/lib/apiError.ts` · `…/src/pages/pe/PurchaseEvaluationsListPage.tsx` · `…/src/pages/khkk/KhkkListPage.tsx` · `…/src/pages/khkk/KhkkCreatePage.tsx` + - KHÔNG deploy (4): `.claude/WAL.md` · `.claude/agents/cicd-monitor.md` · `.claude/skills/iis-deploy-runbook/SKILL.md` · `docs/governance/so-yeu-cau-chu-du-an.md` +- Path filter 7-glob (`docs/**`,`**/*.md`,`.claude/skills/**`,`.claude/agent-memory/**`,`.claude/workflows/runs/**`,`.gitignore`,`scripts/**.md`): 8 file `fe-*/src/**.{ts,tsx}` KHÔNG khớp glob nào ⇒ **CI PHẢI CHẠY** (không docs-only). ✅ + +**Stage 1 = PASS.** + +## Stage 1b — Disk-free VPS (BƯỚC MỚI, lần chạy đầu tiên) + +`ssh vietreport-vps "fsutil volume diskfree C:"` (KHÔNG dùng `| head` — luật S189): + +``` +Total free bytes : 30,878,273,536 ( 28.8 GB) +Total bytes : 58,948,841,472 ( 54.9 GB) +Used bytes : 27,012,255,744 ( 25.2 GB) +``` + +- Free = **28.8 GB** ≫ ngưỡng 5 GB. ✅ (sự cố YC-028 hôm qua: 0 byte ⇒ SQL Error -2) + +**Stage 1b = PASS.** + +## Stage 2 — PRE snapshot (chụp TRƯỚC khi deploy land) + +Đo live lúc ~11:2x, trước khi CI xong: + +| App | JS | CSS | +|---|---|---| +| admin | `index-BZ1PG34E.js` | `index-DVHtHLex.css` | +| eoffice | `index-0ObPwWFB.js` | `index-Ibv3QD_1.css` | + +**Control âm + control DƯƠNG trên bundle PRE** (tải nguyên file, đếm byte UTF-8 bằng python `bytes.count`, KHÔNG `grep -P` — locale Git-Bash giết `-P`): + +| Bundle | size | `Không tìm thấy phiếu` (control DƯƠNG) | `Hệ thống đang chậm hoặc lỗi khi tải phiếu` (marker) | +|---|---|---|---| +| admin `BZ1PG34E.js` | 1.813.772 B, `application/javascript` | **5** | **0** | +| eoffice `0ObPwWFB.js` | 1.728.765 B, `application/javascript` | **6** | **0** | + +⇒ thước SỐNG (control dương bắt được chuỗi tiếng Việt trong bundle) ⇒ marker 0-hit = **vắng THẬT**, không phải hỏng phép đo. +Nguồn marker: `grep -rn` ra **5 site/app** (`KhkkCreatePage:252` · `KhkkListPage:240` · `PurchaseEvaluationsListPage:659,761,817`) × 2 app = 10. + +## Stage 3 — Gitea run poll — 🔴 **FAIL: KHÔNG CÓ RUN NÀO ĐƯỢC TẠO** + +Poll 6 vòng × ~47s (11:27:49 → 11:31:44) + tra lại 11:36. `GET /actions/tasks?limit=8` (`total_count=484`), đầu bảng: + +``` +598 485 9b3c0edb success push 2026-08-11T01:25:05+07:00 +597 484 ef1d4fbb success push 2026-08-10T21:31:18+07:00 +596 483 c7b7ca45 success push 2026-08-10T20:45:18+07:00 +``` + +- **0 hit `92d8215a`** trong TOÀN bộ 484 task (không chỉ trang đầu). +- Trang web `…/actions` (HTML thô): 2 hit `9b3c0ed`, **0 hit `92d8215`**. +- `GET /commits/92d8215a…/status` → `total_count: 0`, `state: ""` (rỗng hoàn toàn). + +### Tiền-đề đã kiểm (không được khai "CI fail" khi chưa loại hết) + +| Nghi vấn | Phép đo | Kết quả | +|---|---|---| +| Push chưa tới remote? | `git ls-remote origin main HEAD` | `92d8215a…` trên **CẢ** `HEAD` + `refs/heads/main` ⇒ **đã tới** | +| Path filter nuốt? | đọc `.gitea/workflows/deploy.yml:17-28` từ ĐĨA | đúng 7 glob; 8 file `fe-*/src/**.{ts,tsx}` **không khớp glob nào** | +| Filter hỏng nói chung? | **cặp đối-chứng** | `9b3c0edb` (docs + `.claude/governance/.session-counter.json` — file KHÔNG bị ignore) → **CÓ** run #485 ✅ · `d4799ddd` (`.claude/WAL.md` đơn độc) → **KHÔNG** run ✅ ⇒ filter chạy ĐÚNG cho commit khác | +| Runner chết? | `Get-Service gitea-runner` | `Running` — **và không liên quan**: run chưa được TẠO thì runner chưa tới lượt | +| Actions bị tắt? | `app.ini:53-54` + API repo | `[actions] ENABLED = true` · `has_actions = True` | +| Push-đè nuốt (gotcha #86)? | `git log 9b3c0edb..92d8215a` | đúng **1** commit, không có push sau | + +### 🔴 Nguyên nhân gốc — ĐO ĐƯỢC, không suy diễn + +| Nguồn | Giá trị | Nghĩa | +|---|---|---| +| `GET /api/v1/repos/…/branches/main` → `commit.id` | `92d8215a6253` | Gitea đọc **từ git trên đĩa** ⇒ ref ĐÃ tiến | +| `GET /api/v1/repos/…` → `updated_at` | **`2026-08-11T01:25:06+07:00`** | Gitea đọc **từ DB** ⇒ vẫn là mốc push TRƯỚC (`9b3c0edb` 01:25:05) | +| reflog local `.git/logs/refs/remotes/origin/main` | `9b3c0edb → 92d8215a` @ **11:25:55** | push thật lúc 11:25:55 | + +⇒ **Ref tiến trên ĐĨA nhưng tầng ứng-dụng Gitea KHÔNG hề xử lý cú push** (0 bump `updated_at`, 0 ActionRun, 0 commit-status). Đây **KHÔNG phải CI fail** — CI chưa từng được gọi. Hook còn nguyên (`hooks/post-receive` 376 B + `post-receive.d/gitea` 142 B, mtime 21/04 = chưa ai đụng) ⇒ nghi hook gọi ngược vào Gitea (`/api/internal/hook/post-receive`) bị lỗi/timeout tại thời điểm push. + +🔴 **Lỗ quan-trắc đi kèm (defect ĐỘC LẬP, đo được):** `C:\gitea\log\gitea.log` = **0 byte** từ `8/11 00:00:00`, trong khi bản xoay `gitea.log.2026-08-11.001.gz` = **12,15 MB**. Gitea xoay log lúc nửa đêm rồi **không mở lại file** ⇒ **mất sạch log server-side 11,5 giờ qua**, đúng lúc cần chẩn. Gitea process `Id=4020` StartTime `8/10 5:09:20 AM`, CPU tích luỹ **37.380 s**. +⚠️ Em main: cú `git push` lúc 11:25:55 có in warning/error dòng nào không? Terminal của em là **nguồn DUY NHẤT còn lại** — log Gitea đã chết. + +## Stage 4 — Test gate — ⬜ N/A + +Baseline `docs/STATUS.md` row Tests = **680** (45 Domain + 635 Infra), Mig canonical **72**. **Không đo được vì không có run.** KHÔNG suy ra pass/fail (vắng-mặt bằng-chứng ≠ bằng-chứng vắng-mặt). + +## Stage 5 — Bundle rotate + byte-marker — 🔴 **FAIL (hệ quả Stage 3)** + +Đo lại LIVE lúc ~11:36 (sau push 10 phút): + +| App | JS trước | JS sau | Rotate? | +|---|---|---|---| +| admin | `index-BZ1PG34E.js` | `index-BZ1PG34E.js` | ❌ **ĐỨNG IM** | +| admin CSS | `index-DVHtHLex.css` | `index-DVHtHLex.css` | ❌ | +| eoffice | `index-0ObPwWFB.js` | `index-0ObPwWFB.js` | ❌ **ĐỨNG IM** | +| eoffice CSS | `index-Ibv3QD_1.css` | `index-Ibv3QD_1.css` | ❌ | + +- Byte-marker `Hệ thống đang chậm hoặc lỗi khi tải phiếu` = **0/10** (kỳ vọng 5/app). Control dương vẫn 5 & 6 ⇒ thước sống. +- ⚠️ Đây **KHÔNG** phải ca FROZEN-lành-tính (#69 mode-B): mode-B là *deploy có chạy, copy lại file byte-identical*. Ở đây **deploy chưa từng chạy** ⇒ prod đang phục vụ **code TRƯỚC bản vá YC-028**. + +## Stage 6 — Smoke + hồi-quy A/059 — ✅ **PASS 5/5** (server ĐÃ KHOẺ) + +| Phép đo | Kỳ vọng | Thực đo | | +|---|---|---|---| +| `GET /health/live` | 200 | **200** · 0,102 s | ✅ | +| `GET /health/ready` | 200 | **200** · 0,167 s | ✅ | +| `POST /api/auth/login` admin | 200 | **200** · 0,294 s · `accessToken` 468 ký tự | ✅ | +| **`GET /api/purchase-evaluations/F02C81D1…` (A/059)** | **200 và < 3 s** | **200** · **0,366 / 0,486 / 0,406 s** (3 lượt) · 18.313 B · `maPhieu=PE/2026/A/059` `phase=10` | ✅ | +| smoke 4 endpoint (`purchase-evaluations` · `contract-signing-plans` · `menus` · `contracts`) | 200 | 200 / 200 / 200 / 200 · 0,269 · 0,124 · 0,164 · 0,121 s | ✅ | +| **control âm** GUID toàn-0 | 404 | **404** | ✅ | + +🔑 **Hồi-quy YC-028 SẠCH:** phiếu tâm sự cố trả **0,37-0,49 s**, cách ngưỡng 3 s ~7 lần và cách mốc sự cố (5-47 s) ~2 bậc độ lớn. Cộng disk 28,8 GB free ⇒ **hạ tầng đã hồi phục thật**, không phải hồi phục trên giấy. +🔑 Control âm 404 còn chứng thêm: BE **phân biệt đúng** 404-thật ⟂ lỗi-khác — đúng tiền-đề mà bản vá FE dựa vào. Bản vá đó **chưa lên prod** (Stage 5), nhưng nền BE cho nó thì đã đúng. + +## VERDICT + +**`FAIL — 8/11 check`** (+1 N/A). Chia 2 vế, **cấm nén**: + +- ✅ **Vế hạ-tầng/hồi-quy: LÀNH.** disk 28,8 GB · health/login/smoke 9/9 · A/059 200 trong 0,4 s · control âm 404. +- ❌ **Vế ship: KHÔNG XẢY RA.** Bản vá YC-028 **chưa có trên prod**. Không phải CI đỏ — **CI chưa từng được gọi**. + +| # | Check | Kết quả | +|---|---|---| +| 1 | Push tới remote (`git ls-remote`) | ✅ | +| 2 | Path filter đúng ⇒ CI phải chạy | ✅ | +| 3 | **Disk free VPS ≥ 5 GB** (bước 1b, lần chạy ĐẦU) | ✅ **28,8 GB** | +| 4 | Gitea tạo ActionRun cho `92d8215a` | 🔴 **FAIL — 0 run** | +| 5 | Bundle rotate ×2 app | 🔴 **FAIL — cả 4 file đứng im** | +| 6 | Byte-marker trong bundle mới | 🔴 **FAIL — 0/10** (control dương 5 & 6 = thước sống) | +| 7 | health live + ready | ✅ | +| 8 | login admin | ✅ | +| 9 | **A/059 200 và < 3 s** | ✅ **0,366-0,486 s** | +| 10 | smoke 4 endpoint | ✅ 4/4 | +| 11 | control âm 404 | ✅ | +| — | test gate 680 | ⬜ N/A — không có run để đọc | + +### Khuyến nghị cho em main (tao READ-only, KHÔNG tự chạy) + +1. **Re-trigger:** `deploy.yml:29` CÓ `workflow_dispatch:` ⇒ bấm **Run workflow** trên `…/actions` (build đúng `main` = `92d8215a`). Rẻ hơn và sạch hơn commit rỗng. +2. **Vá lỗ quan-trắc:** `Restart-Service gitea` để logger mở lại file — `gitea.log` đã **0 byte suốt 11,5 h**. Không vá thì lần tái phát sau cũng **mù y hệt**. +3. **Trả lời 1 câu:** cú `git push` 11:25:55 có in warning gì không? Log Gitea chết rồi ⇒ terminal em là **nguồn cuối cùng**. +4. **Sau khi re-trigger:** gọi lại tao verify trọn (gate 680 + bundle rotate + marker 10/10). + +### Gotcha MỚI đề nghị khắc vào `docs/gotchas.md` + +> **Push land trên đĩa nhưng Gitea KHÔNG xử lý ⇒ CI câm lặng, KHÔNG có run nào để mà đỏ.** +> Phát hiện bằng **2 nguồn lệch nhau trong CÙNG một API**: `GET /repos/{r}` → `updated_at` (đọc **DB**) vs `GET /repos/{r}/branches/main` → `commit.id` (đọc **git trên đĩa**). Head khớp mà `updated_at` đứng ở push TRƯỚC ⇒ tầng ứng-dụng chưa từng thấy cú push. +> 🔴 Nguy ở chỗ **mọi tín hiệu quen đều XANH**: `git ls-remote` đúng SHA · runner `Running` · `[actions] ENABLED=true` · path filter đúng · không có push đè. Chỉ "vắng mặt một cái đáng lẽ phải có" mới tố cáo — cùng lớp `feedback_absence_looks_like_clean`. +> ⇒ **Stage 2 từ nay phải đo `repo.updated_at ≥ thời điểm push`** trước khi kết luận "đang chờ CI". + +--- + +**END VÒNG 1 — VERDICT = FAIL (8/11 check, +1 N/A) · ship KHÔNG xảy ra · hạ-tầng lành** + +--- + +# VÒNG 2 — sau `workflow_dispatch` → run #486 + +## Datum từ lead (bằng chứng, không suy đoán) + +1. `Restart-Service gitea` → Running; `gitea.log` **0 B → 31.946.657 B** ⇒ lỗ quan-trắc ĐÓNG. +2. `workflow_dispatch` qua API → HTTP **204** → **run #486 / task 599**, `head=92d8215a`, `event=workflow_dispatch`. +3. 🔑 **Terminal push 11:25:55 SẠCH** — `remote: Processed 1 references` + `9b3c0edb..92d8215a main -> main`, **0 warning/error**. + ⇒ **Datum quyết định cho gotcha #91:** tầng `git-receive` chạy TRỌN (Gitea *có* nhận, *có* in `remote:`) mà tầng ứng-dụng vẫn không đẻ ActionRun ⇒ chỗ gãy nằm **SAU** receive-pack, **TRONG** notifier/queue Gitea — **không phải** hook chết câm, cũng không phải mạng. Khớp đúng chẩn vòng 1 (`updated_at` DB đứng im ⟂ branch-head trên đĩa đã tiến). + +## Stage 2b — Run #486 + +`599 | 486 | 92d8215a | success | workflow_dispatch | 11:45:40 → 11:53:43` = **8 phút 03 giây** (nhỉnh hơn dải quen 5m3x-6m3x — hợp lý vì gitea vừa restart đang giành CPU, xem Stage 6b). ⚠️ Kiểm #86: #486 là run **CUỐI**, 0 push chen ⇒ đọc đúng run. + +## Stage 3b — Test gate ✅ **680 EXACT** + +Log web-UI `…/actions/runs/486/jobs/0/logs` (18.757 B): + +``` +Passed! - Failed: 0, Passed: 45, Skipped: 0 ← Domain +Passed! - Failed: 0, Passed: 635, Skipped: 0 ← Infrastructure +``` + +**45 + 635 = 680** khớp `docs/STATUS.md` **tách-phần**, Δ**0** — đúng kỳ vọng (commit 0 file test). 0 fail / 0 skip. + +## Stage 4b — Bundle ✅ rotate ×2 + size khớp log CI TỚI BYTE + +| App | PRE | POST | | CI log | prod thật | +|---|---|---|---|---|---| +| admin JS | `index-BZ1PG34E.js` | **`index-DxSej4Da.js`** | ✅ ĐỔI | `1,815.25 kB` | **1.815.258 B** | +| eoffice JS | `index-0ObPwWFB.js` | **`index-BcfxaUBO.js`** | ✅ ĐỔI | `1,794.78 kB` | **1.794.780 B** | +| admin CSS | `index-DVHtHLex.css` | `index-DVHtHLex.css` | ⬜ giữ | `88.39 kB` | — | +| eoffice CSS | `index-Ibv3QD_1.css` | `index-Ibv3QD_1.css` | ⬜ giữ | `94.02 kB` | — | + +🔑 **CSS giữ nguyên là ĐÚNG, không phải cờ đỏ:** diff chỉ thêm chuỗi tiếng Việt + nhánh `isNotFound`, **0 class Tailwind mới** ⇒ Vite sinh CSS byte-identical ⇒ cùng hash. **JS đổi ⟂ CSS giữ = đúng hình dạng của diff** — chính nó là nhân chứng phụ. +🔑 **Ship-proof mạnh nhất:** size prod khớp dòng `dist/assets/…` trong log CI **tới từng byte** ⇒ file trên prod ĐÚNG là file build ở run #486. +✅ **Control âm tầng-2:** hash CŨ rơi về SPA-fallback — `BZ1PG34E` → **900 B `text/html`**, `0ObPwWFB` → **876 B `text/html`** ⇒ bundle cũ **đã bị ghi đè khỏi đĩa**, không phải 2 bản song song. + +## Stage 5b — Byte-marker ✅ **10/10** + +| Bundle | marker `Hệ thống đang chậm…` | control DƯƠNG `Không tìm thấy phiếu` | +|---|---|---| +| admin `DxSej4Da` (1.815.258 B) | **5** ✅ (PRE = 0) | 5 ✅ | +| eoffice `BcfxaUBO` (1.794.780 B) | **5** ✅ (PRE = 0) | 6 ✅ | + +**5/app khớp CHÍNH XÁC 5 site `grep -rn`** (KhkkCreate:252 · KhkkList:240 · PE-List:659,761,817). Differential trọn **0 → 5** hai phía + control dương sống cả trước lẫn sau ⇒ **bản vá YC-028 ĐÃ LÊN PROD THẬT**. + +## Stage 1b(2) — Disk ✅ **28,2 GB** free (ngưỡng 5 GB) — lần chạy thứ 2 của bước mới + +## Stage 6b — Smoke ✅ 8/8 ⟂ 🔴 **A/059 latency FAIL** + +✅ `health/ready` 200 · `login` 200 · `purchase-evaluations` 200 · `contract-signing-plans` 200 · `menus` 200 · `contracts` 200 · **control âm GUID-toàn-0 → 404** ✅ · peer `A/055` 200 **0,609 s** · `A/058` 200 **0,479 s**. + +🔴 **`GET /purchase-evaluations/F02C81D1…` (A/059): 200 ở CẢ 16/16 lượt, nhưng KHÔNG đạt bar `< 3 s`.** + +| Đợt | mẫu | min | median | max | **> 3 s** | +|---|---|---|---|---|---| +| ngay sau deploy (pool sinh 11:53:13) | 3 | 5,03 | 6,40 | 10,70 | 3/3 | +| đường cong warm-up | 6 | 0,436 | — | 7,06 | 2/6 *(giảm đơn điệu 7,06→2,29→0,506→0,485→0,469→0,436)* | +| **burst 1 (~12:0x)** | 8 | 0,610 | **1,034** | 6,205 | **2/8** | +| **burst 2 (sau ~2,5 phút nghỉ)** | 8 | 0,618 | **4,996** | **8,749** | **4/8** | + +🔴 **Tao TỰ BÁC giả thuyết đầu của mình:** thoạt tiên quy cho cold-start (w3wp pid 9156 StartTime **11:53:13** đúng trong cửa sổ deploy) — đường cong warm-up ủng hộ. **Nhưng burst 2 XẤU HƠN burst 1** ⇒ cold-start **bị bác bằng ĐO**, không giữ lời giải thích tiện tay. + +### Truy gốc — loại trừ theo runbook, từng cửa + +| Nghi vấn | Đo | Kết luận | +|---|---|---| +| Disk đầy (YC-028 cũ) | 28,2 GB free | ❌ loại | +| Orphan sshd/powershell ăn core | CPU-delta 5 s ×2 lượt → chỉ `gitea` 2,2 s rồi 1,2 s; **0 sshd** đốt CPU | ❌ loại | +| `AUTO_CLOSE` bật lại (runbook §4) | `SolutionErp is_auto_close_on = **0**` | ❌ loại | +| NTFS nén `.mdf/.ldf` (gotcha #88) | SolutionErp **không nén**; nhưng **`Vietreport_Master.mdf` + `.ldf` ĐANG nén** — DB hàng xóm | ❌ loại (phát hiện kèm → báo riêng) | +| **RAM bị ép → buffer pool SQL teo** | dưới đây | ✅ **KHỚP** | + +``` +RAM total = 4 GB · free = 1,08 GB (27%) +SQL: Target Server Memory = 1.115.736 KB (~1,09 GB) + Total Server Memory = 218.592 KB (~213 MB) ← chỉ được 1/5 cái nó CẦN + Page life expectancy = 522 s · max server memory = 2147483647 (mặc định, KHÔNG ghim) +Top WS: gitea 317 MB (CPU 512 s kể từ restart ~11:44) · w3wp 238 MB · sqlservr 135 MB +``` + +⇒ **Chuỗi nhân-quả đo được:** VPS **4 GB RAM / 3 core** dùng chung với VIETREPORT → buffer pool SQL chỉ giữ **213 MB** trên **1,09 GB** nó nhắm → PLE 522 s → query nặng PE-detail **trượt cache** → đọc đĩa ⇒ **phân bố LƯỠNG CỰC đặc trưng: 0,6-1,0 s (trúng cache) HOẶC 5-8,7 s (trượt cache), gần như trống ở giữa**. Đó là hình dạng của **thiếu RAM**, không phải hình dạng của code chậm. + +🔴 **KHÔNG THỂ do commit này:** `git diff 9b3c0edb..92d8215a` = **8 file FE + 4 file không deploy, 0 file BE, 0 mig** ⇒ DLL API hành vi y hệt bản chạy trước đó. **Độ trễ API không thể do code trong commit này gây ra.** + +🔴 **PHẦN TAO PHẢI NHẬN:** khuyến nghị `Restart-Service gitea` (của chính tao ở vòng 1) khiến gitea nạp lại + tái chỉ mục: **317 MB RSS và 512 giây CPU trong ~25 phút (~34% một core, liên tục)**. Trên máy 4 GB/3 core nó **giành RAM+CPU với SQL đúng lúc đo** ⇒ **một phần con số xấu ở trên là do việc tao đề xuất**. Đổi lại lỗ quan-trắc 11,5 h đã đóng — tao vẫn cho là đáng, nhưng cái giá phải nói ra chứ không giấu vào chữ "transient". + +## VERDICT VÒNG 2 — **PARTIAL 8/9** + +| # | Check | | +|---|---|---| +| 1 | Run #486 tới terminal `success` | ✅ 8m03s | +| 2 | Test gate **680** (45+635) tách-phần, Δ0 | ✅ EXACT | +| 3 | Bundle JS rotate ×2 + size khớp log CI tới byte | ✅ | +| 4 | Byte-marker 10/10 (5/app · PRE=0 · control dương sống) | ✅ | +| 5 | Control âm tầng-2: hash cũ → `text/html` 900/876 B | ✅ | +| 6 | Disk free ≥ 5 GB | ✅ 28,2 GB | +| 7 | health + login | ✅ | +| 8 | smoke 4 endpoint + control âm 404 + 2 peer PE | ✅ | +| 9 | **A/059 200 và < 3 s** | 🔴 **FAIL** — 200 ở 16/16, nhưng **6/16 mẫu > 3 s**, max **8,75 s**, burst-2 median **5,0 s** | + +**Hai vế, cấm nén:** +- ✅ **SHIP: THÀNH CÔNG TRỌN.** Bản vá YC-028 đã lên prod — chứng bằng marker 0→5 hai app + size khớp byte + bundle cũ bị ghi đè. Gate 680 nguyên. Vòng 1 chỉ là Gitea không tạo run, **không hề có lỗi trong code**. +- 🔴 **PERF: bar `<3 s` KHÔNG ĐẠT**, và **không** phải cold-start (burst 2 xấu hơn burst 1). Gốc đo được = **RAM 4 GB bị ép, buffer pool SQL 213 MB/1,09 GB**, nặng thêm vì gitea vừa restart. **Không phải hồi quy của commit này.** + +### Khuyến nghị (tao READ-only) + +1. **Đo lại A/059 sau ~30-60 phút** khi gitea nguội — còn 5-8 s thì đây là **thiếu RAM cấu trúc**, hết là transient. +2. **Ghim `max server memory` cho SQL** (đang mặc định `2147483647`): ~1.200 MB để SQL khỏi đấu RAM hỗn loạn với gitea/w3wp/runner trên máy 4 GB. +3. **Nâng RAM VPS** — 4 GB cho 3 site + SQL + Gitea + runner + LibreOffice là quá mỏng. Đây là **mặt thứ hai của YC-028**: lần trước vỡ ở ĐĨA, lần này ép ở RAM. +4. **`Vietreport_Master.mdf/.ldf` đang NTFS-nén** (gotcha #88, dư âm đợt "giải phóng dung lượng") — DB của VIETREPORT → báo đội đó `compact /u`. +5. Gotcha **#91** khắc theo vòng 1; datum #3 của lead làm nó **sắc hơn**: chết ở **notifier**, không phải receive-pack. + +--- + +**END VÒNG 2 — VERDICT = PARTIAL (8/9) · SHIP PASS TRỌN · PERF A/059 FAIL (gốc hạ-tầng RAM, không phải code)** + diff --git a/docs/STATUS.md b/docs/STATUS.md index a5f7355..8cee79f 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -474,7 +474,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 | **680 PASS** | **45 Domain + 635 Infra** · 🔄 **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 | **88** | 🔄 **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 | **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) | | 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/gotchas.md b/docs/gotchas.md index 9db8eff..49bdf6f 100644 --- a/docs/gotchas.md +++ b/docs/gotchas.md @@ -1421,6 +1421,40 @@ for h in resp.points: # ← .points không phải iterable trực tiếp **Luật:** thấy `Msg 5118` / RECOVERY_PENDING sau sự cố disk ⇒ **`compact ` kiểm cờ nén TRƯỚC** khi nghĩ tới restore backup; và **CẤM bật compression trên cây thư mục SQL DATA** (freeing-space bằng nén = bẫy hẹn giờ cho mọi DB đang offline/detached). Bonus bảo mật lộ ra cùng lượt đọc errorlog: port 1433 phơi Internet, `sa` bị brute-force liên tục từ IP lạ — vá firewall riêng. +### 89. Cache của AUTOMATION phình vô hình ăn sạch ổ C → SQL Error -2 — triệu chứng đội lốt lỗi DATA (Session 189) + +**Triệu chứng:** UAT anh Kiệt: phiếu *"ko duyệt được"* rồi *"Giờ nó ko tìn thấy phiếu"* (A/059 hiện trong cây, panel đỏ "Không tìm thấy phiếu."). Đo: `fsutil` free = **−20.189.184 byte** (âm vào 5GB reserve); log `[ERR] Microsoft.Data.SqlClient … OpenAsync … Error Number:-2` + `SlaExpiryJob/ItTicketSlaJob iteration failed`; file log Serilog hôm trước **biến mất** (đợt "giải phóng dung lượng" quét — cùng đợt NÉN file .mdf template, vân tay #88). + +**Cơ chế:** 2 kho **cache của máy móc** phình theo NHỊP CI chứ không theo hành vi người dùng nên không ai thấy: `C:\gitea\data\repo-archive` **16,49GB** (ZIP sinh mỗi lần tải archive — cache thuần, Gitea tự tái tạo; cron `archive_cleanup` CHƯA bật) + `npm-cache` của SYSTEM **12,76GB** (`System32\config\systemprofile\AppData\Local` — CI build FE chạy dưới SYSTEM, tích từ tháng 4). Disk 0 byte → SQL không cấp phát/grow được → **lệnh GHI chết trước** ("ko duyệt được"), query nặng chết theo (GET detail 500), query nhẹ lúc được lúc không → FE gom mọi nhánh lỗi vào **"Không tìm thấy phiếu"** (#44) ⇒ người dùng lẫn người chẩn đoán **nghi oan phiếu/DB** trong khi phiếu còn nguyên (Phase=ChoDuyet, workflow sống). + +**Fix (S189):** xóa `repo-archive\*` (an toàn — cache) + npm-cache + 567 `u_ex*.log` >30 ngày → free 20GB+; `ALTER DATABASE … SET AUTO_CLOSE OFF` ×3 DB (ON = mỗi connection mở lại DB ~5s — đo `COUNT(*)` 1 dòng mất 5.420ms); FE tách "404 thật ⟂ lỗi hệ thống + nút thử lại" 5 site ×2 app (`lib/apiError.ts isNotFound`). **Guard đã wire:** runbook §"Disk-free + orphan guard" + `cicd-monitor` bước 1b (`fsutil` free <5GB = FAIL). **TODO ngoài giờ:** bật `[cron.archive_cleanup]` Gitea (restart service dùng chung VIETREPORT) + `compact /u` `Vietreport_Master.mdf` (đang mở, cần OFFLINE ~10s). + +**Luật:** prod "chậm / không thấy / không lưu được" ⇒ **`fsutil volume diskfree C:` TRƯỚC** khi nghi data hay code — thông điệp UI chỉ là lớp vỏ (#44); và **cache của automation phải có vòng dọn tự động**, không có thì nó là bom hẹn giờ đúng nghĩa. + +### 90. `ssh … | head` cắt pipe → process Windows-OpenSSH MỒ CÔI spin full core — người chẩn đoán tự làm bẩn hiện trường (Session 189) + +**Triệu chứng:** SAU khi dọn đĩa xong server **vẫn** chậm (anh: *"vẫn ko tải phiếu lên đc, vẫn chậm lắm"*): GET detail MỌI phiếu 5-15s, A/059 (5 NCC) 500-sau-29,8s ×3 lần `"A task was canceled"`, curl chờ được thì 200 sau 47,6s. SQL khai: `wait_type=ASYNC_NETWORK_IO, wait_time=3ms, elapsed=29s` — CPU query chỉ **66ms**, reads ~1.500 ⇒ **SQL xong việc từ lâu, client không nhận nổi data**. CPU-delta 5 giây: **3 process `sshd` = 10,6+10,5+10,5 core-giây trên máy 3 core** = bão hòa 100%. + +**Cơ chế:** lệnh dạng `ssh vps '…quét nặng…' | head -N` phía local đóng pipe sau N dòng → ssh client thoát, **nhưng sshd + lệnh phía Windows-OpenSSH KHÔNG chết** — spin trọn core vô hạn. 3 lượt quét-đo bị cắt = 3 orphan = w3wp đói CPU → materialize kết quả SQL chậm ×100 → mọi API rùa → request vượt trần hủy ~30s → 500 → FE "Không tìm thấy phiếu" **tiếp**, dù gốc disk đã chữa. Người chẩn đoán tự tạo ra tầng bệnh thứ hai bằng chính công cụ chẩn đoán. + +**Fix:** kill orphan sshd — 🔴 **CHỪA chuỗi tổ tiên session đang chạy** (walk `ParentProcessId` từ `$PID` lên, kill sshd ∉ chain). Sau kill: A/059 **47s→0,38s**, phiếu bé 0,17s, inbox 0,12s — tức thì, không cần deploy gì. + +**Luật:** (1) cần cắt output ssh thì `Select-Object -First N` **phía REMOTE**, cấm `| head` phía local; (2) recycle app pool xong phải đợi **cold-start ~48s** rồi mới đo (đo sớm = đo app đang khởi động, ra số rác); (3) mọi op đo/quét nặng trên prod tự hỏi *"op này để lại gì sau khi tôi ngắt?"* — hiện trường bẩn làm phép đo sau nói dối. + +### 91. Push land trên ĐĨA nhưng tầng-app Gitea CÂM → CI không có run để mà đỏ — phát hiện bằng 2 nguồn LỆCH trong CÙNG một API (Session 189) + +**Triệu chứng:** push `92d8215a` terminal SẠCH (`Processed 1 references`), `git ls-remote` đúng SHA — nhưng **0 ActionRun** (0 hit trong 484 task), commit-status rỗng, bundle prod đứng im. **Mọi tín hiệu quen đều XANH**: runner `Running`, `[actions] ENABLED=true`, path-filter đúng (cặp đối chứng: `9b3c0edb` có run · `d4799ddd` WAL-only không run — filter sống), không có push đè (#86). + +**Phép đo tố cáo (2 nguồn trong CÙNG API Gitea):** `GET /repos/{r}/branches/main → commit.id` (đọc **git trên đĩa**) = SHA mới ⟂ `GET /repos/{r} → updated_at` (đọc **DB**) = vẫn mốc push TRƯỚC ⇒ ref tiến trên đĩa nhưng **tầng ứng dụng chưa từng thấy cú push** (nghi internal hook `post-receive` lỗi/timeout đúng thời điểm). KHÔNG phải CI fail — **CI chưa từng được gọi**. Cùng lớp `absence looks like clean`: chỉ "vắng mặt cái đáng-lẽ-phải-có" mới tố cáo. + +**Defect quan trắc đi kèm:** `C:\gitea\log\gitea.log` **0 byte từ 00:00** (xoay log nửa đêm xong không mở lại file) ⇒ mù server-side 11,5h đúng lúc cần chẩn. Restart service mở lại logger (31,9MB flush về). + +**Fix (S189):** `Restart-Service gitea` (không đụng app phiếu — eoffice/admin/api KHÔNG phụ thuộc Gitea) → `workflow_dispatch` qua API (`POST /repos/{r}/actions/workflows/deploy.yml/dispatches`, token lấy từ remote-URL local, HTTP 204) → run **#486** head đúng `92d8215a` → ship trọn (gate 680 EXACT · bundle rotate ×2 khớp CI-log từng byte · marker 10/10). + +**Cost của thuốc — ghi thật, không giấu dưới "transient":** restart Gitea kích **re-index ~25 phút** (317MB RSS + 512s CPU) tranh RAM/CPU với SQL trên máy 4GB/3-core → cửa sổ đo sau đó latency bimodal 0,6s ⟂ 5-8,7s (cache-miss, PLE 522s). Vá tầng RAM cùng lượt: `sp_configure 'max server memory (MB)'` từ **2147483647 (unbounded default!)** → **1200** — ăn ngay không restart, PLE hồi 1123s, A/059 về 0,58s. Máy ít RAM mà SQL unbounded = Windows với SQL giằng RAM qua lại vô hạn. + +**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. + ## Checklist debug bug mới 1. Build pass không? → fail → check using + package version compat diff --git a/docs/governance/error-ledger.md b/docs/governance/error-ledger.md index dd8af86..335b554 100644 --- a/docs/governance/error-ledger.md +++ b/docs/governance/error-ledger.md @@ -232,6 +232,20 @@ Detect by **action-signature** (NOT "AI tự phán có vi phạm không"). Scan > **Maintenance:** append RCA on each AS-hit; promote a guard to `procedural` on its 2nd strike; mark `verified` once it holds through a session; retire by net-effect. Pointer entries only — full narrative lives in session-logs (summary-index). +## RCA `YC028-S189` — sự cố prod 2 tầng, tầng-2 do CHÍNH LEAD gây khi chẩn đoán (2026-08-11) + +**Triệu chứng:** UAT anh Kiệt (Zalo): phiếu A/059 *"ko duyệt được"* → *"Giờ nó ko tìn thấy phiếu"* + *"check kỹ tí"*; sau đợt cứu đầu anh vẫn báo *"vẫn ko tải phiếu lên đc, vẫn chậm lắm"*. + +**Tầng-1 (không do đội):** ổ C VPS **0 byte** (free −20MB vào reserve) — cache automation phình vô hình (`repo-archive` 16,49GB + `npm-cache` SYSTEM 12,76GB) → SQL `Error -2` → lệnh ghi/duyệt chết, FE gom mọi lỗi vào "Không tìm thấy phiếu" (#44 → gotcha **#89**). Xử: dọn ~30GB + `AUTO_CLOSE OFF` ×3 + FE tách 404⟂500 (commit `92d8215a`). + +**Tầng-2 (LỖI LEAD — phần phải RCA):** sau khi dọn đĩa, server VẪN chậm — vì **3 sshd mồ côi do chính các lệnh quét-đo của lead** (`ssh … | head` cắt pipe, process phía Windows-OpenSSH không chết) **bão hòa 3/3 core** → mọi GET detail 5-47s (gotcha **#90**). + +**5-why tầng-2:** (1) Vì sao chậm? — w3wp đói CPU, SQL `ASYNC_NETWORK_IO` chờ client. (2) Vì sao đói? — 3 sshd spin full-core. (3) Vì sao có orphan? — `| head` phía local đóng pipe sớm, ssh client thoát nhưng Windows-OpenSSH **không kill process tree**. (4) Vì sao lead không thấy sớm? — đo bằng `Get-Process` CPU **tích lũy** (thước sai — không phân biệt đang-ăn với đã-từng-ăn); chỉ khi đo **CPU-delta 5s** mới lộ. (5) Vì sao lặp ×3? — mỗi vòng quét mới lại `head`, và lead **không có phản xạ "op này ngắt rồi để lại gì?"** — người chẩn đoán tự làm bẩn hiện trường, rồi đo hiện trường bẩn và kết luận tiếp. + +**Fix:** kill 3 orphan (walk `ParentProcessId` CHỪA ancestor-chain của session mình) → A/059 **47s→0,38s** đo ×2 lượt + inbox 0,12s — executed+verified. **Guard:** luật #90 3 vế (Select-Object -First N phía REMOTE · đợi cold-start ~48s sau recycle · tự hỏi op-để-lại-gì) khắc gotchas + runbook §"Disk-free + orphan guard" + `cicd-monitor` bước 1b; răng thật = cicd-monitor chạy MỖI post-push. + +**Khai thêm 2 nhánh đúng-quy-trình đáng giữ:** lead **đã tự khai** với owner phần lỗi mình gây (không đợi bị bắt); và 2 lần bác-giả-thuyết bằng số (cartesian: đếm đủ 7 bảng mới kết luận · lock-mồ-côi: 0 session ôm transaction) tránh được 2 bản vá sai hướng (AsSplitQuery-vội · recycle-mù). + ## RCA `SCORE-T2-S189` — TẦNG-2 chấm sai PHÉP ĐẾM (2026-08-11, bookstart L15) **Chấm sai — TẦNG NÀO:** **TẦNG-2** (`score-counter`). **Tầng-1 (lead) = 0 lỗi**, khớp 4/4 khoản, số tái lập được (`score-count-auditor` tự chạy lại `nhip-no-probe.ps1`, khớp verbatim; 27/27 dòng `parts=10`, 0 lệch cột). 🔴 **KHÔNG** nhét nhãn *chấm sai* vào `lead_self_audit.flag_classes` — tập ĐÃ NIÊM PHONG `_sealed_P3B_S181`, máy từ chối exit 2.