Files
solution-erp/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-reviewer-gate-k4b.md
2026-08-01 09:59:59 +07:00

3.8 KiB
Raw Blame History

GATE-K4B: IN-PROGRESS

Gate K4b — adversarial review wave K4b (nối ?group= end-to-end), S166

Mốc đo (neo lúc mở gate): HEAD = 14ea2ea (wal: flush 20260801T0945); commit code cuối = 7a903cf. Spec nói HEAD 7a903cf — thực tế trên đĩa có thêm 6 commit wal: phía trên (chưa xác minh chỉ-WAL, sẽ đo). git status --porcelain lúc mở gate: 18 M + 1 ?? (tests/.../KhkkGroupMenuSeedTests.cs). git diff --stat: 541 insertions / 82 deletions / 18 files.

Scope chấm = K4b only: KhkkListPage ×2 · KhkkCreatePage ×2 · fe-user/Layout.tsx (staticMap-G1) · BE ContractSigningPlansController.cs (/deleted +group) · ContractSigningPlanFeatures.cs (ListDeleted). K4a (MenuKeys/DbInitializer/Layout-regex/types/STATUS/test) ĐÃ qua gate K4a — không chấm lại.


Ghi TỪNG MỤC (append khi đo xong, không soạn-gộp)

M1 — Đo nền (tự chạy, không tin claim)

  • dotnet build SolutionErp.slnxBuild succeeded, 0 Warning, 0 Error (56.4s, tôi tự chạy).
  • npx tsc -b fe-admin — EXIT=0; fe-user — EXIT=0 (tôi tự chạy, không đọc log nền của lane).
  • WAL 6 commit 7a903cf..HEAD chỉ chạm .claude/** (đo git diff --stat) ⇒ toàn bộ code K4a+K4b nằm ở working-tree. Mốc so sánh dùng 7a903cf là ĐÚNG về nội dung code dù HEAD ghi khác.

M2 — Mirror 2 app (sha256, 16 ký tự đầu)

file fe-admin fe-user
pages/khkk/KhkkListPage.tsx fa4c7aa268f35eeb fa4c7aa268f35eeb MATCH (khớp con số fa4c7aa2 spec nêu)
pages/khkk/KhkkCreatePage.tsx 545b4c2fe0ebae88 545b4c2fe0ebae88 MATCH
types/khkk.ts 7a99b86518948093 7a99b86518948093 MATCH

Layout.tsx lệch by-design (đã khai trong cả 2 file: admin Khkk_WfView → Designer, không mang group; admin không nới regex Khkk_G*).

M3 — FLAG-1 (code LEAD tự viết) — BE /deleted + group

  • Ký-pháp: public record ListDeletedContractSigningPlansQuery(int? ApprovalGroup = null) : PagedRequest, .... PagedRequestabstract record (Common/Models/PagedResult.cs:15) với 4 property init ⇒ trộn positional-ctor + object-initializer HỢP LỆ (record kế thừa record; nếu PagedRequestclass thì đã không biên dịch được). Build 0E xác nhận.
  • Controller mirror List ĐÚNG TỪNG NÉT: [FromQuery(Name = "group")] int? group = null đặt ở vị trí tham số đầu, new Query(group) { Page = …, PageSize = …, Search = …, SortDesc = … } — cùng khuôn List (:42, anchor này ĐÚNG, tôi đếm lại).
  • Handler: vế lọc if (request.ApprovalGroup is not null) q = q.Where(p => p.ApprovalGroup == request.ApprovalGroup) nằm SAU khối IDOR non-admin và TRƯỚC Search — đúng thứ tự list sống (:755-758). IgnoreQueryFilters().Where(x => x.IsDeleted) giữ nguyên phía trên ⇒ lọc nhóm không đụng vế soft-delete.
  • ContractSigningPlan.ApprovalGroupint không nullable, default = 1 (ContractSigningPlan.cs:41) ⇒ so sánh == request.ApprovalGroup (int? vs int) dịch SQL thành so sánh giá trị, không sinh ca NULL-semantics.
  • Không có validator biên 1..8 cho /deleted — nhưng list SỐNG cũng không có ⇒ consistent, ghi nhận chứ không tính lỗi. ?group=999 = lọc rỗng (200 + items: [], total: 0), không 500. ?group=abc = model-binding fail ⇒ 400 do [ApiController]. Cả hai hành vi giống hệt list sống.
  • FE gửi group cho cả 2 nhánh (KhkkListPage.tsx:124, nằm ngoài tam-nguyên deletedView nên áp cho cả /contract-signing-plans lẫn /deleted) ⇒ khớp BE mới. Title/subtitle màn "Đã xóa" in tên nhóm — hết là lời nói dối vì server đã lọc thật.