# archive/2026-H2 — implementer-frontend (verbatim L2, dựng @S166 2026-08-01)

> Chuyển verbatim từ `MEMORY.md` khi L1 chạm trần đọc. Con-trỏ 1 dòng ở `MEMORY.md`; Ctrl-F tag để nhảy.
> Thứ tự: mới → cũ.

---

## S166 (2026-08-01) — K6-FE admin thu gọn về vai NƠI-CONFIG `[s166, k6-admin-thu-gon, dung-lai-cay-seed-de-dem, route-resolver-khac-menu-row]`
Artifact `runs/2026-07-31-S164-4gd-khkk-fanout/sub-implfe-k6.md`. fe-admin ONLY, **1 file** (+52/−19), fe-user 0-diff.
**Giải ẩn-số spec để mở acceptance:** spec treo "N = 7 hay 8 tuỳ `AwV2_Contract` có tồn tại không". Đo: **0 HIT toàn repo** ⇒ 2 seed leaf + 8 designer. 🔴 **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.** 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ó).
**Khuôn giữ lại — ĐẾM BẰNG CÁCH DỰNG LẠI CÂY THẬT rồi chạy bộ lọc đã ship:** 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ừ file → **166 lá → 15 lá**, liệt đủ tên. **Đối chứng tính đúng của cây dựng lại: 113 key trong `MenuKeys.All` → 0 key thiếu menu-row (invariant T6)** — cây dựng sai thì chỉ số này vỡ ngay, tức phép đo tự kiểm chính nó. **CONTROL-ÂM:** bỏ `'KeHoachKyKet'` khỏi Set ⇒ 15→**63** (+48 = đúng 8 nhóm × 6 leaf).
**Cơ chế đáng nhớ:** `filterForAdmin` lọc **ĐỆ QUY** ⇒ ẩn 1 ROOT là cả cây con chết theo ⇒ tập chỉ liệt 12 ROOT (+`Workflows`/`PeWorkflows` vì chúng là CON của `System`, không chết theo root nào khác). Liệt từng leaf = thừa + phải bảo trì mỗi lần BE thêm key. `System`/`ApprovalWorkflowsV2` là GROUP nên không tính vào số LÁ.
**Vá-14 lineage 2 LẦN ĐẢO** (S29 ẩn → S57 bỏ-ẩn → QĐ7 S164 ẩn LẠI rộng hơn): comment cũ S57 đang nói NGƯỢC luật đang chạy ⇒ gỡ + viết lại kèm câu "cả hai đều từng đúng, mốc sau thắng mốc trước — đừng khôi phục S57 vì tưởng gặp bug". **Display-only #82** khai rõ: 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. Build fe-admin PASS 1961mod `index-B0OrpamE` 620ms.

## S166 (2026-08-01) — K4c-FE WfView lọc theo nhóm (chốt cụm-2) `[s166, khkk-k4c-wfview-loc-nhom, rong-vi-loc-khac-rong-that]`
Artifact `runs/2026-07-31-S164-4gd-khkk-fanout/sub-implfe-k4c.md`. fe-user ONLY, **1 file** (+68/−5), `git diff -- fe-admin` = RỖNG.
Đóng FLAG-3 lượt K4b (`?group=` trên workflow-matrix chưa có consumer ⇒ 8 leaf mở ra 8 trang hệt nhau).
**Đo trước khi gõ (4 thứ, không đoán cái nào):** ① `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 `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 cho cùng payload + 8 ô cache trùng) — khai rõ theo yêu cầu ③ `DbInitializer.cs:609` `code = $"KHKK-N{n}"` + `:623` **`IsUserSelectable = true`** ⇒ 8 wf THỰC SỰ lọt bộ lọc `isUserSelectable:true` của trang (seed `false` thì lọc kiểu gì cũng trang trắng) ④ `PageHeader` bản `@/components/PageHeader` CÓ `actions` — repo có ≥2 bản PageHeader khác chữ ký, **đọc bản đang import** (lặp bài S37).
**Khuôn giữ lại:** chốt chặn KÉP `typeInt === 10 && /^[1-8]$/` — `group` là khái niệm riêng KHKK, type khá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 chuỗi bắt được `"03"` mà `Number()` sẽ nuốt. Lọc giữ **đủ VERSION cùng code** (nghĩa là "quy trình của nhóm", không phải "1 bản ghi"). Link "xem tất cả" bám `useLocation().pathname` vì trang gắn **3 route**. 🔴 **Rỗng-vì-lọc ⟂ rỗng-vì-chưa-có-gì phải khác câu** — dùng chung 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** chạy chính 2 khối đã ship + helper cắt từ types (assert khuôn trước khi bỏ annotation), code sinh theo khuôn đọc thẳng `DbInitializer.cs`: 8 URL → mỗi cái đúng 1 wf khớp số · trần → cả 8 · `group=0/9/abc/rỗng/"03"` → không lọc · `type=1|2|3` kèm group → bỏ qua · 2 version cùng code → giữ đủ · **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). Build fe-user PASS 1950mod `index-DBtbmn5s` 531ms.

