wal: flush 20260801T0952

This commit is contained in:
pqhuy1987
2026-08-01 09:52:41 +07:00
parent 14ea2eaabe
commit 19eff22528
3 changed files with 84 additions and 2 deletions

View File

@ -1,5 +1,7 @@
# Implementer-Frontend Agent — Persistent Memory
- **S166 (08-01) K4b-FE nối `?group=` + vá-3/5/6 [artifact `runs/2026-07-31-S164-4gd-khkk-fanout/sub-implfe-k4b.md`]:** 🔴 **BÀI TO NHẤT — HARNESS ĐO NHẦM CÁI RENDER BỊ REACT VỨT, suýt kết luận NGƯỢC.** `setState` gọi **trong lúc render** ⇒ React **huỷ render đó + render lại ngay với state mới TRƯỚC khi commit**; harness bản-1 chỉ chạy 1 pass nên đọc ra `phase=3` (giá trị của render đã bị vứt) — control-âm cũng ra 3 ⇒ **2 nhánh giống nhau = phép đo mất khả năng phân biệt, trông y như PASS**. Vá = lặp tới khi state đứng yên (đúng ngữ nghĩa React) → A ra `phase=2`, control-âm giữ `3`. Khuôn giữ lại: *mô phỏng hành vi framework thì phải mô phỏng CẢ vòng đời, không chỉ 1 lượt gọi hàm.* 🔴 **Tiền-đề đề-bài sai lần 3 liên tiếp** (S164→S166-K4a→K4b): "`phase` derive từ searchParams thay useState" nguyên văn sẽ **GIẾT 6 chip lọc** (`KpiCard onClick setPhase :153`) — hỏng CÂM, build xanh. Giải = **override 3-trạng-thái** `chipPhase: T|null|undefined` (`undefined`="nghe URL" ≠ `null`="user chọn Tất cả", gộp 2 cái là chọn Tất-cả bị URL kéo ngược) + **reset trong render theo `navKey`** (KHÔNG `useEffect` — effect vẽ 1 lượt dữ-liệu-cũ rồi mới sửa = nháy). Cùng khuôn áp cho CreatePage (8 leaf chung 1 route ⇒ leaf→leaf không remount; so `lastPresetGroup` **KHÔNG** so `approvalGroup`, so nhầm = user bấm đổi nhóm bị kéo về, select thành nút chết). Đo ra BE `/deleted` **thiếu `group`** (`Controller:61-66` vs `List:42`) ⇒ không lọc client-side được (server đếm `total` trên tập chưa lọc → số trang nói dối) ⇒ xử bằng **không cho UI claim nhóm ở màn đã-xóa** (sai-thầm-lặng → thiếu-nhìn-thấy-được) + FLAG lane BE. staticMap G1: fe-user **6/6** mang `group=1`, fe-admin **5/6** (WfView admin trỏ Designer, không nhận group) — lệch có chủ đích. SHA-pair ×2: List `80ebd79d`→**`fc3944df`** · Create `677f4ab8`→**`545b4c2f`** · types `7a99b865` không đổi = **đối chứng dương**. Build ×2 PASS (user `index-C4F7kHKT` 3.6s · admin `index-kv90BBYQ` 0.9s). 4 FLAG treo (BE `/deleted` group · queryKey mang `phase` thay `filter` thô · `?group=` ở workflow-matrix chưa consumer → K5 dùng `khkkGroupWorkflowCode` · `search` cố ý không reset). Tag `[s166, khkk-k4b-group-wire, harness-do-nham-render-bi-vut, override-3-trang-thai, reset-trong-render-khong-useeffect, so-lastpreset-khong-so-state, be-deleted-thieu-group, staticmap-5-tren-6]`
- **S166 (08-01) K4a-FE regex 42 leaf KHKK + single-source nhãn nhóm [artifact `runs/2026-07-31-S164-4gd-khkk-fanout/sub-implfe-k4a.md`]:** 🔴 **Khuôn S164 "đo đĩa TRƯỚC khi gõ" ăn tiếp 2 lần trong CÙNG 1 đề bài** — cả 2 tiền-đề lead đều sai theo hướng *"tưởng chưa có / tưởng có"*: ① đề bài bảo export `KHKK_GROUP_LABELS` **mới**, đĩa đã có `KHKK_APPROVAL_GROUP_LABELS` (`types/khkk.ts:129`, K2 land, **5 call-site**) ⇒ đẻ map-2 là **phá đúng acceptance-5 (single-source) mà nó phục vụ**; K2 còn để sẵn uỷ-quyền trong comment `:125-127`. Đo thêm: nhãn BE ≡ `` `${n}. ${short}` `` cho **8/8** ⇒ giải bằng **DERIVE `khkkGroupMenuLabel(n)`, KHÔNG hardcode lần 2, 0 call-site cũ bị đụng** (hardcode lại = double-numbering "N2 - 2. …" ở select). ② comment vá-8 bảo "gỡ `KeHoachKyKet` khỏi ẩn-admin ⇒ phải nới regex trước", nhưng `isAdminHidden:194` = `key.startsWith('Ct_')` — KHKK **chưa bao giờ bị ẩn** ⇒ ràng buộc ĐANG hiệu lực chứ không phải tương lai; viết comment theo ĐĨA + FLAG "7 nhóm rỗng" (`MenuGroup` chỉ xét `children.length>0`, không xét con resolve được ⇒ header hiện, thân rỗng). **BÀI MỚI đáng giữ: chứng regex bằng `new Function()` chạy CHÍNH khối text đã ship** (`indexOf('const khkkMatch')` → slice → eval), key sinh từ `KhkkGroupNumbers`/`KhkkLeafActions` **đọc thẳng MenuKeys.cs** ⇒ 42/42 resolve, 0 drop, **+3 control-âm** (G9→null · action bogus→null · `Khkk_List` nhóm-1-không-infix→null vì chặn ở staticMap trước). Nhãn cũng kèm control-âm (chèn 'x' vào N4 → phép so FLAG) — 0-hit trần vô nghĩa. Ghi chú khuôn: `Khkk_G1` + 6 leaf **KHÔNG-infix** (S159, cấm đổi = mồ côi permission-row), 42 leaf infix chỉ là nhóm **2..8**; giữ `[1-8]` theo spec = rộng hơn tập seed 1 nấc, cố ý. `group`/`filter`/`view` ∉ `TRANSIENT_QUERY_KEYS:311` ⇒ active-state phân biệt (bài `view=approved` S155). SHA-pair types `eba649cc`→**`7a99b865`** ×2 IDENTICAL; Layout 2 app **cố ý KHÔNG mirror** (user +34 regex · admin +13 comment-only, 0 dòng xoá). Build ×2 PASS (user 1950mod `index-B2v0kn5F` 20.8s · admin 1961mod `index-84zDBHE-` 8.2s). 0 `.cs` (MenuKeys/DbInitializer M = lane BE), 0 chạm KhkkListPage/CreatePage/WorkflowPanel/DetailPage. 3 FLAG treo (tên const · `?group=` chưa có consumer → K4b quyết "vắng group = nhóm 1 hay tất cả" · 7 nhóm rỗng admin). Tag `[s166, khkk-k4a-regex-42-leaf, tien-de-lead-sai-x2, derive-khong-hardcode-lan-2, new-function-chay-text-da-ship, control-am-bat-buoc, nhom1-khong-infix, layout-co-y-khong-mirror]`
- **S164/S165 (07-31) K2-FE KHKK nhóm-duyệt N1N8 [relaunch p2 sau p1 chết #53 — chi tiết `runs/2026-07-31-S164-4gd-khkk-fanout/sub-implfe-k2.md` §1-§5]:** 🔴 **BÀI CHÍNH — tiền-đề lead SAI, đo `git diff` TRƯỚC khi gõ:** lead khai CreatePage "mid-wire, 8 khai báo chưa vào JSX" (TS6133 sẽ đỏ) ⇒ đo đĩa thật thì **8/8 ĐÃ có call-site**; Detail cũng land đủ. Việc thật ≈ **1/4** mô tả — chỉ ListPage dở (p1 kịp thêm mỗi dòng import). Khuôn S114 "WRITE-lane garble ≠ mất code" xác nhận lần nữa: lead đọc transcript dở-dang → kết luận theo Ý ĐỊNH đang chạy, không theo đĩa. **Viết lại phần đã đúng = phá.** ⇒ p2 chỉ sửa ListPage 3 điểm: badge `N{g}` NHÉT VÀO ô "Mã kế hoạch" (không đẻ cột — bảng đã cuộn ngang @1366 do cây GĐ 19rem, ghi chú S162 nằm sẵn trong file) · +cột "Tổng duyệt" Σ`totalApprovedAmount` (vá-8 spec, BE trả **0** cho phiếu chưa duyệt ⇒ ép `—` kẻo đọc nhầm "đã duyệt 0 đ") · **`colSpan` 7→8 hai chỗ** (loading + empty row — quên = lỗi CÂM tới lúc bảng rỗng). `npm run build` = `tsc -b && vite build` + `noUnusedLocals:true` ⇒ build LÀ phép thử TS6133 thật, không cần tin IDE (#68). cp 4 file → SHA-pair **5/5 IDENTICAL** (Panel không sửa = **đối chứng dương**, hash `2B64144EF6E8` BẰNG baseline ⇒ chứng phép đo đúng thứ). diff-stat đối xứng 269+/36 ×2 app. 0 `.cs` (13 `.cs` M/?? là lane BE), 0 route/menu (K4a). Tag `[s164, khkk-nhom-duyet, do-dia-truoc-khi-go, colspan-loi-cam, badge-trong-o-khong-de-cot, panel-doi-chung-duong]`

View File

@ -1,4 +1,4 @@
IMPLFE-K4B: IN-PROGRESS
IMPLFE-K4B: DONE
# Lane FE K4b — nối `?group=` + vá-3/5/6 (S166, 2026-08-01)
@ -36,5 +36,69 @@ SPEC PIN: spec-cụm2 vá 3 (queryKey 2 khoá) · vá 5 (`phase` derive thay mou
### 1.5 Xác nhận đường BE cho nhánh chính
`List :42` `[FromQuery(Name="group")] int? group``ListContractSigningPlansQuery(..., group)` `:49`. ⇒ FE gửi `params.group` là ăn.
## §2 THỰC THI
## §2 THỰC THI (6 file — 3 cặp mirror)
### F1 — `KhkkListPage.tsx` ×2 (+89 / 18 mỗi app, SHA-pair)
- **(a) vá-5:** `urlFilter`/`urlPhase`/`deletedView`/`group` đọc THẲNG từ `searchParams` mỗi render. Bộ lọc chip chuyển sang lớp override `chipPhase: KhkkPhaseValue|null|undefined` (3 trạng thái, `undefined``null` — xem §1.2), `phase = chipPhase !== undefined ? chipPhase : urlPhase`. Reset `chipPhase`+`page` khi `navKey` đổi, **điều chỉnh state trong render** (khuôn React chính thống) chứ không `useEffect``useEffect` sẽ vẽ 1 lượt bằng dữ liệu cũ rồi mới sửa (nháy).
- **(b)** `group` = `?group=` lọc `^[1-8]$`, ngoài dải/vắng ⇒ `null` = xem tất cả nhóm. Gửi `params.group` (chỉ nhánh không-xóa, §1.3).
- **(c) vá-3:** `queryKey: ['khkk-list', { group, phase, search, page, deletedView }]`.
- **(d)** `group !== null` ⇒ title `… — N{group}` + subtitle `khkkGroupMenuLabel(group)` (helper K4a). Nút "Lập kế hoạch" mang `?group=` sang màn tạo (bắt tay vá-6).
- Header file cập nhật bảng URL 6 leaf + khai lý do "8 nhóm chung 1 route ⇒ phải đọc lại mỗi render".
### F2 — `KhkkCreatePage.tsx` ×2 (+24 / 2 mỗi app, SHA-pair)
vá-6: `presetGroup` từ `?group=` (lọc `^[1-8]$`) làm giá trị khởi tạo `approvalGroup`; +đồng bộ lại khi THAM SỐ URL đổi (cùng khuôn F1 — 8 leaf "Thao tác" cũng chung 1 route). 🔴 So với `lastPresetGroup` chứ KHÔNG so với `approvalGroup`: so với `approvalGroup` thì user tự đổi nhóm sẽ bị kéo ngược về nhóm của URL, ô select thành bấm-không-được. `firstFreeGroup`/`selectPe` giữ nguyên (URL là gợi ý, không phải khoá — PE đã dùng nhóm đó vẫn tự nhảy sang nhóm trống).
### F3 — `Layout.tsx` staticMap (KHÔNG mirror, cố ý)
- fe-user: **6/6** leaf G1 mang `group=1` (`Khkk_WfView → /khkk/workflow-matrix?type=10&group=1`).
- fe-admin: **5/6**`Khkk_List/Create/Pending/Approved/Deleted` mang `group=1`; `Khkk_WfView` **KHÔNG** vì bên admin nó trỏ Designer `/system/approval-workflows-v2/ContractSigningPlan`, không phải trang danh sách (§1.4). Khai trong comment tại chỗ.
## §3 CHỨNG (mỗi phép đo kèm CONTROL-ÂM)
**A. Acceptance "leaf→leaf đổi kết quả không F5"** — mô phỏng 1 instance KHÔNG remount, chạy **CHÍNH đoạn text đã ship** (`indexOf('const navKey')` → slice → `new Function`), không chép tay:
```
R1 ?group=2 phase=null (mọi trạng thái)
[user bấm chip "Đã duyệt"]
R2 ?group=2 (URL giữ nguyên) phase=3 ⇒ CHIP CÒN SỐNG (derive không giết nó)
R3 ?group=5&filter=ChoDuyet phase=2 page=1 ⇒ RE-LỌC NGAY, không F5
CONTROL-ÂM (gỡ khối reset ra khỏi chính đoạn trên): phase=3 ⇒ kết dính bộ lọc cũ
= ĐÚNG căn bệnh vá-5 ⇒ phép đo CÓ RĂNG
```
🔴 Bẫy harness đã sập rồi bò ra, ghi lại vì dễ tái phạm: lượt chạy đầu ra `phase=3` ở R3 và tôi suýt đọc thành PASS-nhầm-hướng. Nguyên nhân: `setState` gọi TRONG lúc render thì React **vứt bỏ render đó và render lại ngay với state mới trước khi commit**; harness bản đầu chỉ đo cái render BỊ VỨT. Sửa harness thành vòng lặp tới khi state đứng yên (đúng ngữ nghĩa React) mới ra `phase=2`. **Đo nhầm cái render bị vứt = kết luận ngược.**
**B. vá-3 queryKey** (đọc thẳng dòng trong file):
```
queryKey: ['khkk-list', { group, phase, search, page, deletedView }],
CÓ group · CÓ phase · CÓ search · CÓ page · CÓ deletedView
CONTROL-ÂM khoá bịa "nhomX" → không khớp ⇒ phép đo bắt được cái thiếu
```
Ghi chú spec: đề bài nói "chứa cả `group` lẫn `filter`". Trục `?filter=` nằm trong khoá dưới dạng **đã giải mã**`phase` (`FILTER_TO_PHASE` đơn ánh, và `phase` cũng chính là tham số gửi lên server) ⇒ khoá phủ đủ 2 trục. Nhét thêm `filter` thô chỉ tách đôi ô cache cho 2 request y hệt nhau. Khai để gate soát (FLAG-2).
**C. SHA-pair (3 cặp)**
| file | trước K4b | sau K4b | ×2 app |
|---|---|---|---|
| `KhkkListPage.tsx` | `80ebd79d…` | **`fc3944df7af22bd3c2d29d191b31af7dc13c8377ad1f943e92902da1e13c3b10`** | IDENTICAL |
| `KhkkCreatePage.tsx` | `677f4ab8…` | **`545b4c2fe0ebae88c0d8ffcd4118f85e71b3e6ac2dfe6833d9e6028765f3d564`** | IDENTICAL |
| `types/khkk.ts` (K4a, không sửa lượt này) | — | `7a99b865…` | IDENTICAL (đối chứng dương) |
`Layout.tsx` 2 app KHÁC NHAU by-design (user +50 · admin +28).
**D. Build ×2 PASS, 0 TS error** (`tsc -b && vite build`, `noUnusedLocals` ⇒ build là phép thử TS6133 thật)
| app | bundle js | css | time |
|---|---|---|---|
| fe-user | `index-C4F7kHKT.js` 1,640.77 kB (gzip 414.52) | `index-my6T4K7V.css` 92.62 kB | 3.57s |
| fe-admin | `index-kv90BBYQ.js` 1,723.75 kB (gzip 428.71) | `index-Ct6-3cYU.css` 86.80 kB | 0.86s |
Warning >500KB + `INEFFECTIVE_DYNAMIC_IMPORT` = PRE-EXISTING.
**E. Rào** — 0 chạm `MenuKeys.cs`/`DbInitializer.cs` (2 file M sẵn từ lane BE trước lượt tôi), 0 chạm `KhkkWorkflowPanel.tsx`/`KhkkDetailPage.tsx` (không có trong `git diff --name-only`), 0 chạm `tests/`, **0 commit**.
## §4 FLAG cho lead
**FLAG-1 (MAJOR — cần lane BE, ngoài rào của tôi):** `GET /contract-signing-plans/deleted` **không nhận `group`** (`ContractSigningPlansController.cs:61-66`, đối chiếu `List:42``[FromQuery(Name="group")]`). ⇒ 8 leaf "Đã xóa" trỏ 8 URL khác nhau nhưng trả CÙNG một tập phiếu-của-mọi-nhóm. Không vá được ở FE: lọc client-side sẽ làm `total`/số trang nói dối (server đếm trên tập chưa lọc). Xử tạm trong lượt này = **không để UI claim nhóm ở màn đã-xóa** (title/subtitle giữ nguyên bản "đã xóa", không in tên nhóm) ⇒ sai-thầm-lặng biến thành thiếu-tính-năng-nhìn-thấy-được. Fix thật: thêm `int? group` vào `ListDeletedContractSigningPlansQuery` + controller, 1 lượt nhỏ.
**FLAG-2 (MINOR — chờ gate phán):** queryKey mang `phase` (dạng đã giải mã của `filter`) chứ không mang chuỗi `filter` thô — xem §3B. Nếu gate muốn khớp LITERAL chữ `filter` trong khoá thì nói, tôi thêm (chi phí: 2 ô cache cho cùng 1 request).
**FLAG-3 (MINOR — bàn giao K5):** `?group=` trên `/khkk/workflow-matrix?type=10&group={n}` hiện **chưa có consumer**`WorkflowMatrixViewPage` chỉ đọc `type`. Leaf "Luồng duyệt" của cả 8 nhóm vì thế hiển thị cùng một ma trận. Tham số vẫn cần có (nó là danh tính để sidebar sáng đúng leaf). Nối thật = K5 hoặc lượt sau: lọc workflow theo `Code = KHKK-N{n}` — helper `khkkGroupWorkflowCode(n)` đã có sẵn trong `types/khkk.ts` từ K4a, chờ người gọi.
**FLAG-4 (MINOR — đã quyết, báo để khỏi tưởng sót):** `search` CỐ Ý không reset khi đổi leaf (đang dò một mã rồi nhảy nhóm để tìm tiếp là thao tác có thật); chỉ `chipPhase` + `page` reset. Nếu owner muốn reset cả `search` thì thêm 1 dòng.
IMPLFE-K4B-END

View File

@ -0,0 +1,16 @@
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)