Files
solution-erp/.claude/agent-memory/implementer-frontend/archive/2026-H1.gist.md
pqhuy1987 8a249c512d
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m17s
[CLAUDE] Memory: sleep-compress L2 implementer-frontend — 2026-H1.gist.md additive
HC propose (50/50 pointer count==1) + reviewer Fidelity-gate PASS-với-sửa (1 fix R4 áp + 2 optional áp; 0 bịa, 3 diễn giải ground-truth code). Coverage-diff gate 31/31 token (gotcha#/SHA/surprise/guard/root-cause). G-009 post-scan sạch. Verbatim 2026-H1.md FROZEN không đổi. memory-budget.json: last_sleep_at=2026-07-02 + measured S98 refresh (script output, không hand-edit).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 10:57:45 +07:00

56 lines
11 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"