diff --git a/.claude/agent-memory/reviewer/MEMORY.md b/.claude/agent-memory/reviewer/MEMORY.md index 6008d49..4c1daf0 100644 --- a/.claude/agent-memory/reviewer/MEMORY.md +++ b/.claude/agent-memory/reviewer/MEMORY.md @@ -1,6 +1,7 @@ # Reviewer Agent — Persistent Memory -- **S155-đợt2 (07-27) PE delete-approver DIFF-review #2 (12 FLAG 2H/4M/6L · ĐỪNG-DEPLOY):** 🔴 class MỚI **`hợp-đồng-đứt-giữa-2-bờ`** — FE type chép tay khai `allowApproverDelete: boolean`, BE record chỉ 8-member KHÔNG có cờ ⇒ nút CHẾT mà `tsc` + `dotnet build` + 561-test đều XANH (mọi thước đo đo chỗ khác) ⇒ **phép rẻ nhất = đếm member DTO vs khoá FE + `grep -c ` ở file dựng DTO**. · policy-gate `X.Delete` chặn ĐÚNG nhóm cần dùng (đo DB: 11/13 vai `CanDelete=0`, 2 seeder KHÔNG BAO GIỜ nâng) ⇒ owner gỡ policy; **verify "gỡ authz" = truy TỪNG dòng chặn thành bảng + grep `.First()` fallback + grep `Admin` trong chính file** (gỡ policy sinh LOW mới: endpoint thành máy-dò 4-loại-phản-hồi cho mọi tài khoản). · **mã ĐỔI giữa lượt soi ⇒ re-đo đĩa TRƯỚC verdict**; test đổi chiều (`==X` → `==null`) = **ĐỔI-SPEC hợp lệ ≠ nới-assert** vì 2 mệnh đề loại trừ nhau (nới = mệnh đề mới SUY RA được từ cũ). · cascade `Add con → Remove cha → 1 SaveChanges`: chứng "site duy nhất" bằng liệt-kê-vét-cạn 34 `Remove` → lọc 8 → xét cha-con từng cái. +- **S159 (07-29) dashboard "toàn trình" đợt-4 FE-only (fe-user 984+/337−, PWF 7 FLAG 0H/2M/5L):** 🔴 **`tsc` XANH đo ÍT hơn vẻ ngoài** — `fe-user/tsconfig*.json` KHÔNG có `strict`/`strictNullChecks` (chỉ `noUnusedLocals`) ⇒ **tra cờ tsconfig TRƯỚC khi trích tsc làm bằng chứng**; ngược lại `noUnusedLocals`+pass = bằng chứng MẠNH HƠN grep cho claim "đã gỡ khối X" (compiler chặn nếu sót import). · class **`nhãn khẳng định chiều-ngược`**: dot-map `none='chưa tới'` — không-suy-diễn "đã xong" là đúng, nhưng "chưa tới" CŨNG là khẳng định ⇒ sai cho GĐ ĐỨNG TRƯỚC + sai khi `contractId != null` **có sẵn trong payload**. · pager tính `data.page±1` (keepPreviousData ⇒ stale) thay vì state ⇒ double-click = no-op. · **verify đích điều hướng phải đọc CẢ breakpoint trang đích** (panel `hidden lg:block` ⇒ ngõ cụt mobile; trang đích tự có nhánh `matchMedia` mà caller mới không copy). · regression đo HEAD⟂work bằng `grep -c` từng token: count 5→2 mà KHÔNG mất nhãn (inline-prop → `STAGES[i].title`) ⇒ **đếm lệch phải truy CẤU TRÚC trước khi kêu hồi quy**. · tự tính lại 4 tỉ-số contrast + tra `@theme` mọi `var(--color-*)` (0 dòng `--color-*: initial` ⇒ nấc mặc định còn) — 3/3 số designer ĐÚNG. · lời khai sub-file "766→1.130 dòng" SAI (đĩa 1.413 = 766−337+984). [→ `runs/2026-07-29-S159-tong-quan-pipeline-menu/sub-reviewer-diff-dot4.md` 28,5KB] +- **[→ archive/2026-07.md @S159-curate] S155-đợt2 (07-27) PE delete-approver DIFF-review #2 (12 FLAG 2H/4M/6L · ĐỪNG-DEPLOY):** class **`hợp-đồng-đứt-giữa-2-bờ`** (FE type khai cờ, BE DTO không có ⇒ nút chết mà tsc+build+561-test đều xanh) · policy-gate đo DB 11/13 vai `CanDelete=0` · mã ĐỔI giữa lượt soi ⇒ re-đo đĩa TRƯỚC verdict · đổi-chiều-assert ≠ nới-assert. - **[→ archive/2026-07.md @S155-curate] S155 ×2 (07-27) PE delete-approver — DIFF-review đợt 1 tiền-deploy (14 FLAG 4H/6M/3L/1I, DEPLOY-CÓ-RỦI-RO) + SPEC-review (PWF 4H/8M/5L):** 🔴 class MỚI **`HIGH nằm ở git chứ không ở mã`** — 2/3 file migration UNTRACKED mà snapshot ĐÃ tracked ⇒ `commit -a` nạp model-có-cột nhưng bỏ migration ⇒ `Invalid column name` cả 7 module, build+test vẫn xanh ⇒ **gate deploy phải chấm `git status ^??` cho `Migrations/`** · `ExecuteDelete` ngoài transaction TRƯỚC `SaveChanges` = mất dữ liệu không hoàn tác · purge xoá CỨNG chữ ký phiếu XOÁ MỀM ⇒ **test đang KHOÁ chiều ngược, vá xong test đỏ ≠ hồi quy, CẤM nới assert** · ~600 dòng BE = mã chết (`grep api.put`=0) · **xanh-giả bắt bằng phép 2-thế-giới** (còn-gate ⟂ bỏ-gate cùng cho 0 ⇒ 0 thông tin) · CLEAN chứng bằng **liệt kê vét cạn** write-site + sweep gián-tiếp (#81-EXT) · 2 lỗi TỰ BẮT: trích line-number **từ diff** (diff không mang số dòng file) · `grep -v Snapshot` ăn nhầm tên migration · **file bị sửa TRONG LÚC soi ⇒ neo bằng NỘI DUNG DÒNG + ghi bản-đồ-trôi**. - **[→ archive/2026-07.md @S155-curate] S146·S147·S152-D2 (digest):** S146 lead sửa 1 phép quét 4 lần/phiên vì mã-hoá ý-định NGỮ-NGHĨA vào regex ⇒ nấc đúng = lưới-soát-cho-người + allowlist CÓ TÊN + use⟂mention · S147 **decouple 1 gate ⇒ truy xem prop dùng chung đã mang carve-out chưa** (`itemsReadOnly` ≠ raw `readOnly`); "same fix" cho surface anh-em có thể trúng gate KHÁC · S152-D2 census thay spot 307/307; **3 THƯỚC-HỎNG** (md5 disk vs `git show` = FAIL giả do CRLF⟂LF · `grep -c` dính citation-trap · `grep -iF` MSYS trả 0-hit IM LẶNG trên UTF-8) + máy-trích mù đuôi `{.yml,.json,.sql}`. - **[→ archive/2026-07.md @S145-curate] S145 4-lane (Axis-A/D/E + S145b GATE) — digest:** bất-biến chia-đôi giữa 2 axis nằm ĐÚNG SEAM ⇒ chỉ cross-cut sweep bắt (hook 3→4 path orphan; `session_ctx_kb` ghost-wire #H18) · 'vai-có-sổ' acceptance ⇒ MUST `ls agent-memory//` (folder≠sổ, ring1-audit RỖNG) · stale-fix phải verify caller-TRÊN-ĐĨA — grep-"4 surface"=0-hit là ANTI-TEETH Goodhart; 2-script-same-suffix = conflation-vector · đếm VALID_ROLES = per-file grep bidirectional KHÔNG regex (comment lừa parser). diff --git a/.claude/agent-memory/reviewer/archive/2026-07.md b/.claude/agent-memory/reviewer/archive/2026-07.md index 6b6d860..bd4677f 100644 --- a/.claude/agent-memory/reviewer/archive/2026-07.md +++ b/.claude/agent-memory/reviewer/archive/2026-07.md @@ -156,3 +156,9 @@ - **Tự bắt 2 lỗi của CHÍNH mình trước khi báo:** "Mig 47/50/42/46 MISS" = artifact chuẩn-hoá của tao (`Mig42` liền không dấu cách, có ĐỦ 2 phía) · context-regex `.{70}` trả RỖNG đọc nhầm thành sạch → phải `.{0,70}`. ⇒ **LUẬT: mọi MISS phải literal-grep XÁC-NHẬN 2 phía bằng công-cụ THỨ HAI trước khi vào report.** - D re-ground từ đĩa: `docs/gotchas.md` count=83/max=83 ⇒ 0 nhãn `gotcha #N>83`; soi NGƯỢC 24 dòng gotcha ≤83 tìm mirror-error (run-number đội lốt gotcha) → 0 ca, mọi #N đều là gotcha thật trong ngữ-cảnh nguồn. Tag `[s152, d2-fidelity-gate, coverage-diff, extractor-ext-blindspot, thuoc-hong-x3]` + +--- + +## @S159-curate — moved from L1 (S155-đợt2 verbatim, moved-not-cut) + +- **S155-đợt2 (07-27) PE delete-approver DIFF-review #2 (12 FLAG 2H/4M/6L · ĐỪNG-DEPLOY):** 🔴 class MỚI **`hợp-đồng-đứt-giữa-2-bờ`** — FE type chép tay khai `allowApproverDelete: boolean`, BE record chỉ 8-member KHÔNG có cờ ⇒ nút CHẾT mà `tsc` + `dotnet build` + 561-test đều XANH (mọi thước đo đo chỗ khác) ⇒ **phép rẻ nhất = đếm member DTO vs khoá FE + `grep -c ` ở file dựng DTO**. · policy-gate `X.Delete` chặn ĐÚNG nhóm cần dùng (đo DB: 11/13 vai `CanDelete=0`, 2 seeder KHÔNG BAO GIỜ nâng) ⇒ owner gỡ policy; **verify "gỡ authz" = truy TỪNG dòng chặn thành bảng + grep `.First()` fallback + grep `Admin` trong chính file** (gỡ policy sinh LOW mới: endpoint thành máy-dò 4-loại-phản-hồi cho mọi tài khoản). · **mã ĐỔI giữa lượt soi ⇒ re-đo đĩa TRƯỚC verdict**; test đổi chiều (`==X` → `==null`) = **ĐỔI-SPEC hợp lệ ≠ nới-assert** vì 2 mệnh đề loại trừ nhau (nới = mệnh đề mới SUY RA được từ cũ). · cascade `Add con → Remove cha → 1 SaveChanges`: chứng "site duy nhất" bằng liệt-kê-vét-cạn 34 `Remove` → lọc 8 → xét cha-con từng cái. Tag `[s155-dot2, hop-dong-dut-2-bo, policy-gate-do-DB, re-do-dia-truoc-verdict]` diff --git a/.claude/workflows/runs/2026-07-29-S159-tong-quan-pipeline-menu/run.md b/.claude/workflows/runs/2026-07-29-S159-tong-quan-pipeline-menu/run.md index a4868ac..bf3dbb3 100644 --- a/.claude/workflows/runs/2026-07-29-S159-tong-quan-pipeline-menu/run.md +++ b/.claude/workflows/runs/2026-07-29-S159-tong-quan-pipeline-menu/run.md @@ -76,7 +76,20 @@ · ⚠️ **Lưu ý còn hiệu lực:** nhánh grant seeder = **skip-existing, KHÔNG tự nâng row false** — prod đúng nhờ re-grant SQL + revoker thôi lật; prod nào sau này rơi false thì re-grant tay, đừng chờ seeder. · Baseline mới: admin js `CC1pGE1F`/css `DWDbm5As` · user js `DeAg7MWm`/css `BQIZ-rUe` · 38-key 13/13. - + + +## ĐỢT-4 — "bỏ hết mấy cái này, hiện list chi tiết đàng hoàng" + tab strip menu + "Bảng cứng" + +**Owner verbatim:** ① *"bỏ hết mấy cái này đi, hiện theo list chi tiết đàng hoàng luôn. Nghĩ cách bố trí sao cho nó có danh sách phiếu mà User nhìn vào vẫn thấy đc tổng quan liên kết."* (ảnh khoanh 5 KPI + 2 list gần-đây) ② *"Thêm chữ bảng cứng vào nhé"* (ảnh: `01. HĐ Thầu phụ - Bảng cứng`) ③ *"vậy là tròn 1 quy trình của Solution rồi đấy."* (ghi nhận, không phải lệnh). + +- [x] **Đ4-a — designer redesign** (`sub-frontend-designer-dot4.md`, §5 lead on-behalf): BỎ đúng khối khoanh · THÊM tab 4-GĐ mang count + 2 bảng chi tiết (PE 7 cột + HĐ) + cột **Hành trình 4-chấm** per-row + phân trang + tìm kiếm (param BE verify thật §2) · trang **59,8 KB**, build PASS · 🔴 nỗi lo CSS-token của vai (chết #53 đúng lúc verify): **lead verify hộ SẠCH** — 0 class ghép động, 3 lookup-map tĩnh +- [x] **Đ4-b — reviewer** `PASS_WITH_FLAGS — 7 FLAG (0H/2M/5L)` (`sub-reviewer-diff-dot4.md` 28.510B; vì designer chưa kịp tự-review FD4 ⇒ reviewer là lớp duy nhất, trục 6 a11y đo 10/10 mục). **Disposition TỪNG DÒNG:** + · F1[M] mobile HĐ ngõ-cụt → **FIX** mirror matchMedia trang đích · F2[M] dot "chưa tới" nói chắc điều không biết (3 ca — ca-3: HĐ DangDongDau/DaPhatHanh = GĐ4 thật mà chấm GĐ3) → **FIX** per-dot states + `linked` (contractId THẬT) + `contractDots` suy diễn tuyến-tính-CÙNG-module + aria chỉ kể chấm CÓ thông tin · F3[L] pager race → **FIX** page-state · F4[L] aria-live biến mất khi 0 kết quả → **FIX** role=status sr-only · F5[L] Hành trình mất trên mobile → **FIX** 4-chấm vào sub-line · F7[L] nút X chờ debounce → **FIX** clearSearch() · **F6[L] tab-count vs search → PARK** (chờ UAT; 🔴 **ERRATA commit-message `a2bbcb9`**: câu "co chu thich o code" cho F6 là **SAI** — không có chú thích nào được thêm; disposition thật = PARK. Khai tại đây, không amend) +- [x] **Đ4-c — nhãn "Bảng cứng"**: prod `suffix=7` (UTF-16 file, idempotent NOT-LIKE guard) + seeder tree.Add + labelBackfill 7 entry. 🔸 Theo VERBATIM anh vẽ "Bảng cứng" (nếu ý anh là "Bản cứng" — 1 lệnh đổi được, guard NOT-LIKE vẫn idempotent) +- [x] **Đ4-d — build PASS 495ms · Infra 517 PASS · commit `a2bbcb9` push** (2 file, 1052+/338−) +- [!] **Đ4-e — cicd verify `a2bbcb9`** đang chạy (marker bản mới + control âm "chưa tới"/"HĐ ĐANG SOẠN" vắng + restart-durability lần 2 của 38-key) + + - [x] **T2 — Prod menu data** ✅ **LIVE + VERIFY TRỌN** (`prod-backup-pre-apply.txt` → apply `hide=3 upd=99 ins=376` → `prod-verify-post-apply.txt`): · Inspect lộ thực-trạng ≠ giả định: Master subtree có matrix 13-row/key (canread 2/13) nhưng **`Ct_*` 28 key = 0 ROW** (chưa từng seed), `Contracts` root 1 row ⇒ INSERT 376 + UPDATE 99, số khớp toán từng đồng (9×11=99 · 28×13+12=376) diff --git a/.claude/workflows/runs/2026-07-29-S159-tong-quan-pipeline-menu/sub-reviewer-diff-dot4.md b/.claude/workflows/runs/2026-07-29-S159-tong-quan-pipeline-menu/sub-reviewer-diff-dot4.md index 4436993..b87fca5 100644 --- a/.claude/workflows/runs/2026-07-29-S159-tong-quan-pipeline-menu/sub-reviewer-diff-dot4.md +++ b/.claude/workflows/runs/2026-07-29-S159-tong-quan-pipeline-menu/sub-reviewer-diff-dot4.md @@ -100,3 +100,106 @@ Nền so sánh: `HEAD` = 766 dòng và ĐÃ chứa hero + các bản vá vòng-1 `fe-user/src/pages/UserDashboardPage.tsx:606` đặt `aria-live="polite"` lên dòng "Hiển thị x–y trong N", nhưng chính khối chứa nó bị gỡ khỏi cây khi không có kết quả (`:601` — `if (!data || data.total === 0) return null`). Nghĩa là đúng lúc cần thông báo nhất — gõ một từ khoá không khớp gì — vùng thông báo biến mất, và khối trạng thái rỗng thay thế nó (`:704-731`) thì không nằm trong bất kỳ vùng `aria-live` nào. Người dùng bàn phím/trình đọc gõ xong sẽ không nghe gì cả và không biết bảng vừa đổi. **Fix đề nghị:** giữ vùng `aria-live="polite"` luôn tồn tại (khi rỗng thì thông báo "Không tìm thấy phiếu nào khớp"), hoặc gắn `role="status"` cho khối trạng thái rỗng. + +### FLAG-5 [L] — tính năng chủ đạo của đợt-4 biến mất trên điện thoại + +Cột "Hành trình" — thứ mà chính đầu bài gọi là tầng liên kết thứ hai, và là câu trả lời cho yêu cầu "nhìn vào vẫn thấy được tổng quan liên kết" — được đặt `hidden … md:table-cell` ở cả hai bảng (`fe-user/src/pages/UserDashboardPage.tsx:672` và `:866`, kèm ô dữ liệu `:812` và `:977`). Dưới 768px cột này không tồn tại, nên trên điện thoại người dùng chỉ còn dải hero ở trên, không còn thông tin "từng phiếu đang đứng đâu". + +Chọn ẩn cột trên màn hẹp là quyết định hợp lý về mặt bố cục — tôi không đòi hiện nguyên bảng 7 cột trên điện thoại. Điều đáng ghi là **phần bị hy sinh lại đúng là phần được giao làm**, và dòng phụ gộp cho mobile (`:765-767`) chỉ gộp Dự án + Ngày, không gộp hành trình. + +**Fix đề nghị:** đưa cụm 4 chấm vào dòng phụ trên mobile (cùng chỗ với Dự án · Ngày), hoặc đổi ngưỡng ẩn của cột Người soạn/Ngày để nhường chỗ cho Hành trình. + +### FLAG-6 [L] — hai con số đếm cùng một thứ, hiện cạnh nhau, không khớp nhau khi đang tìm kiếm + +Số trên thẻ tab lấy từ `peTotalQuery` / `contractTotalQuery` (`:1333`) — đây là tổng **không kèm từ khoá**. Bảng ngay bên dưới lại lọc theo `search` (`:1147`, `:1159`), và dòng phân trang in tổng **có kèm từ khoá** (`:608`). Gõ một từ khoá xong, cùng một màn hình sẽ có thẻ tab ghi "Giai đoạn 1 · 57" trong khi dòng dưới ghi "Hiển thị 1–3 trong 3 phiếu". + +Không sai dữ liệu — hai số đo hai thứ khác nhau — nhưng đặt cạnh nhau mà không chú thích thì người đọc phải tự đoán. + +**Fix đề nghị:** khi `search` khác rỗng thì cho thẻ tab đang chọn lấy số từ chính kết quả danh sách (`peList.data.total`), hoặc thêm một chữ nhỏ "trên tổng 57" cạnh dòng phân trang. + +### FLAG-7 [L] — hai nút cùng nghĩa "xoá tìm kiếm" nhưng phản hồi khác nhau + +Nút chữ X trong ô tìm (`:556-558`) chỉ gọi `onChange('')`, tức chỉ đổi `searchInput`, rồi phải chờ hết 300ms gỡ nhịp mới thực sự bỏ lọc. Nút "Xoá tìm kiếm" ở khối trạng thái rỗng (`:717`, `:911`) gọi `clearSearch()` (`:1136-1140`) đặt lại cả `searchInput`, `search` và `page` **ngay lập tức**. Cùng một hành động, một đường có độ trễ, một đường không. + +**Fix đề nghị:** cho nút X gọi thẳng `clearSearch()`. + +--- + +## TRỤC 6 — bảng có tử tế với bàn phím / trình đọc không, và mỗi tab có đủ 3 trạng thái không + +Đo trên mã, từng mục: + +| Mục | Có / Không | Chỗ đo | +|---|---|---| +| `` cho bảng | CÓ, cả 2 bảng, `sr-only`, câu nói rõ cột Hành trình dùng để làm gì | `:660-663`, `:854-857` | +| `scope="col"` mọi ô tiêu đề | CÓ, 7/7 mỗi bảng | `:666-672`, `:860-866` | +| `scope="row"` cho ô đầu dòng | CÓ (ô Mã phiếu / Mã HĐ là ``) | `:740`, `:934` | +| Bấm dòng bằng bàn phím | CÓ — `` chỉ là tiện cho chuột; điểm dừng bàn phím THẬT là `