Files
solution-erp/.claude/agent-memory/implementer-frontend/archive/2026-H1.gist.md
pqhuy1987 c2d01f0dce
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m30s
[CLAUDE] Docs: S152 closeout — bookend hình B trọn 2 đầu + owner (30)(31)(32) + queue memory-ops TRỌN (squash 20 wal:)
- Owner 3-quyết: (30) JUMP để-nguyên-theo-nhịp · (31) 4 persona +NEVER-block (restart ĐÃ THOẢ) · (32) vòng 1→5 AUTO — lô consent gỡ 9 site (5 H1 + ring1-tự-bắt + 3 trio; E-013 EXT)
- Memory-ops TRỌN: A1-A4/D1/D2/R1/B-series — A7 392/392 · mfe 20/20-100% lần đầu · lead-gap drain 24.728→16.485B · strike-ledger + WARN-latch (-Ack = owner-action)
- V4 sleep AUTO đầu: 1 shard thật/16 KÊU · ring4 đầu-đời TRƯỢT-4/5 → 4 fix gist (scribe sub-ring4-close vì return-only)
- Thư model re-stamp: 59ce1d0f → 9c909007 published (G-024a) — adap-report Đính-chính @S152 + sweep 13 bề-mặt LIVE
- Bookend @close: stale 6 FLAG + gap 2 FLAG → vá 8/8; ring2 8/8 ĐẠT (4-A pin-bản-cắt → slot 33) · ring1 48Đ/5T/59-claim
- M9 13 đơn-vị diary (3 S151-deep hồi-tố) · #53 ×10-chắc → tally 60-cận-dưới + errata subject-1b85713
- Session-log 2 run= + completeness-gate 5-vòng ĐẠT · HANDOFF segment S152 (carry 3-đóng/3-mới/23-giữ; slot 33/34)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 01:58:30 +07:00

