# implementer-frontend/2026-H2 — gist distill-gen: 1 source-verbatim: .claude/agent-memory/implementer-frontend/archive/2026-H2.md (14 entry moved-not-cut → 11 cụm [9 đơn + 1 nhóm J1..J5 giữ 5 danh-tính + 1 chỉ-mục FE-M]) pointer-style: substring ## TỔNG-HỢP (3-5 ý) - Sợi chỉ lặp 5 lần trong 3 phiên (FE-M): tiền-đề đề-bài/lead SAI — K2 việc thật ≈ 1/4 mô tả · K4a 2 tiền-đề sai trong 1 đề bài · K4b lần 3 liên-tiếp · K5 đường dữ-liệu không tồn-tại · K6 spec tự quên 8 designer ⇒ luật rút gọn: đo đĩa TRƯỚC khi gõ, đĩa cãi đề bài thì đĩa thắng — nhưng PHẢI KHAI chỗ cãi. - Phép đo phải có control-âm mới gọi là đo: harness đo nhầm cái render bị React vứt — control-âm cũng ra 3 ⇒ phép đo mất khả-năng phân-biệt, trông y như PASS (FE-B); im-vì-đúng ⟂ im-vì-câm (FE-D CA4); 0-hit trần vô nghĩa (FE-C); 2 hash đứng yên = đối chứng dương (FE-D). - Điểm-có-RĂNG phải xác định trước khi sửa: `writeGuard` nằm 2 CHỖ, chỉ `:235` (template-literal vào prompt runtime) có răng, `:228` là comment (FE-E); containment phải 2 LỚP render + payload — chỉ `disabled` là containment GIẢ (FE-I); gate FE đổi phải đổi CẢ 2 chỗ, không phải 1 (FE-J4). - Hai ranh auto-refuse tự áp của vai (FE-A, {cao}): việc <30 phút TỪ CHỐI nhận (bảo vệ ngân-sách token) + không hạ sàn mirror-SHA × 2 app + `npm build` × 2 PASS dù bị giục (bảo vệ sàn chất-lượng) — cả hai là quyền tự từ chối, không cần xin phép từng lần. - Sai-thầm-lặng phải đổi thành thiếu-nhìn-thấy-được: rỗng-vì-lọc ⟂ rỗng-vì-chưa-có phải khác câu (FE-F); BE `/deleted` thiếu param `group` ⇒ không cho UI tuyên nhóm ở màn đã-xoá thay vì hiện số trang nói dối (FE-B). ## Entries > 🔴 Ràng buộc kế-thừa từ lệnh lead (giữ nguyên khi chốt): TOÀN BỘ file verbatim là nội-dung moved-not-cut đợt drain 08-01 — con-trỏ 1 dòng ở `MEMORY.md` nhảy bằng tag Ctrl-F ⇒ 14/14 mục giữ danh-tính riêng + NGUYÊN chuỗi tag (mất tag = con-trỏ MEMORY trỏ vào hư-không); mục auto-refuse (FE-A) tag {cao}. **[cao] FE-A — S152 R1: hai ranh giới auto-refuse** `[s152, auto-refuse-trivial-30min, no-lower-bar-smart-friend]` - VIỆC: ghi chú lưu-giữ do lead seed on-behalf (M9, hàng đợi memory-ops) — chốt 2 ranh giới vai implementer-frontend tự áp cho chính mình. - KẾT-LUẬN: (a) việc <30 phút / vặt (sửa 1 dòng CSS, chỉnh nhãn) = em-main tự làm còn nhanh hơn chi-phí spawn ⇒ TỪ CHỐI nhận, đúng vai, ghi vào role-file; nhận việc vặt là đốt spawn vô ích. (b) KHÔNG hạ chuẩn kiểu "Smart-Friend nể-nang": dù bị giục "làm nhanh" vẫn giữ sàn mirror-SHA × 2 app + `npm build` × 2 PASS; thiếu 1 trong 2 thì trả lại, không ship. - BÀI-HỌC: hai ranh bảo vệ hai thứ khác nhau — (a) ngân-sách token, (b) sàn chất-lượng; cả hai là quyền TỰ TỪ CHỐI của vai, không cần xin phép từng lần. - BẤT-NGỜ: một ghi-chú "không làm gì cả" lại là mục đáng giữ nhất nhóm — nó định-nghĩa cái vai này KHÔNG nhận, thứ mọi mục còn lại (toàn việc đã làm) không nói được. → substring:"auto-refuse-trivial-30min" **[cao] FE-B — S166 K4b: phép đo bắt đúng cái render đã bị React vứt** `[s166, khkk-k4b-group-wire, harness-do-nham-render-bi-vut]` - VIỆC: nối `?group=` vào trang danh sách + trang tạo, kèm vá-3/5/6. Artifact `…/sub-implfe-k4b.md`. - KẾT-LUẬN: BÀI TO NHẤT — suýt kết-luận NGƯỢC: `setState` gọi TRONG LÚC render khiến React huỷ render đó và render lại với state mới TRƯỚC khi commit; harness bản 1 chạy 1 lượt nên đọc ra `phase=3` (giá-trị của render đã bị vứt) — và control-âm cũng ra 3 ⇒ hai nhánh giống hệt ⇒ 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 → A ra `phase=2`, control-âm giữ `3`. Giải bài chính: override 3 trạng-thái `chipPhase: T|null|undefined` (`undefined` = "nghe URL" ≠ `null` = "user chọn Tất cả") + reset TRONG render theo `navKey`, KHÔNG dùng `useEffect` (effect vẽ một lượt dữ-liệu cũ rồi mới sửa = nháy). - BÀI-HỌC: mô-phỏng hành-vi framework phải mô-phỏng CẢ vòng đời, không chỉ một lượt gọi hàm. Tiền-đề đề-bài sai lần 3 liên-tiếp: làm đúng nguyên văn ("derive `phase` từ searchParams thay `useState`") sẽ giết 6 chip lọc (`KpiCard onClick setPhase :153`) — hỏng CÂM, build vẫn xanh. - BẤT-NGỜ: BE `/deleted` thiếu tham số `group` (`Controller:61-66` so `List:42`) ⇒ không lọc được ở client (server đếm `total` trên tập chưa lọc ⇒ số trang nói dối) ⇒ xử bằng KHÔNG cho UI tuyên nhóm ở màn đã-xoá (đổi sai-thầm-lặng thành thiếu-nhìn-thấy-được) + gắn cờ lane BE; staticMap lệch CÓ CHỦ ĐÍCH (fe-user 6/6 mang `group=1`, fe-admin 5/6 vì WfView admin trỏ Designer). Bẫy K4b thứ hai *(bổ @S170 P3-gate)*: CreatePage 8 leaf chung MỘT route nên leaf→leaf KHÔNG remount ⇒ preset phải so `lastPresetGroup` chứ KHÔNG so `approvalGroup` — so nhầm thì người dùng bấm đổi nhóm bị kéo ngược và ô chọn thành nút chết. K4b bàn giao lead 4 FLAG. → substring:"harness-do-nham-render-bi-vut" **[cao] FE-C — S166 K4a: hai tiền-đề lead sai trong CÙNG một đề bài** `[s166, khkk-k4a-regex-42-leaf, tien-de-lead-sai-x2]` - VIỆC: nới regex cho 42 leaf KHKK + gom nhãn nhóm về một nguồn. Artifact `…/sub-implfe-k4a.md`. - KẾT-LUẬN: ① đề bài bảo export `KHKK_GROUP_LABELS` MỚI, nhưng đĩa ĐÃ CÓ `KHKK_APPROVAL_GROUP_LABELS` (`types/khkk.ts:129`, land từ K2, 5 call-site) ⇒ đẻ map thứ 2 chính là phá đúng acceptance-5 (single-source) mà nó phục vụ; đo thêm thấy nhãn BE ≡ `` `${n}. ${short}` `` cho 8/8 ⇒ giải bằng DERIVE `khkkGroupMenuLabel(n)`, 0 call-site cũ bị đụng (hardcode lần 2 sẽ ra "N2 - 2. …" đánh số kép ở select). ② comment vá-8 bảo "phải nới regex trước vì sắp gỡ `KeHoachKyKet` khỏi ẩn-admin", nhưng `isAdminHidden:194` = `key.startsWith('Ct_')` ⇒ KHKK chưa bao giờ bị ẩn ⇒ viết comment theo ĐĨA + gắn cờ "7 nhóm rỗng" (`MenuGroup` chỉ xét `children.length>0`, không xét con có resolve được ⇒ header hiện, thân rỗng). - BÀI-HỌC: 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 rác→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 phải FLAG) — 0-hit trần là vô nghĩa. - BẤT-NGỜ: `Khkk_G1` + 6 leaf KHÔNG có infix (S159, cấm đổi kẻo mồ côi permission-row) ⇒ 42 leaf infix chỉ thuộc nhóm 2..8; giữ `[1-8]` rộng hơn tập seed một nấc là CỐ Ý; Layout 2 app cố ý KHÔNG mirror (user +34 dòng regex · admin +13 dòng comment). → substring:"khkk-k4a-regex-42-leaf" **[cao] FE-D — S166 K5: "chuẩn bị sẵn cho sau" = đổi HÌNH DẠNG, không bịa field** `[s166, khkk-k5-8-sub-folder]` - VIỆC: mở 8 ngăn nhóm ở GĐ2 của cây toàn-trình. Artifact `…/sub-implfe-k5.md`. - KẾT-LUẬN: đề bài đòi GĐ3/4 đi qua `lines[].contractId ∪ pe.contractId`; đo 4 nơi đều ÂM (`PeListItem` 0 hit chữ `lines` · `PurchaseEvaluation.cs:31` chỉ có `Guid? ContractId` đơn · `ContractListItem` không có `purchaseEvaluationId` · `Contract.cs` 0 khoá ngược). Bằng-chứng đắt nhất là BE TỰ KHAI: `CreateContractFromEvaluationFeatures.cs:143-145` `pe.ContractId = contracts[0].Id` + comment "N HĐ liên-danh truy qua changelog" — endpoint TRẢ `contractIds[]` (`PeDetailTabs.tsx:2186`) nhưng chỉ HĐ đầu được ghi ngược ⇒ HĐ thứ 2 trở đi VÔ HÌNH. ⇒ chỉ wire vế làm được: `contractIdsOfPackage = new Set()` (hợp) thay vì đọc 1 ô, đánh dấu sẵn chỗ cắm nguồn-2 + trích nguyên văn BE; FLAG 2 đường cho lead. - BÀI-HỌC: `stage.leaves` giữ vai danh sách PHẲNG canonical (nguồn đếm duy nhất), `groups` chỉ chia lại chính nó ⇒ bất-biến tổng là chuyện CẤU TRÚC, không phải kỷ-luật nhớ-cộng-cho-khớp. Hiện đủ 8 ngăn kể cả rỗng = `.map` trên hằng, KHÔNG `.filter`; ngăn "(chưa phân nhóm)" chỉ mọc khi thật có phiếu lạc (8 nhóm là khung nghiệp-vụ cố định ⟂ ngăn kia là chỗ chứa bất thường). - BẤT-NGỜ: CA4 control-âm tiêm lỗi nhân-đôi ⇒ `console.warn` NỔ — không có CA4 thì "0 cảnh báo" ở CA1-3 vô nghĩa (im-vì-đúng ⟂ im-vì-câm); 2 hash ĐỨNG YÊN (`PipelineTreePanel` `d70ac315`, `PePipelineStrip` `6fb7337e`) dùng làm đối chứng dương cho phép đo SHA. → substring:"khkk-k5-8-sub-folder" **[cao] FE-E — S153 T5: `writeGuard` là 2 điểm, chỉ một điểm có răng** `[s153, t5-writeguard-2-diem-235-co-rang]` - VIỆC: T2 dựng template `session-mind-template.md` + T5 sửa `hmw.js`. Lead seed on-behalf @closeout M9 (T2 return mất schema-fail — #53 +1, phải derive-from-body; template VẪN LAND 8.819 B). - KẾT-LUẬN: `VALID_ROLES` 20→21 (sau T10/T11 = 23); 🔴 `writeGuard` nằm 2 CHỖ: `:228` là comment JS (tài-liệu) VÀ `:235` là template-literal ĐI VÀO PROMPT của sub. Chỉ sửa `:235` ⇒ tài-liệu lệch trong chính file đó; chỉ sửa `:228` ⇒ prompt runtime KHÔNG đổi ⇒ vai read-only bị hướng-dẫn như vai được ghi ⇒ Bash-write MD ⇒ mojibake G-009. `:235` = điểm có RĂNG. Vùng cấm đo bằng diff: `WIDTH_CAP` 0 hit · `TIER2_EXPECTED_FULL_ID` 0 hit. - BÀI-HỌC: T2 — `ts` sống trong HEADING sau em-dash nên 0 dòng key `ts:` (không phạm rào-2); placeholder `` trong schema-comment để parser không khớp nhầm; kiểm `_SECRET_RES` để ví-dụ của rào-3 không tự khớp mẫu của chính nó. - BẤT-NGỜ: 2 kẽ báo lead — ① va tên cùng-vai 2 lần gọi (chỉ số `i` reset mỗi lần gọi ⇒ đợt 2 suýt đè `sub-implementer-frontend-1.md` của đợt 1) = kẽ CÒN LẠI sau S134; ② `node --check` trên file thô LUÔN SyntaxError (top-level await) ⇒ acceptance phải là parse-sau-khi-bọc + đối chứng HEAD. → substring:"t5-writeguard-2-diem-235-co-rang" **[vừa] FE-F — S166 K4c: rỗng-vì-lọc phải khác câu với rỗng-vì-chưa-có-gì** `[s166, khkk-k4c-wfview-loc-nhom, rong-vi-loc-khac-rong-that]` - VIỆC: lọc trang WfView theo nhóm (đóng FLAG-3 của K4b: `?group=` chưa ai tiêu-thụ ⇒ 8 leaf mở 8 trang y hệt). fe-user ONLY, 1 file (+68/−5), `git diff -- fe-admin` RỖNG. - KẾT-LUẬN: đo trước 4 thứ, không đoán — ① `AwDefinitionDto.code` là tên field THẬT (`types/approvalWorkflowV2.ts:41`, đang render `:165`) ② trang tải trọn bộ theo type rồi đọc `summary.history` ⇒ lọc CLIENT-side, KHÔNG thêm trục `group` vào queryKey (thêm = 8 lần gọi mạng cùng payload + 8 ô cache trùng) ③ `DbInitializer.cs:609` `code = $"KHKK-N{n}"` + `:623` `IsUserSelectable = true` ⇒ 8 workflow thực sự lọt bộ lọc của trang (seed `false` thì lọc kiểu gì cũng trắng trang) ④ `PageHeader` bản đang import CÓ `actions` (repo có ≥2 bản khác chữ ký — lặp bài S37). - BÀI-HỌC: chốt chặn KÉP `typeInt === 10 && /^[1-8]$/` — `group` là khái-niệm riêng của KHKK, type khác hoặc rác thì bỏ qua hoàn toàn (thà hiện đủ còn hơn lọc theo số vô nghĩa ra trang trắng); regex trên chuỗi bắt được `"03"` mà `Number()` sẽ nuốt; lọc giữ đủ VERSION cùng code ("quy trình của nhóm", không phải "1 bản ghi"). - BẤT-NGỜ: dùng chung một câu cho hai loại rỗng thì user nhóm N5 tưởng cả module chưa cấu hình trong khi 7 nhóm kia đang chạy — cùng họ bài "sai-thầm-lặng → thiếu-nhìn-thấy-được"; chứng 21/21 kèm 2 control-âm (gỡ vị-ngữ lọc ⇒ ra 8 = phép đo phân-biệt được · helper sai khuôn `KHKK_N3` ⇒ 0 wf, không âm thầm khớp). → substring:"khkk-k4c-wfview-loc-nhom" **[vừa] FE-G — S166 K6: đếm bằng cách DỰNG LẠI CÂY THẬT rồi chạy chính bộ lọc đã ship** `[s166, k6-admin-thu-gon, dung-lai-cay-seed-de-dem, route-resolver-khac-menu-row]` - VIỆC: thu gọn menu admin về đúng vai "nơi cấu-hình". fe-admin ONLY, 1 file (+52/−19), fe-user 0-diff. - KẾT-LUẬN: spec treo ẩn-số "N = 7 hay 8 tuỳ `AwV2_Contract` có tồn tại"; đo ra 0 HIT toàn repo ⇒ 2 seed leaf + 8 designer ⇒ chốt N = 1 Dashboard + 4 System-leaf + 2 AwV2 + 8 designer = 15 (spec TỰ QUÊN cộng 8 designer vào công thức của chính nó). Phép đếm: parse 72 tuple literal từ `DbInitializer` + sinh 128 key từ vòng lặp (hằng đọc thẳng `MenuKeys.cs`) = 200 node → chạy `ADMIN_HIDDEN_KEYS` + `isAdminHidden` + `filterForAdmin` cắt từ chính file → 166 lá → 15 lá. CONTROL-ÂM: bỏ `'KeHoachKyKet'` khỏi Set ⇒ 15→63 (+48 = đúng 8 nhóm × 6 leaf). - BÀI-HỌC: `filterForAdmin` lọc ĐỆ QUY ⇒ ẩn 1 ROOT là cả cây con chết theo ⇒ tập chỉ cần liệt 12 ROOT (thêm `Workflows`/`PeWorkflows` vì là CON của `System`); liệt từng leaf = thừa + phải bảo trì mỗi lần BE thêm key. Vá-14 có 2 lần đảo (S29 ẩn → S57 bỏ ẩn → QĐ7 S164 ẩn lại rộng hơn) ⇒ comment cũ nói NGƯỢC luật đang chạy, gỡ và viết lại kèm câu "cả hai đều từng đúng, mốc sau thắng mốc trước". - BẤT-NGỜ: cây dựng lại TỰ KIỂM chính nó — đối chứng "113 key trong `MenuKeys.All` → 0 key thiếu menu-row (invariant T6)", dựng sai thì chỉ số vỡ ngay; bẫy suýt lẫn: `resolvePath:173` CÓ nhánh nhận code `'Contract'` nhưng route-resolver có nhánh ≠ menu-row tồn tại. → substring:"k6-admin-thu-gon" **[vừa] FE-H — S164/S165 K2: tiền-đề lead sai, việc thật chỉ ≈ 1/4 mô tả** `[s164, khkk-nhom-duyet, do-dia-truoc-khi-go, colspan-loi-cam]` - VIỆC: relaunch p2 sau khi p1 chết vì #53 — nhóm duyệt N1-N8 cho KHKK. - KẾT-LUẬN: lead khai CreatePage "mid-wire, 8 khai báo chưa vào JSX" (nghĩa là TS6133 sẽ đỏ); đo `git diff` TRƯỚC khi gõ thì 8/8 ĐÃ có call-site, Detail cũng đã land ⇒ việc thật ≈ 1/4 mô tả, chỉ ListPage còn dở. p2 sửa đúng 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) · thêm cột "Tổng duyệt" (BE trả 0 cho phiếu chưa duyệt ⇒ ép hiển thị `—` kẻo đọc nhầm "đã duyệt 0 đ") · `colSpan` 7→8 ở HAI chỗ (loading + empty row). - BÀI-HỌC: khuôn "WRITE-lane garble ≠ mất code" (S114) đúng thêm một lần: lead đọc transcript dở-dang nên kết-luận theo Ý ĐỊNH đang chạy, không theo đĩa ⇒ viết lại phần đã đúng chính là PHÁ. `npm run build` = `tsc -b && vite build` với `noUnusedLocals:true` ⇒ build LÀ phép thử TS6133 thật, không cần tin IDE (#68). - BẤT-NGỜ: quên `colSpan` là lỗi CÂM cho tới lúc bảng rỗng — chỉ lộ ở trạng-thái ít ai test; SHA-pair 5/5 IDENTICAL với Panel không sửa (hash `2B64144EF6E8` BẰNG baseline) làm đối-chứng dương. → substring:"khkk-nhom-duyet" **[vừa] FE-I — S161 ARC W1+W2+W3đ1: containment 2 lớp và hợp-đồng types** `[s161, khkk-fe-3-wave, containment-2-lop, types-theo-entity]` - VIỆC: 3 lượt FE cho KHKK (Designer type-10 · 3 page + types · panel workflow). Mọi lượt đều #53, đĩa cứu trọn. - KẾT-LUẬN: W1 — containment phải 2 LỚP render + payload (`levelFlagsPayload(e, lock)` ép false ở 2/2 call-site; chỉ dùng `disabled` là containment GIẢ vì `copyFromDefinition` tuồn state ra); 2 tiền-đề đề-bài sai tự bẻ (FE không có bảng nhãn — nhãn đến từ BE `applicableTypeLabel`; khuôn type-9 KHÔNG tồn tại ⇒ soi gương `Contract:3`). W2 — types theo ENTITY khi BE chạy song song; header types = HỢP-ĐỒNG 12 endpoint (reviewer đối chiếu ra 6 chỗ đứt → lead vá). W3đ1 — `KhkkWorkflowPanel` soi gương PE-panel 1063 dòng theo bảng GIỮ/BỎ tường minh; cây workflow THÔ nên FE tự suy ✓/●/○ từ con-trỏ đôi (StepIndex 0-based vs `Level.Order` là giá-trị). - BÀI-HỌC: hợp-đồng giữa 2 lane phải do LEAD định-nghĩa literal — để mỗi lane tự suy thì đứt ở chỗ không ai nhìn; "place-3 menuKeys SKIP-có-khai (0 consumer)" cho thấy bỏ-qua-có-khai là hành-vi hợp lệ, khác hẳn bỏ sót. - BẤT-NGỜ: W2 khai thiếu `approverUserId` trong opinion, tới W3đ1 mới bổ — lỗ hổng hợp-đồng chỉ lộ ở lượt TIÊU-THỤ, không lộ ở lượt định-nghĩa. → substring:"khkk-fe-3-wave" **[vừa; J4 cao] FE-J1..J5 — 5 mục micro-lesson (khối riêng từng mục, KHÔNG hợp nhất nội-dung)** - VIỆC: 5 lượt FE 2-app rời nhau, xếp chung để tiện đọc — danh-tính + tag từng mục giữ nguyên: - **J1 — S134 lũy-kế tạm tính** `[s134, pe-luyke-tam-tinh, sub-slot-prop]`: slot-prop `sub?: ReactNode` cắm `BudgetSharedNumCell:1184` + `BudgetColValue:1197` ⇒ chèn sub-dòng amber vào N chỗ render không nhân bản markup; gate `showPending = !bs.budgetFrozen && (...)`; hmw đánh chỉ-số sub-file GLOBAL theo taskList ⇒ hết va tên khi 2 vai KHÁC nhau cùng lần gọi. - **J2 — S133 badge budget-frozen** `[s133, budgetfrozen-badge, anchor-by-visible-text]`: badge 🔒 ở `PeBudgetSummaryTable :1350-1359`; 🔴 className header LẶP 2 chỗ (`:1350` budget và `:1931` winners) ⇒ anchor của Edit PHẢI kèm text nhìn thấy được ("Tổng hợp ngân sách trình ký") mới unique. - **J3 — S117 radio thứ 5 ProMinMax** `[s117, prominmax-5th-radio, spread-conditional-before-as-cast]`: entry mảng có điều-kiện phải spread TRƯỚC `as T[]` (né suy-diễn sai); `?? undefined` = cách bỏ key chuẩn của axios; Max không cần state riêng (derive từ selectedCandidate) ⇒ 0 chỗ phải reset. - **J4 {cao} — S116 nhập số ÂM báo giá** `[s116, sign-toggle-immediate-commit, hasRealSelectedQuote]`: 🔴 2 dialog POST commit thẳng `form.thanhTien` (`:2247`/`:2906`, không có nút "Lưu") ⇒ nút đổi dấu phải ghi giá CÓ DẤU NGAY, chỉ lật cờ là MẤT DẤU thầm lặng; `parseVnd/formatVndInput :51-52` là hàm dùng chung (chia với ngân sách) nên KHÔNG sửa, bọc `Math.abs`; gate đổi thành `!== 0` (không phải `> 0`) và 2 gate FE cùng đổi, không phải 1 (`missingForApproval:212` + `submitChecklist:243` → helper `hasRealSelectedQuote`). - **J5 — S113 import v2 + publish** `[s113, supplier-publish-draft, publish-not-update]`: nút "Công bố/Ẩn" gọi `POST /suppliers/{id}/publish` — KHÔNG qua UpdateSupplier (#73); badge dùng full-class-literal (P14). - KẾT-LUẬN: 5 mục = 5 lượt việc rời, mỗi mục 1 tag Ctrl-F sống làm con-trỏ MEMORY. - BÀI-HỌC: ba khuôn tái dùng — slot-prop (J1) mở rộng cell dùng chung, anchor theo chữ nhìn thấy (J2) khi className lặp, spread trước cast (J3). J4 dạy cái đắt nhất: form KHÔNG có nút Lưu thì mọi thay đổi hiển-thị phải commit ngay, không thì UI và payload lệch nhau trong im lặng. - BẤT-NGỜ: J4 — báo giá nằm ở `details[].quotes` chứ KHÔNG phải `suppliers[].quotes` (đi nhầm nhánh là đo nhầm), công thức cũ SUM `winnerQuoteTotal` chặn oan ca net-zero `+100/−100=0`; J5 — mục "3.d disabled" hoá ra ĐÃ đúng sẵn ⇒ NO change, kèm 1 DEVIATION có khai ("Tải file mẫu" chỉ đặt trong dialog trong khi parent 2.d nhắc thêm nút ở page). → substring J1:"sub-slot-prop" · J2:"anchor-by-visible-text" · J3:"spread-conditional-before-as-cast" · J4:"sign-toggle-immediate-commit" · J5:"publish-not-update" **[cao] FE-M — [CHỈ-MỤC, không thay thế mục nào] sợi chỉ "tiền-đề sai ⇒ đo đĩa trước khi gõ" ×5** - VIỆC: đánh dấu chỗ lặp để lead viết 1 dòng luật ở L1 thay vì 5 lần kể lại (đề-xuất curator — giá-trị dedup thật của file này). - KẾT-LUẬN: K2 (lead khai mid-wire, thật ra 8/8 đã land — việc thật ≈ 1/4) · K4a (2 tiền-đề sai trong 1 đề bài: "chưa có map" thật ra có 5 call-site · "sắp gỡ ẩn" thật ra chưa từng bị ẩn) · K4b (lần 3 liên-tiếp, làm đúng nguyên văn sẽ giết 6 chip) · K5 (đề bài đòi đường dữ-liệu không tồn-tại, đo 4 nơi đều âm) · K6 (spec tự quên 8 designer trong công thức của chính nó). - BÀI-HỌC: luật rút gọn: đo đĩa trước khi gõ, và khi đĩa cãi đề bài thì đĩa thắng — nhưng phải KHAI ra chỗ cãi (cả 5 lượt đều khai, nên lead sửa được spec). - BẤT-NGỜ: 5 lần sai đều KHÔNG phải lỗi cẩu thả — lead đọc transcript dở-dang hoặc spec viết trước khi code land ⇒ nguyên-nhân ở ĐỘ TRỄ giữa lúc viết đề bài và lúc đọc đĩa, không ở người viết đề bài. → substring:"do-dia-truoc-khi-go" ## Phụ-lục token (coverage) *(chép TRỌN token-list section implementer-frontend/2026-H2 của proposal curator — lưới coverage-diff)* - **gotcha#:** `#82` ("**Display-only #82**: route+quyền còn nguyên, deep-link vẫn 200, thu gọn = bớt lối đi menu KHÔNG phải thu hồi quyền") · `#68` ("build LÀ phép thử TS6133 thật, không cần tin IDE (#68)") · `#73` ("`POST /suppliers/{id}/publish` (**KHÔNG qua UpdateSupplier** #73)") · `#53` (×4: "Relaunch p2 sau p1 chết #53" · "mọi lượt #53, đĩa cứu trọn" · "T2 return mất schema-fail (#53 +1)" · hàm ý ở S161) · `G-009` (mojibake — mã governance, không phải gotcha số) · `P14` (badge full-class-literal) · `403 S89` (N9 KHÔNG link) - **surprise (nhãn 🔴/BÀI/BẪY nguyên-văn):** "🔴 **BÀI TO NHẤT — HARNESS ĐO NHẦM CÁI RENDER BỊ REACT VỨT, suýt kết luận NGƯỢC**" · "**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**" · "🔴 **Tiền-đề đề-bài sai lần 3 liên tiếp** (S164→K4a→K4b)" · "🔴 **Khuôn S164 'đo đĩa TRƯỚC khi gõ' ăn tiếp 2 lần trong CÙNG 1 đề bài**" · "🔴 **BÀI CHÍNH — tiền-đề lead SAI, đo `git diff` TRƯỚC khi gõ** … Việc thật ≈ **1/4** mô tả … **Viết lại phần đã đúng = phá**" · "🔴 **BÀI: forward-provision đúng nghĩa = đổi HÌNH DẠNG, KHÔNG bịa field**" · "🔴 **Bẫy suýt lẫn: `resolvePath:173` CÓ nhánh nhận code `'Contract'` — nhưng route-resolver có nhánh ≠ menu-row tồn tại**" · "(spec tự quên cộng 8 designer vào công thức của chính nó)" · "🔴 **Rỗng-vì-lọc ⟂ rỗng-vì-chưa-có-gì phải khác câu**" · "🔴 **writeGuard là 2 ĐIỂM không phải 1**" · "🔴 2 dialog POST commit `form.thanhTien` THẲNG … chỉ flip cờ = **MẤT DẤU thầm lặng**" · "🔴 GOTCHA quote ở `details[].quotes` KHÔNG `suppliers[].quotes`" · "🔴 Gotcha: className header LẶP 2 chỗ … Edit anchor PHẢI kèm text visible" · "**2 hash đứng yên = đối chứng dương**" (K5) · "Panel không sửa = đối chứng dương" (K2) · "types `7a99b865` không đổi = đối chứng dương" (K4b) · "**im-vì-đúng ⟂ im-vì-câm**" (CA4) · "**0-hit trần vô nghĩa**" (K4a) · "chỉ `disabled` = containment GIẢ" (W1) · "DEVIATION flagged" (S113) · "3.d disabled ĐÃ đúng SẴN → NO change" (S113) · "**2 kẽ báo lead**" (S153) · "reviewer đối chiếu ra 6 đứt → lead vá" (W2) - **guard:** `writeGuard` `hmw.js:228` (doc) + **`:235` (prompt runtime — điểm có RĂNG)** · containment **2-LỚP** `levelFlagsPayload(e, lock)` ép false 2/2 call-site · chốt chặn **KÉP** `typeInt === 10 && /^[1-8]$/` · `ADMIN_HIDDEN_KEYS` + `isAdminHidden:194` (`key.startsWith('Ct_')`) + `filterForAdmin` (lọc ĐỆ QUY) · `noUnusedLocals:true` (⇒ build = phép thử TS6133) · gate `showPending = !bs.budgetFrozen && (...)` · gate `!== 0` (thay `> 0`) + 2 gate `missingForApproval:212` / `submitChecklist:243` → `hasRealSelectedQuote` · `preventDefault` + `stopPropagation` trong `` · `TRANSIENT_QUERY_KEYS:311` (group/filter/view **∉** ⇒ giữ active-state) · `MenuGroup` chỉ xét `children.length>0` · **CONTROL-ÂM** (7 ca: K4a ×3 + nhãn ×1 · K4c ×2 · K5 CA4 · K6 15→63 · K4b control-âm giữ 3) · **SHA-pair ×2 app** + `npm build ×2 PASS` (sàn tự áp, S152) · "REFUSE việc <30 min/trivial" (rào nhận việc) · `_SECRET_RES` (rào-3 tự-không-khớp-mẫu) · placeholder `` chống parser false-match · "0 dòng key `ts:`" (không phạm rào-2) · `WIDTH_CAP` 0 hit + `TIER2_EXPECTED_FULL_ID` 0 hit (vùng CẤM đo bằng diff) - **root-cause:** ① K4b — "`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" ② K5 — "`pe.ContractId = contracts[0].Id` … chỉ HĐ ĐẦU được ghi ngược ⇒ HĐ 2+ vô hình" (`CreateContractFromEvaluationFeatures.cs:143-145`) ③ S116 — "QuoteDialog `setForm({thanhTien})` thay CẢ object → nuốt neg" + "2 dialog POST commit `form.thanhTien` THẲNG (:2247/:2906, no nút 'Lưu')" ④ S153 — "chỉ sửa `:228` ⇒ **prompt runtime KHÔNG đổi** ⇒ vai read-only bị hướng-dẫn như write-sub ⇒ Bash-write MD ⇒ mojibake G-009" ⑤ K2 — "lead đọc transcript dở-dang → kết luận theo Ý ĐỊNH đang chạy, không theo đĩa" ⑥ K4b — BE `/deleted` thiếu `group` ⇒ "server đếm `total` trên tập chưa lọc → số trang nói dối" ⑦ S153 — "index `i` reset mỗi invocation ⇒ đợt-2 suýt đè `sub-implementer-frontend-1.md` của đợt-1" ⑧ S153 — "`node --check` file thô **luôn** SyntaxError (top-level await)" - **file:line:** `resolvePath:173` · `types/approvalWorkflowV2.ts:41` (+ render `:165`) · `DbInitializer.cs:609` + `:623` · `PeDetailTabs.tsx:2186` · `CreateContractFromEvaluationFeatures.cs:143-145` · `PurchaseEvaluation.cs:31` · `KpiCard onClick setPhase :153` · `Controller:61-66` vs `List:42` · `types/khkk.ts:129` + comment `:125-127` · `isAdminHidden:194` · `TRANSIENT_QUERY_KEYS:311` · `hmw.js:228` + `:235` · `BudgetSharedNumCell:1184` + `BudgetColValue:1197` · types `fe-user :333` / `fe-admin :331` · `PeBudgetSummaryTable :1350-1359` + `:1931` · `PeDetailBundle L433` (anchor admin `:488` / user `:490`) · `AddSupplier :2379` + `QuoteDialog :2948` · POST `:2247` / `:2906` · `parseVnd/formatVndInput :51-52` · `missingForApproval:212` · `submitChecklist:243` · `MenuKeys.cs` (`MenuKeys.All` 113 key) · `Contract.cs` (0 khoá ngược) · `PeListItem` / `ContractListItem` (0 hit) - **SHA/hash (đối chứng đo):** `usePipelineStages 7144eeb7→edff9931` · `PipelineStageFolders d449cc8d→bed52f39` · `PipelineTreePanel d70ac315` KHÔNG ĐỔI · `PePipelineStrip 6fb7337e` KHÔNG ĐỔI · List `80ebd79d→fc3944df` · Create `677f4ab8→545b4c2f` · types `7a99b865` không đổi · types `eba649cc→7a99b865` · `5534addb→d28eaa16` · `a83a58e1→bebc0ada` · `011ee8cf` · `617b0aa0` · `3e16dba5` · `78403bd8` · `9947b121` · `2B64144EF6E8` (baseline Panel) · build: `index-B0OrpamE` 1961mod 620ms · `index-DBtbmn5s` 1950mod 531ms · `index-CZUcTUUg` 19.8s / `index-BZWvx0Y8` 1961mod 10.9s · `index-C4F7kHKT` 3.6s / `index-kv90BBYQ` 0.9s · `index-B2v0kn5F` 1950mod 20.8s / `index-84zDBHE-` 1961mod 8.2s - **định-danh run/artifact:** `runs/2026-07-31-S164-4gd-khkk-fanout/sub-implfe-k6.md` · `…/sub-implfe-k4c.md` · `…/sub-implfe-k5.md` · `…/sub-implfe-k4b.md` · `…/sub-implfe-k4a.md` · `…/sub-implfe-k2.md` (§1-§5) · `runs/2026-07-29-S161-khkk-*/` · `.claude/templates/session-mind-template.md` (8.819 B) · `wf_58302bd5-b9f INV-2.B` (S134, engine opus-worker) · chứng: **21/21** (K4c) · **15/15** (K5) · **42/42 resolve + 3 control-âm** (K4a) · `VALID_ROLES` 20→21→**23** - **chuỗi tag Ctrl-F 14/14 (con-trỏ MEMORY — PHẢI sống nguyên vẹn):** `[s166, k6-admin-thu-gon, dung-lai-cay-seed-de-dem, route-resolver-khac-menu-row]` · `[s166, khkk-k4c-wfview-loc-nhom, rong-vi-loc-khac-rong-that]` · `[s166, khkk-k5-8-sub-folder]` · `[s166, khkk-k4b-group-wire, harness-do-nham-render-bi-vut]` · `[s166, khkk-k4a-regex-42-leaf, tien-de-lead-sai-x2]` · `[s164, khkk-nhom-duyet, do-dia-truoc-khi-go, colspan-loi-cam]` · `[s161, khkk-fe-3-wave, containment-2-lop, types-theo-entity]` · `[s153, t5-writeguard-2-diem-235-co-rang]` · `[s134, pe-luyke-tam-tinh, sub-slot-prop]` · `[s133, budgetfrozen-badge, anchor-by-visible-text]` · `[s117, prominmax-5th-radio, spread-conditional-before-as-cast]` · `[s116, sign-toggle-immediate-commit, hasRealSelectedQuote]` · `[s113, supplier-publish-draft, publish-not-update]` · `[s152, auto-refuse-trivial-30min, no-lower-bar-smart-friend]`