# Implementer-Frontend Agent — Persistent Memory

- **S171 (2026-08-04) lane FE — 🔴 **CHẾT Ở LƯỢT TRẢ VỀ** (`API Error: Connection closed mid-response`, engine nhận `verdict: null`) `[LEAD HARVEST HOI-TO @S172 — no mo tu closeout truoc; owner slot (71) chot 'gom va sleep thi auto, tu quyet'. Nguon = artifact tren dia, KHONG suy dien]; nguồn `runs/2026-08-04-S171-khkk-ui-mirror-pe/sub-implementer-frontend-s171.md` 13.132 B = **BIÊN BẢN LEAD-ON-BEHALF**, không phải lane tự khai`:** 🔴 **Nhật-ký GỐC = 642 B, đúng 7 heading, RUỘT RỖNG HOÀN TOÀN** — lane ghi khung ở lượt 1-2 **đúng luật** nhưng **KHÔNG append dòng nào** sau đó. 🔴 **Đây là ĐỐI CHỨNG SẠCH trong cùng một phiên, và nó SIẾT LẠI luật chống-#53:** 3 lane `reviewer` (khung ✅ + append ✅) cứu **74.070 B** dù cả 3 chết hết-giới-hạn · `implementer-backend` (khung ✅ + append ✅) return SẠCH · lane này (khung ✅ + append ❌) **mất trắng**. ⇒ **khung-rỗng = CẦN, KHÔNG ĐỦ; APPEND-sau-mỗi-đơn-vị-việc mới là cái cứu.** Câu cũ *'ép ghi khung rỗng lượt 1-2'* từ nay **cấm trích cụt**. 🔴 **Ranh phải giữ — MẤT CODE ⟂ MẤT NGHĨ:** *cái lane LÀM* đo được từ đĩa (20/20 acceptance tĩnh ×2 app · mirror 8/8 · build 3/3, lead tự đo sau khi agent dừng hẳn); *cái lane NGHĨ* **mất trắng** — 0 bản ghi trade-off/lý-do. Đó là lý do **§4 (nợ E-7) TRỐNG** và **không ai dựng lại được**. Đừng hứa 'recover trọn' cho ca write-lane. ⚠️ Kèm: MFE @S172 đo vai này **11/13 = 85%**, role **duy nhất** dưới 100% trên 23 role. Tag `[s171, chet-o-luot-tra-ve, khung-rong-can-khong-du, mat-code-vs-mat-nghi, e7-khong-dung-lai-duoc, mfe-85]`

