diff --git a/.claude/WAL.md b/.claude/WAL.md index 80716a8..bf5b928 100644 --- a/.claude/WAL.md +++ b/.claude/WAL.md @@ -33,9 +33,13 @@ chain: → ✅W2 7b291014 on-behalf (worker #53 chết SAU code TRƯỚC sổ — lead đo trọn ô máy: C-11 2 vế + X-3 + C-3 4/4 SHA-cặp-mới 4fab77b8/322d19f8 + C-10 0/0 + build×2 EXIT=0 + eslint 47/42 baseline + tsc 0; diff tự khai delta WorkflowHistoryPanel dời panel-2 + - refactor derived-state né set-state-in-effect) → 🔄 W3 wf_10fe88a1-db6 (pendingMe - đổi-endpoint inbox, 2 file) → W5. PUSH GOM CHỜ. W5 + reviewer-gate. - DEFERRED-TO-LOCAL-VERIFY dồn: W4(i-iii)+W1(i-iii)+W2(ii,iii,v,vi,vii) — đo ④ sau W3. + refactor derived-state né set-state-in-effect) → ✅W3 ed3b39ff on-behalf (#53 chết + đúng bước commit — artifact GHI TRỌN 10/10 ô + tự bắt ô-2a-không-răng thay bằng + discriminator code-only; lead spot-check 4 phép khớp từng số) → 🔄 W5 3-worker tuần tự: + W5a test-specialist wf_2914dd19-e86 (13 Fact T-matrix chứng ĐỎ, không commit) → + W5b implementer-backend (làm xanh) → W5c implementer-frontend (nút Xóa panel ×2) → + lead commit MỘT phát + reviewer-gate. PUSH GOM CHỜ. + DEFERRED-TO-LOCAL-VERIFY dồn: W4+W1+W2+W3 ô hành-vi — đo ④ một thể sau W5. [!] TỒN CŨ — CỬA NGOÀI-GIỜ (anh gật giờ): dọn workspace runner + dispatch verify CI · (3B) Gitea cron + compact Vietreport_Master + báo VIETREPORT · đo lại A/059 [ ] chờ anh (10+3 tồn): (76)(77)(78)(79) · (89) · (74) · value_protect · lật ô YC-008↔YC-010 diff --git a/.claude/workflows/runs/2026-08-12-S190-soltuon-dieu-chinh-hd/sub-b6-w3-worker.md b/.claude/workflows/runs/2026-08-12-S190-soltuon-dieu-chinh-hd/sub-b6-w3-worker.md index e927402..91bccbd 100644 --- a/.claude/workflows/runs/2026-08-12-S190-soltuon-dieu-chinh-hd/sub-b6-w3-worker.md +++ b/.claude/workflows/runs/2026-08-12-S190-soltuon-dieu-chinh-hd/sub-b6-w3-worker.md @@ -1,7 +1,68 @@ -# sub-b6-w3-worker — implementer-frontend (W3, YC-029) +# sub-b6-w3-worker — implementer-frontend (W3, YC-029 · plan SOLTUON-12-08-2026) + +> Wave W3 = "leaf **Đang duyệt** ở lại `/my-contracts`" (FE-only, fe-user, 2 file). +> Ranh: chỉ `fe-user/src/components/Layout.tsx` + `fe-user/src/pages/contracts/MyContractsPage.tsx` + file này. ## Việc đã làm +### 0. Baseline đo TRƯỚC khi gõ (đĩa, không chép artifact) + +| Phép đo | Số | Ghi chú | +|---|---|---| +| `npx eslint .` fe-user | **47** (41E/6W) | khớp kỳ vọng plan Đ-1 | +| nhánh `action === 'Pending'` họ `Ct_*` | `Layout.tsx:164` = `` `/inbox?type=${typeInt}` `` | W1 đã đẩy số dòng từ ~150 → 164 | +| `grep -c pendingMe MyContractsPage.tsx` | **0** | khớp kỳ vọng | +| `git log --oneline -3` | `7b291014` W2 · `09ebd20e` W1 · `1cc16a2d` W4 | đọc trạng thái SAU 3 wave, không dựa mô tả cũ | + +Đọc thêm (chỉ-đọc, không sửa): `ContractsController.cs:41-43` — `[HttpGet("inbox")] Inbox(CancellationToken ct) => GetMyInboxQuery()` ⇒ **0 tham số**, trả `List` (KHÔNG phân trang). Khuôn PE: `PurchaseEvaluationsListPage.tsx:77-99` (deleted TRƯỚC pendingMe → đổi endpoint → ép shape `:98`). + +### 1. `fe-user/src/components/Layout.tsx` (2 chỗ) + +- **`:174` nhánh `Pending` họ `Ct_*`** — `` `/inbox?type=${typeInt}` `` → `` `/my-contracts?type=${typeInt}&pendingMe=1` ``, kèm comment 6 dòng khai: `pendingMe` là **danh-tính leaf** (không vào `TRANSIENT_QUERY_KEYS`), trang tự đổi endpoint, và **route `/inbox` + `InboxPage` GIỮ NGUYÊN** làm hộp thư tổng. +- **`:61-66` vá comment stale** — câu cũ *"Duyệt to `/inbox` (pending THEIR approval)"* hết đúng cho họ `Ct_*` sau đổi; viết lại + trỏ mục cố định `USER_FIXED_TOP` / `staticResolvePath('__inbox')` là đường vào hộp thư tổng. +- **KHÔNG chạm:** `TRANSIENT_QUERY_KEYS:382` (W1 đã viết sẵn câu cấm cho `ct`/`view`/`pendingMe` — C-7 giữ 0) · `CT_TYPE_PARAM_PATHS:37` (đã có `/my-contracts` ⇒ accordion `?type=` vẫn mở đúng nhóm ở URL mới; `/inbox` giữ trong allowlist vì URL đó còn sống) · nhánh `Pe_*:203` · họ `Khkk_*`. + +### 2. `fe-user/src/pages/contracts/MyContractsPage.tsx` (6 chỗ) + +1. **`:63` đọc danh-tính** — `const pendingMe = searchParams.get('pendingMe') === '1'` (so chuỗi, không `Number()`). +2. **`:87` queryKey** — `['my-contracts', typeFilter, phaseFilter, showDeleted, search, pendingMe]` (+trục `pendingMe`: 2 chế độ đọc **2 nguồn khác nhau**, dùng chung ô cache = trả nhầm danh sách). +3. **`:93` nhánh mới trong queryFn** — `if (pendingMe && !showDeleted) {` ⇒ `:102` `api.get('/contracts/inbox')` (KHÔNG param) → lọc `type` **client** (`c.type === typeFilter`, bỏ qua khi `typeFilter == null`) → ép shape `{ items, total: items.length, page: 1, pageSize: items.length }` đúng khuôn PE `:98`. + - Vế `&& !showDeleted` = hiện thực hoá "**deleted đứng TRƯỚC pendingMe**" (2 view loại trừ). URL lai `?deleted=1&pendingMe=1` ⇒ vẫn vào `/contracts/deleted`, không rơi vào inbox (inbox không bao giờ trả HĐ đã xóa ⇒ sẽ rỗng THẦM LẶNG). + - Comment tại chỗ khai **vì sao không gửi param**: `GET /contracts` không có `pendingMe` (`ContractsController.cs:17-26`), ASP.NET nuốt im lặng ⇒ ra y hệt list thường (hỏng câm). + - Comment `[carry:w7b-inbox-param]`: **lệch-W7 CHẤP NHẬN** — W7/S187 đã đẩy lọc `type` lên server cho 2 nhánh kia; nhánh này lọc client vì BE chưa có param (`GetMyInboxQuery` = record 0 tham số, còn `Take(100)` cứng). Nâng BE = đợt khác. +4. **`:215` nhãn chế độ** — chip amber "Đang duyệt — đến lượt tôi" (khuôn 2 chip sẵn có `Đã xóa — chỉ xem` / `Đã phát hành`). Cùng URL gốc + cùng tiêu đề mà đổi nguồn dữ liệu ⇒ không có nhãn thì user tưởng HĐ biến mất. +5. **`:300` khoá ô tìm** — `disabled={pendingMe}` + `value={pendingMe ? '' : search}` + **chữ HIỆN** (`

` ngay dưới ô) *"Chế độ 'Đang duyệt' đọc hộp thư đến — chưa hỗ trợ tìm kiếm phía máy chủ."*. 🔴 Không dùng `title` (tooltip trên control `disabled` bị Chromium nuốt — bài S182). **Phân trang: trang này KHÔNG có UI phân trang** (`pageSize: 100` cố định, 0 nút trang) ⇒ không có gì để ẩn; đã khai trong comment thay vì im lặng bỏ ô. +6. **`:177` + `:327` rỗng-vì-lọc ⟂ rỗng-vì-chưa-có-gì** — `emptyTitle`/`emptyDescription` dẫn xuất: pendingMe ⇒ *"Không có HĐ chờ bạn duyệt"* / *"Không HĐ «{loại}» nào đang đến lượt bạn duyệt."*; giữ nguyên 2 câu cũ cho deleted / list thường. +7. **`:15-18` khai 2 nguồn ở đầu file** (panel-3 có 2 nguồn, chọn bằng `?pendingMe=1`). + ## Ô acceptance +> Lệnh chạy tại repo root, nối `;`, `|| true`. `grep -F` cho pattern chứa `?`. + +| # | Ô | Lệnh | Số | Verdict | +|---|---|---|---|---| +| 1 | `pendingMe=1` trong Layout ≥1 | `grep -cF 'pendingMe=1' …/Layout.tsx` | **6** | ✅ PASS | +| 2a | spec-literal `'/inbox?type=` = 0 | `grep -cF "'/inbox?type=" …/Layout.tsx` | **0** | ✅ PASS *(ô này ĐÃ = 0 cả ở baseline — mã dùng backtick, không nháy đơn ⇒ **ô không có răng**, xem 2b/2d)* | +| 2b | dạng THẬT `/inbox?type=` | `grep -cF '/inbox?type=' …/Layout.tsx` | **1** | ⚠️ 1 hit CÒN LẠI = **comment** `:497` mô tả allowlist accordion (`/inbox?type=N` vẫn auto-expand vì `/inbox` còn trong `CT_TYPE_PARAM_PATHS` ⇒ **câu này vẫn ĐÚNG**, không phải dư-lượng) | +| 2c | CONTROL-DƯƠNG (HEAD) | `git show HEAD:… \| grep -cF '/inbox?type='` | **2** (`:164` mã + `:487` comment) | ✅ thước có răng | +| 2d | **discriminator CODE-ONLY** (bỏ dòng `//` — bài S188 bẫy tự-trích-dẫn) | `grep -nF … \| grep -vE '^[0-9]+: *//' \| wc -l` | **0** | ✅ PASS (nhánh `Pending` cũ hết) | +| 2e | CONTROL-DƯƠNG code-only (HEAD) | như trên trên HEAD | **1** | ✅ thước phân biệt được | +| 3 | `get('pendingMe')` trong Page ≥1 | `grep -cE "get\(['\"]pendingMe['\"]\)"` | **1** | ✅ PASS | +| 4 | `contracts/inbox` trong Page ≥1 | `grep -cF 'contracts/inbox'` | **4** | ✅ PASS | +| 5 | **C-7** transient leak = 0 | `grep -A6 'TRANSIENT_QUERY_KEYS = ' \| grep -cE "'(ct\|view\|pendingMe)'"` | **0** | ✅ PASS | +| 6 | queryKey chứa `pendingMe` | `grep -nF "queryKey: ['my-contracts'"` | `:87 … , search, pendingMe]` | ✅ PASS | +| 7 | **C-1a** eslint fe-user ≤47 | `npx eslint .` | **47** (41E/6W) — KHÔNG TĂNG | ✅ PASS | +| 7b | eslint file-wave | `-f json` per-file | `Layout.tsx` **4E/0W** (`static-components`, `set-state-in-effect` — **0 ruleId mới**) · `MyContractsPage.tsx` **0E/0W** | ✅ PASS (Đ-1 ≤4E) | +| 8 | **C-2** build fe-user | `npm run build` | `✓ built in 537ms`, **EXIT 0** | ✅ PASS *(2 warning chunk-size + `INEFFECTIVE_DYNAMIC_IMPORT realtime.ts` = **PRE-EXISTING**)* | +| 9 | route `/inbox` nguyên trạng | `grep -nF inbox fe-user/src/App.tsx` | `:58 } />` | ✅ PASS (0 chạm `InboxPage`) | +| 10 | scope | `git status --porcelain` | đúng **2 file** wave `M` (các file `.claude/**`, `docs/**` dirty là của lead, **worker không chạm**) | ✅ PASS | + +**Ô máy: 10/10 PASS** (ô 2a khai thẳng là ô **không có răng** — thay bằng 2d + control 2c/2e). + ## DEFERRED/DỪNG + +- **DEFERRED-TO-LOCAL-VERIFY — ô hành-vi W3(i):** so-số 2 chiều (user có ≥1 HĐ đến-lượt + ≥1 không-đến-lượt ⇒ bật `pendingMe=1` list ÍT HƠN + HĐ không-đến-lượt VẮNG; control-dương: user ngoài luồng ⇒ **0 dòng**, không phải cả list). Cần API chạy + dữ liệu duyệt thật ⇒ chưa dựng được trong lượt worker. +- **DEFERRED-TO-LOCAL-VERIFY — ô hành-vi W3(ii):** click leaf "Đang duyệt" ⇒ URL ở lại `/my-contracts?type=N&pendingMe=1` + đúng 1 leaf sáng (menu active-state). +- **`[carry:w7b-inbox-param]`** (nợ khai công khai, KHÔNG tự làm — ngoài wave): `GET /contracts/inbox` 0-param + `Take(100)` ⇒ (a) lọc `type` phải làm ở client, (b) user >100 HĐ chờ duyệt có thể mất dòng IM LẶNG. Vá đúng = nới `GetMyInboxQuery(type)` + bỏ/nới `Take` — việc BE, đợt khác. +- **KHÔNG DỪNG theo Đ-1..Đ-10:** eslint không tăng · không đụng file ngoài wave · neo `file:line` của plan lệch **có kiểm soát** (plan ghi `Layout.tsx:150`, thực tế `:164` do W1 chèn comment — Đ-5 ngưỡng ±15 dòng, lệch 14 ⇒ trong ngưỡng, không leo thang). +- Ô SHA-mirror: **KHÔNG áp dụng** cho W3 — cả 2 file là fe-user-only (fe-admin không có `MyContractsPage`; `Layout.tsx` 2 app cố ý khác UX). 0 cặp mirror trong wave này. diff --git a/.claude/workflows/runs/2026-08-12-S190-soltuon-dieu-chinh-hd/sub-b6-w5a-tests.md b/.claude/workflows/runs/2026-08-12-S190-soltuon-dieu-chinh-hd/sub-b6-w5a-tests.md new file mode 100644 index 0000000..c8d59f2 --- /dev/null +++ b/.claude/workflows/runs/2026-08-12-S190-soltuon-dieu-chinh-hd/sub-b6-w5a-tests.md @@ -0,0 +1,19 @@ +# W5a — test-before ContractDeleteGuardTests (YC-029) + +> Vai: test-specialist · run `2026-08-12-S190-soltuon-dieu-chinh-hd` · GHI TỪNG MỤC, không dựng dàn bài rồi điền. + +## Baseline + +CHƯA ĐO. + +## 13 ca đã viết (map T-n → tên Fact) + +CHƯA VIẾT. + +## Kết quả chạy ĐỎ + +CHƯA CHẠY. + +## DỪNG/GHI CHÚ + +CHƯA CÓ.