diff --git a/.claude/WAL.md b/.claude/WAL.md index 44a6843..4fe6ebd 100644 --- a/.claude/WAL.md +++ b/.claude/WAL.md @@ -34,8 +34,9 @@ next: 5/6 OG CHỐT (1 schema-GỘP+UI-1-1 · 2 đội-hình-cũ · 7 N-phiếu+ B2-r cụm-1 ✅ 3/3 lane (L1 2/9-claim-đúng GỠ-(i)-:342 [invest đọc-ngược-comment] · L2 FAIL 3H · L3 PWB 5H) → spec-cum1-chi-tiet-31-07-2026.md FINAL (nền 33KB + 12 VÁ thắng-xung-đột; blocker: key-VÀO-All + sweep-ép-false-AllowApproverFinalize ≥4-site + KhkkCreatePage-×2-app + submit-guard-CatalogEntryId). - 🆕 OG-9 (L2-M3): K3 bật duyệt-sớm KHKK = ĐẢO phán quyết "PHÁ VỠ" @S155 (5 nơi ghi) — cần anh xác nhận - TƯỜNG MINH (câu IV). Chặn K3 (a)-(e); K1/K2 KHÔNG chặn. + OG-9 ✅ CHỐT (câu IV): anh xác nhận đảo "PHÁ VỠ" @S155 — "chỗ budget chốt chặn là a Chương" ⇒ K3 (a)-(e) + HẾT CHẶN. + OG-3 NÂNG: trần budget = HUMAN-GATE VĨNH VIỄN (a Chương quyết cần-CEO-hay-không) — XÓA mọi + ý "enforce máy wave sau" khỏi backlog. 7/7 OG nghiệp-vụ đã chốt; còn OG-6 soát-bảng (chặn seed) + OG-5. wf: B2 cụm-2 wf_15a42015-aea ĐANG CHẠY (1 lane fable đan-xen: K4 sidebar 49-row · K5 cây groups · K6 admin). CHỜ ANH: (I) bản-15-trang · (II) B2-14-dòng+"/"=hoặc · (III) ai bấm Đưa-vào-HĐ · (IV=OG-9) đảo PHÁ-VỠ-S155. Sổ fable-lane: StructuredOutput-fail 3/5 (2 att gần nhất SẠCH — đan-xen tương quan?). diff --git a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/spec-4gd-khkk-tong-quat-31-07-2026.md b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/spec-4gd-khkk-tong-quat-31-07-2026.md index f936149..a80c099 100644 --- a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/spec-4gd-khkk-tong-quat-31-07-2026.md +++ b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/spec-4gd-khkk-tong-quat-31-07-2026.md @@ -47,5 +47,8 @@ Chuỗi 4 GĐ per-gói-thầu theo SOL-CCM-SP-002: GĐ1 Duyệt NCC (PE = CHA, | OG-6 | Soát bảng transcribe 86 dòng (gõ tay từ ảnh; 4 ô ⚠️ cuối file) HOẶC anh gửi file 15-trang | ⏳ đã giải thích, chờ anh soát | K1 (seed) | | OG-8 | Authz per-nhóm | ✅ **CHỐT @S164** (*"Đúng chính xác. Cho hiển thị, phân quyền tính sau."*) — đợt này 8 nhóm = hiển thị + luồng duyệt; XEM/TẠO chung bộ khóa module; authz-per-nhóm = wave sau khi anh gọi | — | | OG-5 | Extract mơ hồ → hỏi khi gặp | mở | K1 | +| OG-9 | Bật lại duyệt-sớm KHKK = ĐẢO phán quyết "PHÁ VỠ" @S155 | ✅ **CHỐT @S164** (*"đúng, chỗ budget chốt chặn là a Chương, anh ấy sẽ quyết cái j cần quyền CEO và cái j anh ấy đc duyệt"*) — K3 việc (a)-(e) HẾT CHẶN: port nhánh finalize + cột runtime + gỡ khóa FE + sweep ép-false ≥4 site + sửa comment/panel/HANDOFF khai supersede-S155 | K3 mở | + +> 🔴 **OG-3 NÂNG CẤP @S164 (cùng lời chốt OG-9):** *"vậy bài toán trần budget xem như ĐÓNG. Vì người chốt chặn budget là anh Chương."* ⇒ NĐUQ/ngưỡng tiền = **HUMAN-GATE VĨNH VIỄN** (TP.CCM Phan Văn Chương quyết cái gì cần CEO) — **XÓA ý "enforce máy = wave opt-in sau"** khỏi mọi spec; cột NĐUQ trên danh mục thuần ghi chú. Không còn wave threshold nào trong backlog. END spec v2 — 8 wave · 8 quyết định · 6 OG · 14 vá B1b áp đủ. diff --git a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/spec-cum1-chi-tiet-31-07-2026.md b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/spec-cum1-chi-tiet-31-07-2026.md index 9c6b0b4..59c4efe 100644 --- a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/spec-cum1-chi-tiet-31-07-2026.md +++ b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/spec-cum1-chi-tiet-31-07-2026.md @@ -16,7 +16,7 @@ K1 master-data danh mục (86 row) · K2 phiếu mang hạng-mục + nhóm + fre 9. **[L1-F7] Chứng NULL-unique vá lại**: unique cũ bị DROP trong chính Mig 71 — mắt xích thật chặn 2-line-NULL-cùng-(Plan,Supplier) phải nêu ĐÚNG (submit-guard vá-5 + backfill line cũ gán từ header-group; đừng dựa index đã chết). 10. **[L1-F9/L2-M2] Sửa nhãn IgnoreQueryFilters**: số đúng = "0 hit trong `DbInitializer.cs`" (toàn src/Backend = 29 hit/5 file — claim "duy nhất"/"0-hit" là sai lây từ comment code); đơn thuốc seed `:2970` thiếu IgnoreQueryFilters vẫn ĐÚNG. 11. **[L1-e] Roster: acceptance đo DANH TÍNH** — K3.e += mục "mở workflow nhóm N, danh sách người duyệt KHỚP bảng đội hình từng người" + xử 2 cặp gần-trùng (`truong.le` vs `tring.le` vs `truong.nguyen` · `thanh.lethanh` "Lê Thanh Bình" vs `binh.le` "Lê Văn Bình") bằng đối chiếu **dept + chức danh trên bản LIVE**, không match tên trần; dept-lookup `:185` thêm null-guard. -12. **[L1-F3/F4] Trích dẫn `:44-46` ĐỦ + khai supersede**: nguyên văn có "chủ dự án phán 'PHÁ VỠ' @S155" — spec PHẢI khai K3 = **ĐẢO phán quyết cũ theo lời owner @S164**, kèm việc (e): sửa comment `:36-43`/`:44-46` + 2 lệnh tự-kiểm `:292-293` dán PR + cập nhật `KhkkWorkflowPanel.tsx:8` ×2 app + HANDOFF:77. 🔴 **[OG-9 — L2-M3] chờ anh xác nhận TƯỜNG MINH** (câu IV đã gửi): bật lại duyệt-sớm cho KHKK = đảo "PHÁ VỠ" @S155. **Chặn K3 việc (a)-(e); K1/K2 KHÔNG chặn.** +12. **[L1-F3/F4] Trích dẫn `:44-46` ĐỦ + khai supersede**: nguyên văn có "chủ dự án phán 'PHÁ VỠ' @S155" — spec PHẢI khai K3 = **ĐẢO phán quyết cũ theo lời owner @S164**, kèm việc (e): sửa comment `:36-43`/`:44-46` + 2 lệnh tự-kiểm `:292-293` dán PR + cập nhật `KhkkWorkflowPanel.tsx:8` ×2 app + HANDOFF:77. ✅ **[OG-9 CHỐT @S164]** anh xác nhận TƯỜNG MINH: *"đúng, chỗ budget chốt chặn là a Chương… vậy bài toán trần budget xem như đóng"* ⇒ đảo "PHÁ VỠ" @S155 hợp lệ — **K3 (a)-(e) HẾT CHẶN**. Kèm: NĐUQ = human-gate VĨNH VIỄN (không còn wave enforce-máy nào; xóa mọi ý "opt-in sau"). ## ③ Checklist final cụm-1 - [ ] K1: 7 mục nền + **"Admin thấy leaf sidebar ×2 app"** + test seed-grant + drift 2 row STATUS khai cùng commit. diff --git a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-invest-fable-b2-cum2.md b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-invest-fable-b2-cum2.md new file mode 100644 index 0000000..07e7a6c --- /dev/null +++ b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-invest-fable-b2-cum2.md @@ -0,0 +1,74 @@ +# SUB-INVEST /fable-real — B2 CỤM-2: SPEC CHI TIẾT K4 + K5 + K6 + +> Run `2026-07-31-S164-4gd-khkk-fanout` · agent investigator-codebase (Fable) · luật ĐAN XEN: đọc đủ 1 section → viết ngay section đó. + +## K4 Sidebar 8 nhóm × 6 leaf + +### K4a — 49 row menu + MenuKeys.All + grant 3-site (0 phụ thuộc — chạy song song cụm-1) + +1. **MenuKeys.cs — 49 key VÀO `MenuKeys.All`** (vá-1 cụm-1, 3-lane hội tụ): 7 group `Khkk_G2..G8` + 42 leaf `Khkk_G{n}_{WfView|List|Create|Pending|Approved|Deleted}` (n=2..8). Đề xuất factory `KhkkGroupKey(n)`/`KhkkLeafKey(n,action)` mirror khuôn `ContractTypeGroup`. **G1 GIỮ NGUYÊN 7 key cũ không-infix** (`Khkk_G1` + `Khkk_WfView`… — seed `DbInitializer.cs:1780-1787`): đổi key = mồ côi permission-row (S155, 13 row/key). ⚠️ Khai precedent-split để reviewer khỏi tưởng mâu thuẫn: `Ct_*`/`Pe_*` leaf CỐ Ý ngoài All (`DbInitializer.cs:2193-2195` "All… KHÔNG chứa Pe_* leaf") ⟂ catalog/AwV2 leaf TRONG All — cụm-2 đi nhánh TRONG-All theo vá-1 (ít site mirror hơn: Admin tự grant). Policy sinh thêm 49×4 registered-unused = vô hại, endpoint vẫn chỉ dùng `KeHoachKyKet.*` (#85). +2. **Seed menu-row**: thêm loop G2..G8 cạnh `:1780-1787`, khuôn PE-loop `:1896-1913` — group (parent `KeHoachKyKet`, Order n, icon FileCheck) + 6 leaf y nhãn/icon/Order G1 (`:1782-1787`: Network/List/Plus/CheckCircle2/CheckCheck/Trash2, Order 1-6). Upsert `:1926-1943`: key mới đi nhánh INSERT; key cũ chỉ re-set Order. +3. **labelBackfill G1** (dict `:1953-1995`, apply `:1997-2005` — sửa chữ trong `tree.Add` VÔ TÁC DỤNG DB đang chạy, comment `:1959-1961`): `Khkk_G1` "1. Kế hoạch ký kết HĐ (NCC-TP)" → nhãn nhóm 1. Bảng nhãn 8 nhóm (đề xuất từ `danh-muc-sp002-transcribe.md:5-16`; owner chỉnh CHỮ được, KEY không đổi): + | Key | Nhãn đề xuất | + |---|---| + | Khkk_G1 (labelBackfill) | 1. Thiết bị, máy móc, vật tư phụ (A1) | + | Khkk_G2 | 2. Kết cấu, hạ tầng & DV hỗ trợ thi công (A2+A3) | + | Khkk_G3 | 3. Hoạt động văn phòng (A4) | + | Khkk_G4 | 4. Vật tư (B1) | + | Khkk_G5 | 5. Thầu phụ xây dựng (B2) | + | Khkk_G6 | 6. Thầu phụ MEP (B3) | + | Khkk_G7 | 7. BCH công trường (C1-C3) | + | Khkk_G8 | 8. HĐ chưa ủy quyền (CEO) | +4. **Grant 3-site — QUYẾT: KHÔNG retire site nào, EXTEND cả 3.** Evidence: + - Site-1 `SeedAdminPermissionsAsync` loop `MenuKeys.All` `:2058` **chỉ Admin-role** (`:2049-2050`) → 49 key vào All ⇒ Admin TỰ có row, 0 code thêm. + - Site-2 `KhkkKeys()` `:2185-2189` (feed `reviewKeys` `:2216` → CanRead MỌI role, skip-existing): **extend += 7 group + 42 leaf**. KHÔNG retire — group-key CẦN CanRead row để render mà site-3 CỐ Ý không cấp group (comment `:2418-2419` "Khkk_G1 = group điều hướng, CanRead-all đã có từ nhánh read-only"). + - Site-3 `SeedKeHoachKyKetAccessAsync` `:2415-2472` (UPGRADE-if-exists, R+C+U 13/13 role, Delete=Drafter+Admin `:2437`): keys `:2420-2425` **extend += 42 leaf** (giữ convention không cấp cờ ghi cho group). KHÔNG retire — All-loop chỉ Admin; reviewKeys chỉ CanRead; cờ GHI chỉ site-3 phát. +5. 🔴 **Sửa grounding đề bài (evidence ngược):** bảng kỳ-vọng 13 role KHÔNG phải "7 vai có grant + 6 vai 0-leaf". `SeedKeHoachKyKetAccessAsync` loop `roleManager.Roles` `:2427` = **13/13 role R+C+U** (O-A "hiển thị hết để góp ý" `:2408-2410`). "7 vai" là seeder PE `:2669-2675` — không áp KHKK. Bảng kỳ-vọng đúng: 13/13 thấy đủ 48 leaf; cột Delete ✓ chỉ Drafter+Admin. +6. **Drift khai cùng commit** (bài cụm-1): STATUS 2 row — Menu keys 64→**113** (55→64 đã khai cụm-1) · Policies 256→**452** (+49×4). MenuItems DB +49 (Khkk% tổng 56 row). Route-map ×2 app khai trong PR (K4c). + +### K4b — 4 leaf list nhận `?group=` (CHỜ K2: param `group` BE `ContractSigningPlanFeatures.cs:622-625` + DTO `approvalGroup`) + +7. `KhkkListPage.tsx` ×2 app (mirror SHA — header tự khai `:3-4`): đọc `searchParams.get('group')` cạnh `filter`/`view` `:60-61` → truyền `group` vào params API `:75-82`, CẢ nhánh `/deleted` `:74`. Chip lọc giữ state-only `:11-12` (không ghi URL — bug UAT S155); `group` nằm trong URL leaf nên NavLink active-logic ăn tự nhiên. +8. **6 URL leaf G1 CŨ phải mang `group=1`** (phiếu cũ backfill `ApprovalGroup=1` — QĐ6 spec-tổng `:16`): sửa staticMap fe-user `Layout.tsx:59-62` + `:82-83` (List `/khkk/list?group=1` · Pending `?group=1&filter=ChoDuyet` · Approved `?group=1&filter=DaDuyet` · Deleted `?group=1&view=deleted` · Create `/khkk/create?group=1` · WfView `?type=10&group=1`). Root `KeHoachKyKet:79` GIỮ `/khkk/list` không-group = xem tất. Query-string là DANH TÍNH leaf (active-check `:341-347` + bài TRANSIENT `:318-331`) → group phân biệt G1..G8 sạch. +9. Nếu thiếu K2-param mà ship K4b: leaf G2..G8 List hiển thị TẤT phiếu (silent-wrong, không lỗi build) → acceptance mục 2 bắt buộc đo SAU K2. + +### K4c — regex staticMap + WfView/Create (CHỜ K3: 8 Code `KHKK-N1..N8`) + +10. fe-user `Layout.tsx`: regex MỚI sau `staticMap[key]` `:131`, khuôn Pe-regex `:154`: `/^Khkk_G([1-8])_(WfView|List|Create|Pending|Approved|Deleted)$/` → List `/khkk/list?group=n` · Pending `+&filter=ChoDuyet` · Approved `+&filter=DaDuyet` · Deleted `+&view=deleted` · Create `/khkk/create?group=n` · WfView `/khkk/workflow-matrix?type=10&group=n`. (G1 dùng key cũ nên đi staticMap mục 8; regex `[1-8]` phòng key G1-infix tương lai, không hại.) +11. **WfView lọc theo nhóm** (vá l2-F3 B1b): `WorkflowMatrixViewPage` hiện chỉ lọc type (`:52-71`) → đọc `group` → chỉ hiện workflow `Code === 'KHKK-N'+n` (convention Code do K3 seed; map group↔Code = 1 chỗ export const dùng chung). +12. **Create preset**: `KhkkCreatePage` ×2 app đọc `group` → preselect picker nhóm (picker + body `approvalGroup` đã là việc vá-4 cụm-1; đây CHỈ preset giá trị). +13. **fe-admin mirror**: fe-admin `Layout.tsx` có staticMap RIÊNG — grep `Khkk_` bên đó trước khi nới. ⚠️ fe-user `:152-153` khai fe-admin KHÔNG có `WorkflowMatrixViewPage` (bài Pe S155: thêm WfView = link chết) → xem chứng ở K6-read bên dưới; nếu fe-admin không route matrix ⇒ regex fe-admin BỎ nhánh WfView (leaf ẩn admin-side #50 conscious-choice, ghi comment), còn lại mirror y fe-user. + +### Acceptance K4 (đo được, UI-first — bài cụm-1) +- [ ] A1. Login Admin ×2 app: root GĐ2 → **8 group × 6 leaf = 48 leaf HIỆN sidebar** (đếm từ UI, không chỉ curl). +- [ ] A2. Login vai thường (nv.test Drafter): CŨNG 48 leaf (13/13 grant — mục 5); bấm TỪNG LOẠI leaf ở nhóm ≠1: List/Pending/Approved/Deleted lọc đúng group+phase; Create preset đúng nhóm; WfView chỉ matrix `KHKK-N{n}`. +- [ ] A3. Bảng 13 role × {R,C,U,D} khớp seeder (R/C/U ✓ 13/13 · D ✓ Drafter+Admin): PermissionsPage matrix + 1 curl DELETE vai thường → 403 (2-tầng #82). +- [ ] A4. Key cũ 0 mồ côi: `SELECT DISTINCT MenuKey FROM Permissions WHERE MenuKey LIKE 'Khkk%'` ⊆ `MenuItems.Key`; 7 key G1 cũ nguyên row. +- [ ] A5. Restart API ×2 lần: MenuItems `Khkk%` = **56 row** đúng (7 cũ + 49 mới), không nhân; labelBackfill đổi đúng 1 nhãn G1. +- [ ] A6. Drift 2 row STATUS (Menu keys 113 · Policies 452) cùng commit + route-map ×2 app khai PR. + +## K5 Cây sub-folder + +**QUYẾT khuôn: thêm `groups?: PipelineGroup[]` mang leaves[] THẬT — KHÔNG đi đường `content`.** Evidence bẫy: `stageCount` (`PipelineStageFolders.tsx:74-76`) — nhánh `content` bắt truyền `count` tay (`:68-69` "Bắt buộc khi dùng content — component không đếm hộ được") còn nhánh leaves tự đếm; `content` còn mất luôn hideEmpty/failed/leaf-styling. `groups` với leaves thật ⇒ count DERIVED, bất biến cấu trúc `stage.leaves == concat(groups[].leaves)` ⇒ Σ đúng by-construction. + +1. **Type** (`PipelineStageFolders.tsx` vùng `:58-72`): `export type PipelineGroup = { key: string; label: string; leaves: PipelineLeaf[] }` + `PipelineStage.groups?: PipelineGroup[]`. GIỮ `stage.leaves` = flat tổng (nguồn `stageCount:74-76` KHÔNG đổi — badge folder cha đếm như cũ). +2. **Render**: trong khối `ml-3 border-l` (`:156`), nếu `stage.groups?.length` → mỗi group 1 sub-`
` (chevron + nhãn + chip đếm `g.leaves.length`, tái dùng token `STAGE_STYLE:39-44`), ruột = đúng `