Files
solution-erp/.claude/agent-memory/cicd-monitor/MEMORY.md
2026-08-01 09:14:40 +07:00

108 lines
23 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CI/CD Monitor Agent — Persistent Memory
- **S166 (08-01) re-verify `15349e8` K3 — PASS 6/6 `[LEAD SEED ON-BEHALF TẠI-CỬA — mày chết #53 ×2 (lượt-1 session-limit 18:50 @S165 sau §0 · lượt-2 S166 sau §1-§5, return "Smoke is clean" mà §6 chưa ghi đĩa); §6 lead đo lại 4 phép on-behalf, KHÔNG tin lời khai suông]`:** Run **#436** success 6m11s (match theo `head_sha`, control neo run kề = #435 K2). Gate **614 = 45D + 569I** khớp TỪNG VẾ (+5 khớp subject K3); control ÂM `grep -c 'Failed!'` = 0. Mig **GIỮ 71** — mục "GIỮ NGUYÊN" ít bit ⇒ răng lấy từ **control DƯƠNG cùng phiên** (§4 seed chứng startup-hook sống). **§4 = khuôn mẫu dự-đoán-trước-khi-đo**: đọc seeder → dự đoán 8wf×3step×11level + finalize 2+8=10 → sqlcmd khớp TỪNG ĐỒNG + shape element-wise từng email + 3-nguồn-độc-lập (source ⟂ sqlcmd ⟂ Serilog `Seeded 8 KHKK group workflows`) + control ÂM (OUTSIDE=2 nguyên · OUTOFSET=0). **§5 FROZEN-eoffice = hệ-quả bắt-buộc dự-đoán TRƯỚC curl** (diff fe-user 14 dòng toàn `//` comment → Vite strip → byte-identical; marker POS `KhkkN[1-8]` admin=1/eoffice=0 + NEG transition PRE-có-POST-mất; ❌ marker `earlyFinalizeLocked` LOẠI vì tên biến local bị minifier đổi = 0 bit). **Recycle-proof 2 nguồn**: PID 5364→**1900** StartTime 18:27:28 + Serilog dòng-đầu 18:27:40 (deploy xoá logs/). Ghi chú route: KHKK = kebab-case `/api/contract-signing-plans` (đề bài lead ghi nhầm không-gạch). Evidence: `runs/2026-07-31-S164-4gd-khkk-fanout/sub-cicd-verify-k3.md` (§6 + END do lead). Tag `[s166, run-436, pass-6-6, du-doan-truoc-khi-do, frozen-la-he-qua, marker-bien-local-0-bit, chet-53-x2-artifact-cuu, seed-on-behalf-tai-cua]`
- **07-31 verify `0779f2d` S164 K2 SP-002 KHKK mang hạng-mục + nhóm duyệt N1-8 (23 file: 9 BE + 8 FE + 3 test + Mig 71 ×3 + 2 ignored-path):** ⚠️ **PARTIAL 5/6** — deploy KHỎE, điểm trừ = **phép đo không có răng**, không phải lỗi ship. Run **#435** (id=548) success **6m12s**. Gate **609 = 45D + 564I** (0 fail/skip) — Domain **GIỮ 45**, Infra 553→**564 = +11** khớp TỪNG VẾ kỳ-vọng "9 lane + 2 gate-fix". Mig 71 `20260731085624_AddKhkkApprovalGroupCatalogAndFinalizeRuntime` TOP prod (MIGCOUNT=71), `sys.tables` **97 GIỮ NGUYÊN** khớp `grep -c CreateTable`=**0** (control dương `grep -c AddColumn`=4). 🔴 **BÀI LỚN NHẤT — ACCEPTANCE RƠI VÀO CHÂN-LÝ-RỖNG**: `ContractSigningPlans` prod **0 dòng cả PRE lẫn POST** ⇒ cả 3 acceptance mục 4 (`COUNT(group=1)==COUNT(*)``0==0`; `NOT BETWEEN 1 AND 8`=0; Σ tiền `NULL==NULL`) đều "xanh" mà **0 bit thông tin** — backfill hụt sẽ trông Y HỆT. Tác giả Mig 71 đã khai bẫy này ở comment ("đo local Σ = tập RỖNG hai phía… phép đo prod mới có răng") — nay prod cũng rỗng nốt ⇒ phép đo prod CŨNG hết răng. KHÔNG hạ FAIL (không có gì sai) nhưng CẤM đọc thành PASS. 🔑 **Đối-sách khi acceptance rỗng → thay bằng bằng-chứng KHÔNG-phụ-thuộc-số-dòng**: (a) 4 cột đúng kiểu/null/default qua `INFORMATION_SCHEMA`; (b) **hoán-đổi index chứng bằng LIỆT KÊ TRỌN HỌ 14 index** — 3-cột unique filtered `([IsDeleted]=(0))` CÓ + 2-cột cũ MẤT, vắng-mặt suy từ danh-sách-đầy-đủ nên là vắng-mặt ĐO ĐƯỢC (mạnh nhất mục 4); (c) Σ bất-biến chứng theo **CẤU TRÚC**: grep cột tiền trên Mig 71 ra 2 hit **đều là comment `//` dòng 23-24** ⇒ 0 câu lệnh thực thi, đúng kể cả khi có dữ liệu; (d) EF materialize 200-không-500. 🔑 **MỚI — chứng query-param ĐƯỢC WIRE bằng bind-error**: `?group=abc`**400** (param lạ mà bị nuốt thì ASP.NET trả 200) + khớp nguồn `Controller:42 [FromQuery(Name="group")] int? group`; `?group=1/2/8` chỉ ra `{"items":[],"total":0}` = VACUOUS. Áp bài K1: soi **thân RAW** trước khi tin parser ⇒ rỗng THẬT. Control DƯƠNG đường-ống list: `/api/catalogs/contract-catalog` **86 item** (seed K1 nguyên) ⇒ ống chạy, `items:[]` là bảng rỗng thật. Bundle **4/4 ROTATE** (admin `CKgq0YlH``DVQEwGwf`, user `CHMCU35C``CZAYiWWa`) + hash CŨ **và hash BỊA** cùng rơi `text/html` 900/876 B ⇒ `text/html` thật sự nghĩa "không tồn tại"; LM 17:10:20/17:11:20 in-window. Smoke: `/health/{ready,live}` 200 + bare `/health` **404** control · no-token **401** kèm **2 route BỊA 404**. w3wp **PID 5364 = `SolutionErp-Api`** (map PID→pool bằng `appcmd list wp` vì VPS có 6 w3wp chung VIETREPORT — CẤM đoán PID) StartTime 17:11:25 + Serilog dòng-đầu `Applying migrations...` 17:11:27 ⇒ mọi đo DB từ 17:12+ = SAU recycle. Quan sát cho K4b: `?group=999`**200** (biên API không chặn ngoài-miền 1..8, hiện vô hại); ghost-window `EndedByLevelFinalize` luôn 0 tới khi K3 land = ĐÚNG hiện trạng. Evidence: `runs/2026-07-31-S164-4gd-khkk-fanout/sub-cicd-verify-k2.md`. Tag `[run-435, partial-5-6, chan-ly-rong-vacuous, liet-ke-tron-ho-index, wire-proof-bang-bind-400, gate-609, mig-71, map-PID-pool]`
- _(**07-31 `50e6d8c` S164 K1 SP-002 danh-mục-loại-HĐ +Mig 70 — PASS 6/6**: run #434 6m31s, gate 598, `sys.tables` 96→97. 🔑 **TAM-GIÁC 3 NGUỒN ĐỘC-LẬP** cho seed (source-parse `DbInitializer` == sqlcmd prod == **API materialize qua EF**, cả 3 ra 86 + phân-bố `1:5·2:10·3:5·4:23·5:14·6:15·7:13·8:1`; nguồn 3 mạnh nhất vì đi trọn Domain→Infra→Api). ⚠️ **BÀI: 0-hit là DƯƠNG-GIẢ tới khi soi format** — grep `Passed!` ra 0 chỉ vì log dùng 2-khoảng-trắng; cùng class parse seeder ra 78 (regex line-anchored, tuple xuống dòng) → vứt số, parse lại 86. ⚠️ **hash CI ≠ hash local build** ⇒ CẤM lấy hash local làm mốc kỳ-vọng prod. verbatim → `archive/2026-07.md` via `_INDEX`)_
- _(**07-30 `bfc7b79` S162 cây-4-folder-GĐ FE-ONLY — PASS 5/5**: run #432 5m35s, gate 590. 🔴 **LUẬT CẤM `HEAD~1`** — Stop-hook WAL auto-commit giữa lúc verify làm `HEAD~1` đổi nghĩa ⇒ tao kết SAI "marker vô giá trị"; đo lại bằng SHA tuyệt đối `X^..X` thì 0 hit ⇒ marker hợp lệ. 🔑 **ĐẾM TRỌN HỌ bằng 1 REGEX** `pipeline-[a-z-]{0,24}index` ra đúng 3 phần tử khớp source element-wise (vừa vế DƯƠNG vừa tự chứng regex không quét bừa). ⚠️ suýt FAIL-OAN: nhãn cũ 8/app sống hợp lệ — control ÂM phải ĐỘC-QUYỀN. verbatim → `runs/2026-07-30-S162-cay-4-folder-gd/sub-cicd-monitor-2.md`)_
- _(**#427 S161 W1 KHKK `a8027c0` +Mig 69 `AddContractSigningPlans` — PASS 6/6**: gate 566, 5m40s; `sys.tables` **89→96** (+7 khớp 7 CreateTable), MenuItems 142 GIỮ. 🔑 **khuôn ĐO-CỜ không đếm ROW** (row tồn tại sẵn ⇒ đếm row = 0-bit; đo PRE/POST **cờ** 7 key 13/0/0/0→13/13/13/2) + 🔑 **control ÂM NỘI-HỌ: `Khkk_G1` bị seeder CỐ Ý bỏ khỏi tập** ⇒ phải đứng yên, và nó đứng yên ⇒ chứng 'đúng NGƯỜI, đúng TẬP'. Đọc seeder **dự đoán `0 added + 91 upgraded` TRƯỚC khi đo → log prod khớp từng đồng**. verbatim → `archive/2026-07.md` substring:"a8027c0` W1 KHKK")_
- _(**#426 S159 đợt-5 `ea962b1` menu 6-leaf + `GET /contracts/deleted` — PASS 9/9**: gate 562. 🔑 **LUẬT gốc control-ÂM-phải-ĐỘC-QUYỀN** (cứu false-fail: chuỗi brief bảo phải vắng vẫn sống hợp lệ ở `aria-label` ⇒ grep repo-wide TRƯỚC; chuỗi dùng-chung chỉ làm marker DƯƠNG). 🔑 **SEEDER-INSERT delta khớp TOÁN**: MenuItems **119→142 = +23 = 7+7+7+2**. Endpoint mới 401 **kèm route bịa 404** ⇒ 401 CÓ NGHĨA. ⚠️ bẫy phạm-vi query: `Ct_*` đẻ thêm 21 key ⇒ `LEFT(MenuKey,3)` không so được với đợt trước. verbatim → `archive/2026-07.md` substring:"#426 S159 đợt-5")_
- _(**S159 arc đợt-1→4 (#422 · #423 · #424 · #425) + S157 #419** — gate 562. 🔴 **#423 PARTIAL 8/9 = phát hiện lớn nhất arc**: `RevokeTemporarilyHiddenModulesAsync` UNGATED lật 447/494 row grant SQL tay; tao sót vì **đo TRƯỚC recycle** → đã nâng thành luật 3-CHÂN-KIỀNG ở 'Recurring bug patterns'; đóng ở #424 (guard 2-vế, 494/494 sống qua restart ×3). 🔑 #425 comment-strip **dự-đoán-trước-rồi-mới-đo**; FROZEN vẫn lành tính nếu LM in-window (#69 mode-B). 🔑 #422 **marker PHÂN-BIỆT-PHIÊN-BẢN** (cặp chữ cũ/mới ⇒ chứng prod chạy bản ĐÃ-FIX); ⚠️ **LỆCH-số phải truy PHẠM-VI-QUERY trước khi hô drift**. 🔑 #419 byte-marker **count = SỐ FILE trong diff**. ⚠️ **ĐÍNH CHÍNH: `#422`/`#425` CHƯA TỪNG vào archive** (mất lớp chưng-cất, KHÔNG mất nguyên-liệu — còn ở `runs/2026-07-29-S159-tong-quan-pipeline-menu/cicd-verify-{b5799fc,a2bbcb9}.md`); **bài: stub tự khai "đã archive" là CLAIM, phải đo mới được viết**. verbatim (#423/#424/#419) → `archive/2026-07.md`)_
- _(**#416 S155 `b1bae77` Workflow duyệt-TẠI-CHỖ +Mig 68 APPLIED-PROD 6/6** [gate 551; FLAG 'mig rớt khỏi commit' loại-trừ 3 lớp — mạnh nhất `GET /approval-workflows-v2` trả cột ×54 **materialize qua EF từ DB thật**] · **#407 S135 `4510cea` mã-dự-án normalize 5/5** [gate 532] · **#399 S134 `6664a61` PE lũy-kế 0-mig 5/5** [gate 528; 🔑 `ct=text/html` discriminator SẠCH HƠN size-919B vì shell-size drift] · **#398 S133 `d4fbced` +Mig 67 budget-freeze 5/5** [gate 520; backfill 18/18; python text=True crash cp1252 → dùng bytes] · verbatim → `archive/2026-07.md` via `_INDEX`)_
> **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).
> Full verbatim run history pre-S40 → git `d2f52ba` + `archive/2026-05-{runs,q2,q3,q4}.md` + `archive/2026-06.md` + `archive/2026-07.md`. 🗺️ **Lookup map: `archive/_INDEX.md`** — 1 dòng/bản-ghi + con-trỏ substring (sha-keyed, Ctrl-F fallback); đọc verbatim + `2026-0{5,6}.gist.md` theo nhu cầu. See 🔄 Curate-trigger log for archive-move history.
---
## 🎯 Role baseline
Read-only CI/CD + post-deploy verifier SOLUTION_ERP. Polls Gitea Actions API, verifies test gate + deploy ship + prod health. Tools: Read, Grep, Glob, Bash, WebFetch + 5 RAG MCP. Output: PASS/FAIL + evidence <500 words. Skills: `iis-deploy-runbook` + `dependency-audit-erp` + `ef-core-migration`. Spawn ~150K trade-off catch fail tự động.
---
## ⚠️ cicd-lane return-garble tracker (#53)
cicd-lane đôi khi trả nhầm slice transcript thay verdict **tao TÁI PHẠM dai nhất roster** (×5/6 qua S114; S117/S118 ×3; all-agents ×66-cận-dưới [[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 đã chứng minh (S158, lần 5): ghi-đĩa TRONG LÚC LÀM** prompt đúng KHÔNG chặn được, artifact mới chặn (vai có-ép mất **0 B**, vai không-ép mất TOÀN BỘ). file evidence trong run-folder SẢN PHẨM CHÍNH, ghi từng mục ngay khi bằng chứng, kết bằng `END … VERDICT=`; return chỉ tóm tắt.
---
## 🚨 Recurring CI/CD bug patterns (catch priority)
- **#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 HEAD~1 HEAD` vs `paths-ignore` **7-entry từ D10 S111 `8aa3869` (#495): `['docs/**','**/*.md','.claude/skills/**','.claude/agent-memory/**','.claude/workflows/runs/**','.gitignore','scripts/**.md']`**. Gitea evaluates push *range* — ≥1 commit có non-ignored file → toàn range build. ⚠️ **AS-6 BASELINE SHIFT: từ #495, memory-push chứa `memory-budget.json`/agent-memory-file/run-trace HẾT trigger CI (pattern #490/#492 DEAD).**
- **#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 thể ROTATE vừa 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-<BỊA>.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 (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.
- **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.
---
## 📋 5-stage checklist (EVERY run)
- **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 **590 PASS** (45 Domain + 545 Infra; run #432 `bfc7b79` 07-30; climb 590/566/562/551/532/528/520/509/505/501/495/486/458/440/421). 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 4.6 (S29 CRITICAL):** sqlcmd seed sample verify post-deploy (NOT chỉ schema). Seed=runtime-row-insert (no history/tables advance) verify via DB-query (e.g. Permissions count), NOT bundle-frozen-alone.
- Discovery: record enum cần input SỐ (SOL không `JsonStringEnumConverter`). sqlcmd ssh Windows-auth `\\\\SQLEXPRESS` 4-backslash. INFRASTRUCTURE seed ungated / DEMO seed gated gotcha #51.
- **Stage 5 Report** PASS/FAIL + evidence + MEMORY update.
---
## ⚠️ Anti-patterns (DO NOT)
1. Push fix code READ only, escalate em main · 2. Speculate fail without log · 3. Skip post-deploy bundle hash (biggest catch) · 4. Skip MEMORY · 5. Poll forever (max 10 iter) · 6. Auto-rollback (escalate + recommend) · 7. Verify docs-only (SKIPPED-DOCS return ngay)
---
## 🧠 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).
- **SSHPS quoting (S42):** nested bashsshpowershell mangles `$var`/`\"` use `iconv UTF-16LE | base64` `powershell -EncodedCommand $B64`; OR write ps1-file + `powershell -File $(cygpath -w)`. sqlcmd over SSH direct `-W -h -1` pw-inline. sys-catalog string-concat `COLLATE DATABASE_DEFAULT` (collation conflict).
- **Tests baseline:** **590 PASS** (45 Domain + 545 Infra · 0 fail/skip; run #432 `bfc7b79` 07-30 đo LIVE web-UI log). 🔑 **Số cụ thể 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 runs+tasks logs đều 404)** mỗi project 1 dòng `Passed! - Failed: 0, Passed: N, Skipped: 0`; đối chiếu TÁCH-PHẦN (45/545) chứ đừng chỉ khớp tổng. Delta test mới = so run trước.
- **Mig latest repo: Mig 69 `AddContractSigningPlans`** (`20260729122015`, S161 7 CreateTable, `sys.tables` 89→**96**, applied-prod #427). Path `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/` (đếm `.cs` excl Designer/Snapshot). Prod check `sqlcmd __EFMigrationsHistory ORDER BY MigrationId DESC`. **`sys.tables`(is_ms_shipped=0)=96** (sau Mig 69; 89 trước đó) AddColumn/data-only KHÔNG đổi số no-new-table commit giữ nguyên, đừng FAIL. **NO-Mig proof = history NOT-advance + empty `git diff <base>..<head> -- '*Migrations*'`.** Bảng mig đầy đủ skill `ef-core-migration` + `docs/STATUS.md`.
- **Bundle hash live:** ship-baseline = run **#432** (07-30 `bfc7b79`) admin js `DXoYDy7R`/css `BZ5F-g1w` · eoffice js `o8KDluD1`/css `yNZDmVNz` (cả 4 rotate, LM in-window). **SNAPSHOT-per-run, KHÔNG phải mốc ổn định** (#69 TWO-MODE) LUÔN re-snapshot LIVE sau status=success; ship-proof = LM-in-window + real-size + marker, KHÔNG phải hash-delta. id run_number (key theo head_sha).
- **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-erp\api\logs\solution-erp-YYYYMMDD.log` (skill-runbook `…\Logs\log-*.txt` STALE). Level=Information (seed/revoke lines ghi). deploy xoá luôn `logs/` chỉ còn từ deploy cuối. Grep revoke-line + seeder-line cùng-giây để audit seed-race. *(S162: bản cũ dòng này chứa byte BEL 0x07 do `\a` bị nuốt — đã sửa.)*
## 🔑 Critical config (flag commit nếu tái xuất)
Node CI `20.x` (`feedback_node_cicd`) · MediatR `12.4.1` (gotcha #1, flag `Version="14`) · Swashbuckle `6.9.0` (gotcha #2) · act_runner manual checkout (#39) · npm cache DISABLED (#40, flag `cache: npm`)
---
## 🎯 Per-NV admin opt-in wire — 10-point checklist (cumulative S22→S23)
Cross-ref `feedback_per_nv_permission_scope` (10 điểm đầy đủ đó). 2 điểm HAY SÓT nhất: **(9) Lookup discrimination** (`FirstOrDefault` thêm `ApproverUserId==actorId` + admin fallback) · **(10) Controller body record count == Command record count**. Miss 9-10 bug prod im-lặng 2-3 ngày. Sau refactor OR-of-N: `grep -n "FirstOrDefault.*Order.*==" *.cs`.
## 📊 Run stats baseline
BE (test+build) ~90s · FE × 2 ~60s/app · deploy ~30s · **total ~3min code / 0s docs-only**. >5min → escalate. Recent runs all ~4m40-5m.
---
## 📅 Recent runs (compressed — verbatim → archive via `_INDEX.md`)
- _(**S98-S132 #383-#397/#485-#496 all GO** (gate 440→509): **AS-6 #41 soi TỪNG file vs 7-glob** (`.ps1`/`.json` thoát `scripts/**.md` ⇒ vẫn trigger CI) · `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'** · Mig 63-67 applied-prod · verbatim → `archive/2026-07.md`)_
- _(**PASS-pattern library S69b→S104 #307-#494** all GO — recipe: **SCHEMA-ADD** [AddColumn×N-exact + table-count-invariant + DTO-live] · **DATA-BACKFILL** [Mig-top==HEAD + rows-flip-EXACT + control-untouched + derived-API-live] · **DATA-SEED** [pre/post row-delta + DLL-mtime + w3wp-recycle]; **#472 PROD Identity table NON-prefix `Roles`** · verbatim → git + `2026-06/07.md`)_
- **Older (S83 #325 ← S29 #232) → git `d2f52ba` + `archive/2026-06.md`** (incl #291 06-16 FAIL [gotcha #65]); pre-S38 → `archive/2026-05-*`.
---
## 🔄 Curate trigger
- >~30KB → archive recent runs → L2 `archive/<period>.md` (byte-exact append) + `_INDEX.md` substring pointer. Dup failure patterns → merge. Stale >3mo → remove.
- **Last curate: 2026-07-30 S162 (self #432-run: 26.9KB→<17.1KB #427+#426 verbatim`archive/2026-07.md` moved-not-cut; #422/#423/#424/#425/#419 merged-stub; baselines gate 562590 + bundle + Mig 69). **0-BYTE NEAR-MISS**: `io.open(mem,'w')` TRUNCATE trước khi `UnicodeEncodeError` (emoji viết bằng cặp surrogate `\ud83d\udd34`) file về 0 B; cứu bằng `git checkout HEAD --`. **LUẬT: script sửa MEMORY phải build chuỗi XONG → ghi temp → `os.replace`, và verify byte>0 ngay sau.**
- **Prev:** S118 07-14 (#387/#384/#383/#496/#495archive) · S111 20.517 · S107 20.416.9 · S104 20.517.9 · S98 19.917.06 (`2026-07.md` NEW) · S80 38.816 · S70 65.223.2 · S66 · S40.