## S166 (2026-08-01) — K5-FE cây toàn-trình: GĐ2 mở 8 sub-folder NHÓM `[s166, khkk-k5-8-sub-folder]`
Artifact `runs/2026-07-31-S164-4gd-khkk-fanout/sub-implfe-k5.md`.
🔴 **BÀI: forward-provision đúng nghĩa = đổi HÌNH DẠNG, KHÔNG bịa field.** Đề bài đòi GĐ3/4 đi qua `lines[].contractId ∪ pe.contractId`. Đo 4 nơi → **cả 4 âm**: `PeListItem` 0 hit chữ `lines` · `PurchaseEvaluation.cs:31` chỉ `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Đ 2+ vô hình. ⇒ Wire vế làm được: `contractIdsOfPackage = new Set<string>()` (union) thay vì đọc 1 ô, chỗ cắm nguồn-2 đánh dấu sẵn + trích nguyên văn BE; FLAG 2 đường cho lead (PE list DTO trả `contractIds[]` — rẻ, dữ liệu đã có; hoặc K7 `Line.ContractId`).
**Khuôn giữ lại:** `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 Σ là chuyện **CẤU TRÚC**, không phải kỷ luật nhớ-cộng-cho-khớp; `stageCount`/`hideEmpty`/chip số 0 phải sửa. Tách `LeafList` dùng chung thân-folder ⟂ thân-ngăn (0 nhân bản markup). Nút tên ngăn trong `<summary>` phải `preventDefault`+`stopPropagation`, không thì mỗi lần đi trang kèm 1 nhịp đóng/mở nhấp nháy. Ngăn mặc định ĐÓNG (8 ngăn × N gói trên cột trái 19rem = tường chữ). Hiện đủ 8 kể cả rỗng = `.map` trên hằng, KHÔNG `.filter` — mirror tiền lệ `SoonChip`; ngăn "(chưa phân nhóm)" CHỈ mọc khi thật có phiếu lạc (8 nhóm = khung nghiệp vụ cố định ⟂ đây = chỗ chứa bất thường).
**Chứng:** chạy chính khối đã ship qua `new Function`, **15/15 OK** — CA1 rải đều (8 ngăn, Σ 5=5, nhóm-3 rỗng vẫn hiện, route `?group=1..8` khớp URL leaf K4b) · CA2 phiếu lạc (9 ngăn, gom đủ 4 ca null/9/undefined/thiếu-field) · CA3 gói rỗng (vẫn 8, không đẻ ngăn lạc) · **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). 4 phép thế text đều assert-khớp trước khi chạy, không khớp thì script tự chết.
**SHA 4/4 pair ×2 app:** `usePipelineStages` `7144eeb7`→**`edff9931`** · `PipelineStageFolders` `d449cc8d`→**`bed52f39`** · `PipelineTreePanel` `d70ac315` KHÔNG ĐỔI · `PePipelineStrip` `6fb7337e` KHÔNG ĐỔI ⇒ **2 hash đứng yên = đối chứng dương** cho phép đo. Build ×2 PASS (user `index-CZUcTUUg` 19.8s · admin 1961mod `index-BZWvx0Y8` 10.9s). 0 BE, 0 K4b file, 0 commit. 3 FLAG (lines[].contractId · ngăn mặc-định-đóng · `truncated` không lặp ở từng ngăn).

