Files
solution-erp/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-cicd-verify-k2.md
2026-07-31 17:20:31 +07:00

11 KiB
Raw Blame History

CICD-K2: PARTIAL — 5/6 mục (mục 4 = CHÂN-LÝ-RỖNG, không phải sạch)

CI/CD verify K2 SP-002 — commit 0779f2d

Artifact chống #53 — ghi TỪNG MỤC NGAY KHI ĐO. Vai: cicd-monitor (READ-only). Ngày: 2026-07-31. Deploy KHỎE, KHÔNG khuyến nghị rollback. Điểm trừ duy nhất = phép đo mục 4 không có răng (bảng rỗng), không phải lỗi deploy.


Mục 0 — Push + path-filter (tiền đề)

$ git log -1 --format='%H|%s|%ci'
0779f2d45a932ec7c279c191d9ae3fe3505442af|[CLAUDE] Contract: K2 SP-002 phieu KHKK mang hang-muc + nhom duyet N1-8 (Mig 71) + FE x2 app + 11 test|2026-07-31 17:05:27 +0700

$ git log origin/main..HEAD --oneline
(RỖNG — đã push trọn)

git diff --name-only 0779f2d^..0779f2d = 23 file: BE 9 · Mig 71 ×3 file (3-file rule đủ) · FE 8 (admin ×4 + user ×4 đối xứng) · test 3 · paths-ignore 2 (docs/STATUS.md, .claude/skills/ef-core-migration/SKILL.md — KHÔNG chặn vì ≥1 file code). Neo bằng SHA tuyệt đối 0779f2d^..0779f2d (LUẬT S162: cấm HEAD~1, Stop-hook WAL auto-commit).

