15 KiB
cicd-monitor — verify deploy a8027c0 (W1 KHKK, Mig 69 ContractSigningPlans)
Agent: cicd-monitor (READ-only) · S161 · 2026-07-29 Spec acceptance:
.claude/workflows/runs/2026-07-29-S160-khkk-dryrun-plan/spec-wave-w1-schema-designer-29-07-2026.md§③-B Ghi-đĩa-TRONG-lúc-làm (chống #53). Mỗi mục ghi NGAY khi có bằng chứng. Kết bằng dòngEND … VERDICT=.
CHÂN 0 — Push + path-filter (pre-condition)
| Mục | Giá trị |
|---|---|
| Commit | a8027c04fe929750998f5c20e23854f9d0e7a765 |
| Subject | [CLAUDE] Domain+App+Infra+Admin: W1 KHKK — Mig 69 ContractSigningPlans 7 bang + policy KeHoachKyKet + seeder UPGRADE-if-exists + CodeGen KHKK + Designer type-10 |
| Commit time | 2026-07-29 20:04:39 +0700 |
git log origin/main..HEAD |
rỗng ⇒ đã push |
| Files changed | 30 file, +8987 / −22 |
| Path-filter (#41) | 29/30 file NON-ignored (src/Backend/**, fe-admin/src/**, tests/**); chỉ docs/STATUS.md bị ignore ⇒ CI PHẢI trigger ✅ |
Mig 69 on-disk (3-file rule OK):
20260729122015_AddContractSigningPlans.cs(372 dòng) +.Designer.cs(6960) +ApplicationDbContextModelSnapshot.cs(+550)grep -c migrationBuilder.CreateTable= 7 — đúng 7 bảng, 0 ALTER bảng cũ:ContractSigningPlans·…Lines·…DossierItems·…LevelOpinions·…Attachments·…Changelogs·…Approvals
Byte-marker (chân 4) — kiểm ĐỘC QUYỀN TRƯỚC khi đo (luật S159):
rg QT-KHKK-V2-001toàn repo: 1 hit code =fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx:170([TYPE_INT_CONTRACT_SIGNING_PLAN]: 'QT-KHKK-V2-001',trongDEFAULT_CODE_BY_TYPE)fe-user/src= 0 hit ⇒ chuỗi ĐỘC QUYỀN cho fe-admin, sinh mới @S161- ⟹ Dự đoán TRƯỚC khi đo: admin bundle mới = 1 occurrence · user bundle = 0 · admin bundle CŨ (
D0sXA0fe) = 0.
BASELINE PRE-DEPLOY (đo 20:08:31 +0700, TRƯỚC khi run #427 xong)
| Chỉ số | Giá trị pre-deploy |
|---|---|
sys.tables WHERE is_ms_shipped=0 |
89 |
MAX(MigrationId) |
20260727033522_AddPeAllowApproverDelete (Mig 68) |
COUNT(*) MenuItems |
142 |
bảng ContractSigningPlan% |
0 |
| admin bundle | js index-D0sXA0fe.js · css index-DWDbm5As.css |
| user bundle | js index-Y6dW_5CM.js · css index-6YIAufJR.css |
Kỳ vọng post-deploy: tables 89 → 96 (+7) · MIGTOP → 20260729122015_AddContractSigningPlans · MenuItems 142 GIỮ · CSP tables 0 → 7.
Baseline CỜ policy (chân 5) — đo 20:09, TRƯỚC deploy
13 role prod. 8 key họ KHKK, tất cả = rows=13 · R=13 · C=0 · U=0 · D=0:
KeHoachKyKet · Khkk_G1 · Khkk_WfView · Khkk_List · Khkk_Create · Khkk_Pending · Khkk_Approved · Khkk_Deleted
⟹ xác nhận tiền-đề F-S1: row ĐÃ có sẵn CanRead-only ⇒ đếm ROW = 0-bit, chỉ CỜ mới chứng UPGRADE ăn.
DỰ ĐOÁN TRƯỚC KHI ĐO (đọc seeder DbInitializer.SeedKeHoachKyKetAccessAsync trong diff, KHÔNG đọc kết quả)
Tập key seeder đụng = 7 (:2405+): KeHoachKyKet + Khkk_{WfView,List,Create,Pending,Approved,Deleted}.
🔑 Khkk_G1 CỐ Ý NGOÀI tập (comment: "group điều hướng — KHÔNG cần cờ ghi") ⟹ control ÂM nội-họ: nếu Khkk_G1 cũng bật C/U thì phép đo của tao vô nghĩa (chứng tỏ có thứ khác lật bừa cả họ).
| Đại lượng | Dự đoán |
|---|---|
7 key đích: SUM(CanCreate) |
13 mỗi key (13/13 role) |
7 key đích: SUM(CanUpdate) |
13 mỗi key |
7 key đích: SUM(CanDelete) |
2 mỗi key (CHỈ Admin + Drafter) — chiều ÂM: 11 role còn lại phải =0 |
Khkk_G1 (control ÂM) |
C=0 · U=0 · D=0 GIỮ NGUYÊN |
| Log seeder | 0 added + 91 upgraded (91 = 7 key × 13 role, mọi row tồn tại sẵn) |
| MenuItems | 142 GIỮ (seeder không thêm menu-row; KeHoachKyKet menu-row đã có từ S159) |
CHÂN 1 — CI run
| Mục | Giá trị |
|---|---|
| Run | #427 (task id 540) |
| head_sha | a8027c04 ✅ khớp (key theo sha, KHÔNG theo run_number) |
| created_at | 2026-07-29T20:04:48+07:00 |
| Kết luận | status=success @2026-07-29T20:10:28+07:00 |
| Duration | 5m40s (20:04:48 → 20:10:28) — trong dải ~5m30-6m gần đây |
| Run URL | https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp/actions/runs/427 |
Test gate (log web-UI …/actions/runs/427/jobs/0/logs, 17.936 B): 2 dòng Passed!, đối chiếu TÁCH-PHẦN:
Passed! - Failed: 0, Passed: 45, Skipped: 0, Total: 45 <- SolutionErp.Domain.Tests
Passed! - Failed: 0, Passed: 521, Skipped: 0, Total: 521 <- SolutionErp.Infrastructure.Tests
⟹ 566 PASS (45 D + 521 I), 0 fail, 0 skip = ĐÚNG con số spec chờ (562 → 566, +4 test mới W1). ✅
Dòng deploy (cuối log, giờ UTC trong log = +07 −7h):
2026-07-29T13:10:05.8595528Z Deploy done. App pool started.
2026-07-29T13:10:19.7741482Z API /health/live -> 200
✅ CHÂN 1 = PASS
CHỨNG MINH RESTART THẬT (điều kiện tiên quyết của chân 2/3/5 — luật 3-chân-kiềng)
| Bằng chứng | Giá trị |
|---|---|
w3wp process mới |
pid 4980 · StartTime = 2026-07-29 20:10:05 — TRÙNG KHỚP dòng CI App pool started 20:10:05 |
SolutionErp.Api.dll mtime |
2026-07-29 20:07:50 — trong cửa-sổ deploy (20:04:48–20:10:28) ⇒ file BE thật sự bị ghi đè |
⟹ Mọi số đo DB dưới đây lấy lúc 20:11+, tức SAU mốc recycle 20:10:05 ⇒ hợp lệ (tránh bẫy đo-trước-recycle S159 đợt-2).
CHÂN 2 — Mig 69 áp prod
| Chỉ số | Pre | Post | Kỳ vọng | Kết quả |
|---|---|---|---|---|
MAX(MigrationId) |
20260727033522_AddPeAllowApproverDelete |
20260729122015_AddContractSigningPlans |
= Mig 69 | ✅ |
COUNT(*) __EFMigrationsHistory |
68 | 69 | 69 | ✅ |
sys.tables WHERE is_ms_shipped=0 |
89 | 96 | 89→96 (+7) | ✅ khớp TOÁN |
bảng ContractSigningPlan% |
0 | 7 | 7 | ✅ |
7 bảng đếm đích danh (khớp 1:1 với 7 CreateTable trong file mig):
ContractSigningPlans · …Lines · …DossierItems · …LevelOpinions · …Attachments · …Changelogs · …Approvals
✅ CHÂN 2 = PASS (bằng chứng sqlcmd prod trực tiếp, không phải suy từ CI-log)
CHÂN 3 — Menu KHÔNG đổi
COUNT(*) MenuItems: pre 142 → post 142 ⇒ GIỮ NGUYÊN, 0 row thêm ✅
(W1 chỉ đụng policy-key KeHoachKyKet trong MenuKeys.All, menu-row Khkk_* đã seed từ S159 — đúng Δ-1 của spec.)
✅ CHÂN 3 = PASS
CHÂN 6 (phần 1) — 3 domain sống
| URL | HTTP |
|---|---|
https://api.solutions.com.vn/health/live |
200 ✅ |
https://admin.solutions.com.vn/ |
200 ✅ |
https://eoffice.solutions.com.vn/ |
200 ✅ |
(đo 20:11:30; lưu ý /health trần = 404 UNMAPPED — đường thật là /health/live + /health/ready)
CHÂN 4 — Bundle byte-marker (#77) + 3 tầng control ÂM
| App | Pre-deploy | Post-deploy | Trạng thái |
|---|---|---|---|
| admin js | index-D0sXA0fe.js |
index-M8SDA7Ps.js |
🔄 ROTATE |
| admin css | index-DWDbm5As.css |
index-C3xzT0H9.css |
🔄 ROTATE |
| user js | index-Y6dW_5CM.js |
index-Y6dW_5CM.js |
⏸️ FROZEN — lành tính (xem dưới) |
| user css | index-6YIAufJR.css |
index-6YIAufJR.css |
⏸️ FROZEN — lành tính |
Ship-proof THẬT (không dựa hash-delta — #69 TWO-MODE):
| Bằng chứng | admin js MỚI | user js (frozen) |
|---|---|---|
Content-Type |
application/javascript |
application/javascript |
| Size thật | 1.679.452 B | 1.595.577 B |
Last-Modified |
Wed, 29 Jul 2026 13:09:05 GMT = 20:09:05 +07 | 13:10:00 GMT = 20:10:00 +07 |
| Trong cửa-sổ deploy (20:04:48–20:10:28)? | ✅ | ✅ |
⟹ user FROZEN + LM TRONG cửa-sổ = re-ship byte-identical (#69 mode-B), KHÔNG phải bundle-cũ-kẹt. Đúng kỳ vọng: commit chỉ đụng fe-admin/src, fe-user 0 file.
Byte-marker QT-KHKK-V2-001 — đo SAU khi đã chứng ĐỘC QUYỀN:
| Phép | Dự đoán (ghi trước) | Đo được | KL |
|---|---|---|---|
| admin bundle MỚI | 1 | 1 | ✅ khớp SỐ FILE nguồn (1 site ApprovalWorkflowsV2Page.tsx:170) |
| user bundle (control ÂM cross-app) | 0 | 0 | ✅ chứng marker không "khớp bừa" |
admin bundle CŨ D0sXA0fe (control ÂM tầng-2) |
biến mất | http=200 ct=text/html 900 B |
✅ bundle cũ đã bị GHI ĐÈ khỏi đĩa |
hash BỊA ZZZZZZZZ (mốc so trap SPA-fallback) |
text/html | http=200 ct=text/html 900 B |
✅ trùng khít mốc ⇒ 200 của hash-cũ là fallback, KHÔNG phải file thật |
✅ CHÂN 4 = PASS (3 tầng control ÂM đều nói cùng một chuyện)
CHÂN 5 — ĐO CỜ policy-grant (F-04 / F-1 — vế acceptance đang treo)
Đo SAU recycle 20:10:05. Đối chiếu PRE → POST, mọi key 13 role:
| MenuKey | PRE (R/C/U/D) | POST (R/C/U/D) | Dự đoán | KL |
|---|---|---|---|---|
KeHoachKyKet |
13/0/0/0 | 13/13/13/2 | 13/13/2 | ✅ |
Khkk_WfView |
13/0/0/0 | 13/13/13/2 | 13/13/2 | ✅ |
Khkk_List |
13/0/0/0 | 13/13/13/2 | 13/13/2 | ✅ |
Khkk_Create |
13/0/0/0 | 13/13/13/2 | 13/13/2 | ✅ |
Khkk_Pending |
13/0/0/0 | 13/13/13/2 | 13/13/2 | ✅ |
Khkk_Approved |
13/0/0/0 | 13/13/13/2 | 13/13/2 | ✅ |
Khkk_Deleted |
13/0/0/0 | 13/13/13/2 | 13/13/2 | ✅ |
🔑 Khkk_G1 (control ÂM nội-họ) |
13/0/0/0 | 13/0/0/0 | KHÔNG đổi | ✅ |
Vế DƯƠNG (role non-Admin): Drafter trên cả 7 key đích = C=1 · U=1 · D=1 ⇒ thoả acceptance "≥1 role non-Admin có CanCreate=1 ∧ CanUpdate=1".
Vế ÂM (CanDelete chỉ Drafter/Admin): liệt kê TOÀN BỘ row có CanDelete=1 trong họ KHKK = đúng 14 row = {Admin, Drafter} × 7 key, không thừa 1 role nào ⇒ 11 role còn lại D=0. ✅
🔑 Vì sao Khkk_G1 là control ÂM có RĂNG: seeder liệt kê tay 7 key và cố ý bỏ Khkk_G1 ("group điều hướng — KHÔNG cần cờ ghi"). Nếu Khkk_G1 cũng bật C/U thì phép đo vô nghĩa (chứng tỏ có thứ khác lật bừa cả họ). Nó đứng yên ⇒ đúng NGƯỜI làm, đúng TẬP.
Chứng độc lập từ log API prod (C:\inetpub\solution-erp\api\logs\solution-erp-20260729.log), khớp con số tao ghi TRƯỚC khi đo:
2026-07-29 20:10:18.902 +07:00 [INF] Seeded KeHoachKyKet access: 0 added + 91 upgraded
(W1 GD2 golive - 13/13 role C+U, Delete=Drafter/Admin; UPGRADE-if-exists F-S1)
0 added + 91 upgraded — 91 = 7 key × 13 role, khớp từng đồng dự đoán. added=0 ⇒ đúng đường UPGRADE-if-exists, KHÔNG insert (F-S1 đóng: nhánh insert-NO-OP đã bị tránh). Timestamp 20:10:18 nằm SAU restart 20:10:05 ⇒ seeder chạy trong lần khởi động này, và chạy SAU revoke (đúng thứ tự SeedAsync).
✅ CHÂN 5 = PASS — đo CỜ (không đếm ROW), đủ 2 chiều dương/âm + control ÂM
CHÂN 6 (phần 2) — Bearer + label type-10
Login admin@solutions.com.vn → accessToken dài 468 ⇒ OK.
| Endpoint (admin bearer) | HTTP |
|---|---|
GET /api/contracts |
200 ✅ |
GET /api/purchase-evaluations |
200 ✅ |
GET /api/menus |
200 ✅ |
GET /api/approval-workflows-v2 |
200 ✅ |
control: GET /api/khong-ton-tai-abc |
404 ⇒ 200 ở trên CÓ NGHĨA, không phải catch-all |
F-7 label BE ăn prod — GET /api/approval-workflows-v2?applicableType=10 trả nguyên văn:
{"types":[{"applicableType":10,"applicableTypeLabel":"Kế hoạch ký kết HĐ","active":null,"history":[]}]}
⟹ type 10 SỐNG trên prod với nhãn VN "Kế hoạch ký kết HĐ" (không rơi fallback ToString() tiếng Anh). active:null + history:[] = chưa có quy trình type-10 nào — ĐÚNG kỳ vọng W1 (admin sẽ dựng tay), xác nhận chéo bằng sqlcmd COUNT(*) ApprovalWorkflows WHERE ApplicableType=10 = 0.
✅ CHÂN 6 = PASS
Kiểm tra phụ (không nằm trong 6 chân — chống hồi-quy)
| Phép | Kết quả | Nhận định |
|---|---|---|
Họ Ct_ (S159 durability) |
637/637 row CanRead, 49 distinct key | ✅ 100%, revoker KHÔNG lật gì |
MenuItems root (ParentKey IS NULL) |
12 | ✅ khớp mốc S159 |
MenuItems trùng key |
0 | ✅ |
| Menu-row họ KHKK | 8 (KeHoachKyKet + 7 Khkk_*) |
✅ không đổi |
ContractSigningPlans rows |
0 | ✅ đúng — W1 schema-only, chưa có dữ liệu |
⚠️ Đính chính trí-nhớ của tao (không phải lỗi deploy): ghi chú S159 nói họ Ct_ có "59 key ⇒ tập gốc 38". Đo thật hôm nay: 49 distinct key, trong đó 21 key mang 3 hậu tố mới ⇒ tập lọc còn 28 key × 13 role = 364, nên 364/364 chứ không phải 494/494. Denominator lệch là do PHẠM-VI QUERY + số cũ ghi sai, KHÔNG phải mất quyền — bằng chứng mạnh hơn: đo TOÀN họ Ct_ được 637/637.
⚠️ 2 phát hiện phụ cần báo (KHÔNG do commit này gây ra, KHÔNG chặn W1)
- Tài khoản UAT
nv.test@solutions.com.vnKHÔNG TỒN TẠI trên prod.POST /api/auth/login→ 401"Email hoặc mật khẩu không đúng."; sqlcmdSELECT COUNT(*) FROM Users WHERE Email LIKE 'nv.test%'= 0 (tổng 18 user). ⇒ Phép thử 2-bearer (gotcha #44 — soi 403 im lặng cho role non-Admin) hiện KHÔNG chạy được trên prod. Commita8027c0không đụng Users ⇒ không phải hồi-quy của lượt này (nhiều khả năng demo-seed bị gate — gotcha #51). Đề xuất em-main: chọn 1 tài khoản non-Admin CÓ THẬT làm mốc UAT mới, hoặc bỏ vế 2-bearer khỏi checklist cho tới khi có tài khoản. - Đường log API trong memory của tao đã STALE.
C:\inetpub\solution-erppi\logs= không tồn tại; đường THẬT =C:\inetpub\solution-erp\api\logs\solution-erp-YYYYMMDD.log. Đã sửa trong MEMORY.
TỔNG KẾT
| Chân | Nội dung | Verdict |
|---|---|---|
| 1 | CI run #427 PASS + test gate 566 (45D+521I) | ✅ PASS |
| 2 | Mig 69 áp prod (MIGTOP + 96 bảng + 7 bảng CSP) | ✅ PASS |
| 3 | MenuItems 142 GIỮ NGUYÊN | ✅ PASS |
| 4 | Byte-marker QT-KHKK-V2-001 + 3 tầng control ÂM |
✅ PASS |
| 5 | ĐO CỜ policy-grant 2 chiều + control ÂM Khkk_G1 |
✅ PASS |
| 6 | Smoke 3 domain + bearer + label type-10 | ✅ PASS |
Không có mục nào FAIL. Không đề xuất rollback.
END cicd-verify a8027c0 — VERDICT=PASS 6/6