77 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Implementer-Frontend — Gist 2026-H1 (sleep-compress P1)
> distill-gen: 1
> source-verbatim: archive/2026-H1.md (10 record: S79 · S78 · S76 · 06-17 office-dashboard · S65 · Dept-parentId 06-16 · S57bis · S55 · S54 · S52)
> pointer-style: substring (grep -F count==1 vào 2026-H1.md; SHA/tag-keyed, NO line-hint)
> Sinh S98 (2026-07-02) /sleep-recovery-memory-l2 — HC propose + reviewer Fidelity-gate PASS-với-sửa (1 fix R4 áp + 2 optional áp). Coverage: 100% gotcha#/surprise/guard/root-cause/SHA-anchor giữ. N/A-có-chủ-đích: bundle-name (rotate mỗi build, gotcha #69) · build mod-count/0-TS-err chi tiết (ephemeral) · line-số cũ (stale — PeDetailTabs đã phình, TÊN symbol giữ đủ) · boilerplate "NO ambiguity"/"pre-existing warn".
## Cụm 1 [cao] — S76+S79 (06-19): PE money/budget UI ×2 app (PeDetailTabs/PeWorkflowPanel/PeWorkspaceCreateView)
- **VIỆC:** S76 ngân sách ma trận 3 cột (DỰ ÁN hiển-thị-only `—` / PRO / CCM) + Block A flex→`<table border-collapse>` viền ô + badge `✎ NS PRO`(amber)/`✎ NS CCM`(sky) per-approver. S79 live thousand-sep 2 comp (VndInlineEdit + BudgetCell) + ghi chú giá PRO/CCM (SuggestedPriceRows) + typo → "d. Bảng so sánh giá".
- **KẾT-LUẬN:** sep = `formatVndInput(parseVnd(e.target.value))` on-keystroke (helper module-level reuse; dialog money inputs đã có → no-op; KHÔNG đụng tel/%/qty). Note = absolute-set 3-field `{minPrice,maxPrice,note}` / `{ccmPrice,note}` + 3 call-site giá echo note (`ev.*SuggestedPriceNote` — wildcard pro/ccm theo từng mut); `hasAnyValue +!!note` chống early-return nuốt note-only readonly. Badge gate `a.canEditProBudget/canEditCcmBudget`; proFull=proInit+proAdj; type +proInitialAmount/+proAdjustmentAmount (proEstimateAmount LEGACY). Part-1 mirror dùng Python difflib slice-region. Comment + `PeAttachmentPurposeLabel[4]` ngoài spec GIỮ. SHA-pair PeDetailTabs `285ebcf9` / PeWorkspaceCreateView `99caed4b`. Build PASS ×2.
- **BÀI-HỌC:** flex+gap KHÔNG ra "bảng" — giống Excel phải `<table>` viền ô. Type file 2 app DIVERGE → +field sửa CẢ 2 file riêng; chỉ cp file đã byte-identical.
- **BẤT-NGỜ:** cwd-misland — sub `cd` vào app-dir để build → MEMORY Write tương-đối rơi stray `fe-*/.claude` → em-main harvest về + `.gitignore` guard `fe-*/.claude/`.
- → substring:"formatVndInput(parseVnd(e.target.value))" · → substring:"flex+gap KHÔNG ra" · → substring:"proFull=proInit+proAdj" · → substring:"fe-user/.claude"
## Cụm 2 [vừa] — S78 (06-19): PE "focus mode" overlay mirror fe-user→fe-admin CONVERGE
- **VIỆC:** port redesign focus-mode (em-main chốt UX; fe-admin only, NO BE): PeWorkflowPanel + PurchaseEvaluationsListPage.
- **KẾT-LUẬN:** panel PURE-ADDITIVE +`onApproved?` prop + `wasReject` (byte-mirror công thức isReject) → chỉ forward-approve auto-đóng; Trả-lại/Từ-chối giữ overlay. Page bỏ 3-panel grid → overlay `fixed inset-0 z-50` slide-right (~200ms, double-rAF kích transition) + a11y đủ bộ (role=dialog/aria-modal · Esc · focus-trap · scroll-lock · backdrop-click · prefers-reduced-motion); `selectRow``setParam('id')` UNCONDITIONAL; bottom detail-page GIỮ `useNavigate` (deep-link compat). SHA-pair page `011968bb` / panel `1dc24641`.
- **BÀI-HỌC:** mirror PHẢI PRESERVE props phía fe-admin (`readOnly={true}`, `readOnly={!pendingMe}`); converge đo bằng diff+sha256sum, KHÔNG mắt.
- **BẤT-NGỜ:** props 2 app tình cờ trùng → redesign làm 2 app CONVERGE byte-identical (không chủ đích trước).
- → substring:"if(!wasReject)" · → substring:"prefers-reduced-motion" · → substring:"011968bb" · → substring:"Tình cờ trùng fe-user"
## Cụm 3 [vừa] — 06-17: Văn phòng số "Bảng điều khiển" mirror 4 file + 4-place ×2 app
- **VIỆC:** mirror fe-user→fe-admin ui/PageHeader · KpiCard · WidgetCard · pages/office/OfficeDashboardPage + wiring 4-place ×2 app.
- **KẾT-LUẬN:** PREREQ sync index.css TRƯỚC (fe-admin STALE pre-S66; utility identical, chỉ gotcha-66 diffs; WidgetCard gradient header `text-white!`) SHA `e8631471` → cp 4 file SHA IDENTICAL `6ff5303f`/`f8042ade`/`9221cbed`/`c6d9dc08`, 0 import fix. Route prefix MỚI `/office/dashboard` sống cạnh flat office routes; menuKeys `OffDashboard:` mirror BE string exact + đúng THỨ TỰ BE (sau Off, trước OffDanhBa); Layout staticMap = 4th place gotcha#50; leaf admin auto via BE All[].
- **BÀI-HỌC:** mirror page = check css/utility prereq TRƯỚC khi cp. Tồn tại 2 PageHeader SONG SONG: ui/PageHeader (richer header — eyebrow/icon/accent/breadcrumb) ≠ @/components/PageHeader (constrained) — chọn đúng cái, KHÔNG phải rename/replace.
- **BẤT-NGỜ:** không (full precedent Pattern 16-bis; gotcha-66 đã biết).
- → substring:"e8631471" · → substring:"OffDashboard:" · → substring:"richer header" · → substring:"prefix convention"
## Cụm 4 [cao] — S57bis (06-11) + S65 (06-16): 2 slice PE ×2 app đứt #53, em-main proxy
- **VIỆC:** S57bis WorkItem select ("c. Hạng mục" — PeWorkspaceCreateView + PeHeaderForm + PeDetailTabs + types +3 field), return-truncated #53 GIỮA mirror fe-admin. S65 mục "e. Link hồ sơ" NAS (HoSoLinkRow + rename "Dự trù PRO"→"Ngân sách PRO"), died-empty #53 trong Workflow fan-out.
- **KẾT-LUẬN:** em-main solo/proxy hoàn tất. S57bis PeHeaderForm SHA IDENTICAL sau solo-mirror 7+3 edits ×2 app; option label `[Category] Code — Name` + canSubmit require; route reuse `/catalogs/work-items` KHÔNG endpoint mới. S65 ship Run #293 PASS, GET phiếu thật `hoSoLink:null` backward-compat, render `<a target=_blank rel=noopener noreferrer>` null-safe, PUT echo required+hoSoLink.
- **BÀI-HỌC:** slice lớn mirror-2-app-trong-1-spawn = điểm gãy LẶP → per-app stage. Recovery = đọc DISK trước, đừng redo mù. Rename = grep MỌI occurrence — S65 sót badge "DỰ TRÙ PRO" (agent chỉ đổi row label), em-main self-gate bắt, vá ×2 app.
- **BẤT-NGỜ:** work LANDED on disk dù return rỗng — #53 chỉ cắt return/MEMORY-update, KHÔNG cắt render.
- → substring:"mirror-2-app-trong-1-spawn" · → substring:"render landed on disk" · → substring:"DỰ TRÙ PRO" · → substring:"[Category] Code — Name" · → substring:"Run #293" · → substring:"/catalogs/work-items"
## Cụm 5 [vừa] — S55 (06-09) + Dept-parentId (06-16): master-data enrich Case-1 (NO 4-place)
- **VIỆC:** S55 Project +4 optional field (year/investor/location/package) ×2 app. 06-16 Department +parentId cây tổ chức, fe-admin ONLY (intentional, no SHA).
- **KẾT-LUẬN:** enrich scaffold = types +field (Input auto-inherit via Omit) + FormState string + convert-on-submit (`year?Number():null`, `x||null`) + openEdit `??''` + Dialog input đúng vị trí + 1 cột table. Dept: native `<select>` value `''`↔null sentinel + load-all `['departments-all']` pageSize 200 + exclude-self `.filter(d=>d.id!==form.id)` khi Edit (chống tự-làm-cha; cycle sâu BE guard 409, BE local `0f44d97`). S55 SHA-pair master.ts `93ac1b0f` / ProjectsPage `b002061` (7-hex verbatim as-is).
- **BÀI-HỌC:** enrich = S42 pattern string-form + convert-on-submit; admin-only enrich hợp lệ khi feature admin-facing (ghi rõ intentional, không SHA).
- **BẤT-NGỜ:** `package` là TS object-key HỢP LỆ (reserved chỉ khi binding-identifier) → `form.package` build sạch, không cần rename.
- → substring:"valid TS object KEY" · → substring:"d.id!==form.id" · → substring:"0f44d97" · → substring:"93ac1b0f"
## Cụm 6 [cao] — S52+S54 (06-08): ItTicket reassign — diverge (S52/S53) rồi CONVERGE bằng capability-flag
- **VIỆC:** S52 reassign fe-admin ONLY (Pencil→Dialog+Select; GET /users pageSize 200 lazy `enabled:target!==null`; PUT assign→204 NO json read; preselect assignee) + AttendanceReport menu-only 2/4-place. S54 tổ IT tự reassign → CONVERGE 2 app (arc "REVERSE S52/S53 divergence" — verbatim header ghi S53, entry xây admin-only là S52 cùng cluster). Entry S54 gốc mang bracket "[harvested by em main — agent MEMORY write mis-landed, B2/B3]" — instance #2 của misland-pattern (cạnh S76 Cụm 1).
- **KẾT-LUẬN:** S54 đổi nguồn dropdown → `GET /it-tickets/assignable-staff` trả `{canReassign:bool, staff[]}``[Authorize]` any-auth KHÔNG 403 (chống gotcha #44); FE `canReassign ?? false` fetch-on-mount (KHÔNG `enabled`), nút bọc `{canReassign && …}`; viết admin canonical → cp → SHA `4bcaf2f` ×2. Menu-only wiring = 2/4-place khi page+route đã có (menuKeys + Layout staticMap gotcha #50; types/menu.ts key:string KHÔNG typed-union → no mirror).
- **BÀI-HỌC:** BE-computed capability-flag = single-source thay FE đoán role → 2 app converge lại sau intentional divergence; muốn kết thúc divergence, đi qua BE flag.
- **BẤT-NGỜ:** types/workflowApps.ts 2 app vốn NOT SHA-identical (fe-admin có AttendanceReportDto P11-E, fe-user thiếu — pre-existing) → khởi nguồn doctrine type-diverge.
- → substring:"canReassign:bool" · → substring:"4bcaf2f" · → substring:"AttendanceReportDto" · → substring:"enabled:target!==null" · → substring:"key:string NOT typed-union" · → substring:"gotcha #50"
## Reflection-synthesis (5)
- **R1 [cao] Mirror-proof bằng máy:** 1 app canonical → cp → sha256sum; UX khác → mirror tay + diff; converge/diverge kết luận bằng HASH, không mắt (xuyên S54/S55/06-17/S78/S79). → substring:"KHÔNG mắt" · → substring:"không diff mắt"
- **R2 [cao] Type-file DIVERGE doctrine:** types/*.ts 2 app KHÔNG đảm bảo identical (gốc AttendanceReportDto P11-E S54; S76 confirm purchaseEvaluation.ts DIFFER) → +field = edit CẢ 2 file riêng; hậu duệ edit-identical + verify-not-in-diff (S87+/S88, đã ở L1). → substring:"types DIFFER" · → substring:"AttendanceReportDto"
- **R3 [cao] #53 = failure-mode cố hữu của slice-lớn-mirror-1-spawn:** ×2 liên tiếp (S57bis truncated giữa mirror; S65 died-empty fan-out); work thường ĐÃ land disk → recovery đọc disk + em-main self-gate quét sót; mitigation per-app stage. → substring:"died-empty #53" · → substring:"mirror-2-app-trong-1-spawn"
- **R4 [vừa] Vòng đời divergence 2 app:** admin-only hợp lệ nhưng thường bị kéo CONVERGE khi user-side cần (S52/S53→S54 reassign; S78 focus-mode tình cờ trùng) → thiết kế để converge rẻ: BE capability-flag; mirror luôn PRESERVE props phía đích. → substring:"converge lại sau intentional divergence" · → substring:"Tình cờ trùng fe-user"
- **R5 [vừa] Wiring delta ngoài Pattern 16-bis:** menu-only = 2/4-place; menuKey mirror BE string + đúng THỨ TỰ BE; route prefix mới (`/office/`) hợp lệ cạnh flat routes; types/menu.ts key:string → không mirror union; leaf perm admin auto BE All[]. → substring:"gotcha#50" · → substring:"key:string NOT typed-union"
## CỤM BỔ SUNG [coverage-diff @S152 D2 — máy-trích nguyên-văn + lead-review; KHÔNG phải re-distill, distill-gen giữ]
> Token load-bearing có trong verbatim `2026-H1.md` mà gist thiếu — mỗi dòng = token + TRÍCH NGUYÊN-VĂN dòng chứa nó (Ctrl-F token trong verbatim để đọc trọn). Chuẩn gate: gotcha#/Mig/file-ref/guard phải sống trong gist.
- gotcha **#70** · trích: "- **E2 #70 stale-echo guard:** 2 ô PRO `adjustMut` (row3 NS-kỳ-này :1548 + row8 GT-còn-lại :1649) chỉ `saving={adjustMut.isPending}``|| peFetching`"
- Mig **Mig 60** · trích: "PROD LIVE. Đối xứng fix endsBeforeCeo (Mig 60). EDIT1 mở rộng `PeFinalizeChip.tsx` (2 app) + EDIT2 wire `phase={p.phase}` ở 6 call-site."
- file-ref **App.tsx** · trích: "(C) **4-place ×2 app:** page(mirror) + App.tsx import+`<Route path='/office/dashboard'>` (NEW `/office/` prefix convention — page file ở pages/office"
- file-ref **DepartmentsPage.tsx** · trích: "rtmentInput auto-inherit via Omit · (2) DepartmentsPage.tsx: import Select + FormState/emptyForm +`parentId:string` ('' khi rỗng) + load-all query `['"
- file-ref **ItTicketsPage.tsx** · trích: "All[]→admin auto. **Task C reassign:** ItTicketsPage.tsx top-comment updated DIVERGES fe-user. Per-card Pencil button (cạnh 👤 assignee) → Dialog (siz"
- file-ref **Layout.tsx** · trích: "string exact, after OffChamCong) · (2) Layout.tsx staticMap +Off_AttendanceReport:'/attendance/report' (4th place gotcha #50). `types/menu.ts` = Menu"
- file-ref **PeDetailTabs.tsx** · trích: "4 edit `PeDetailTabs.tsx` (SHA-pair) + 2 wording `types/purchaseEvaluation.ts` (DIVERGED — KHÔNG SHA-pair, edit identical 2 file)."
- file-ref **PeFinalizeChip.tsx** · trích: "endsBeforeCeo (Mig 60). EDIT1 mở rộng `PeFinalizeChip.tsx` (2 app) + EDIT2 wire `phase={p.phase}` ở 6 call-site."
- file-ref **PeWorkflowPanel.tsx** · trích: "4 chuỗi-edit `PeWorkflowPanel.tsx` × {admin,user} = 8 hunk. PROD LIVE UAT (anh Kiệt xem trực tiếp). File 2 app byte-identical trước+sau → SHA-pair đún"
- file-ref **PeWorkspaceCreateView.tsx** · trích: "esentation+wiring `PeDetailTabs.tsx` (+`PeWorkspaceCreateView.tsx` typo). BE thêm `note` body song song; FE chốt UX rồi."
- file-ref **ProjectsPage.tsx** · trích: "rojectInput auto-inherit via Omit · (2) ProjectsPage.tsx (single-Dialog CRUD, NO separate pages): FormState +4 `string` (form dùng string, convert on"
- file-ref **PurchaseEvaluationsListPage.tsx** · trích: "- **PurchaseEvaluationsListPage.tsx** STRUCTURAL port: bỏ 3-panel grid → list-state `mx-auto max-w-5xl` panel (4-level tree giữ verbatim) + focus-stat"
- file-ref **SupplierImportDialog.tsx** · trích: "NEW `components/master/SupplierImportDialog.tsx` (folder `master/` MỚI cả 2 app) + wire `pages/master/SuppliersPage.tsx` (nút 'Import Excel NCC' outli"
- file-ref **SuppliersPage.tsx** · trích: "er/` MỚI cả 2 app) + wire `pages/master/SuppliersPage.tsx` (nút 'Import Excel NCC' outline cạnh 'Thêm NCC' trong PageHeader actions flex + render dial"
- file-ref **menuKeys.ts** · trích: "2 prior). Only 2 of 4-place needed: (1) menuKeys.ts +OffAttendanceReport='Off_AttendanceReport' (mirror BE string exact, after OffChamCong) · (2) Layo"
- file-ref **realtime.ts** · trích: "ting warn: @import CSS-order + >500KB + realtime.ts dynamic. NO BE touch. Full precedent. Tag `[supplier-import-excel, multipart-preview-confirm, enum"