19 KiB
archive/2026-H2 — implementer-frontend (verbatim L2, dựng @S166 2026-08-01)
Chuyển verbatim từ
MEMORY.mdkhi 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) — 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.