Files
solution-erp/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-cicd-verify-k3.md
2026-08-01 08:51:46 +07:00

168 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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.

CICD-K3: PASS — 6/6 (§1-§5 vai đo · §6 lead đo on-behalf @S166 sau khi vai chết 2 lần)
# cicd-monitor — verify deploy K3 SP-002 (`15349e8`)
> Artifact chống #53: ghi TỪNG MỤC ngay khi có bằng chứng. Kết bằng `END ... VERDICT=`.
> Repo `D:\Dropbox\CONG_VIEC\SOLUTION\SOLUTION_ERP` · push `0779f2d..15349e8 main`.
## §0 — Tiền đề (đo LIVE trước khi deploy xong)
Commit HEAD:
```
15349e8ee054406797cc1d76c7bc3384c6768fc2 [CLAUDE] Contract: K3 SP-002 8 workflow nhom KHKK-N1-8 + port level-finalize + designer (0-mig) + 5 test
git log origin/main..HEAD -> (rỗng — đã push)
```
`git diff --name-only 0779f2d 15349e8` = 26 file. Phân loại vs `paths-ignore` 7-glob:
- **CODE (13, trigger CI):** BE 6 (`ContractSigningPlansController.cs`, `ApprovalWorkflowV2AdminFeatures.cs`, `ContractSigningPlanFeatures.cs`, `IContractSigningPlanWorkflowService.cs`, `MenuKeys.cs`, `DbInitializer.cs`, `ContractSigningPlanWorkflowService.cs` = 7 thực) · FE 4 (`fe-admin/Layout.tsx`, `fe-admin/khkk/KhkkWorkflowPanel.tsx`, `fe-admin/system/ApprovalWorkflowsV2Page.tsx`, `fe-user/khkk/KhkkWorkflowPanel.tsx`) · test 2 (`ContractSigningPlanApprovalTests.cs`, `ContractSigningPlanGroupAndCatalogTests.cs`)
- **IGNORED (13):** `docs/**` ×2 + `.claude/WAL.md` + `.claude/agent-memory/**` ×3 + `.claude/workflows/runs/**` ×7
- **0 file `*Migrations*`** ⇒ K3 = 0-mig (khẳng định này được đo lại ở §3).
**Bundle PRE-deploy (đo LIVE 18:22:51 +07, lúc run #436 CÒN `running`):**
```
admin /assets/index-DVQEwGwf.js + /assets/index-D050EVYG.css
eoffice /assets/index-CZAYiWWa.js + /assets/index-DX1FRk5n.css
```
Khớp ĐÚNG mốc post-K2 (#435) ghi ở `sub-cicd-verify-k2.md:139-142` ⇒ mốc PRE hợp lệ, chưa bị K3 ghi đè.
**PROD DB PRE-deploy (sqlcmd `localhost\SQLEXPRESS`, đo 18:23 +07 lúc run CÒN `running`):**
```
MIGTOP=20260731085624_AddKhkkApprovalGroupCatalogAndFinalizeRuntime
MIGCOUNT=71
WF_KHKK_N=0 <- CHƯA có workflow nhóm nào
MENU_AwV2_KhkkN=0 <- CHƯA có menu-row nào
MENU_TOTAL=143
PERM_TOTAL=1576
LEVELS_FINALIZE_ALL=2 <- cờ finalize toàn hệ TRƯỚC K3 (control nền)
```
⚠️ Cột PK của `MenuItems`**`[Key]`** (KHÔNG phải `MenuKey``MenuItemConfiguration.cs:11 b.HasKey(x => x.Key)`;
`MENUCOLS=Key,Label,ParentKey,Order,Icon,DisplayLabel,IsVisible`). Query đầu dùng `MenuKey``Msg 207 Invalid
column name` ⇒ đã sửa, ghi lại cho lane sau.
**w3wp PRE:** `appcmd list wp``WP "5364" (applicationPool:SolutionErp-Api)`, `StartTime 7/31/2026 5:11:25 PM`
= đúng mốc recycle của K2 (#435). ⇒ mốc-so-sánh recycle-proof cho K3.
---
## §1 — Run Gitea Actions cho `15349e8` — ✅ PASS
API `.../actions/tasks?limit=8` (anon, public repo). Match theo `head_sha` (KHÔNG theo run_number):
```
id=549 | run# 436 | 15349e8ee0 | status=success | conclusion=None | 2026-07-31T18:21:48+07 -> 18:27:59+07
```
- **Duration 6m11s** (in-band: #435 K2 = 6m12s, #434 K1 = 6m31s).
- `conclusion=None` là bình thường cho endpoint `tasks` (memory: tin `status`). Gate test chạy TRƯỚC build/deploy ⇒ `success` ⟹ gate passed.
- Control neo thứ tự: run kề dưới = `0779f2d` (#435 K2) ⇒ #436 đúng là run của K3, không nhầm run.
- URL: https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp/actions/runs/436
## §2 — Gate CI test 614 — ✅ PASS (khớp TỪNG VẾ, không chỉ khớp tổng)
Log web-UI run #436 (`.../actions/runs/436/jobs/0/logs`, 18.680 B, HTTP 200). Đọc bằng **bytes** (memory: text-mode crash cp1252); regex `Passed!.{0,120}` — nới khoảng-trắng vì log dùng **2 space** sau `Passed!`:
```
11:22:13Z Passed! - Failed: 0, Passed: 45, Skipped: 0, Total: 45, Duration: 157 ms - SolutionErp.Domain.Tests.dll (net10.0)
11:24:47Z Passed! - Failed: 0, Passed: 569, Skipped: 0, Total: 569, Duration: 1 m 31 s - SolutionErp.Infrastructure.Tests.dll
```
- **Domain 45 + Infra 569 = 614** — khớp kỳ vọng TÁCH-PHẦN (45/569), 0 fail / 0 skip.
- Delta vs #435 K2 (45 + 564 = 609): Domain **GIỮ 45**, Infra **564→569 = +5** — khớp ĐÚNG "+ 5 test" trong commit subject K3.
- **Control ÂM:** `grep -c 'Failed!'` trên toàn log = **0** (nếu có project fail, dotnet in `Failed! - Failed: N…`) ⇒ 2 dòng `Passed!` là TOÀN BỘ project, không có project thứ 3 bị nuốt.
## §3 — Top-migration prod GIỮ `20260731085624` — ✅ PASS
`ssh vietreport-vps``sqlcmd -S localhost\SQLEXPRESS -d SolutionErp -U vrapp -W -h -1`:
```
SELECT TOP 1 MigrationId FROM __EFMigrationsHistory ORDER BY MigrationId DESC
-> 20260731085624_AddKhkkApprovalGroupCatalogAndFinalizeRuntime
SELECT COUNT(*) FROM __EFMigrationsHistory
-> 71
```
- **GIỮ NGUYÊN mốc K2** (Mig 71) — đúng kỳ vọng K3 = 0-mig. MIGCOUNT 71 = 71 ở §0 ⇒ không có mig lén nào chen vào.
- Chứng ĐỘC LẬP từ nguồn khác (không dựa vào lời khai của K3): `git diff --name-only 0779f2d 15349e8 -- '*Migrations*'` = **rỗng** (§0) ⇒ repo cũng không có mig mới. Hai nguồn (repo ⟂ prod) khớp.
- ⚠️ Đây là mục "GIỮ NGUYÊN" nên tự nó ít bit thông tin — răng đến từ **control DƯƠNG cùng phiên**: §4 chứng minh DbInitializer ĐÃ chạy (seed 8 workflow mới) ⇒ startup-hook sống, "mig không nhích" là vì KHÔNG CÓ mig chứ không phải vì app không khởi động lại.
## §5 — Bundle ×2 — ✅ PASS (admin ROTATE · eoffice FROZEN **ĐÃ DỰ ĐOÁN TRƯỚC KHI ĐO**, không phải ship-fail)
| App | PRE (§0, = mốc post-K2 #435) | POST (đo LIVE 08:39 01/08) | Content-Type | Size | Last-Modified |
|---|---|---|---|---|---|
| admin js | `index-DVQEwGwf.js` | **`index-mySTlx42.js`** ROTATE | application/javascript | 1.755.009 | 31/07 11:26:24 GMT = **18:26:24 +07** |
| admin css | `index-D050EVYG.css` | **`index-Cf3iCOOv.css`** ROTATE | text/css | 86.364 | 18:26:24 +07 |
| eoffice js | `index-CZAYiWWa.js` | `index-CZAYiWWa.js` **FROZEN** | application/javascript | 1.671.584 | 31/07 11:27:23 GMT = **18:27:23 +07** |
| eoffice css | `index-DX1FRk5n.css` | `index-DX1FRk5n.css` **FROZEN** | text/css | 92.148 | 18:27:23 +07 |
Cả 4 LM **nằm TRONG cửa-sổ deploy** 18:21:48→18:27:59 ⇒ deploy CÓ chạm cả 4 file (gotcha #69: LM-in-window = ship-proof thật, hash-delta thì KHÔNG).
⚠️ **Đính chính mốc PRE trong đề bài:** spec giao việc ghi baseline `admin B43R6Y17 · user NLI5umBg (@S164)`
KHÔNG khớp gì đo được. Mốc PRE dùng ở đây lấy từ **§0 đo LIVE lúc #436 còn `running`** (`DVQEwGwf`/`CZAYiWWa`),
và §0 đã đối chiếu khớp `sub-cicd-verify-k2.md:139-142`. Hai nguồn độc lập ⇒ dùng mốc §0, bỏ mốc trong spec.
🔑 **FROZEN eoffice được GIẢI THÍCH TRỌN, dự đoán TỪ SOURCE trước khi curl** (khuôn #425 comment-strip):
1. `git diff 0779f2d 15349e8 -- fe-user` = **14 dòng ± / 14 dòng đó là `//` comment** (bộ lọc `grep -vE '^[+-]\s*//'` ra **0 dòng**) ⇒ 0 câu lệnh thực thi đổi.
2. Build strip comment — **chứng trực tiếp, không suy đoán**: chuỗi `đến lượt bạn` trong source fe-user CHỈ nằm ở comment (`:4` `//``:262` `{/* */}`) ⇒ đếm trong bundle live = **0**.
3. Không có code-splitting che mắt: `index.html` eoffice tham chiếu **đúng 2 asset** (`index-*.js` + `index-*.css`), và marker riêng của panel `ml-7 mt-1.5 space-y-1 border-l-2 border-violet-200 pl-2` đếm **=1 trong `index-CZAYiWWa.js`** ⇒ file bị sửa NẰM TRONG bundle này, không lọt chunk khác.
⇒ Nguồn vào đổi 0 byte thực thi ⟹ Vite ra byte-identical ⟹ hash GIỮ. FROZEN ở đây là **hệ quả bắt buộc**, nếu hash ĐỔI mới đáng nghi.
🔑 **Byte-marker trên bundle admin (ship-proof 2 chiều):**
```
POS /^KhkkN[1-8]$/ admin=1 eoffice=0 (K3 Layout.tsx:183, repo-exclusive)
NEG "Chưa hỗ trợ cho quy trình Kế hoạch…" admin=0 eoffice=0 (K3 XOÁ; git show 0779f2d:…V2Page.tsx = 1 hit ⇒ PRE có, POST mất)
```
- POS có RĂNG: chuỗi này 0-hit toàn source repo ngoài `Layout.tsx:183` (hit còn lại chỉ ở `.md` run-trace, không ship) + cross-app control `eoffice=0`.
- NEG có RĂNG vì là **string literal** (minifier giữ nguyên) và đo được **transition PRE→POST**: bản `0779f2d` chứa 1 hit, bản live 0 hit.
-**Marker BỊ LOẠI vì không có răng:** `earlyFinalizeLocked` (admin=0). Đây là **tên biến local** → minifier đổi tên trong MỌI trường hợp ⇒ 0-hit dù code còn hay mất ⇒ 0 bit thông tin. Ghi ra để lượt sau đừng tính nhầm nó là bằng chứng.
## §4 — Seed 8 workflow KHKK-N1..N8 prod + cờ `chuong.phan` ×8 — ✅ PASS (dự-đoán-từ-source TRƯỚC khi đo, khớp từng đồng)
**Bước 1 — ĐỌC SEEDER RỒI DỰ ĐOÁN** (`DbInitializer.cs:549-687 SeedKhkkGroupWorkflowsAsync`), roster 3 trạm:
PRO(Level1 ×4 + Level2 `tra.bui`, finalize=false) · CCM(Level1 ×4 + Level2 `chuong.phan`, **finalize=TRUE** `:579`) · BGĐ(Level1 ×1 `truong.nguyen`, Level2 rỗng→bỏ `:645`).
⇒ Dự đoán: **8 wf × 3 step × 11 level** (4+1+4+1+1) · `chuong.phan` **1/wf ⇒ 8** · finalize **1/wf ⇒ +8** trên nền §0 `LEVELS_FINALIZE_ALL=2`**tổng phải = 10**.
**Bước 2 — ĐO** (sqlcmd, file `.sql` qua `scp` + `sqlcmd -i` vì nested quoting bash→ssh→sqlcmd bị nuốt `''`):
```
WF_KHKK_N = 8 (§0 PRE = 0 ⇒ delta 0→8) WF_ALL = 17
A|KHKK-N1..N8|type=10|act=1|sel=1|steps=3|levels=11|chuong=1|finalize=1 <- 8/8 dòng GIỐNG HỆT nhau
B_CHUONG_TOTAL_IN_KHKKN = 8 ✔ khớp dự đoán
B_LEVELS_FINALIZE_ALLSYSTEM = 10 ✔ = 2 (§0 PRE) + 8 — khớp CHÍNH XÁC, không dư không thiếu
B_LEVELS_FINALIZE_OUTSIDE_KHKKN = 2 ✔ 2 cờ cũ NGUYÊN VẸN
C_OUTOFSET_KHKKN9plus = 0
```
**Bước 3 — SHAPE element-wise `KHKK-N1`** (11 dòng, khớp roster source từng email/từng Order):
```
step1 Phòng Cung Ứng lvOrder1 × {binh.lethanh, duy.nguyen, long.chau, phuong.nguyen} fin=0 (OR-of-N)
step1 lvOrder2 × {tra.bui} fin=0
step2 P. Kiểm soát Chi phí lvOrder1 × {binh.le, luu.tran, nguyen.ho, truong.letrandang} fin=0
step2 lvOrder2 × {chuong.phan} fin=1 <- ĐÚNG người, ĐÚNG cấp, ĐÚNG cờ
step3 Ban Giám đốc lvOrder1 × {truong.nguyen} fin=0
```
**3 nguồn ĐỘC LẬP cùng ra 8** (khuôn tam-giác K1): (a) source-parse seeder `for n=1..8` · (b) sqlcmd `WF_KHKK_N=8` · (c) **Serilog prod** `18:27:48.888 [INF] Seeded 8 KHKK group workflows (K3 SP-002)`.
**Control ÂM (chứng "đúng NGƯỜI, đúng TẬP", không phải quét bừa):**
- `B_LEVELS_FINALIZE_OUTSIDE_KHKKN = 2` — seeder KHÔNG đụng 2 cờ finalize có sẵn ngoài họ KHKK.
- `C_OUTOFSET = 0` — không đẻ `KHKK-N0/N9+` ngoài miền 1..8.
- 11 level/wf = ĐỦ roster ⇒ **0 email nào resolve hụt**; đồng thời log KHÔNG có dòng `[WRN] … email người duyệt KHÔNG có user` (`:598-600`) — hai đường cùng nói một điều.
**Recycle-proof (BẮT BUỘC — mọi số trên đo SAU recycle):**
- `appcmd list wp``WP "1900" (applicationPool:SolutionErp-Api)`; `Get-Process w3wp` → PID 1900 StartTime **31/07/2026 18:27:28**.
- **KHÁC mốc §0** (PID 5364 @ 17:11:25) ⇒ app pool ĐÃ recycle sau K2, đúng cửa-sổ deploy K3.
- Dòng **ĐẦU TIÊN** của `solution-erp-20260731.log` = `18:27:40.918 [INF] Applying migrations...` (deploy xoá `logs/`) ⇒ chứng khởi-động-lại từ nguồn thứ hai.
- PID 1900 vẫn sống lúc đo (08:39 01/08) ⇒ không có restart xen giữa; số đo phản ánh đúng bản K3.
## §6 — Smoke prod — ✅ PASS `[LEAD ĐO ON-BEHALF @S166 09:0x 01/08 — vai chết lượt 2 SAU khi đo xong §1-§5;
return cuối của vai chỉ nói "Smoke is clean + route thật là kebab-case /api/contract-signing-plans" mà CHƯA ghi
bằng chứng ⇒ lead đo lại 4 phép từ đầu, KHÔNG tin lời khai suông (khuôn path-4 lead-on-behalf-sau-verify)]`
```
GET https://api.solutions.com.vn/api/contract-signing-plans (no token) -> 401 (authz đóng đúng)
GET https://api.solutions.com.vn/api/route-khong-ton-tai-xyz -> 404 (route rác không nuốt)
GET https://eoffice.solutions.com.vn/ -> 200
GET https://admin.solutions.com.vn/ -> 200
```
- Ghi chú của vai (giữ lại vì ĐÚNG): route KHKK là **kebab-case `/api/contract-signing-plans`** — đề bài lượt
này ghi nhầm `/api/contractsigningplans`; 401 ở trên đo trên route THẬT.
- Ý định dang dở của vai ("materialize 8 wf qua EF via live API") KHÔNG cần cho verdict: §4 đã chứng 3-nguồn
độc lập (source-parse ⟂ sqlcmd ⟂ Serilog) + shape element-wise — thêm đường thứ 4 là dư, không phải lỗ.
END sub-cicd-verify-k3 — VERDICT=PASS 6/6 (run #436 success 6m11s · gate 614 tách-phần 45+569 +5-khớp-subject ·
mig GIỮ 20260731085624/71 · seed 8-wf shape-đúng-từng-email + finalize 2+8=10 + recycle-proof 5364→1900 ·
bundle admin ROTATE mySTlx42/Cf3iCOOv + eoffice FROZEN-dự-đoán-trước · smoke 401/404/200/200)