14 KiB
14 KiB
c2-l1
LANE 1/3 — LENS QUYẾT-vs-CODE. Đối tượng: sub-invest-fable-b2-cum2.md (K4 sidebar 49-row · K5 cây groups · K6 admin 12-key blacklist; 0 mig).
Dòng-1: C2L1: 4/7 ĐÚNG · 0 SAI · 3 LỆCH — 7 finding (2 MAJOR · 3 MEDIUM · 2 LOW), 0 blocker về QUYẾT.
Findings
F-1 [MEDIUM · LỆCH lý-do, giữ kết-luận] "group-key CẦN CanRead row để render" — bác bằng code
- Spec
:24biện hộ site-2 extend +7 group vì "group-key CẦN CanRead row để render". GetMyMenuTreeQuery.cs:96—static bool HasAccess(MenuNodeDto n) => n.CanRead || n.Children.Any(HasAccess);(filter:97-101) ⇒ node không có CanRead vẫn được giữ nếu bất kỳ con nào readable.- ⇒ CanRead cho
Khkk_G2..G8không phải điều kiện cần. Quyết định (extend site-2) vẫn hợp lý vì tính nhất-quán với G1, nhưng LÝ DO sai ⇒ đẻ bất-biến giả cho lượt sau. - Đề nghị: đổi câu lý do thành "đồng bộ với G1 (
KhkkKeys():2185-2189đang cấp CanRead choKhkk_G1), KHÔNG phải điều kiện render".
F-2 [🔴 MAJOR · mâu thuẫn NỘI TẠI: acceptance A1/A2 không thể PASS ở nấc spec tự xếp]
- Spec
:119— "Acceptance A1/A3-A6 đo được NGAY sau K4a";:104— ship K4a một mình thì "leaf RENDER được, LỌC chưa đúng";:33gọi đó là "silent-wrong". - Code bác:
fe-user/src/components/Layout.tsx:336-338⇒ leaf không có route bị DROP IM LẶNG (đúng gotcha #50 mà chính spec trích ở mục 7/10; comment cùng fileconst path = resolvePath(node.key) const location = useLocation() if (!path) return null:86+:98nói y vậy). - Route cho
Khkk_G{n}_{...}chỉ sinh ở K4c mục 10 (regexresolvePath), mà sơ đồ:114+ thứ tự:117xếp K4c CUỐI CÙNG vì "chờ K3". - ⇒ Trạng thái thật sau K4a-một-mình: 7 group
Khkk_G2..G8hiện rỗng, 42 leaf KHÔNG hiện ⇒ A1 ("48 leaf HIỆN sidebar") FAIL by-construction; A2 cũng không bấm được. Failure-mode tệ hơn mô tả của spec (vô hình ≠ lọc sai). - Đề nghị (rẻ): tách K4c mục 10 khỏi phụ-thuộc K3 và ship CÙNG K4a. Regex chỉ cần khuôn URL (
/khkk/list?group=n,&filter=…,?view=deleted,/khkk/create?group=n,/khkk/workflow-matrix?type=10&group=n) — không cần CodeKHKK-N{n}. Chỉ mục 11 (WfView lọcCode === 'KHKK-N'+n) mới thật sự chờ K3. Sửa kèm::104,:119, sơ đồ:106-115, checklist:125.
F-3 [MEDIUM · chú-thích đặt nhầm app] BÁC-2 đúng, nhưng K6 là "điều kiện tiên quyết ẩn" của K4c-13
- Verify 2 dòng spec nêu:
fe-admin/src/components/Layout.tsx:186-190filterForAdminđệ quy (.filter(n => !isAdminHidden(n.key)).map(n => ({...n, children: filterForAdmin(n.children)}))), gọi tại:315⇒ ẩn root = subtree không render ✅ ĐÚNG. - fe-admin không có
WorkflowMatrixViewPage: grep toàn repo — file chỉ ởfe-user/src/pages/pe/WorkflowMatrixViewPage.tsx; fe-admin chỉ có comment nhắc tên tại:130; route/khkk/workflow-matrixchỉ tồn tạife-user/src/App.tsx:83✅ ĐÚNG (comment lệch-có-chủ-đích:129-133khớp verbatim). - 🔴 Nhưng spec mục 13 + K6-4 dặn ghi comment khai lệch tại regex fe-user. Rủi ro nằm ở fe-admin: ai gỡ
'KeHoachKyKet'khỏiADMIN_HIDDEN_KEYSlượt sau ⇒ 42 leaf render,resolvePathfe-admin trả null (fe-admin/Layout.tsx:268-270) ⇒ drop im lặng. - Đề nghị: comment nằm NGAY tại
ADMIN_HIDDEN_KEYS/isAdminHidden(fe-admin/Layout.tsx:182-184), nội dung "gỡKeHoachKyKetkhỏi set ⇒ phải nới regexKhkk_G{n}_*cùng lượt, nếu không 42 leaf drop im lặng (#50)".
F-4 [MEDIUM · dụng cụ đo sai tầng] C1: SQL MenuItems LIKE 'AwV2%' KHÔNG chốt được N
- Đo thật Dev LocalDB (
SolutionErp_Dev):SELECT [Key] FROM MenuItems WHERE [Key] LIKE 'AwV2%' OR [Key] LIKE 'Khkk%'⇒ AwV2 = 2 row (AwV2_DuyetNcc,AwV2_DuyetNccPhuongAn), Khkk = 7 row (Khkk_G1+ 6 leaf) ✅ khớp "7 key cũ".AwV2_Contract= 0 hit toànsrc/ · scripts/ · fe-*/src(chỉ có so chuỗicode === 'Contract'ởfe-admin/Layout.tsx:169). - 🔴 Row MenuItems tồn tại không kéo theo hiện sidebar:
AwV2_Contract∉MenuKeys.All(MenuKeys.cs:180-183chỉ 3 key AwV2) ⇒ site-1:2058không phát row Admin, cũng không nằm trongreviewKeys(:2210-2224) ⇒resolvedthiếu key ⇒HasAccess=false(GetMyMenuTreeQuery.cs:96) ⇒ bị lọc khỏi cây dù row tồn tại. N=8 chỉ xảy ra khi prod có CẢ row MenuItems LẪN row Permissions CanRead (grant tay). - Đề nghị đổi phép đo C1 sang JOIN, chạy trên env nghiệm thu (prod), không phải Dev:
SELECT m.[Key] FROM MenuItems m JOIN Permissions p ON p.MenuKey=m.[Key] JOIN Roles r ON r.Id=p.RoleId WHERE r.Name='Admin' AND p.CanRead=1 AND m.[Key] LIKE 'AwV2%'.
F-5 [LOW-MED · A5 đo nhãn sai dụng cụ] DisplayLabel che Label ở fe-user
MenuItem.cs:12-13khai rõ:IsVisible=false→ ẩn khỏi/menus/mefe-user, fe-admin vẫn thấy;DisplayLabel="X"→ fe-user render X thay Label, fe-admin giữ Label.- Xác nhận code:
fe-user/Layout.tsx:204-206effectiveLabel = (displayLabel && trim) || label; fe-admin 0 hitdisplayLabel. - ⇒ A5 "labelBackfill đổi đúng 1 nhãn G1" đo bằng UI ×2 app có thể ra 2 nhãn khác nhau nếu prod từng đổi tên qua MenuVisibility (Mig 27) ⇒ chẩn đoán nhầm "backfill không ăn".
- Đề nghị: A5 đo CỘT —
SELECT [Key],Label,DisplayLabel FROM MenuItems WHERE [Key]='Khkk_G1', khai rõ nếuDisplayLabel IS NOT NULL.
F-6 [🔴 MAJOR · nền drift treo vào thứ tự ship] "64→113 / 256→452" chỉ đúng NẾU cụm-1 land trước
- Số học tự nhất quán ✅: 49 = 7 + 42 · 64×4 = 256 · 113×4 = 452 · 64+49 = 113.
- Nền hiện tại đo thật:
MenuKeys.Allđếm tay = 55 (Dashboard 1 + Master-set 4 + Catalogs 5 + Contracts/Forms/Reports 3 + PE 1 + Hrm 2 + HrmConfig 5 + Vehicles/Drivers 2 + Off 3 + PhongHop 4 + DeXuat 4 + DonTu 4 + DatXe/ItTicket/ChamCong/AttReport/HrmDash 5 + Personal 1 + KeHoachKyKet 1 + System-set 7 + AwV2 3) — khớp canonicaldocs/STATUS.md:470-471(Policies 220 = 55×4 · Menu keys 55). Cụm-1 khai 55→64 (+9) tạispec-cum1-chi-tiet-31-07-2026.md:8. - 🔴 Nhưng cụm-2 tự xếp
:118"K6 + K4a chạy song song cụm-1 ngay hôm nay (0 chặn)" + sơ đồ:109-110vẽ K4a độc lập ⇒ K4a có thể land TRƯỚC K1. Khi đó dòng đúng là 55→104 · 220→416; ghi "64→113 · 256→452" = ghi số SAI vào canonical (đúng loại lỗi mà B1/detector đang canh). - Đề nghị: khai drift theo DELTA (
+49 key · +196 policy) + lệnh đo lạiMenuKeys.Alltừ mã tại thời điểm commit; hoặc ràng buộc cứng "K4a ship SAU K1". - Phụ:
MenuItems LIKE 'Khkk%'= 56 sau K4a ✅ (7 đo thật + 49 mới) — rootKeHoachKyKetkhông khớp LIKE nên 56 đúng.
F-7 [LOW · neo số trong chú thích] comment :1959-1961 tự trỏ upsert STALE
- Spec mục 3 trích comment
DbInitializer.cs:1959-1961("sửa chữ trongtree.AddVÔ TÁC DỤNG") — comment đó tự ghi upsert ở ":1893-1905", nhưng upsert THẬT ở:1926-1943(đúng như spec mục 2 ghi). - Diff K4a chạm ngay khối này ⇒ sửa luôn cite trong comment (bài S161: diff sửa mã phải sửa MỌI số/neo trong chú thích).
Bảng verify claim chịu lực (grep/đo, KHÔNG suy)
| # | Claim (spec) | Kết | Dòng chứng |
|---|---|---|---|
| a1 | site-1 All-loop CHỈ Admin | ĐÚNG | DbInitializer.cs:2047 method · :2049 FindByNameAsync(AppRoles.Admin) · :2058 foreach (var menuKey in MenuKeys.All) · :2061 RoleId = adminRole.Id |
| a2 | KhkkKeys():2185-2189 feed reviewKeys |
ĐÚNG (EXACT) | :2185-2189 (7 key: Khkk_G1+6 leaf) → :2216 .Concat(KhkkKeys()).Concat(HdcKeys()) trong reviewKeys :2210-2224; method SeedAllRolesReviewReadPermissionsAsync :2144, gọi :2090 |
| a3 | site-2 = CanRead mọi role, skip-existing | ĐÚNG | :2225 roleManager.Roles · :2251 continue (non-Pe skip) · :2254-2260 insert CanRead=true, CanCreate=isPe(false), CanUpdate=false, CanDelete=false |
| a4 | site-3 độc quyền cờ GHI, comment :2418-2419 |
ĐÚNG (verbatim) | :2415 method · :2418-2419 "Khkk_G1 = group điều hướng, CanRead-all đã có từ nhánh read-only" · :2440-2447 nâng R/C/U + D · site-2 không bao giờ set C/U/D cho key non-Pe |
| a5 | "KHÔNG retire site nào" | ĐÚNG | 3 site không giao nhau về cờ; thứ tự :2090 site-2 → :2097 revoke (chỉ Hrm*/Off*/Personal) → :2110 site-3 ⇒ Khkk không bị revoke |
| a6 | đủ chỗ sửa cho 13-role THẤY leaf | LỆCH | thiếu ràng buộc thứ tự cho resolvePath fe-user ⇒ F-2. Không thiếu: fe-*/lib/menuKeys.ts (0 hit Khkk cả 2 file) · IsVisible mặc-định true (MenuItem.cs:15 + MenuItemConfiguration.cs:18) nên fe-user/Layout.tsx:200 không nuốt row mới |
| b | :2427 13/13 role · Delete=Drafter+Admin :2437 · keys từ đâu |
ĐÚNG | :2427 var roles = await roleManager.Roles.ToListAsync() (KHÔNG lọc) · AppRoles.cs:23-28 All = 13 · đo SELECT COUNT(*) FROM Roles = 13 · :2437 role.Name == Admin || Drafter · biến keys = :2420-2425, 7 phần tử, KHÔNG có Khkk_G1 ⇒ convention "group không nhận cờ ghi" là THẬT ⇒ site-3 "+42 leaf (không group)" nhất quán |
| b2 | BÁC-1: "7 vai" là của PE, không áp KHKK | ĐÚNG | :2669-2675 roleNames = Drafter/DeptManager/Procurement/CostControl/ProjectManager/Director/AuthorizedSigner = 7 |
| c | fe-admin khỏi regex (đệ quy + thiếu page) | ĐÚNG (+F-3) | fe-admin/Layout.tsx:186-190 đệ quy, gọi :315 · WorkflowMatrixViewPage chỉ ở fe-user (fe-user/App.tsx:83 route /khkk/workflow-matrix) · comment :129-133 |
| d1 | bẫy content bắt truyền count tay |
ĐÚNG (EXACT) | PipelineStageFolders.tsx:68-69 "Bắt buộc khi dùng content" · :74-76 stageCount · nhánh content render :164-165 TRƯỚC leaves ⇒ mất empty-state :166-167 + <ul> :169-202 · type :58-72 · ml-3 border-l :156 · STAGE_STYLE:39-44 · hideEmpty :228 |
| d2 | 3 file mirror; PipelineTreePanel không cần đổi |
ĐÚNG | PipelineTreePanel.tsx:16-17 import usePipelineStages+PipelineStageFolders · :93 buildStages · :299 <PipelineStageFolders ⇒ consumer, groups chảy qua. Bonus: 4 file hiện byte-identical ×2 app (10.677 B · 15.819 B) ⇒ B2 đo được ngay |
| d3 | builder GĐ2/GĐ3/GĐ4 cite | ĐÚNG (±1) | GĐ2 usePipelineStages.ts:188-206 (spec ghi 189-206) · approvalGroup 0 hit ⇒ đúng là chờ K2 · "(ngoài chỉ mục 200)" :224 (comment :222-223) · c?.hasSealedCopy :231 · c.type :240 |
| e | 12 key blacklist đủ root, không ẩn nhầm | ĐÚNG (12/12 cite EXACT) | Master :1760 · Contracts :1771 · KeHoachKyKet :1775 · HopDongCung :1776 · Forms :1788 · Reports :1789 · Workflows :1795 · PurchaseEvaluations :1797 · PeWorkflows :1798 · Hrm :1809 · Off :1827 · Personal :1853 (+HrmConfig parent Master :1816). Root trong seed = 12 (thêm Dashboard :1756, System :1790) ⇒ phủ 10 root + 2 con-System; CHỪA Dashboard + System-4-leaf :1791-1794 + AwV2 :1801-1803 ⇒ 0 root nghiệp vụ sót · 0 System-leaf ẩn nhầm · AwV2 sống |
| f | SQL chốt N 7/8 | LỆCH | xem F-4 (đo Dev = 2 AwV2; cần JOIN Permissions + đo trên prod) |
| g | drift 64→113 · 256→452 | LỆCH nền | số học đúng; nền 55/220 (docs/STATUS.md:470-471) ⇒ xem F-6 |
Verified-OK (không phải lỗi — ghi để lane khác khỏi false-flag)
AuthorizePolicyRegressionTests.cs:139+:402chỉ assertMenuKeys.All.Should().Contain(...), không assert|All|⇒ thêm 49 key KHÔNG vỡ test hiện có; "1 test BE" ở checklist:129là bổ sung, không phải sửa test cũ.- "Policy sinh thêm 49×4 registered-unused = vô hại" ✅ —
Program.cs:82loop thuầnAll × Actions, không nơi nào đếm. groupKHÔNG nằm trongTRANSIENT_QUERY_KEYS(fe-user/Layout.tsx:307=id,q,editHeader,page,phase,awId) ⇒groupmang danh tính trongqueryMatches/isActive(:326,:347) ⇒ quyết định K4b mục 8 (6 URL G1 phải manggroup=1) là BẮT BUỘC, spec ghi đúng.- Root
KeHoachKyKetcó con ⇒ renderMenuGroup, không điMenuLeaf⇒ không đụng active-check; staticMap:79trở thành đường chết vô hại. - fe-admin vẫn có
pages/khkk/{KhkkListPage,KhkkCreatePage,KhkkDetailPage,KhkkWorkflowPanel}.tsx⇒ mục 13 "fe-admin VẪN sửa 2 page (deep-link)" có cơ sở;PipelineTreePanelcòn nhúng ởfe-admin/pages/khkk/KhkkListPage.tsx:139+pages/contracts/HardCopiesPage.tsx:117⇒ cây K5 sống trên admin qua deep-link dù K6 ẩn menu (không mâu thuẫn; B1 nên đo trên fe-user và khai rõ).
Verdict
PASS-WITH-FLAGS — 0 blocker về QUYẾT (khuôn groups K5, blacklist-12 K6, 3-site-grant K4a đều đứng vững trước code). 7 flag: 2 MAJOR phải xử trước khi ship (F-2 tách regex khỏi K3 ⇒ A1 mới đo được · F-6 khai drift theo DELTA hoặc ràng buộc thứ tự), 3 MEDIUM (F-1 sửa lý-do · F-3 dời comment sang fe-admin · F-4 đổi SQL sang JOIN + đo prod), 2 LOW (F-5 A5 đo cột · F-7 vá cite stale trong comment).