# sub-b6-w1-worker — implementer-frontend (B6, wave W1, YC-029)

Run: 2026-08-12-S190-soltuon-dieu-chinh-hd · plan SOLTUON-12-08-2026
Ranh: Write/Edit CHỈ {fe-user/src/components/Layout.tsx, fe-user/src/pages/pe/WorkflowMatrixViewPage.tsx, file này}

## BASELINE ĐO TRƯỚC KHI SỬA (BƯỚC 2 — 2026-08-12)

| Phép đo | Lệnh | Baseline | Khớp pin? |
|---|---|---|---|
| eslint app | `cd fe-user; npx eslint .` | **47 problems (41 errors, 6 warnings)**, EXIT=1 | ✅ khớp pin 47 (41E/6W) |
| eslint file-wave | `npx eslint src/components/Layout.tsx` | **4 errors, 0 warning**, EXIT=1 | ✅ khớp pin 4E |
| ruleId baseline Layout.tsx (json) | — | `325:10 react-hooks/static-components` · `416:8 react-hooks/static-components` · `453:8 react-hooks/static-components` · `478:17 react-hooks/set-state-in-effect` | ✅ đúng 4 dòng 325/416/453/478 |
| C-6 baseline | `grep -c "chung 1 bộ" fe-user/src/components/Layout.tsx` | **1** | (phải về 0 sau W1) |
| ô X-5 baseline | `grep -ci "chung 1 bộ" …/WorkflowMatrixViewPage.tsx` | **1** (chữ HOA "CHUNG 1 bộ" ⇒ `grep -c` case-sensitive = 0, `-ci` = 1 — ô X-5 dùng `-ci` nên CÓ RĂNG) | (phải về 0) |
| C-7 baseline | `grep -A6 "TRANSIENT_QUERY_KEYS = " … \| grep -cE "'(ct\|view\|pendingMe)'"` | **0** | (giữ 0) |
| C-8 vế-1 baseline | `grep -cnE "get\(['\"]ct['\"]\)" Layout.tsx` | **0** | (phải ≥1 sau W1) |
| C-8 vế-2 baseline | `grep -rnF 'workflow-matrix?type=3&ct=' fe-user/src \| wc -l` | **1** | canh-thoái-lui (phải CÒN) |
| C-9 baseline | `grep -oE "['\"]HD-(…)['\"]" WfViewPage \| sort -u \| wc -l` | **0** | (phải = 7) |

Tiền-đề đối chiếu ĐĨA (không chép plan): `ContractType.cs:5-11` = 1..7 đúng thứ tự `ContractApprovalWorkflowFamily.cs:33-42`; `Layout.tsx:16-29` có sẵn `TYPE_CODE_TO_INT`/`INT_TO_TYPE_CODE`; accordion `:470-485`; `TRANSIENT_QUERY_KEYS :350`; `WorkflowMatrixViewPage.tsx:84-99` khuôn `group`.

## Việc đã làm (7 mục)

- **[1] Layout accordion — ưu tiên `ct` + allowlist pathname EXACT.** `fe-user/src/components/Layout.tsx`: thêm const module-level `CT_TYPE_PARAM_PATHS = ['/my-contracts','/contracts/new','/inbox'] as const` (đặt ngay dưới `INT_TO_TYPE_CODE`, kèm chú vì-sao-KHÔNG-`startsWith`); trong `useEffect` accordion, khối `ct` ĐI TRƯỚC `type` (`rawCt !== null && /^[1-7]$/.test(rawCt)` → `INT_TO_TYPE_CODE[Number(rawCt)]` → `setExpandedCtCode` → `return`); nhánh Pe `startsWith('/purchase-evaluations')` GIỮ NGUYÊN VỊ TRÍ ĐẦU của khối `type`; nhánh Ct đổi `else` trần → `else if ((CT_TYPE_PARAM_PATHS as readonly string[]).includes(location.pathname))`. Cast `as readonly string[]` vì tuple `as const` không nhận `.includes(string)` (TS6).
- **[2] Layout `&view=approved`.** Leaf `Ct_*_Approved`: `/my-contracts?type=${typeInt}&phase=9` → `…&phase=9&view=approved` + 3 dòng chú khoá-định-danh (khuôn PE `:185`).
- **[3] Layout comment stale lật.** Khối chú trên regex `Ct_<Code>_<Action>` bỏ câu "HĐ chung 1 bộ workflow V2", viết lại: 7 bộ per-loại `HD-*` @S188 + nguồn BE + phân biệt `type=3`=ApplicableType ⟂ `ct`=danh-tính leaf.
- **[4] TRANSIENT_QUERY_KEYS KHÔNG đụng** — set giữ nguyên 6 phần tử `['id','q','editHeader','page','phase','awId']`. Chỉ THÊM chú CẤM-thêm 3 param danh-tính, đặt **TRƯỚC** dòng `const TRANSIENT_QUERY_KEYS = …` (cố ý: ô C-7 quét `-A6` từ dòng đó xuống ⇒ để chú phía sau sẽ tự tạo dương-giả).
- **[5] WfView `INT_TO_HD_CODE` 7-entry.** `fe-user/src/pages/pe/WorkflowMatrixViewPage.tsx`: const cục-bộ `Record<number,string>` chép TỪNG KÝ TỰ từ `ContractApprovalWorkflowFamily.cs:33-42` (đã MỞ file BE đọc; `ContractType.cs:5-11` xác nhận 1..7 khớp thứ tự `Members`), mỗi dòng chú tên enum nguồn. KHÔNG import từ fe-admin.
- **[6] WfView nhánh `ct` gate 3 vế.** `const rawCt = searchParams.get('ct')` + `typeInt === 3 && rawCt !== null && /^[1-7]$/.test(rawCt) ? INT_TO_HD_CODE[Number(rawCt)] : null`; `workflows` = ternary lồng `khkkGroup !== null ? … : hdCode !== null ? filter(wf.code === hdCode) : allWorkflows` (nhánh `group` GIỮ NGUYÊN). `queryKey` KHÔNG đổi (vẫn `['workflow-matrix', typeInt]`) + chú lý do lọc-client-side.
- **[7] WfView comment stale thứ 2 lật (X-5).** Khối `:58-60` bỏ "HĐ dùng CHUNG 1 bộ workflow V2", viết lại 7 bộ per-loại + vì sao 7 leaf vẫn chung `type=3`.

