13 KiB
c2-l2
LANE 2/3 — lens ĐỦ-vs-SPEC cho cụm-2 (K4 sidebar 49-row · K5 cây groups · K6 admin 12-key blacklist).
Nguồn chấm: sub-invest-fable-b2-cum2.md + spec-4gd-khkk-tong-quat-31-07-2026.md (§③ cụm-2, §② QĐ6/7, OG-8) + spec-cum1-chi-tiet-31-07-2026.md (12 vá).
Tất cả evidence dưới đây tao tự đo lại trên ĐĨA/DB (không tin lời khai của lane).
Findings
HIGH
H1 — queryKey thiếu group ⇒ 8 nhóm dùng CHUNG 1 ô cache (silent-wrong, build xanh).
- Claim của lane: K4b mục 7 (
sub-invest-fable-b2-cum2.md:31) liệt đúng 2 chỗ —searchParams.get('group')cạnh:60-61và truyền vàoparams:75-82(cả nhánh/deleted:74) — KHÔNG cóqueryKey. - Evidence:
fe-user/src/pages/khkk/KhkkListPage.tsx:70→queryKey: ['khkk-list', { phase, search, page, deletedView }]. File này SHA-identical với fe-admin (sha25616 ký tự đầu =40a650bf6964ab4bcả 2 app) ⇒ lỗi nhân đôi. - Hệ quả: đổi
?group=KHÔNG đổi khoá cache ⇒ leaf nhóm 2 render danh sách nhóm 1 (rồi bị ghi đè chéo).tsc+npm run build+ test đều XANH. Cùng họ với S159-③ (2 khoá cho 1 resource) nhưng CHIỀU NGƯỢC: 1 khoá cho 8 resource. - Sev: HIGH. Đề nghị: mục 7 thêm gạch "
groupVÀOqueryKey:70"; A2 thêm bước "bấm G1→G2→G1 KHÔNG F5, số phải đổi".
H2 — Cỗ máy làm 42 leaf mới bấm-được (regex) nằm ở K4c (chờ K3), nhưng acceptance A1/A2 đo nó ở nấc K4a/K2 ⇒ A1 bất-khả-PASS.
sub-invest:43A1 = "48 leaf HIỆN sidebar" và:119khẳng định "A1/A3-A6 đo được NGAY sau K4a". Nhưng URL choKhkk_G{n}_*chỉ sinh ở mục 10 (:37) = K4c, mà K4c:114/:120CHỜ K3.- Evidence code:
fe-user/src/components/Layout.tsx:338if (!path) return nulltrongMenuLeaf⇒ key không có trongstaticMap/regex = DROP IM LẶNG (chính gotcha #50 mà file này tự dán ở:151-153). Sau K4a-only: 7 group-row hiện, 42 leaf KHÔNG render ⇒ A1 đếm 6, không phải 48. - Sev: HIGH (acceptance khẳng định đo được ở nấc mà nó chắc chắn trượt).
- Đề nghị: tách mục 10 → 10a (List/Pending/Approved/Deleted/Create — 0 phụ thuộc, kéo về K4a) + 10b (WfView filter theo Code — giữ ở K4c chờ K3). Hoặc sửa A1 thành "đo sau K4a+10a".
H3 — 2 con số drift STATUS SAI trong MỌI kịch bản của chính thứ tự ship lane đề xuất.
- Lane:
sub-invest:27/:128"Menu keys 64→113 · Policies 256→452 (55→64 đã khai cụm-1)". - Canonical đo tươi:
docs/STATUS.md:471Menu keys = 55;:470Policies = 220 (DERIVED|MenuKeys.All| × 4). - Base 64 = 55 + 9, mà 9 key cụm-1 = 1
ContractCatalog(K1) + 8AwV2_KhkkN*(K3) theospec-cum1-chi-tiet:8. Thứ tự ship lane đề xuất (:117) làK6 → K4a → [K2] → K4b+K5 → [K3] → K4c⇒ lúc commit K4a, K3 chưa land ⇒ STATUS đang ở 55 hoặc 56, không bao giờ 64. Ghi "64→113" = ghi sai canonical màgovernance-detectors.ps1đọc. - Sev: HIGH (số mang nhãn đo-lường; phép kiểm làm nó trượt =
grep 'Menu keys' docs/STATUS.mdngay trước commit). - Đề nghị: khai DELTA (+49 key / +196 policy) + luật "đọc row STATUS ngay trước commit rồi cộng delta" (B1).
MED
M1 — Lý do giữ site-2 SAI (kết luận vẫn ổn). sub-invest:24 khẳng định "group-key CẦN CanRead row để render". Đo: src/Backend/SolutionErp.Application/Permissions/Queries/GetMyMenuTree/GetMyMenuTreeQuery.cs:96-97 — static bool HasAccess(MenuNodeDto n) => n.CanRead || n.Children.Any(HasAccess) ⇒ cha render khi CON có CanRead. Extend site-2 cho 7 group-key = tuỳ chọn (thêm ~91 row Permission), không phải điều kiện render. Giữ hay bỏ đều được — nhưng phải sửa lý do, kẻo lượt sau suy sai sang cây khác.
M2 — "3 site" bỏ sót site thứ 4 lịch sử (revoker #84); tao đo hộ: an toàn. RevokeTemporarilyHiddenModulesAsync (DbInitializer.cs:2286) được gọi ở :2097 — GIỮA site-2 (:2090) và site-3 (:2110). Prefix revoke đo được :2297-2300 = Hrm* / Off* / Personal ⇒ Khkk* KHÔNG dính. Kết luận lane đúng nhưng chưa từng soi chỗ đã lật 447/494 row (#84). Đề nghị: thêm 1 dòng "site-4 revoker: đã soi, 0 match Khkk" — A5 (restart ×2) chính là phép thử nó.
M3 — phase không re-sync theo URL ⇒ A2 "lọc đúng group+phase" sẽ trượt khi bấm leaf→leaf. fe-user/src/pages/khkk/KhkkListPage.tsx:63-65 khởi tạo useState(initialFilter ? FILTER_TO_PHASE[...] : null) — chỉ chạy lúc MOUNT; grep cả file: setPhase( duy nhất :153 (chip lọc). Route /khkk/list không keyed (fe-user/src/App.tsx:81 element={<KhkkListPage />}) ⇒ đổi query KHÔNG remount. deletedView thì đọc thẳng searchParams :61 nên phản ứng đúng — lệch nhau trong cùng 1 file. Pre-existing với 4 leaf G1, nhưng ×8 nhóm và A2 đo trúng nó. Đề nghị: K4b thêm việc "derive phase từ searchParams" HOẶC hạ A2 xuống "mỗi leaf mở bằng F5".
M4 — "Thao tác" (Create) bị dời từ K4b sang K4c ⇒ trễ 1 wave vô cớ + lệch spec owner đã đọc. spec-4gd-khkk-tong-quat:29 xếp "K4b 4 leaf list + Thao tác (chờ K2 param)"; sub-invest:29 đổi tiêu đề K4b thành "4 leaf list" và đẩy Create preset xuống :39 (mục 12, K4c "chờ K3"). Preset chỉ cần picker của vá-4 cụm-1 (K2) — 0 phụ thuộc K3. Đề nghị: trả mục 12 về K4b (đi cùng 10a ở H2).
M5 — 8 nhãn menu tự chốt ngoài artifact OG-6, mà chi phí sửa-sau KHÔNG rẻ như lane nói. sub-invest:131 "owner chỉnh CHỮ tự do… không chặn wave nào" >< chính :11 khai "sửa chữ trong tree.Add VÔ TÁC DỤNG DB đang chạy" (comment DbInitializer.cs:1959-1961) ⇒ mỗi lần owner đổi chữ sau khi seed = +1 dòng labelBackfill + restart. Bảng OG-6 owner đang soát (danh-muc-sp002-transcribe.md:7-16) chỉ có cột Nhóm/Ký-chính/Nháy — không chứa 8 nhãn này. Kèm 1 sai lệch nội dung: nhãn N2 "Kết cấu, hạ tầng & DV hỗ trợ thi công (A2+A3)" mất vế "phá dỡ" (transcribe:29 A2 = "Kết cấu, hạ tầng, phá dỡ"; dòng :30 A2-01 = "Thầu phụ hạ tầng & phá dỡ"). 6/8 nhãn còn lại khớp transcribe (N7 rút gọn thành tên KHỐI "BCH công trường" — chấp nhận được). Đề nghị: đính 8 nhãn vào chính file OG-6 để owner soát 1 lượt, trước K4a.
M6 — Quyết "fe-admin KHÔNG nới regex" buộc chặt vào K6, mà K6 là display-only đảo được bất cứ lúc nào. sub-invest:40/:94 (dead-by-K6) >< :93 (route admin còn nguyên, thu gọn = menu). Nếu owner bỏ ẩn KeHoachKyKet trên admin ⇒ 48 leaf DROP IM LẶNG (#50), không ai nhớ ràng buộc. Đề nghị: nới regex fe-admin luôn (rẻ, 2 file đang SHA-identical) HOẶC ghi ràng buộc ngược ngay tại ADMIN_HIDDEN_KEYS: "gỡ key này ⇒ PHẢI nới regex Layout.tsx trước".
M7 — [Q6 tự-chốt] "Group RỖNG không render" đi ngược scope "cây mirror sidebar". sub-invest:55. Spec ① :7 gọi mảnh-3 là "cây mirror sidebar"; DoD K5 spec-tổng:30 = "sub-folder khớp sidebar". Sidebar LUÔN đủ 8 nhóm (menu-row cố định) còn cây ẩn nhóm rỗng ⇒ 2 mặt hết mirror; user tìm "nhóm 5" trên cây tưởng mất. Lý do lane nêu (8 folder rỗng = noise) hợp lý nhưng đây là quyết-định hiển-thị của owner. Đề nghị: hỏi 1 câu / hoặc mặc định hiện đủ 8 + toggle "ẩn nhóm rỗng".
LOW
L1 — A5 đo nhãn G1 có thể PASS-ở-DB nhưng SAI-ở-UI. MenuItem.cs:13,16: DisplayLabel override label cho fe-user (IsVisible=false ẩn khỏi /menus/me). labelBackfill sửa Label, không sửa DisplayLabel. Đo Dev: 10/10 row Khkk* + KeHoachKyKet có DisplayLabel = NULL, IsVisible = 1 ⇒ hiện an toàn. Đề nghị A5 thêm vế AND DisplayLabel IS NULL.
L2 — [PASS mạnh, có số] Tập key K6 ĐỦ trên Dev; C1 chốt N = 7. Đo SolutionErp_Dev: MenuItems TOTAL = 142 (khớp con số STATUS ghi cho prod); root (ParentKey NULL) = 12 = {Dashboard 10, PurchaseEvaluations 25, KeHoachKyKet 26, Hrm 28, Off 29, Personal 30, Contracts 31, HopDongCung 32, Forms 40, Reports 50, Master 80, System 90} ⇒ 10/12 nằm trong ADMIN_HIDDEN_KEYS, 2 chừa đúng {Dashboard, System}; con của System = 7 {ApprovalWorkflowsV2, MenuVisibility, Permissions, PeWorkflows, Roles, Users, Workflows} ⇒ ẩn 2 (Workflows, PeWorkflows), chừa 5. AwV2_* = đúng 2 row, 0 AwV2_Contract ⇒ N = 7 lá (Dashboard + Users + Roles + Permissions + MenuVisibility + 2 AwV2). Còn lại chỉ cần xác nhận prod.
L3 — "route-map ×2 app khai trong PR" trỏ artifact không tồn tại. 0 hit route-map trong docs/PROJECT-MAP.md|STATUS.md|HANDOFF.md; và cụm-2 KHÔNG đẻ route React nào — 48 leaf tái dùng /khkk/list|create|workflow-matrix đã có (fe-user/src/App.tsx:81-83). Surface drift thật = resolvePath trong Layout.tsx (fe-user) + 2 row STATUS. Đề nghị đổi chữ thành "bảng key→URL dán PR body".
L4 — Thứ tự sidebar 4 GĐ không liền mạch (đo Dev, xem L2): GĐ1=25, GĐ2=26, Hrm 28 / Off 29 / Personal 30, GĐ3=31, GĐ4=32. Sau K4a, GĐ2 phình thành 8 nhóm ngồi giữa dải bị chen. Owner S159 vẽ "menu = 4 giai đoạn" (DbInitializer.cs:1772-1773) nên đây là thứ anh ấy sẽ thấy ngay. Không thuộc scope — đề nghị hỏi 1 câu (đổi Order 27/28), cơ chế upsert re-set Order đã có (:1926-1943).
L5 — Lý luận bất-đối-xứng giữa K4c và K5 về bề mặt admin. PipelineTreePanel render trong fe-admin/src/pages/khkk/KhkkListPage.tsx:139 và fe-admin/src/pages/contracts/HardCopiesPage.tsx:117 ⇒ sau K6 hai trang này mất lối vào từ menu admin (chỉ deep-link). Lane đã khai chuyện "dead-by-K6" cho regex nhưng KHÔNG khai cho K5. Giữ mirror (đúng §3.9) nhưng nên khai 1 dòng để lượt sau không tưởng thừa.
L6 — B2 khả thi, 0 nợ drift ẩn (đo trước): 6/6 cặp file ×2 app hiện SHA-identical — usePipelineStages.ts 7144eeb7… · PipelineStageFolders.tsx d449cc8d… · PipelineTreePanel.tsx d70ac315… · KhkkListPage.tsx 40a650bf… · KhkkCreatePage.tsx ea51e762… · types/khkk.ts 8b757f5b… (types/khkk.ts ĐÃ tồn tại sẵn ⇒ K5 mục 5 là mở rộng, không phải tạo mới).
Điểm chịu được soi (khai để không hạ chuẩn 1 chiều)
- P1 — mục 5 "sửa grounding 13 vai" ĐÚNG:
DbInitializer.cs:2427looproleManager.Roles(mọi role),allowDelete = Admin || Drafter(:2436-2437); "7 vai" thật sự là seeder PE:2669-2675. Lane bác đúng đề bài, có chứng. - P2 — "extend
KhkkKeys()" là BẮT BUỘC, không thừa:InReviewScope(key) => key.StartsWith("Pe_")(DbInitializer.cs:2158) ⇒ đưa 49 key vàoMenuKeys.AllKHÔNG tự kéo chúng vào site-2. - P3 — trích dẫn dòng sạch 100%: tao soi ~25 cite (seed
:1760/:1771/:1775/:1776/:1780-1787/:1788/:1789/:1791-1794/:1795/:1797/:1798/:1801-1803/:1809/:1827/:1853·KhkkKeys():2185-2189·SeedKeHoachKyKetAccessAsync:2415+ keys:2420-2425·isAdminHidden:182-184+filterForAdmin:186-190+ comment S57:179-181· fe-userLayout.tsx:59-62/:79/:82-83/:341-347·KhkkListPage:60-61/:74/:75-82·PipelineStageFolders:58-72/:68-69/:74-76·usePipelineStages:189+/:222-224/:231/:240) — 0 sai. - Q5 trả lời: việc "viết lại comment
:177-184" CÓ trong danh sách (K6 mục 2) và mô tả đúng::179-181= "[S57] BỎ ẩn Danh mục… (đảo S29 hide)" ⇒ QĐ7 là lần đảo THỨ 2. Không rơi. - Q1 trả lời gọn: K4a-labelBackfill ✓ · K4b
?group✓ (thiếu queryKey — H1) · K4c WfView-lọc-Code ✓ (mục 11, KHÔNG rớt) · Create-preset ✓ nhưng sai wave (M4) · K5 GĐ3/4 group-byc.type✓ (mục 4, có làm, không chỉ GĐ2) · K5 fallback "(chưa phân nhóm)"/"(ngoài chỉ mục)" ✓ · K6git diff --stat -- fe-user/src = 0 file✓ (C2). Mục RƠI thật sự = 1:queryKey. - Q4 trả lời: precedent-split khai ĐỦ 2 khuôn (
sub-invest:9+ evidenceDbInitializer.cs:2193-2195). Chỗ người build dễ nhầm = (a) tưởng "vào All ⇒ site-2 tự có" — bác bằng P2; (b) tưởng "không có row ⇒ không render" — bác bằng M1.
Verdict
PASS-WITH-FLAGS — 16 finding (3 HIGH · 7 MED · 6 LOW), 0 blocker chặn ship nếu H1+H2+H3 được áp trước khi K4a/K4b vào việc. Nền cụm-2 chắc: tập key K6 đo được là ĐỦ (L2), trích dẫn sạch 100% (P3), 2 claim chịu-lực đúng (P1/P2). Lỗ nằm ở 3 chỗ quen thuộc: cache-key (H1), thứ-tự-vs-acceptance (H2), số-tuyệt-đối-trong-doc (H3).