# 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"