## Ô acceptance (kết quả từng ô)

> Mọi ô đo SAU khi 7 việc đã land. **Răng của phép đo = DELTA so baseline đo ở đầu file** (ô nào 0→N hoặc 1→0 thì chính baseline là control: nó chứng lệnh grep bắt được cái nó phải bắt, không phải "im vì câm").

| Ô | Lệnh | Kỳ vọng | ĐO ĐƯỢC | Verdict |
|---|---|---|---|---|
| **C-8 vế-1** | `grep -nE "get\(['\"]ct['\"]\)" fe-user/src/components/Layout.tsx` | ≥1 | **1** — `:501 const rawCt = params.get('ct')` (baseline 0) | ✅ PASS |
| **C-8 vế-2** (canh-thoái-lui) | `grep -rnF 'workflow-matrix?type=3&ct=' fe-user/src` | ≥1 (PHẢI CÒN) | **2 hit thô** → xem ghi chú bẫy dưới; hit CODE THẬT = **1** (`Layout.tsx:165`) | ✅ PASS (URL giữ nguyên `?type=3&ct=`) |
| **C-9** | `grep -oE "['\"]HD-(NT-NCC\|NT-DV\|TP\|GK\|NCC\|DV\|MB)['\"]" …/WorkflowMatrixViewPage.tsx \| sort -u \| wc -l` | =7 | **7** (`HD-DV`,`HD-GK`,`HD-MB`,`HD-NCC`,`HD-NT-DV`,`HD-NT-NCC`,`HD-TP`) — baseline 0 | ✅ PASS |
| **C-6 vế-âm** | `grep -c "chung 1 bộ" …/Layout.tsx` | =0 | **0** (baseline 1) | ✅ PASS |
| **C-6 vế-dương** | `grep -cE "7 bộ\|per-loại\|S188" …/Layout.tsx` | ≥1 | **1** | ✅ PASS |
| **ô X-5** | `grep -ci "chung 1 bộ" …/WorkflowMatrixViewPage.tsx` | =0 | **0** (baseline **1** — chữ HOA, chỉ `-ci` mới thấy) | ✅ PASS |
| **C-7** | `grep -A6 "TRANSIENT_QUERY_KEYS = " …/Layout.tsx \| grep -cE "'(ct\|view\|pendingMe)'"` | =0 | **0** — cửa sổ 7 dòng in ra để mắt nhìn: set vẫn đúng `['id','q','editHeader','page','phase','awId']` | ✅ PASS |
| **C-1a** (app) | `cd fe-user; npx eslint .` | ≤47, KHÔNG TĂNG | **47 problems (41 errors, 6 warnings)** — Y HỆT baseline | ✅ PASS |
| **C-1b** (file-wave) | `npx eslint src/components/Layout.tsx -f json` | ≤4E **và 0 ruleId mới** | **4 errors / 0 warning**; ruleId: `react-hooks/static-components` ×3 (`342`,`438`,`475`) + `react-hooks/set-state-in-effect` ×1 (`504`) = **cùng bộ 4 ruleId baseline**, chỉ trôi số dòng do +comment | ✅ PASS |
| **C-2** (build) | `cd fe-user; npm run build` | EXIT 0 | **EXIT=0**, `✓ built in 12.15s` | ✅ PASS |

**Ô máy: 10/10 PASS.**

⚠️ **Bẫy TỰ-TRÍCH-DẪN ở C-8 vế-2 (đúng lớp bài S188).** Lệnh `-rnF` ra **2 hit**, nhưng hit thứ 2 (`Layout.tsx:496`) là **chính comment tôi vừa viết** đang nhắc lại nguyên văn URL đó, không phải code. Discriminator: bỏ dòng bắt đầu bằng `//` ⇒ còn **1 hit code thật** `Layout.tsx:165` `` return `/contracts/workflow-matrix?type=3&ct=${typeInt}` ``. Ô vẫn PASS theo cả 2 cách đếm, nhưng ghi ra đây để lượt sau đừng đọc "2" thành "có 2 chỗ sinh URL".

