Files
solution-erp/.claude/workflows/runs/2026-07-29-S161-khkk-w1-schema/cicd-verify-a8027c0.md
2026-07-29 20:23:52 +07:00

15 KiB
Raw Blame History

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òng END … 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-001 toàn repo: 1 hit code = fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx:170 ([TYPE_INT_CONTRACT_SIGNING_PLAN]: 'QT-KHKK-V2-001', trong DEFAULT_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:4820: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 142GIỮ 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:4820: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 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 upgraded91 = 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.vnaccessToken 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 prodGET /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)

  1. Tài khoản UAT nv.test@solutions.com.vn KHÔNG TỒN TẠI trên prod. POST /api/auth/login401 "Email hoặc mật khẩu không đúng."; sqlcmd SELECT 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. Commit a8027c0 khô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.
  2. Đườ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 a8027c0VERDICT=PASS 6/6