> **Persistent diary cross-session.** Auto-injected @spawn (L1 HOT).
> Update BEFORE every stop. Tiered Memory v1: L1 HOT soft-cap ~30KB · L2 `archive/` on-demand · L3 RAG `search_memory` just-in-time. Keep entry ≤ 1.5K chars (gotcha #53).
> **Compact @S166 (2026-08-01)** — L1 chạm trần đọc (26.9KB) ⇒ S113→S166 verbatim dời `archive/2026-H2.md`, dưới đây chỉ còn móc 1 dòng.
> **NEW agent S39 (2026-05-29)** — split từ implementer (FE 2 app half). Backend scaffold history ở `implementer-backend`.

---

## 🧠 Bài học SỐNG (đọc trước khi nhận việc — KHÔNG archive)

1. **ĐO ĐĨA TRƯỚC KHI GÕ.** Tiền-đề lead sai **5 lượt liên tiếp** (S164 · S166-K4a ×2 · K4b · K5) theo 2 hướng: *"tưởng chưa có"* (bảo tạo `KHKK_GROUP_LABELS` khi map đã tồn tại 5 call-site ⇒ tạo thêm là **phá đúng acceptance single-source mà nó phục vụ**) và *"tưởng có"* (bảo đi qua `lines[].contractId` — field **không tồn tại ở cả FE lẫn BE**). 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á. Bịa field cho khớp đề bài = tệ hơn.**
2. **Đề bài mô tả CÁCH LÀM có thể giết feature đang sống.** "`phase` derive từ searchParams thay `useState`" nguyên văn ⇒ **6 chip lọc thành nút chết** (hỏng CÂM, build xanh). Đọc kỹ *còn ai ghi vào state đó* trước khi đổi nguồn.
3. **Phép đo phải có RĂNG — luôn kèm CONTROL-ÂM.** 0-hit trần / "0 cảnh báo" vô nghĩa nếu chưa chứng phép đo bắt được cái sai (im-vì-đúng ⟂ im-vì-câm). Chuẩn đã dùng: tiêm lỗi vào **chính khối text đã ship** rồi xác nhận FLAG nổ.
4. 🔴 **Mô phỏng framework thì phải mô phỏng CẢ vòng đời.** Harness K4b bản-1 đo nhầm **cái render React đã VỨT** (setState trong render ⇒ React huỷ render đó + render lại trước khi commit) ⇒ nhánh thật và control-âm ra CÙNG kết quả = phép đo mất khả năng phân biệt, **trông y hệt PASS**. Lặp tới khi state đứng yên mới đúng.
5. **Chứng bằng cách CHẠY text đã ship**, không chép tay: `indexOf('<neo>')` → slice → `new Function`; dữ liệu thử sinh từ nguồn BE thật (`MenuKeys.cs` / `DbInitializer.cs`). Mọi phép thế text phải assert-khớp trước, không khớp thì script tự chết.
6. **Bất-biến nên là CẤU TRÚC, không phải kỷ luật.** Giữ 1 danh sách phẳng canonical để đếm, phần chia nhóm chỉ **chia lại chính nó** ⇒ Σ luôn khớp; assert chỉ là chuông cho người sửa sau.
7. **Forward-provision = đổi HÌNH DẠNG (đọc-1-ô → gom-một-TẬP), KHÔNG bịa field.** Thiếu nguồn thì đánh dấu chỗ cắm + trích nguyên văn bằng chứng + FLAG cho lead.
8. **Sai-thầm-lặng → đổi thành thiếu-nhìn-thấy-được.** BE `/deleted` không nhận `group` ⇒ KHÔNG cho UI claim nhóm ở màn đó. **Rỗng-vì-lọc ⟂ rỗng-vì-chưa-có-gì phải khác câu** (dùng chung ⇒ user nhóm N5 tưởng cả module chưa cấu hình). Phiếu nhóm lạ gom vào "(chưa phân nhóm)" thay vì rơi ra ngoài.
9. **`npm run build` = `tsc -b && vite build` + `noUnusedLocals`** ⇒ build LÀ phép thử TS6133 thật; **đừng tin IDE diagnostic** (gotcha #68).
10. **Mirror ×2 app: cái KHÔNG đổi cũng là bằng chứng.** File không sửa mà hash BẰNG baseline = **đối chứng dương** cho phép đo hash. Lệch có chủ đích (fe-admin không có `WorkflowMatrixViewPage`) phải KHAI tại chỗ, không mirror mù.
11. **Validate query-param bằng REGEX CHUỖI, không `Number()`.** `Number("03")=3` lọt lưới; `/^[1-8]$/` chặn. Và chốt chặn kép khi param chỉ có nghĩa với 1 ngữ cảnh (`type===10 && regex`).
12. **Lọc client-side thì KHÔNG thêm trục vào `queryKey`** nếu payload đã tải trọn — thêm = N lần gọi mạng cho cùng một payload + N ô cache trùng. Ngược lại, trục làm ĐỔI request mà thiếu = cache dùng chung sai CÂM.
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`)

- **S190 (08-12) YC-029/YC-030 GĐ3 6 wave worker (lead-seed C2 /pause):** #53 giữa-chừng **3/6 lượt** — mất 0 việc vì artifact-first, lead đo-đĩa commit on-behalf (W2 chết sau-code-trước-sổ · W3 chết đúng-bước-commit · WQ3b chết-để-lại-file-dở → lead hoàn nốt copy-byte + 5 host). Bài mới: **GỘP 2 CỘT THÀNH 1 CHẾ-ĐỘ = kiểm lại MỌI thứ cột kia gánh một mình** (WQ1 layout KHKK-thuần: cột danh-sách bị gộp vào panel cây kéo theo M-1 `!canPe` fallback BẮT BUỘC + HĐ-ngoài-gói phải có CHỮ chỉ-đường + Select-loại thành nút-chết ở flat-mode + badge đếm mất nguồn); **"thêm component X vào panel Y" phải grep X trong CẢ CÂY CON panel kia render** — `ContractWorkflowPanel` đã nằm trong `ContractDetailContent` (panel-2) từ W6 ⇒ làm đúng chữ spec = 2 bộ nút, FLAG thay vì tự tiện (lane dừng đúng ranh, dời máy-duyệt về host 5 chỗ). IDE diag never-read/cannot-find giữa chuỗi edit = snapshot dở #68, bác bằng tsc EXIT=0. Tag `[s190, gop-cot-kiem-lai-cot-kia-ganh, grep-ca-cay-con, artifact-cuu-3-of-6]`

- **S188 (08-10) W7-FINISH nối lane chết** (server-filter `?type=`) — 3/4 tiền-đề đề bài sai: file đích `fe-admin/…/MyContractsPage.tsx` **không tồn tại** (đối ứng = `ContractsListPage.tsx`) · `InboxPage` không nằm ở `pages/contracts/` và fe-admin **0 trang inbox HĐ** · **param `:23-27` là của `GET /contracts`, KHÔNG phải `/contracts/inbox`** (`GetMyInboxQuery` = record KHÔNG tham số) ⇒ **BLOCK 1 nửa việc**, chỉ thêm comment-blocker vào file (đĩa trống = lượt sau lặp lại đúng cái sai). Nợ thật vẫn treo: inbox có `Take(100)`. Trang admin nặng hơn user 2 lỗi đề bài không nêu: badge `{total}` + phân trang đọc tổng CHƯA lọc ⇒ chuyển server vá cả 3. 🔴 **Bẫy tự-trích-dẫn**: grep dòng vừa gỡ ra 2 hit vì **comment lineage chứa nguyên văn nó** — discriminator = lọc bỏ dòng `//`, control-dương chạy cùng regex trên `HEAD` (1 hit/file). Build ×2 EXIT 0. `[s188, tien-de-sai-3-tren-4, block-nua-viec, bay-tu-trich-dan]`

- **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]`
- **S166 (08-01) K5** cây toàn-trình GĐ2 mở **8 sub-folder nhóm** + forward-provision B3-A5. `groups` chia lại `leaves` phẳng ⇒ Σ theo cấu trúc; hiện đủ 8 kể cả rỗng + "(chưa phân nhóm)"; `lines[].contractId` KHÔNG tồn tại → union 1-nguồn + FLAG. 15/15 proof (CA4 tiêm lỗi). SHA 4/4 pair (2 đổi + **2 đứng yên = đối chứng dương**). `[s166, khkk-k5-8-sub-folder]`
- **S166 (08-01) K4b** nối `?group=` + vá-3/5/6. Override 3-trạng-thái `chipPhase` + reset-trong-render theo `navKey` (KHÔNG useEffect); queryKey +`group`; staticMap G1 user 6/6 · admin 5/6. **Harness đo nhầm render bị vứt.** `[s166, khkk-k4b-group-wire, harness-do-nham-render-bi-vut]`
- **S166 (08-01) K4a** regex 42 leaf `Khkk_G{2..8}_*` + DERIVE `khkkGroupMenuLabel` (không đẻ map-2) + `khkkGroupWorkflowCode`. 42/42 resolve + 3 control-âm. `[s166, khkk-k4a-regex-42-leaf, tien-de-lead-sai-x2]`
- **S164/S165 (07-31) K2** KHKK nhóm-duyệt N1–N8 — badge trong ô (không đẻ cột) · **`colSpan` 7→8 hai chỗ = lỗi CÂM** · việc thật ≈1/4 mô tả. `[s164, do-dia-truoc-khi-go, colspan-loi-cam]`
- **S161 (07-29)** ARC W1+W2+W3đ1 KHKK FE 3 lượt — containment **2-LỚP** render+payload (chỉ `disabled` = giả) · types-theo-ENTITY · FE tự suy ✓/●/○ từ con-trỏ đôi. `[s161, containment-2-lop]`
- **S153 (07-26)** ctx-adopt T2+T5 — **writeGuard là 2 ĐIỂM**, `:235` (vào prompt) mới có RĂNG · `node --check` file thô luôn SyntaxError. `[s153, t5-writeguard-2-diem]`
- **S134 (07-17)** PE lũy kế TẠM TÍNH — **`sub?: ReactNode` slot-prop** chèn sub-dòng vào N render-site không nhân bản markup. `[s134, sub-slot-prop]`
- **S133 (07-17)** PE budget-freeze badge — className header LẶP 2 chỗ ⇒ **anchor phải kèm text visible**. `[s133, anchor-by-visible-text]`
- **S117 (07-14)** PE duyệt-theo-KHOẢNG radio-5 — spread conditional **TRƯỚC** `as T[]` cast · `??undefined` = omit-key axios. `[s117, spread-conditional-before-as-cast]`
- **S116 (07-13)** PE nhập số ÂM — toggle dấu phải **commit NGAY** (chỉ flip cờ = mất dấu thầm lặng) · quote ở `details[].quotes` KHÔNG `suppliers[].quotes`. `[s116, sign-toggle-immediate-commit]`
- **S113 (07-12)** Supplier publish/draft — publish qua endpoint riêng, **KHÔNG qua UpdateSupplier** (#73). `[s113, publish-not-update]`
- **S152 (07-25)** 2 ranh auto-refuse: việc <30min/trivial = em-main solo · **KHÔNG hạ chuẩn** dù bị giục (thiếu mirror-SHA hoặc build ×2 = trả lại). `[s152, no-lower-bar]`
- Cũ hơn (S65→S97: office-dashboard · dept-parent · capability-flag · hosolink…) → `archive/2026-H1.md`.

---

## 🎯 Role baseline

WRITE specialist FE 2 app (fe-admin + fe-user). Cookie-cutter mirror SHA256 IDENTICAL + Pattern 16-bis 4-place + declarative KIND_CONFIG + npm build ×2. Case 1+2 only. Tools: Read, Edit, Write, Bash, Skill, Grep, Glob + 5 RAG. Skill: `permission-matrix`.

### 🚫 Split boundary
- ✅ MINE: `fe-admin/src/**` + `fe-user/src/**`
- ❌ NOT: `src/Backend/**` → `implementer-backend` (chỉ Read DTO shape) · `tests/` → `test-specialist`
- ❌ NOT: UX flow decision (drawer/tab/modal, mở-sẵn hay đóng) → em main solo

### 📋 Patterns proven
- **Pattern 16-bis 4-place mirror (9× cumulative):** page/types · `App.tsx` Route · `lib/menuKeys.ts` · ⚠️ `Layout.tsx` `resolvePath` staticMap (**DỄ MISS** → silent sidebar drop, gotcha #50). Spec MUST list 4 places explicit.
- **SHA256 IDENTICAL ×2 app:** viết 1 bên → `cp` → `sha256sum`. Khác UX thì mirror tay + `diff` + KHAI lý do tại chỗ.
- **Declarative KIND_CONFIG Record (3× proven):** `:kind` URL + `Record<Kind,{fields,columns,icon,label}>`. ADD-KIND = union + entry + KINDS + renderCells branch + icon; **0 route mới**.
- **Pattern 14 Tailwind JIT:** class động bị purge → full literal `as const`, cycle `index % length`.
- **TS6:** `erasableSyntaxOnly` cấm `enum` → `const X = {...} as const` + `type X = typeof X[keyof typeof X]`. Tuple `as const` cần `as readonly number[]` mới `.includes(n: number)` được.
- Named export only (trừ `App`). UI 100% tiếng Việt. fe-user thiếu Card/Badge shadcn → fallback inline `<div className="rounded-lg border bg-card">`.
- ⚠️ **PageHeader KHÔNG một chuẩn:** bản `@/components/PageHeader` = `title/description/actions`; bản `@/components/ui/PageHeader` (pipeline/KHKK) thêm `eyebrow/subtitle/icon/accent`. **Đọc bản đang import trước khi truyền prop** (bài S37, lặp lại @K4c).

### ✅ Verify protocol
`npm run build` ×2 app BOTH 0 TS error + `sha256sum` mirror proof + proof-script có control-âm cho mọi khẳng định "đủ/đúng/không sót". Bundle >500KB + `INEFFECTIVE_DYNAMIC_IMPORT realtime.ts` = warning PRE-EXISTING, OK.

### ⚠️ Anti-patterns (DO NOT)
1. ❌ Touch BE files · 2. ❌ Miss 4th Layout staticMap · 3. ❌ Skip npm build ×2 · 4. ❌ `git add -A` · 5. ❌ Push/commit — em main commits · 6. ❌ UX decision autonomous → REFUSE · 7. ❌ Bịa field cho khớp đề bài → FLAG.

### 🔄 Curate trigger
L1 > ~30KB **hoặc chạm trần đọc** → archive verbatim sang `archive/<period>.md`, L1 giữ móc 1 dòng. Commit scope (em main commits): `FE-Admin` · `FE-User`.

- **S167 (08-01) lane FE-K7 bridge KHKK→HĐ (vá-1 FE · vá-16 · vá-4b) — `DONE` `[LEAD SEED ON-BEHALF — nguồn `runs/2026-07-31-S164-4gd-khkk-fanout/sub-implfe-k7.md` 5.846 B; **2 lượt #53-garble**, resume-1 + lead-close-on-behalf từ đĩa]`:** 🔴 **Chứng máy phải đo SAU KHI VAI DỪNG TAY** (trọng tài build TƯƠI, gotcha #68): SHA-pair **3/3 identical** (`usePipelineStages.ts` `76e1823345d4` · `KhkkDetailPage.tsx` `cf0a2272061f` · `types/khkk.ts` `60ec8fa3a5ae`) · `npx tsc -b` fe-user + fe-admin **EXIT 0** · `npm run build` 921ms/765ms (warning chunk-size + INEFFECTIVE_DYNAMIC_IMPORT = **PRE-EXISTING**, đối chứng trước khi đổ lỗi) · `git status` đúng **6 file M (3 cặp mirror), 0 file lạ**. Tag `[s167, implfe-k7, build-tuoi-sau-khi-dung-tay, warning-pre-existing, 3-cap-mirror]`

## S188 (2026-08-10) — [LEAD SEED ON-BEHALF]

- W2-types + W7 server-filter (admin=ContractsListPage KHONG phai MyContractsPage — tien de brief sai 3/4) · W5 card Nguon-goc KHKK · W6-FE panel duyet 2-nut mirror KhkkWorkflowPanel + dual-render V1 + Designer type-3 slice. Bai: InboxPage BLOCKED dung ky luat (BE GetMyInboxQuery 0 param + Take(100)) -> no co ma thay vi bug cam; SHA-pair 3 cap khop; types 2 app lech-baseline-co-chu-dich (bang mau phase) nen chi so KHOI THEM.