ℹ️ **Build warning = PRE-EXISTING, không phải sản phẩm của W1:** `chunk > 500 kB` + `[INEFFECTIVE_DYNAMIC_IMPORT] src/lib/realtime.ts` — cả 2 đã có từ trước (đối chứng: nhật ký vai này ghi đúng 2 warning này ở S167).

### Ô hành-vi — DEFERRED (không dựng dev server theo lệnh giao việc)

| Ô | Nội dung | Trạng thái |
|---|---|---|
| ✅W1(i) | mở nhóm 01 → click "Luồng duyệt" → accordion GIỮ nhóm 01; control-âm `ct=5` → mở nhóm 05 | **DEFERRED-TO-LOCAL-VERIFY** |
| ✅W1(ii) | `?type=1&phase=9&view=approved` ⇒ CHỈ leaf "Đã duyệt" sáng | **DEFERRED-TO-LOCAL-VERIFY** |
| ✅W1(iii) | vòng `ct ∈ 1..7`: ≥1 quy trình đúng họ + 0 dòng khác họ + `ct=1` VẮNG `QT-HD-V2-001` + control `ct=99` KHÔNG lọc | **DEFERRED-TO-LOCAL-VERIFY** — 🔴 ô này là ô DUY NHẤT khoá được mapping int→mã (C-9 chỉ đếm 7 mã distinct, mapping sai vẫn xanh). Bù rủi ro bằng đường khác: mapping chép trực tiếp từ `ContractApprovalWorkflowFamily.cs:33-42` đã MỞ ĐĨA, và `ContractType.cs:5-11` được đo để xác nhận 1..7 đúng thứ tự `Members`. Vẫn CẤM tick tới khi chạy thật. |

## DEFERRED/DỪNG

**COMMIT: `09ebd20e`** — `[CLAUDE] FE-User: YC-029 W1 — danh-tính menu 7 loại HĐ (accordion ct + view=approved + WfView slice HD-*)`, **ĐÚNG 2 file** (`fe-user/src/components/Layout.tsx` +50/−?, `fe-user/src/pages/pe/WorkflowMatrixViewPage.tsx`), 84 insert / 9 delete. `git add` đích danh (Đ-4). **KHÔNG push** (deploy CHỜ). Cây fe-user sạch sau commit.

### Điều kiện dừng Đ-1..Đ-10: **0 điều kiện chạm**

- Đ-1 eslint: app 47→**47** (không tăng), file-wave 4E→**4E**, 0 ruleId mới ⇒ không chạm. KHÔNG dùng `eslint-disable` chỗ nào.
- Đ-4 scope: chỉ 2 file wave. Cây có 5 file khác đang `M` (`.claude/commands/pause.md`, `tiep.md`, `.session-counter.json`, `docs/HANDOFF.md`, `docs/governance/so-yeu-cau-chu-du-an.md`) — **KHÔNG phải của tôi, KHÔNG add, KHÔNG chạm**.
- Đ-5 neo lệch: 0. Mọi neo plan W1 đo lại khớp đĩa trước khi sửa (accordion `:470-485` · comment `:141-142` · Approved `:152` · `TRANSIENT :350` · WfView `:58-60` + khuôn `group` `:84-99`).
- Đ-8 ranh chờ-anh: không chạm Q3/Q4/Q5/W8/W9.

### DEFERRED (không phải bỏ — là nợ có tên)

1. **3 ô hành-vi W1(i)(ii)(iii) = `DEFERRED-TO-LOCAL-VERIFY`** — lệnh giao việc cấm dựng dev server. 🔴 Trong đó **W1(iii) là ô DUY NHẤT có răng với mapping `int → HD-*`**: C-9 chỉ đếm 7 mã distinct, nếu tôi chép lệch cặp (vd `6: 'HD-NT-DV'`, `7: 'HD-NT-NCC'`) thì C-9 VẪN = 7 và build VẪN xanh. Người chạy local-verify phải làm đúng vòng `ct ∈ 1..7` + control `ct=99`.
2. **Gợi ý cho worker W3** (không phải việc của tôi, chỉ khai để khỏi trùng): chú CẤM-thêm ở trên `TRANSIENT_QUERY_KEYS` đã nêu sẵn cả `pendingMe` ⇒ W3 chỉ cần GIỮ, không phải viết lại; và ô C-7 dùng chung 1 lệnh cho cả 3 param.
3. **Ghi nhận (không hành động):** khối chú "lọc client-side" của nhánh `group` nay nằm TRƯỚC khối `ct` mới trong file WfView, đọc hơi so-le. Không sửa vì (a) đúng nghĩa cả 2 nhánh, (b) chạm thêm = drift ngoài 7 việc được giao (Đ-4).
