Files
solution-erp/.claude/agent-memory/implementer-frontend/archive/2026-H2.md
2026-08-01 10:27:38 +07:00

17 KiB
Raw Blame History

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) — 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 7144eeb7edff9931 · PipelineStageFolders d449cc8dbed52f39 · 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 80ebd79dfc3944df · Create 677f4ab8545b4c2f · 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/viewTRANSIENT_QUERY_KEYS:311 ⇒ active-state phân biệt (bài view=approved S155). SHA-pair types eba649cc7a99b865 ×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 N1N8 [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 :235 template-literal ĐI VÀO PROMPT sub; chỉ sửa :235 ⇒ doc lệch trong CÙNG file, chỉ sửa :228prompt 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 5534addbd28eaa16 ×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 a83a58e1bebc0ada ×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.