Files
solution-erp/.claude/workflows/runs/2026-07-29-S159-tong-quan-pipeline-menu/cicd-verify-2a72695.md
2026-07-29 12:47:37 +07:00

11 KiB
Raw Blame History

cicd-verify 2a72695 — S159 ĐỢT-3 "gỡ nhánh S92 khỏi revoker + skeleton menu GĐ2/GĐ4 + ComingSoonPage"

Ghi-TỪNG-MỤC-trong-lúc-làm (chống #53). File = SẢN PHẨM CHÍNH; return chỉ tóm tắt. Đợt-1 b5799fc (PASS 9/9) · đợt-2 1a47a61 (PARTIAL 8/9, FAIL mục-9 durability) — 2 file cũ KHÔNG chạm. Bắt đầu: 2026-07-29 ~12:35+07 · cicd-monitor (READ-only)

🎯 Mục 6(a) là phép PASS/FAIL của cả arc: grant 38-key có SỐNG SÓT restart không. Đúng bài tao tự đính chính đợt-2: phải đo SAU status=success VÀ SAU thời điểm pool recycle.


M1 — Push + phạm vi · PASS

git log -1 → 2a7269581c96bd0d3ce939b4dceaeb847df76a89
             [CLAUDE] Infra: go nhanh S92 khoi revoker (owner supersede: hien thi het)
             + skeleton menu GD2/GD4 + ComingSoonPage x2   ·  2026-07-29 12:34:15 +0700
git log origin/main..HEAD → EMPTY (đã push)

Range 1a47a61..2a72695 = 15 file, 466+/45. Trong đó 9 file CODE:

File vai trò
src/Backend/…/Persistence/DbInitializer.cs 🔴 gỡ nhánh [S92] khỏi revoker + grant bền vào reviewKeys
tests/…/Application/AdminOnlyModulesRevokeTests.cs REGRESSION-GUARD mới
tests/…/Application/ProcurementMasterAccessSeedTests.cs cập nhật theo spec-change
fe-admin/src/pages/ComingSoonPage.tsx · fe-user/…/ComingSoonPage.tsx MỚI ×2
fe-admin/src/App.tsx · Layout.tsx · fe-user/src/App.tsx · Layout.tsx route + menu 2 app

6 file còn lại = .claude/** (memory + run-trace, ignored bởi 7-glob). Cả 2 app FE đều sửa ⇒ bundle 2 app đều phải rotate.

M2 — Migration VẮNG là ĐÚNG · PASS

git diff --name-only 1a47a61..2a72695 -- '*Migrations*'  →  0

BE-change = seeder + revoker logic (runtime), không sinh schema. Mig giữ 68 / 89 bảng.

📌 BASELINE TRƯỚC RESTART (đo 12:35:47+07, run #424 vừa start — pool CHƯA recycle)

Đây là "vế trước" của phép durability. Ghi lại để đối chứng sau restart:

Phép đo Giá trị TRƯỚC restart
38-key set (per-key canread) min=13 max=13 keys=38 (lead re-grant 447 đã ăn)
Khkk_% permission rows 65 (= 5 key × 13 role)
Hdc_% permission rows 91 (= 7 key × 13 role)
Tổng skeleton 156 khớp con số lead báo
Control âm Hrm%/Off%/Personal non-Admin canread 0

Bundle trước deploy: admin js D6Pz7nfV css B1ErASuI · user js Bpb8WEuY css 7oAYFlzM — khớp CHÍNH XÁC baseline hậu-đợt-2 ⇒ baseline hợp lệ, đợt-3 chưa land.

M3 — Poll terminal · PASS

  • id=537 · run_number=424 · head_sha 2a726958 · created 12:34:22 → status=success 12:40:18
  • Duration 5m56s (đợt-1 5m59s · đợt-2 5m44s) — ổn định.

M3b — Test gate · 562 EXACT

Passed! - Failed: 0, Passed:  45, Skipped: 0, Total:  45, 200 ms - SolutionErp.Domain.Tests.dll
Passed! - Failed: 0, Passed: 517, Skipped: 0, Total: 517, 1m36s  - SolutionErp.Infrastructure.Tests.dll

Failed! ×0. 45+517 = 562 — giữ nguyên, đúng kỳ vọng: 3 test được SỬA theo spec-change (không thêm test mới) ⇒ tổng không đổi. Regression-guard nằm TRONG test cũ.

Nội dung guard (đọc diff, không suy đoán)AdminOnlyModulesRevokeTests.cs tách 2 vế:

  • StillHiddenKeys = Hrm / Off / Personal → sau revoke PHẢI CanRead=false
  • ReopenedKeys = Contracts/Ct_ThauPhu/Master/Suppliers/Catalogs/CatalogWorkItems → sau revoke PHẢI GIỮ CanRead=true ⇒ ai re-add nhánh [S92] vào predicate là test ĐỎ ngay. Guard có RĂNG thật (assert 2 chiều).

DbInitializer.cs: nhánh || p.MenuKey == MenuKeys.Contracts || p.MenuKey.StartsWith("Ct_") || Master/Suppliers/Projects/Departments/Catalog* đã GỠ khỏi predicate revoker; reviewKeys +ContractMenuKeys() +MasterMenuKeys() +KhkkKeys() +HdcKeys(). ⚠️ Ghi chú code nêu rõ nhánh grant là skip-existing ⇒ trên prod row false KHÔNG tự nâng; prod dựa vào re-grant SQL tay (đã chạy) + revoker không lật nữa. Đúng: phép 6(a) dưới đây kiểm chính điều đó.

M4 — Bundle rotate · PASS 4/4 (đo 12:40:45+07, sau status=success)

App asset TRƯỚC SAU KQ
admin js D6Pz7nfV CC1pGE1F
admin css B1ErASuI DWDbm5As
user js Bpb8WEuY DeAg7MWm
user css BQIZ-rUe7oAYFlzM BQIZ-rUe

Cả 2 app đều sửa (App.tsx + Layout.tsx + ComingSoonPage.tsx mới) ⇒ rotate 4/4 nhất quán với diff.


🔴 M6 — RESTART-DURABILITY (mục CHÍNH của cả arc) · PASS

Bước 0 — CHỨNG MINH pool ĐÃ recycle THẬT (không đo sớm như đợt-2)

Bằng chứng Giá trị
w3wp PID 5508 StartTime 12:39:56 — nằm trong cửa sổ deploy (12:34:22→12:40:18)
SolutionErp.Api.dll LastWriteTime 12:37:38 — binary mới đã copy
Dòng ĐẦU của logs\solution-erp-20260729.log 2026-07-29 12:40:03.239 +07:00 [INF] Applying migrations...
Log LastWriteTime 12:40:37

🔑 Vì step deploy Remove-Item …\api\* xoá cả logs/, việc dòng đầu log là 12:40:03 chứng minh file được tạo lại SAU deploy ⇒ đây đúng là lần khởi động mới, và DbInitializer (seeder + revoker) đã chạy trong lần đó. Mọi số dưới đây đo SAU thời điểm này — sửa đúng lỗi đo-sớm của đợt-2.

(a) 38-key set — PHÉP PASS/FAIL CỦA CẢ ARC · PASS

A_POST38  min=13  max=13  keys=38  totcr=494
Mốc min/max tổng canread
Đợt-2 SAU restart (#423) — hỏng 1 / 2 47 / 494
Đợt-3 TRƯỚC restart (12:35:47, sau re-grant tay) 13 / 13 494 / 494
Đợt-3 SAU restart (#424, đo ~12:41) 13 / 13 494 / 494

Grant 38-key SỐNG SÓT app-pool recycle. Chính phép đo này ở đợt-2 cho ra 0 key còn 13; nay giữ nguyên 38/38 key ở 13/13. Fix của lead ăn thật, không phải trùng hợp — vì: (i) đã chứng minh có restart thật ở Bước 0, (ii) predicate revoker đã gỡ nhánh S92, (iii) control (c) cho thấy hàm revoker vẫn chạy chứ không chết.

(b) Skeleton GĐ2/GĐ4 · PASS

B_SKEL   Khkk=65  Hdc=91  tot=156        (Permissions rows)
B2_SKELCR min=13 max=13 keys=12          (mỗi key đủ 13 role canread)

65 = 5 key × 13 · 91 = 7 key × 13 · tổng 156 khớp con số lead báo; 12 key × 13 = 156 nhất quán.

(c) Control ÂM — revoker CÒN SỐNG · PASS

C_CTRL hrmoff_nonadmin_cr = 0

Hrm%/Off%/Personal với role ≠ Admin vẫn canread = 0 ⇒ gỡ nhánh S92 KHÔNG làm chết cả hàm; vế StillHiddenKeys vẫn thực thi đúng. (Đây là phép chống "Goodhart": nếu ai đó vô hiệu hoá luôn revoker thì (a) cũng xanh — nhưng (c) sẽ đỏ. (a) xanh (c) xanh mới là fix ĐÚNG.)

(d) Control Pe_% · PASS

D_PE rows=197 cr=185      (dùng LIKE 'Pe%' — đúng hình-dạng query gốc)

M7 — Log seeder sau restart · PASS (khai đầy đủ)

findstr "Revoked" trên log mới ⇒ KHÔNG có dòng nào. Đây là kết quả ĐÚNG chứ không phải thiếu dữ liệu: RevokeTemporarilyHiddenModulesAsync chỉ ghi log khi rows.Count > 0; nay tập ẩn chỉ còn Hrm/Off/Personal và chúng đã ở canread=0 từ trước ⇒ 0 row cần lật ⇒ im lặng. Khớp control (c)=0. So đợt-2: 447 row bị lật → nay 0.


M5 — Byte-marker ComingSoonPage trên 2 bundle · PASS

Source ComingSoonPage.tsx 2 app đều 2.553 B (mirror): chứa Phân hệ đang được triển khai ×1

  • Quay lại ×1; không chứa Sắp triển khai.
Chuỗi USER bundle ADMIN bundle KQ
Phân hệ đang được triển khai 1 1 dương cả 2 (chính là ComingSoonPage mới)
Quay lại 4 4 dương cả 2
Sắp triển khai 2 1 dương cả 2 (nhãn GĐ có sẵn, không phải ComingSoonPage)

Artifact thật + in-window:

USER  index-DeAg7MWm.js  ct=application/javascript  size=1.587.996  LM=12:39:51+07
ADMIN index-CC1pGE1F.js  ct=application/javascript  size=1.678.733  LM=12:38:56+07

Control âm: hash CŨ (Bpb8WEuY / D6Pz7nfV) đều rơi về ct=text/html 876 B / 900 B ⇒ bundle đợt-2 đã biến mất khỏi đĩa = chứng ghi-đè thật.

M8 — Smoke · PASS 4/4

/health/live 200 Healthy · /health/ready 200 Healthy · admin 200 · eoffice 200

M9 — Menu-tree skeleton · PASS 5/5

MENU Khkk=5  Hdc=7          (MenuItems rows)
ROOT=12                      DUPKEYS=0
Hrm=0  Off=0  Personal=0     (IsVisible)
NUM(label '01.'-'07.')=14 →  NUMSPLIT ct=7  hdc=7  other=0

Cấu trúc Khkk_* đúng phân cấp thiết kế:

KeHoachKyKet (root)
└── Khkk_G1  ord1   "1. Kế hoạch ký kết HĐ (NCC-TP)"
    ├── Khkk_List     ord1
    ├── Khkk_Create   ord2
    ├── Khkk_Pending  ord3
    └── Khkk_Approved ord4

🔎 Giải-trình con số 14 (đừng đọc nhầm thành drift): kỳ vọng ban đầu là 7 nhãn 01.-07., đo ra 14. Tách theo prefix key: Ct_=7 + Hdc_=7 + other=0. Đúng thiết kế — diff ghi rõ GĐ4 "tái dùng đúng nhãn đã đánh số 01.-07." (tree.Add(("Hdc_"+code, label, …)) dùng chung typeLabels). Vậy 14 = 7 gốc + 7 mirror, không phải nhân đôi nhầm (DUPKEYS=0, root vẫn 12).


VERDICT: PASS — 9/9

# Mục KQ
1 Push + range 15 file (9 code), CI fire đúng
2 Migration VẮNG là ĐÚNG (0 file *Migrations*)
3 Run #424 success 5m56s
4 Test gate 562 (45+517, 0 fail) + regression-guard 2 vế có RĂNG
5 Bundle rotate 4/4 (2 app đều sửa) + control hash-cũ text/html
6 Marker ComingSoonPage dương cả 2 bundle + Quay lại ×4
7 Smoke health ×2 + FE ×2
8 Menu-tree: Khkk=5 · Hdc=7 · root=12 · 3 root vis=0 · nhãn 7+7 · DUPKEYS=0
9 🔴 RESTART-DURABILITY: 38-key = 13/13/38 (494/494) SAU restart THẬT PASS
├ (b) skeleton 156 perm, 12 key × 13
├ (c) control âm Hrm/Off/Personal non-Admin = 0 (revoker còn sống)
└ (d) Pe% 197/185

🎯 Kết luận arc 3 đợt

Regression tao bắt ở đợt-2 (mục 9, 447/494 row rơi) đã đóng. Bằng chứng đóng KHÔNG dựa vào "CI xanh" mà dựa vào 3 chân kiềng: (i) chứng minh có restart THẬT (w3wp 12:39:56 + log mở lại 12:40:03), (ii) 38-key giữ 13/13/38 sau restart đó, (iii) control âm (c) chứng minh revoker vẫn chạy — nên (ii) không phải do vô hiệu hoá cả hàm. Kèm regression-guard trong test: re-add nhánh [S92] ⇒ CI đỏ, không cần trông vào trí nhớ.

Baseline mới: admin js CC1pGE1F / css DWDbm5As · user js DeAg7MWm / css BQIZ-rUe · gate 562 · Mig 68 / 89 bảng · menu root 12 · 38-key 13/13.