## S166 (2026-08-01) — K4b-FE nối `?group=` + vá-3/5/6 `[s166, khkk-k4b-group-wire, harness-do-nham-render-bi-vut]`
Artifact `…/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: *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→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 cho CreatePage (8 leaf chung 1 route ⇒ leaf→leaf không remount; so `lastPresetGroup` **KHÔNG** so `approvalGroup` — so nhầm thì 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.

## S166 (2026-08-01) — K4a-FE regex 42 leaf KHKK + single-source nhãn nhóm `[s166, khkk-k4a-regex-42-leaf, tien-de-lead-sai-x2]`
Artifact `…/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 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: 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.
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; Layout 2 app **cố ý KHÔNG mirror** (user +34 regex · admin +13 comment-only). Build ×2 PASS (user 1950mod `index-B2v0kn5F` 20.8s · admin 1961mod `index-84zDBHE-` 8.2s).

## S164/S165 (2026-07-31) — K2-FE KHKK nhóm-duyệt N1–N8 `[s164, khkk-nhom-duyet, do-dia-truoc-khi-go, colspan-loi-cam]`
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) · +cột "Tổng duyệt" Σ`totalApprovedAmount` (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). diff-stat đối xứng 269+/36− ×2 app.

## S161 (2026-07-29) — ARC W1+W2+W3đ1 KHKK FE, 3 lượt `[s161, khkk-fe-3-wave, containment-2-lop, types-theo-entity]`
Chi tiết = sub-md `runs/2026-07-29-S161-khkk-*/` (mọi lượt #53, đĩa cứu trọn).
**W1** Designer type-10: containment **2-LỚP render+payload** (`levelFlagsPayload(e, lock)` ép false 2/2 call-site — chỉ `disabled` = containment GIẢ vì `copyFromDefinition` tuồn state); 2 tiền-đề đề-bài SAI tự bẻ (FE không có bảng nhãn — nhãn từ BE `applicableTypeLabel`; khuôn type-9 không tồn tại → mirror `Contract:3`).
**W2** 3 page + types ×2 SHA-pair; types-theo-ENTITY khi BE song song; header types = HỢP-ĐỒNG 12-endpoint (reviewer đối chiếu ra 6 đứt → lead vá; bài: hợp-đồng phải LEAD-định-nghĩa literal); WfView 2-app-2-đường (user matrix?type=10 whitelist+10 · admin Designer deep-link); place-3 menuKeys SKIP-có-khai (0 consumer).
**W3đ1** `KhkkWorkflowPanel` mirror PE-panel 1063-dòng theo bảng **GIỮ/BỎ tường minh** (BỎ đủ 2 đường cấm + price-picker/budget/4-radio); cây workflow THÔ — **FE tự suy ✓/●/○ từ con-trỏ đôi** (StepIndex 0-based vs Level.Order giá-trị); bổ `approverUserId` opinion (W2 khai thiếu).

## S153 (2026-07-26) — arc ctx-adopt T2 + T5 `[s153, t5-writeguard-2-diem-235-co-rang]`
Lead seed on-behalf @closeout M9; T2 return mất schema-fail (#53 +1) → derive-from-body, template VẪN LAND 8.819B; T5 return CLEAN.
**T2 template** `.claude/templates/session-mind-template.md` — 3 marker (RULES-START/END · MIND-TOP) · **0 dòng key `ts:`** (ts sống trong HEADING sau em-dash ⇒ không phạm rào-2) · 6 hit `rào-` (3 rào × tên+lý-do) · chiều chèn **NGƯỢC** `_context` khai TO trong khối luật · placeholder `<k>` trong schema-comment chống parser false-match · kiểm `_SECRET_RES` để ví-dụ rào-3 không tự khớp mẫu.
**T5 `hmw.js`** 3/3 acceptance: VALID_ROLES 20→21 (sau T10/T11 = **23**) + 🔴 **writeGuard là 2 ĐIỂM không phải 1** — `:228` JS-comment doc **và** `:235` template-literal ĐI VÀO PROMPT sub; chỉ sửa `:235` ⇒ doc lệch trong CÙNG file, 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. `:235` = **điểm có RĂNG**. Vùng CẤM đo bằng diff: `WIDTH_CAP` 0 hit · `TIER2_EXPECTED_FULL_ID` 0 hit.
**2 kẽ báo lead:** ① **naming-collision cùng-role 2-invocation** (index `i` reset mỗi invocation ⇒ đợt-2 suýt đè `sub-implementer-frontend-1.md` của đợt-1) — kẽ CÒN LẠI sau S134 ② `node --check` file thô **luôn** SyntaxError (top-level await) ⇒ acceptance phải là **wrapped-parse + đối-chứng HEAD**.

## S134 (2026-07-17) — PE lũy kế TẠM TÍNH FE 2-app `[s134, pe-luyke-tam-tinh, sub-slot-prop]`
wf_58302bd5-b9f INV-2.B · return sạch 0-garble · engine opus-worker.
types ×2 +5 field `pending*` (DIVERGE — edit riêng từng app cùng old_string sau `budgetFrozen`) · PeDetailTabs `5534addb`→**`d28eaa16`** ×2: `BudgetSharedNumCell:1184`+`BudgetColValue:1197` +**`sub?: ReactNode` prop** → sub-dòng amber "Tạm tính" row1/2 (withDetail "+X từ N phiếu chưa duyệt", N8 count-TƯƠNG-ỨNG delta KHÔNG list.length) + row5/6 + block liệt kê `pendingPriorPes` dưới matrix (N9 KHÔNG link — 403 S89) + gate `showPending = !bs.budgetFrozen && (...)` · PeWorkflowPanel `a83a58e1`→**`bebc0ada`** ×2: banner M2-gate `!readOnly && phase === ChoDuyet` + reminder trong Dialog TRÊN price-picker. npm ×2 PASS.
Learned: (1) **`sub?: ReactNode` slot-prop trên cell-component dùng chung = chèn sub-dòng vào N render-site không nhân bản markup**; (2) hmw đánh index sub-file GLOBAL theo taskList — hết collision S133 khi 2 role KHÁC nhau cùng invocation.

## S133 (2026-07-17) — PE budget-freeze FE types+badge `[s133, budgetfrozen-badge, anchor-by-visible-text]`
4 file — types 2-app +`budgetFrozen: boolean` (fe-user `:333` · fe-admin `:331`, 2 file KHÔNG mirror nhưng block PeBudgetSummary nội-dung identical → 1 old_string dùng chung); PeDetailTabs 2-app byte-identical: badge amber `🔒 Ngân sách chốt tại thời điểm duyệt` @ header `PeBudgetSummaryTable` `:1350-1359` (`normal-case tracking-normal` OVERRIDE `uppercase tracking-wide`). 🔴 Gotcha: className header LẶP 2 chỗ (`:1350` budget + `:1931` winners) → Edit anchor PHẢI kèm text visible "Tổng hợp ngân sách trình ký" mới unique. Hash `5534addb` ×2; build ×2 PASS.

## S117 (2026-07-14) — PE "duyệt theo KHOẢNG" radio-5 ProMinMax ×2 app `[s117, prominmax-5th-radio, spread-conditional-before-as-cast]`
Ô "CHỌN GIÁ CHỐT" cấp cuối +radio-5 "PRO — cả Min và Max" (source=ProMinMax) CHỈ khi `proSuggestedMinPrice!=null&&proSuggestedMaxPrice!=null`. types DIVERGE (`PeDetailBundle` L433 +`approvedPriceMaxAmount`, anchor same-string khác line admin488/user490). PeWorkflowPanel `011ee8cf`: `priceCandidates` +`amountMax?`; entry qua `...(hasBothProPrices?[{...}]:[])` **spread TRƯỚC `as T[]` cast**; submit `??undefined` (axios bỏ key). PeDetailTabs `617b0aa0`: label-map + value ternary `min đ – max đ`.
**LESSON:** conditional array entry = spread trước cast (né inference lỗi); `??undefined` = omit-key axios chuẩn; Max KHÔNG cần state riêng (derive từ selectedCandidate) → 0 reset-site. Build ×2 PASS.

## S116 (2026-07-13) — PE cho nhập SỐ ÂM báo giá ×2 app `[s116, sign-toggle-immediate-commit, hasRealSelectedQuote]`
PeDetailTabs BYTE-IDENTICAL, cp fe-user→fe-admin. Âm = phát-sinh-giảm/hoàn tiền NCC = giá THẬT. 5 change.
Sign-toggle 2 dialog (AddSupplier :2379 + QuoteDialog :2948); `neg` = useState RIÊNG (QuoteDialog `setForm({thanhTien})` thay CẢ object → nuốt neg). 🔴 2 dialog POST commit `form.thanhTien` THẲNG (:2247/:2906, no nút "Lưu") → toggle PHẢI ghi giá-CÓ-DẤU NGAY, chỉ flip cờ = **MẤT DẤU thầm lặng**. GLOBAL `parseVnd/formatVndInput :51-52` KHÔNG sửa (share ngân sách) → `Math.abs` wrap. Gate `!==0` (KHÔNG `>0`). **2 FE gate ĐỔI (KHÔNG 1):** `missingForApproval:212` + `submitChecklist:243` → helper `hasRealSelectedQuote`. 🔴 GOTCHA quote ở `details[].quotes` KHÔNG `suppliers[].quotes`. Cũ SUM winnerQuoteTotal: net-zero +100/−100=0 chặn oan. SHA `3e16dba5` ×2; build ×2 PASS.

## S113 (2026-07-12) — Supplier import v2 FE ×2 app `[s113, supplier-publish-draft, publish-not-update]`
3 file×2app: 2 SHA-pair + types DIVERGED (edit-identical KHÔNG cp). types/master: Supplier +`isPublic`. SupplierImportDialog `78403bd8`: +"Tải file mẫu" (blob `GET /suppliers/import/template`) · cột "Mã" `<td>`→`<Input>` editable (updateRow map-patch) · +`mstMissing` amber warn. 3.d disabled ĐÃ đúng SẴN → NO change. SuppliersPage `9947b121`: +cột "Trạng thái" badge full-class-literal(P14) · Công bố/Ẩn → `POST /suppliers/{id}/publish` (**KHÔNG qua UpdateSupplier** #73) · filter Select `published` · actions w-32→w-40. NO 4-place mirror (page ĐÃ routed). Build ×2 PASS.
DEVIATION flagged: "Tải file mẫu" đặt CHỈ trong dialog (② canonical + ③ acceptance) — parent 2.d loosely list page-btn nữa.

## S152 (2026-07-25) — R1 retention-note 2 ranh auto-refuse `[s152, auto-refuse-trivial-30min, no-lower-bar-smart-friend]`
M9 on-behalf lead — queue memory-ops.
**(a) Việc < 30 min / trivial:** fix 1-dòng CSS / label-polish = em-main solo nhanh hơn spawn-cost — REFUSE nhận đúng role-file; nhận việc trivial = đốt spawn vô ích.
**(b) KHÔNG hạ chuẩn "lower bar" kiểu Smart-Friend nể-nang:** bị giục "làm nhanh" vẫn giữ sàn mirror-SHA ×2-app + npm build ×2 PASS; thiếu 1 trong 2 = trả lại chứ không ship.
