wal: flush 20260808T1211
This commit is contained in:
@ -26,11 +26,13 @@
|
||||
13. **Muốn ĐẾM cái UI hiển thị thì DỰNG LẠI dữ liệu thật rồi chạy chính bộ lọc đã ship** — đừng đếm bằng mắt/suy luận. Và cho phép đo **tự kiểm**: cây menu dựng lại được đối chứng bằng invariant T6 (mọi key `MenuKeys.All` phải có menu-row) — dựng sai thì chỉ số đó vỡ trước.
|
||||
14. **Route-resolver có nhánh ≠ thứ đó tồn tại.** `resolvePath` nhận code `'Contract'` không có nghĩa menu-row `AwV2_Contract` tồn tại (0 hit repo). Cùng họ với "bịa field": đọc nhánh code rồi suy ra dữ liệu là suy ngược.
|
||||
15. 🔴 **Khuôn chỉ port được khi VỎ TRANG cùng hình dạng — đo BỀ NGANG bằng SỐ trước khi mirror.** Khuôn cột-trái `19rem` sinh ra cho trang GIẤY-CUỘN; trang 3-PANEL cao-cố-định (`h-[calc(100vh-4rem)]` + `grid-cols-[320px_1fr_360px]`, grid KHÔNG `gap`) không có 304px dư: sidebar `xl:w-80` ⇒ @1366 panel chi tiết 346px→**42px**. Mirror-y-hệt ở đây = hỏng CÂM (build xanh). Cộng bề ngang ra số TRƯỚC, và nhớ cơ chế thu gọn của component có thể là `lg:hidden` = chỉ sống ở dải mà panel kia đã ẩn ⇒ vô dụng ở desktop.
|
||||
16. 🔴 **`title` trên control `disabled` = CÂM ở Chromium** (Chrome/Edge nuốt sự kiện chuột của form-control disabled ⇒ tooltip không bao giờ bật; `<label>` bao ngoài KHÔNG tự thừa kế). Chỗ cắm đúng = `<label>`/wrapper **không disabled**. Hệ quả nặng hơn: **thông tin PHÂN BIỆT không được nằm sau hover** — 1 ô khoá "vì lý do KHÁC" mà chỉ nói bằng tooltip thì trông y hệt N ô khoá "chưa áp dụng" = **ngược nghĩa**, phải có chữ nhìn-thấy-được. Thêm chữ vào ô con của `grid grid-cols-2`: sibling auto-place rơi sang **cột 1** (đọc nhầm sang ô bên trái + đẩy lệch các ô `col-span-2`) ⇒ ghim `col-start-2`.
|
||||
|
||||
---
|
||||
|
||||
## 📌 Nhật ký (móc 1 dòng — verbatim → `archive/2026-H2.md`, cũ hơn → `archive/2026-H1.md` + `_INDEX.md`)
|
||||
|
||||
- **S182 (08-08) vá MAJOR-1** (reviewer pre-commit bắt) — 8 `title` dời `<input disabled>` → `<label>` ở `ApprovalWorkflowsV2Page` (admin-only, 0 cặp mirror). Reviewer liệt 7 site, grep cùng-lớp ra **site thứ 8 `:1443` (K3, có sẵn)** — vá kèm. Ô #4 thêm **chữ hiện** `col-start-2` vì tooltip là thông-tin-phân-biệt. Vùng cấm đo lại sau khi vá: 9/9 `checked=` byte-identical · #8 `Finalize` 0 `disabled`/0 `title`. Control-âm: tiêm lại 1 title vào input ⇒ checker nổ đúng 1. **FLAG-C treo:** nút "+ Thêm NV" `:1226` cùng bệnh, ngoài scope ⇒ khai không chạm. `[s182, title-tren-disabled-la-cam, grep-cung-lop-ra-site-thu-8]`
|
||||
- **S167 (08-01) GĐ3** cây 4-folder lên cụm Duyệt-HĐ → **BLOCKED @§0, 0 file chạm**. 7 loại HĐ = 1 component trang (`?type=`), KHÔNG 7 file. 3 lệch tiền-đề: vỏ 3-panel không nhận khuôn cột-19rem (@1366 còn 42px) · fe-user có 3 ứng viên trang (`MyContracts`/`Inbox`/`WfMatrix`) vs admin 1 · cặp trang KHÁC FILE (`ContractsListPage`⟂`MyContractsPage`) ⇒ SHA-pair identical bất khả thi (đối chứng dương: 4 cặp component/hook/HardCopies BẰNG nhau). +K6 đã ẩn `Contracts` khỏi sidebar admin ⇒ nửa admin là trang deep-link-only. `[s167, khuon-can-cung-vo-trang, sub-implfe-gd3.md]`
|
||||
- **S166 (08-01) K6** admin thu gọn về vai NƠI-CONFIG (fe-admin ONLY 1 file, fe-user 0-diff). `filterForAdmin` lọc ĐỆ QUY ⇒ chỉ liệt 12 ROOT; **dựng lại cây seed 200 node rồi chạy bộ lọc đã ship ⇒ 166 lá → 15 lá liệt đủ tên**, tự-kiểm bằng invariant T6 (113 key `All` → 0 thiếu row). Comment vá lineage **2 lần đảo**. `[s166, k6-admin-thu-gon, route-resolver-khac-menu-row]`
|
||||
- **S166 (08-01) K4c** WfView lọc nhóm (chốt cụm-2, fe-user ONLY 1 file, fe-admin 0-diff). Lọc `wf.code === khkkGroupWorkflowCode(n)` client-side ⇒ **không đổi queryKey** (khai rõ); rỗng-vì-lọc tách câu riêng. 21/21 proof + 2 control-âm. `[s166, khkk-k4c-wfview-loc-nhom]`
|
||||
|
||||
@ -167,3 +167,39 @@ Hoàn tác: SHA trước/sau `dda4be1f9d4dabd…b69b7f71` **BẰNG NHAU** (byte-
|
||||
- ⚠️ **Nợ nhận-thức (không chặn):** 8 panel nhóm + panel `/ContractSigningPlan` **dùng CHUNG 1 ô cache** (queryKey không có `khkkGroup` — cố ý, vì lọc client-side trên payload đã tải trọn). Invalidate sau khi lưu làm tươi cả 9. Nếu sau này BE nhận `code=` làm tham số **request** thì BẮT BUỘC thêm `code` vào queryKey, nếu không 9 màn dùng chung cache sai CÂM.
|
||||
- ⚠️ **Chưa chạy (ngoài tay lane FE):** acceptance A1/A2b/A3/A4 là **UAT động** (bấm thật trên 8 leaf, sửa-tại-chỗ ⇒ 204, tạo v02 N1 kiểm 7 nhóm không mất badge). Lane này chỉ chứng được phần **tĩnh**. A3 phụ thuộc BE lane (tắt active per-CODE) — 2 nửa phải test CÙNG nhau.
|
||||
- ✅ **Không nợ build/mirror:** 0 lỗi TS ×2 app · SHA-pair 2/2 · 4-place 4/4 · 27/27 proof + 3 control.
|
||||
|
||||
## §5 — vá MAJOR-1 (reviewer pre-commit, lượt sau)
|
||||
|
||||
**Bệnh:** `title=` nằm trên chính `<input type="checkbox" disabled={isKhkk}>`. Chromium (Chrome/Edge = trình duyệt đích của owner) **nuốt sự kiện chuột của form-control disabled** ⇒ tooltip KHÔNG bao giờ bật; `<label>` bao ngoài không mang `title` nên rê vào chữ cũng câm. Quyết định D3=C của owner là *"mờ ô chưa nghe **+ tooltip**"* ⇒ mất vế tooltip thì admin chỉ còn 7 ô xám vô cớ. Nặng nhất là ô **#4 `allowReturnToDrafter`**: câu *"KHKK hiện luôn trả về người soạn khi Trả lại — cờ chưa phân nhánh"* là thông tin **DUY NHẤT** phân biệt #4 với 6 ô "chưa áp dụng" — câm ⇒ #4 trông y hệt 6 ô kia = **ngược nghĩa**.
|
||||
|
||||
**(a) Dời `title` từ `<input>` lên `<label>` — 8/8 site** (label không disabled nên hover chữ là hiện):
|
||||
|
||||
| # | cờ | title cũ ở input | title mới ở label |
|
||||
|---|---|---|---|
|
||||
| 1 | `allowReturnOneLevel` | :1361 | :1366 (label @:1364) |
|
||||
| 2 | `allowReturnOneStep` | :1372 | :1379 (@:1377) |
|
||||
| 3 | `allowReturnToAssignee` | :1383 | :1392 (@:1390) |
|
||||
| 4 | `allowReturnToDrafter` | :1399 | :1410 (@:1408) |
|
||||
| 5 | `allowApproverEditDetails` | :1410 | :1432 (@:1430) |
|
||||
| 6 | `allowApproverEditBudget` | :1421 | :1445 (@:1443) |
|
||||
| 7 | `allowApproverSkipToFinal` | :1443 ← **tiền lệ CÙNG BỆNH từ K3**, reviewer chỉ đích danh | :1469 (@:1467) |
|
||||
| 9 | `allowApproverDelete` | :1471 | :1499 (@:1497) |
|
||||
|
||||
**(b) RIÊNG ô #4 — thêm chữ NHÌN THẤY ĐƯỢC** (`fe-admin/.../ApprovalWorkflowsV2Page.tsx:1421-1429`, ngay dưới `</label>` #4 @:1420): `{isKhkk && <div className="col-start-2 -mt-0.5 pl-4 text-[10px] leading-snug text-slate-500">{khkkDrafterHint}</div>}`. Thông tin phân-biệt **không được phụ thuộc hover**. Dùng `col-start-2` (không phải sibling thường) vì #4 nằm ô **hàng 2 cột 2** của `grid grid-cols-2`: sibling auto-place sẽ rơi xuống **cột 1** ⇒ đọc nhầm sang ô #3 và đẩy lệch 5 ô `col-span-2` phía dưới. `col-start-2` ghim dòng chữ đúng dưới ô của nó; 5 ô `col-span-2` kế tiếp vẫn auto-place xuống hàng mới, 0 reflow.
|
||||
|
||||
**Chuỗi #4 gom thành 1 const** `khkkDrafterHint` (`:722`, cạnh `khkkNotYetHint`) vì nay dùng ở **2 chỗ** (tooltip + chữ hiện) — 2 bản literal sẽ trôi lệch nhau. Kèm comment tại `:715-721` ghi rõ **vì sao** `title` phải ở label, để lần sau không ai dời ngược lên input rồi làm câm lại.
|
||||
|
||||
**Vùng CẤM — đo lại sau khi vá, xác nhận nguyên vẹn:**
|
||||
- 9/9 `checked=` **byte-identical** với bản trước khi vá (`:1371 :1384 :1397 :1415 :1437 :1450 :1474 :1488 :1504`) — gồm cả `checked={isKhkk ? false : entry.allowApproverSkipToFinal}` (quyết định K3, giữ) và `checked={entry.allowReturnToDrafter}` (**FLAG-B §4 vẫn treo y nguyên, lượt này KHÔNG động**).
|
||||
- Ô **#8 `allowApproverFinalize`** (`:1484-1492`): **0** `disabled`, **0** `title` — không chạm.
|
||||
- `levelFlagsPayload` (`:258`, call `:880/:920`): không chạm. `isKhkk` (`:714`): không chạm.
|
||||
|
||||
**Proof — checker có RĂNG (control-âm):** script parse tag-owner của mọi `title=` trong file (`scratchpad/check-title-owner.js`).
|
||||
- Bản đã ship: `title=` tổng **22** · tham chiếu `isKhkk` **8** · owner=`<label>` **8/8** · owner=`<input>` **0**.
|
||||
- **CONTROL-ÂM:** tiêm lại 1 `title` vào `<input>` ô #1 ⇒ checker **nổ đúng 1** (`owner=<input> :1373`). ⇒ 0-hit ở bản thật là **im-vì-đúng**, không phải im-vì-câm.
|
||||
|
||||
**Build:** `npx tsc --noEmit -p tsconfig.app.json` → **exit 0** (🔸 dùng `-p`; `npx tsc --noEmit` TRẦN biên dịch 0 file = xanh giả — đúng FLAG-A §4). `npm run build` fe-admin → **✓ built in 30.18s, 0 lỗi TS**; warning `>500 kB` + `INEFFECTIVE_DYNAMIC_IMPORT realtime.ts` + `@import` = **PRE-EXISTING**.
|
||||
|
||||
**Mirror:** `ApprovalWorkflowsV2Page.tsx` **chỉ tồn tại ở fe-admin** (fe-user: file không có, `ls pages/system | grep -i workflow` = 0, `grep khkkNotYetHint|khkkDrafterHint fe-user/src` = **0 hit**) ⇒ **không có cặp để mirror**, đúng lệch-có-chủ-đích đã khai §3.6. Lượt này chạm **đúng 1 file**.
|
||||
|
||||
**⚠️ FLAG-C (cùng lớp, KHÔNG chạm — xin lead quyết):** grep cả file thấy **1 site nữa cùng bệnh** ngoài phạm vi MAJOR-1: nút **"+ Thêm NV"** (`:1226-1248`) có `disabled={addDisabled}` + `title=` giải thích **lý do bị khoá** (*"Cấp N-1 phải có ≥1 NV trước"* / *"Hết NV khả dụng…"*) đặt **trên chính `<button disabled>`** ⇒ cùng Chromium suppress, admin không biết vì sao nút xám. Đây là bug **có sẵn từ trước S182**, không thuộc D3=C, và TASK ghi rõ *"1 việc DUY NHẤT — KHÔNG làm gì khác"* ⇒ **để nguyên, chỉ khai**. (Hai nút `title="Lên"/"Xuống"` `:1155/:1164` cũng disabled ở rìa nhưng title chỉ là **nhãn**, không mang thông tin lý-do ⇒ không cùng lớp thiệt hại.)
|
||||
|
||||
@ -60,3 +60,31 @@ CHƯA ĐO
|
||||
## §8 — Bug prod phát hiện / spec drift
|
||||
|
||||
CHƯA ĐO
|
||||
|
||||
## §9 — VÁ MAJOR-3 (reviewer pre-commit) — D6 ở đường TẠO · ĐÃ ĐO
|
||||
|
||||
- ts: 2026-08-08 (lượt resume riêng, sau khi reviewer-precommit ra finding). Scope: **đúng 1 `[Fact]`**, 0 chạm `src/**`, 0 sửa 14 ca cũ, 0 commit.
|
||||
- **Lỗ:** `EnsureWorkflowPinnableAsync` cắm 2 site (Create `ContractSigningPlanFeatures.cs:507` · UpdateDraft `:642`) mà T7 (`ContractSigningPlanApprovalTests.cs:1402`) gọi `UpdateDraftAsync` ở CẢ 4 chân ⇒ đường TẠO 0 răng đo.
|
||||
- **Nhà:** `tests/SolutionErp.Infrastructure.Tests/Application/ContractSigningPlanGroupAndCatalogTests.cs`
|
||||
— 1 `[Fact]` `Create_PinQuyTrinhDaNgungHoacChuaGhim_ThiBiChan_VaKhongDotMa` (đặt cạnh ca rào (v) để dùng chung `SeedPeForCreateAsync` + `CreateAsync` + `ReadPlanScopesAsync`).
|
||||
— `SeedWorkflowAsync` +3 tham số **ĐUÔI** optional (`version` / `isActive` / `isUserSelectable`, default giữ hành vi cũ) ⇒ **15 call-site cũ 0 đổi chữ**; cùng khuôn `ContractSigningPlanApprovalTests.cs:135-140`.
|
||||
|
||||
**Thiết kế phép đo (vì sao 3 chân này phân biệt được 2 thế giới):**
|
||||
|
||||
| Chân | Seed (khác nhau ĐÚNG Version + 1 cờ) | Kỳ vọng | Bịt cái "xanh giả" nào |
|
||||
|---|---|---|---|
|
||||
| (1) | `KHKK-N1` v2 `IsActive=false` | 409 chứa "ngừng hoạt động" | — |
|
||||
| (2) | `KHKK-N1` v3 `IsUserSelectable=false`, **`IsActive` VẪN true** | 409 chứa "ghim" **∧ message ≠ chân (1)** | 2 cờ cùng false ⇒ `!IsActive` nổ trước ⇒ chân (2) đo lại chân (1) = phép đo RỖNG |
|
||||
| (3) | `KHKK-N1` v1 active+ghim | tạo được, mã **`KHKK/2026/001`** | rào chặn-tuốt (chân 3 đỏ) ⟂ rào bị gỡ khỏi Create (chân 1-2 xanh giả) |
|
||||
|
||||
- **Code cùng họ `KHKK-N1` + phiếu nhóm 1** ⇒ rào (v) `:515` KHỚP nên im lặng; mọi 409 quan sát được chỉ có thể từ D6. Ba message của các rào khác trong đường tạo đều khác chữ ("không thuộc loại…" / "đã có kế hoạch ký kết cho nhóm duyệt N…" / "Phiếu nhóm N{g} đang gắn quy trình N{k}") ⇒ 2 phép `Contain` định danh D6 duy nhất.
|
||||
- **Ordering ĐO TRỰC TIẾP, không suy luận:** sau 2 lần 409, assert `WorkflowAppCodeSequences.Any(Prefix=="KHKK/2026") == false` — nếu ai dời D6 xuống SAU `codeGen.GenerateAsync` (`:524`) thì hàng sequence đã tồn tại với `LastSeq=2` và chân (3) ra `003`. Codegen **tự commit** transaction riêng (`ContractSigningPlanCodeGenerator.cs:23-43`) ⇒ số mất vĩnh viễn, không rollback theo request.
|
||||
- **Không để lại phiếu nửa vời:** `ReadPlanScopesAsync` rỗng sau 2 lần 409 (D6 ném trước `db.ContractSigningPlans.Add` `:577`), rồi `BeEquivalentTo` đúng 1 `PlanScope("KHKK/2026/001", 1, dangDung)` sau chân (3).
|
||||
- 🔴 **CỐ Ý KHÔNG pin chuỗi `wf.Code` trong message** dù prod có chèn (`:342`/`:345`): bất-biến mà spec tuyên bố là "**2 câu TÁCH**", pin thêm chữ Code là biến chi-tiết trình bày thành yêu cầu (bài S164 không-pin-config-lie). Vế `NotBe` giữa 2 message mới là thứ khoá đúng tuyên bố đó.
|
||||
|
||||
**Đo (lệnh chuẩn `-p:BuildInParallel=false -maxcpucount:1`):**
|
||||
- Ca mới chạy CÔ LẬP (`--filter`): `Passed: 1, Failed: 0`.
|
||||
- Full-suite `dotnet test SolutionErp.slnx`: **Domain 45 + Infra 615 = 660 PASS, 0 FAIL** (nền 659 → **Δ+1**, 0 hồi quy).
|
||||
- `git status`: đổi DUY NHẤT `…/ContractSigningPlanGroupAndCatalogTests.cs` (+106/−7). `git diff --stat -- src/` = 3 file của lane implementer-backend (có TRƯỚC lượt này), **0 dòng của tôi**.
|
||||
|
||||
**Bug prod / spec drift lượt này:** KHÔNG có. Guard hành xử đúng như spec W1b2 mô tả ở cả 3 chân.
|
||||
|
||||
Reference in New Issue
Block a user