Files
solution-erp/.claude/workflows/runs/2026-07-30-S162-cay-4-folder-gd/sub-reviewer-1.md
2026-07-30 09:46:39 +07:00

4.8 KiB
Raw Blame History

sub-reviewer-1 — T2 review đối-kháng cây 4-folder GĐ (S162, 2026-07-30)

Ghi-đĩa-trong-lúc-làm (chống #53). Append theo từng mục soi. Verdict cuối ở cuối file.

Phạm vi đo (từ đĩa, git status --porcelain + git diff --stat)

  • 6 file tracked M: fe-{user,admin}/src/pages/pe/PurchaseEvaluationsListPage.tsx · .../khkk/KhkkListPage.tsx · .../contracts/HardCopiesPage.tsx — tổng +168/16.
  • 6 file MỚI untracked: fe-{user,admin}/src/components/pipeline/{PipelineStageFolders,PipelineTreePanel}.tsx + fe-{user,admin}/src/hooks/usePipelineStages.ts.
  • Ngoài src: memory frontend-designer (MEMORY.md + archive/2026-06.md + archive/_INDEX.md), .session-counter.json, run-folder artifacts (2 png + 2 harness .txt + sub-frontend-designer-0.md).

MỤC 1 — Claim "khối <li> phiếu giữ nguyên từng byte" — ĐẠT

git diff -- .../pe/PurchaseEvaluationsListPage.tsx (cả 2 app, diff IDENTICAL):

  • Hunk A @@ -29,6 +29,8 — +2 import. 0 xoá.
  • Hunk B @@ -47,6 +49,14 — +8 dòng (6 comment + usePipelineStages() + filterActive). 0 xoá.
  • Hunk C @@ -507,7 +517,12 — xoá ĐÚNG 1 dòng <ul className="ml-3 divide-y divide-slate-100 border-l border-slate-200">.
  • Hunk D @@ -607,6 +622,7 — +1 dòng ) })} />. 0 xoá. ⇒ Tổng xoá = 1 dòng. Khối <li> KHÔNG có dòng - nào ⇒ CLAIM ĐỨNG. Class cũ không mất: ml-3 border-l border-slate-200 → prop className wrapper; divide-y divide-slate-100 giữ ở <ul> trong.

MỤC 6 — Biên giới — ĐẠT

  • git diff --name-only (tracked): 0 file src/Backend, 0 tests/, 0 seeder/menu (#84), 0 PePipelineStrip.tsx. Chỉ 6 file FE + memory/run-artifact ngoài src.
  • grep -rn "export default" trong 6 file mới ⇒ NONE (named export only, đúng §3.9).
  • Nhãn VERBATIM PipelineStageFolders.tsx:22-27: 1:'Duyệt NCC' · 2:'Kế hoạch Hợp đồng' · 3:'Duyệt Hợp đồng' · 4:'Hợp đồng cứng' — khớp lời owner từng chữ.
  • Mirror SHA256 6/6 PAIR (tự đo lại, không tin bảng designer): PipelineStageFolders 05c93c3619a9cfec · PipelineTreePanel 76e5dafb239e402a · usePipelineStages e762d5eb9c3a057a · PE page 1d8d8f75c7d2cc14 · Khkk page 40a650bf6964ab4b · HardCopies page d30a911e7483f96a.
  • types/contracts.ts lệch 2 app CHỈ ở giá trị ContractPhaseColor (cùng bộ key 1-10,99) ⇒ hook SHA-chung import an toàn, badgeClass undefined vẫn có fallback PipelineStageFolders.tsx:189.

MỤC 3 — Permission-gate 2 tầng — 1 LỖ THẬT (latent) + 1 điểm sai luận-cứ

Key FE (usePipelineStages.ts:41-44) vs BE:

  • Khkk_List/Khkk_G1/KeHoachKyKet — cả 3 tồn tại thật (DbInitializer.cs:2184-2188 KhkkKeys + MenuKeys.cs:41).
  • Hdc_<7 code> + HopDongCung — tồn tại thật (DbInitializer.cs:1868/2191).
  • Ct_<code>_List + Contracts — tồn tại thật.
  • can(k) = node.canRead (usePermission.ts:20), findNode không thấy ⇒ false ⇒ OR-nhiều-key hợp lệ về mặt đọc cây.

🔴 F: OR-nhiều-key ĐÁ NHAU với policy API cho GĐ2. ContractSigningPlansController.cs:36 = [Authorize(Policy="KeHoachKyKet.Read")], và MenuPermissionHandler.cs:40 so khớp CHÍNH XÁC p.MenuKey == req.MenuKey (KHÔNG kế thừa, KHÔNG OR con). ⇒ role có Khkk_List=1KeHoachKyKet=0 sẽ: FE cho canKhkk=true → gọi API → 403khkkFailed → GĐ2 hiện "—" dưới MỌI gói thầu, và claim "0 rác 403" vỡ. Đo Dev DB: 13/13 role đều có Contracts=1KeHoachKyKet=1 ⇒ HIỆN CHƯA vỡ; prod chưa đo được (chỉ localhost+secret). Là lỗ THIẾT KẾ chờ 1 lần chỉnh ma trận quyền. 🟡 luận-cứ trong comment usePipelineStages.ts:10-13 ("gate phải OR nhiều key vì root có thể canRead=false") đúng cho TẦNG HIỂN THỊ nhưng sai đích cho tầng API: query nào cũng nên gate bằng ĐÚNG key policy của endpoint (KeHoachKyKet cho /contract-signing-plans). 🟢 /contracts KHÔNG có policy action-level (ContractsController.cs:16-17, chỉ [Authorize] trần class) ⇒ nhánh enabled: canContract || canHardCopy KHÔNG sinh 403. Đổi lại: đây là bare-[Authorize] sẵn có của hệ (#82), diff này không làm tệ thêm nhưng có nới hiển-thị: user chỉ được cấp Hdc_ThauPhu vẫn thấy leaf GĐ4 của MỌI loại HĐ (usePipelineStages.ts:211) vì gate là OR-toàn-nhóm chứ không theo c.type. 🟢 Không quyền ⇒ enabled:false (:77, :91, PipelineTreePanel.tsx:108) ⇒ KHÔNG gọi API; folder khoá render nhánh riêng KHÔNG <details> (PipelineStageFolders.tsx:99-113). Fetch lỗi ⇒ failed ⇒ badge "—" + body "Không tải được…" (:140-156) ⇒ cây không vỡ. ĐẠT.