Mục 0b — SNAPSHOT PRE-MIGRATE (đo lúc run #435 còn running, 17:07)

MIGTOP=20260731072441_AddContractCatalogEntries      <- Mig 70 (K1), Mig 71 CHƯA áp
PLANS=0 · LINES=0 · SUMPE=NULL|SUMPROP=NULL · SYSTABLES=97
admin   /assets/index-CKgq0YlH.js + /assets/index-DD3xQhNt.css
eoffice /assets/index-CHMCU35C.js + /assets/index-CdXRNT7E.css   (khớp mốc K1 #434)

🔴 KHAI TRƯỚC — MỤC 4 SẼ RỖNG, KHÔNG PHẢI SẠCH. Xem mục 4.


Mục 1 — Gitea Actions run PASS

Run #435 (id=548), 0779f2d4
Status success
Cửa sổ created 17:05:33 → updated 17:11:45 = 6m12s
URL https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp/actions/runs/435

Poll 6 vòng (17:08:31 → 17:12:26), running×5 → success. 6m12s ≈ K1 #434 (6m31s) — hợp lý cho +Mig +11 test, chưa tới ngưỡng escalate.

Mục 2 — Test gate 609 PASS (khớp TÁCH-PHẦN, không chỉ khớp tổng)

Log web-UI .../actions/runs/435/jobs/0/logs (18.677 B), grep nới Passed![^\r\n]*:

Passed!  - Failed:     0, Passed:    45, Skipped:     0, Total:    45, Du
Passed!  - Failed:     0, Passed:   564, Skipped:     0, Total:   564, Du
Domain Infra Tổng
K1 #434 45 553 598
K2 #435 45 564 609
delta 0 +11 +11

Kỳ vọng 609 = 598 + 9 lane + 2 gate-fix ⇒ khớp từng vế: Domain đứng yên, +11 rơi TRỌN vào Infra — đúng nơi 3 file test mới nằm (ContractSigningPlanApprovalTests / ~GroupAndCatalogTests / ~SchemaTests). 0 fail, 0 skipped. Control ÂM: grep chuỗi bịa ZZZ_KHONG_TON_TAI_9x7 = 0 hit. ⚠️ Dùng regex nới ngay từ đầu vì bài K1: log dùng 2 khoảng trắng sau Passed!.

Mục 3 — EF migration prod + chứng-restart PASS

MIGTOP=20260731085624_AddKhkkApprovalGroupCatalogAndFinalizeRuntime   <- TOP, đúng Mig 71
MIGCOUNT=71
SYSTABLES=97      (GIỮ NGUYÊN — Mig 71 có 0 CreateTable)

sys.tables 97 == 97 khớp phép đo nguồn grep -c "CreateTable" = 0 (control dương: grep -c "AddColumn" = 4 ⇒ grep có tìm được thứ có thật).

3-CHÂN-KIỀNG (né bẫy #423 đo-trước-recycle):

  1. Restart thật: appcmd list wpWP "5364" (applicationPool:SolutionErp-Api); Get-Process w3wp → PID 5364 StartTime 17:11:25 ∈ cửa sổ deploy (17:05:3317:11:45). (5 w3wp còn lại là pool VIETREPORT/Default — đã map PID→pool để không nhận nhầm.)
  2. Dòng ĐẦU Serilog: 2026-07-31 17:11:27.310 +07:00 [INF] Applying migrations...
  3. Mọi phép đo DB dưới đây chạy từ 17:12+ ⇒ SAU mốc recycle.

Mục 4 — Data prod sau migrate ⚠️ PARTIAL — CHÂN-LÝ-RỖNG

Số đo nguyên văn (POST-migrate, sau recycle):

TOTAL=0 · GROUP1=0 · OUTOFRANGE=0 · LINES=0 · SUMPE=NULL|SUMPROP=NULL
CTRL_SUPPLIERS=60 · CTRL_CATALOG=86      <- control dương: bộ đếm CÓ đếm được
Acceptance yêu cầu Kết quả Giá trị bằng chứng
COUNT(ApprovalGroup=1) == COUNT(*) 0 == 0 RỖNG — 0 bit thông tin
COUNT(ApprovalGroup NOT BETWEEN 1 AND 8) = 0 0 RỖNG
Σ PeReferenceAmount/ProposedAmount NULL|NULL (PRE cũng NULL|NULL) RỖNG hai phía

🔴 ContractSigningPlans prod có 0 dòng cả TRƯỚC lẫn SAU. Ba acceptance "xanh" nhưng không mang thông tin nào về việc backfill có chạy đúng — backfill hụt sẽ trông y hệt. Chính tác giả Mig 71 đã khai bẫy này ("đo local Σ = tập RỖNG hai phía … phép đo prod mới có răng"); nay prod cũng rỗng ⇒ phép đo prod cũng KHÔNG có răng. Không hạ thành FAIL (không có gì sai), nhưng CẤM đọc thành PASS.

Bằng chứng THAY THẾ — không phụ thuộc số dòng (CÓ RĂNG):

(a) 4 cột, đúng kiểu/nullability/default (INFORMATION_SCHEMA.COLUMNS):

ContractSigningPlans     |ApprovalGroup       |int             |null=NO |def=((0))
ContractSigningPlans     |EndedByLevelFinalize|bit             |null=NO |def=(CONVERT([bit],(0)))
ContractSigningPlanLines |CatalogEntryId      |uniqueidentifier|null=YES|def=-
ContractSigningPlanLines |TenHangMuc          |nvarchar(500)   |null=YES|def=-

(b) HOÁN-ĐỔI INDEX — chứng bằng liệt kê TRỌN HỌ (14 index cả 2 bảng), không bằng grep-rỗng:

✅ CÓ : IX_ContractSigningPlanLines_ContractSigningPlanId_SupplierId_CatalogEntryId
        unique=1 | filter=([IsDeleted]=(0)) | cols=ContractSigningPlanId,SupplierId,CatalogEntryId
✅ CÓ : IX_ContractSigningPlanLines_CatalogEntryId          (cols=CatalogEntryId)
✅ CÓ : IX_ContractSigningPlans_ApprovalGroup_Phase         (cols=ApprovalGroup,Phase)
❌ MẤT: IX_ContractSigningPlanLines_ContractSigningPlanId_SupplierId  (unique 2-cột cũ)

Vắng-mặt của index cũ suy ra từ danh sách đầy đủ 14 index có thật, nên đây là vắng-mặt ĐO ĐƯỢC chứ không phải "truy vấn trả rỗng đọc thành sạch". Đây là phép đo mạnh nhất của mục 4.

(c) Bất-biến Σ tiền — chứng theo CẤU TRÚC bằng phép ĐO nguồn: grep -nE "PeReferenceAmount|ProposedAmount|ApprovedAmount" trên Mig 71 → chỉ 2 hit, cả 2 ở dòng 23-24 và đều là comment //0 câu lệnh thực thi chạm cột tiền. migrationBuilder.Sql = đúng 1 câu (dòng 61, backfill ApprovalGroup=1). Σ bất biến đúng kể cả khi bảng có dữ liệu.

(d) Materialize qua EF: xem mục 6 — ?group=N trả 200 (không 500) ⇒ cột ApprovalGroup tồn tại & query được xuyên Domain→Infra→Api trên DB thật.

Mục 5 — Bundle ×2 app (#77) PASS — 4/4 ROTATE

app asset TRƯỚC (K1 #434) SAU (K2 #435)
admin js CKgq0YlH DVQEwGwf
admin css DD3xQhNt D050EVYG
eoffice js CHMCU35C CZAYiWWa
eoffice css CdXRNT7E DX1FRk5n

Hash CŨ chết (control âm tầng-2 — bundle cũ biến mất khỏi đĩa):

admin  /assets/index-CKgq0YlH.js -> 200|text/html|900   (SPA fallback, không phải JS)
eoffice/assets/index-CHMCU35C.js -> 200|text/html|876
admin  /assets/index-ZZBIADAT.js -> 200|text/html|900   <- hash BỊA, cùng dấu hiệu

Hash bịa cho ra ĐÚNG dấu hiệu của hash cũ ⇒ text/html thật sự nghĩa là "file không tồn tại" (né bẫy SPA-fallback-200 S72; dùng Content-Type chứ không neo byte).

Last-Modified in-window: admin 10:10:20 GMT = 17:10:20 +07 · eoffice 10:11:20 GMT = 17:11:20 +07 — cả hai ∈ (17:05:33, 17:11:45). Cache-Control: no-cache cả 2 (hardening S88).

Mục 6 — Smoke PASS

Phép đo Kỳ vọng Thực tế
/health/ready 200 200
/health/live 200 200
/health trần (control) 404 ⇒ 200 ở trên có nghĩa
GET /api/contract-signing-plans?group=2 KHÔNG token 401 401
route BỊA /api/contract-signing-plans-KHONG-TON-TAI?group=2 404 404
route BỊA 2 /api/zzz-fake-9x7 404 404
admin.solutions.com.vn 200 200
eoffice.solutions.com.vn 200 200

401 (route có thật, có gác) tương phản 404 ×2 (route không có) ⇒ 401 CÓ NGHĨA.

Có token admin (login /api/auth/login, accessToken len=468):

?group=1 -> 200  {"items":[],"total":0,"page":1,"pageSize":20,"totalPages":0,...}
?group=2 -> 200  (thân y hệt)
?group=8 -> 200  (thân y hệt)
inbox    -> 200      deleted -> 200

"mọi item approvalGroup==1" ⇒ VACUOUS (0 item) — không tính là bằng chứng. ⚠️ Đã soi thân RAW (không tin bộ parse) đúng bài K1 "0-hit là dương-giả tới khi soi format": thân thật là {"items":[],"total":0} ⇒ rỗng THẬT, không phải lỗi đọc format.

Chứng group ĐƯỢC WIRE (không bị nuốt im lặng):

?group=abc -> 400   <- bind int THẬT; nếu param không tồn tại, ASP.NET bỏ qua query lạ và trả 200

Khớp nguồn: ContractSigningPlansController.cs:42[FromQuery(Name = "group")] int? group = null:49 truyền vào ListContractSigningPlansQuery(..., group).

Control DƯƠNG cho đường ống list: GET /api/catalogs/contract-catalog86 item (seed K1 còn nguyên) ⇒ token + serializer + đường ống list CHẠY, nên items:[] của KHKK là bảng rỗng thật, không phải ống vỡ.


Lệch / quan sát (không chặn deploy)

  1. ?group=999 → 200 (không 400). Miền nghiệp vụ là 1..8 nhưng biên API không chặn ngoài-miền; hiện vô hại (trả rỗng) và mục 4 đã xác nhận 0 dòng ngoài 1..8. Ghi cho K4b cân nhắc cùng lớp với validator CatalogEntryId bắt buộc mà Mig 71 đã hẹn.
  2. Ghost-window EndedByLevelFinalize — tác giả khai trước: K2 đẻ cột, K3 tiêu thụ; tới khi K3 land cột luôn = 0. Đã xác minh cột tồn tại bit NOT NULL default 0; không coi "toàn 0" là dữ liệu hỏng.
  3. Mục 4 rỗng — xem trên. Khi có phiếu KHKK thật trên prod, cần đo LẠI 3 acceptance đó thì mới có răng; hiện chưa thể kết luận backfill đúng/sai từ dữ liệu.

Bảng tổng

# Mục Verdict
1 Gitea run 0779f2d success PASS
2 Test gate 609 = 45D + 564I PASS
3 Mig 71 TOP + chứng-restart PASS
4 Data prod sau migrate ⚠️ PARTIAL — vacuous (schema/index CÓ răng )
5 Bundle ×2 app rotate + hash cũ chết PASS
6 Smoke 401/404/200 + group wired PASS

Không ghi gì lên prod ngoài đọc (toàn bộ là SELECT / curl GET / login POST lấy token).

END — VERDICT=PARTIAL 5/6 (mục 4 vacuous, deploy khỏe, không rollback)