Files
solution-erp/.claude/agent-memory/implementer-frontend/MEMORY.md
2026-08-01 13:17:52 +07:00

12 KiB
Raw Blame History

Implementer-Frontend Agent — Persistent Memory

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.

📌 Nhật ký (móc 1 dòng — verbatim → archive/2026-H2.md, cũ hơn → archive/2026-H1.md + _INDEX.md)

  • 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 (ContractsListPageMyContractsPage) ⇒ 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 N1N8 — 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 → cpsha256sum. 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 enumconst 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.