Files
solution-erp/.claude/agent-memory/frontend-designer/MEMORY.md
pqhuy1987 881783c335 [CLAUDE] Docs: S96 monthly drift audit + FD memory distill + mega-line re-tier
Monthly drift audit (2026-07-01, cron ngày 1) — workflow-prepared (wf_2d687d27-175, 4 clusters draft→adversarial-verify), em-main single-writer applied:
- ef-core-migration/SKILL.md: latest-mig 56→59 + append table rows 57/58/59 + Mig 14 + Phase-7 Budget annotate DROPPED Mig 50 (S61)
- skills/README.md: ef-core row Mig 55→59/S87 + gotcha cite #69→#76 (5 latest, authoritative gotchas.md headings)
- docs/CLAUDE.md: Budget module marked REMOVED Mig 50 — §1 item 6 + roadmap Phase 7/8 rows (strike-through, non-destructive)
- docs/governance/vocab-alias-map.md (NEW): 3 vocab-forks documented (run-trace / Ngân sách PRO / all-inherit canonical) + governance-detectors.ps1 $ExcludeExact entry (anti self-match)
- re-tier STATUS/HANDOFF mega-line 5→3 blurbs (S92/S91 → archive verbatim, conservation delta=0, BOM preserved) + hoist 3 UAT-carry items to blockers

FD memory distill (H17 loop strike-1 → CLEARED, GAPS: none):
- frontend-designer L1 26129B→21492B: 5 old Activity war-stories → archive/2026-06.md + _INDEX + gist; Lessons value-protect distilled to L1
- budget.json measured.frontend-designer refreshed

Detector 27→25 (2 real drift cleared; remaining = documented false-positives module-local/never-built-defer). AS-10 residual reviewer sub-write reverted (git-diff backstop, E-006 recurrence — guard HELD).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 13:04:15 +07:00

21 KiB
Raw Blame History

frontend-designer — MEMORY (L1 HOT)

8th sub-agent (S47, 2026-06-02). Role: FE design/UX/aesthetic cho 2 app SOLUTION_ERP. Floor FD1FD10 (AI_INFRA canonical, KHÔNG hạ). Forked frontend-designer.agent.template.md.

Role + boundary

  • MINE: FE design/UX/redesign — fe-admin/src/** + fe-user/src/** styling/component/page/design-system/a11y/responsive/micro-interaction. Design-by-code (React/Tailwind/shadcn), KHÔNG Figma.
  • NOT MINE: BE/DB/business-logic (implementer-backend) · cookie-cutter mechanical mirror theo spec (implementer-frontend — KHÔNG double-touch cùng file UI) · test (test-specialist).
  • store_memory GỠ (broadcast 2026-06-02) → ghi finding/token/component vào FILE NÀY; em main + re-index đưa vào RAG.

SE design-system (FD1 — DÙNG, KHÔNG reinvent) — VERIFIED S47

  • Brand primary #1F7DC1 · font Be Vietnam Pro (Vietnamese diacritics) · Tailwind tokens · ERP shell (TopBar + Bell + UserMenu).
  • ⚠️ Token source = Tailwind v4 CSS-first (NO tailwind.config.js file — template path stale). Tokens live in fe-user/src/index.css @theme{} block (mirror fe-admin/src/index.css). Read TRƯỚC khi build.
    • Brand scale --color-brand-50..900; --color-brand-600 = #1f7dc1 (exact logo). Accent red --color-accent-500/600 (from ® mark). Be Vietnam Pro + JetBrains Mono via Google Fonts @import in index.css. body 14px / lh 1.55 / letter-spacing -0.003em.
  • UI primitives are hand-rolled cva (NOT vanilla shadcn copy): fe-user/src/components/ui/{Button,Input,Label}.tsx. Button variants primary/secondary/outline/ghost/danger × sm/md/lg; focus-visible ring brand-500 + disabled:opacity-50 already wired. Input has focus-visible border-brand-500 + ring. REUSE these, don't reinvent.
  • Stack: React 19 + Vite 8 + TS 6 + TanStack Query + lucide-react + sonner. Node v22 local (engines >=20).
  • UI 100% tiếng Việt · Named export (trừ App) · TS6 const X = {...} as const thay enum · PageHeader chỉ {title, description, actions} · Duplicate 2 app CÓ CHỦ ĐÍCH (§3.9).

FD2 visual-verification rig (SE-specific) — VERIFIED RAN end-to-end S47

  • Dev: cd fe-admin && npm run dev → :8082 (proxy /api→:5443) · cd fe-user && npm run dev → :8080.
  • Auth: ERP behind login — token localStorage solution-erp-admin-token / solution-erp-user-token. Authed page screenshot cần API+SQL chạy + login fixture (seed JWT). Public /login chụp trực tiếp.
  • PROVEN rig (webapp-testing skill = Python Playwright, NOT npm @playwright/test):
    • Bash tool is POSIX bash despite env "PowerShell" note → use cd "abs/path" (NO cd /d). Forward-slash Windows paths work.
    • Chromium for Testing already installed (C:\Users\pqhuy\AppData\Local\ms-playwright\chromium-1223). Python 3.11 + playwright binding present & drives headless OK. NO install needed.
    • Run pattern: python <skill>/scripts/with_server.py --server "npm run dev" --port 8080 --timeout 90 -- python my_shot.py (helper starts/stops dev). Write my_shot.py in fe-user dir, delete after (throwaway, not app code).
    • Screenshot: browser.new_page(viewport={w,h}, device_scale_factor=2)page.screenshot(full_page=True)Read PNG to NHÌN.
  • 🪲 2 Vite-dev gotchas (cost me 2 failed runs): (1) wait_until="networkidle" NEVER fires — Vite HMR websocket stays open → use domcontentloaded + wait_for_selector("form"). (2) FIRST goto after cold server triggers Vite dep-optimize compile (>15s) → add a warm-up goto with 60s timeout before the viewport loop, else first viewport times out.
    • 🪲 (3) Authed-page (Dashboard…) S55 BLOCKER: dotnet run API binds HTTPS :5443 + HTTP :5444 (Program.cs overrides ASPNETCORE_URLS), nhưng vite.config.ts proxy target = http://localhost:5443 → protocol mismatch → /api login fail → kẹt /login. ĐỂ chụp authed: temp-set proxy https://localhost:5443 (secure:false sẵn) + restart Vite + REVERT sau; HOẶC run API HTTP-only. + Dashboard = ProtectedRoute (cần JWT) → Playwright login THẬT (fill admin@solutions.com.vn/Admin@123456 + submit + wait url≠/login). S55 rig này chặn cả designer + em main → đáng tin nhất = deploy prod rồi login thật xem authed pages (đừng vật lộn dev-rig cho authed screenshot).
  • Fallback khi stack chưa chạy: static component preview / screenshot /loginKHÔNG bỏ soi (FD2 cấm ship-unseen).

Component inventory (built/verified — chống reinvent)

  • 3 shared UI cho Văn phòng số / E-Office (S69, 2026-06-17) — PURO-style + HRM visual language. ALL build-PASS 0 TS:
    • fe-user/src/components/ui/PageHeader.tsxricher page header (eyebrow/title/subtitle/icon/accent/actions/breadcrumb). ⚠️ KHÁC @/components/PageHeader (constrained {title,description,actions}) — module path @/components/ui/PageHeader riêng, KHÔNG collision. icon-chip accent-tinted + title text-xl font-bold accent-head. Local ACCENT map {chipBg,chipFg,head} (brand=text-brand-800, rest -700). Title trên nền SÁNG → KHÔNG cần text-white!.
    • fe-user/src/components/ui/KpiCard.tsxclickable stat card = FILTER chip (PURO: row KpiCards thay tabs). icon-chip + .stat-value text-2xl accent + .label-eyebrow. active = bg-{x}-50 + border-{x}-300 + ring-{x}-500. a11y FULL: onClickrole=button+tabIndex=0+Enter/Space (e.key===' ')+aria-pressed=active+focus-visible ring; no-onClick = inert div. hover -translate-y-0.5 + motion-reduce:transform-none.
    • fe-user/src/components/ui/WidgetCard.tsxdashboard widget container (PURO HomePage). Wrap .card-accent (rail via inline --accent). Header: brand=.app-gradient-brand text-white · non-brand=tinted bg-{x}-50 bar. gotcha 66 APPLIED: gradient <h3> title = text-white! (bang) — plain text-white thua unlayered h1-h4 rule. Props: title/icon/accent/stats[]/onExpand/onRefresh/children/empty/emptyText. stats[] = clickable StatChip row (mỗi chip a11y button khi có onClick). empty → muted icon-chip + emptyText. Header IconButton (RefreshCw/Maximize2) contrast-adapt gradient↔tinted, aria-label.
    • 3 đều: NAMED export · import type (verbatimModuleSyntax) · cn from @/lib/cn · lucide-react · accent palettes stop 50/100/500/600/700 ONLY (no -800) → head/value -700 (brand -800 OK) · icon-chip recolor ['--chip-bg' as string]/['--chip-fg' as string] inline (pattern từ HRM Card). NO new npm dep. Build tsc -b && vite PASS 0 TS, 24.87s (warning @import-order + chunk-size = pre-existing). fe-admin NOT mirrored (separate pass nếu cần). FD2 authed-screenshot SKIP (components chưa wired vào page nào — pure library; visual verify khi page tiêu thụ chúng).
  • fe-user/src/pages/office/OfficeDashboardPage.tsxE-Office landing dashboard (S69, 2026-06-17) — PURO HomePage, COMPOSES the 3 shared ui widgets. build-PASS 0 TS, 434ms. Layout: ui/PageHeader (eyebrow "Văn phòng số" / title "Bảng điều khiển" / icon LayoutDashboard / accent brand) on top → grid grid-cols-1 lg:grid-cols-3 gap-5: LEFT lg:col-span-2 = stack of 4 WidgetCard (Đề xuất brand / Đơn từ teal / Ticket CNTT violet / Phòng họp hôm nay amberx — each body = row of 3 KpiCard filter-chips except Phòng họp = 1 KpiCard + next-4 booking peek list) · RIGHT lg:col-span-1 = "Công việc của tôi" WidgetCard (brand-50 hero count myTodo + 3 clickable MetricRow) + "Thao tác nhanh" .card-accent panel (3 Button primary/secondary/outline). Stacks 1-col <lg. Hooks REUSED (verbatim queryKey+endpoint = shared TanStack cache, NO new API, NO new BE): proposals ['proposals',{…}]GET /proposals (+ separate inboxOnly:true query for "Cần duyệt" = needs-my-action signal) · đơn-từ ['/leave-requests'|'/ot-requests'|'/travel-requests',{page:1}]→those 3 endpoints, merged+countByStatus client-side · tickets ['it-tickets']GET /it-tickets · meetings ['meeting-bookings',{…}]GET /meeting-bookings windowed to today (local-midnight→+1d ISO). Counts ALL client-side (countByStatus reducer; status enums from @/types/proposal + @/types/workflowApps). States graceful per-widget: isError→WidgetError (retry btn, accent-500 chip) · isLoading→WidgetSkeleton (3 pulse bars, motion-reduce:animate-none) · empty→WidgetCard empty prop. NEVER blocks page. Routing question: only /proposals/new exists as real create route → quick-actions "Tạo đề xuất"→/proposals/new, "Tạo đơn"→/workflow-apps/leave (đơn-từ landing, NO standalone /new), "Tạo ticket"→/it-tickets (ticket landing) — every link hits an EXISTING route (no * fallback dead-link). onExpand per widget → its real route. a11y: KpiCard/MetricRow clickable = role=button+Enter/Space+focus-ring; reduced-motion honored. Routing/menu wiring NOT done (next agent's job — page NOT imported in App.tsx yet). fe-admin NOT mirrored. FD2 authed-screenshot SKIP (ProtectedRoute + gotcha #3 rig blocks authed; visual verify via deploy).
  • fe-user/src/pages/LoginPage.tsx — login (public, no auth). Layout: gradient bg + 2 blur blobs + centered max-w-md card (bg-white/90 backdrop-blur) → logo / brand eyebrow / subtitle / Email+Mật khẩu / full-width Đăng nhập. Uses ui/{Button,Input,Label}. Solid baseline; nearly identical in fe-admin (mirror candidate).
  • fe-user/src/pages/hrm/EmployeesListPage.tsx2-panel master-detail HRM (S66 refine, was 3-panel S65): shell lg:grid-cols-[22rem_1fr] xl:grid-cols-[24rem_1fr]. CỘT TRÁI = <div flex flex-col gap-4> chứa Org-tree (TRÊN, lg:max-h-[44%] lg:shrink-0, cuộn riêng) + List+filter (DƯỚI, flex-1, cuộn riêng). CỘT PHẢI = Detail 5-tab (flex-1, rộng). <lg → 1-col (tree→list→detail) + mobile tree-toggle treeOpenMobile (tree hiddenflex). Org tree = recursive TreeNode consume GET /api/departments/tree (DepartmentTreeNode {id,code,name,parentId,directEmployeeCount,totalEmployeeCount,children}); gốc "SOLUTION COMPANY" (companyOpen) → pickDept(null)=all; CountBadge (totalEmployeeCount, active=brand-600 fill) → deptId URL param. Detail = avatar header (.app-gradient-brand + initials-in-rounded-2xl) + 5-tab nav (Tổng quan/Thân nhân/Trình độ/Kinh nghiệm/Hợp đồng) count pills + brand underline. Accent system (S66 việc 2+3): type Accent='brand'|'teal'|'violet'|'amberx'|'greenx' + ACCENT map (chipBg/chipFg/head/rail/labelText). Card nhận accent prop → .icon-chip tinted (--chip-bg/--chip-fg inline) + heading text-{x}-700 + left rail pseudo before:content-[''] before:w-1 before:bg-{x}-500 (clip qua overflow-hidden). Field label = text-{x}-700 uppercase semibold (was slate-400), value = font-medium text-slate-900 (was slate-800). OverviewTab: 1 accent/card (Thông tin chung=brand, Sức khoẻ/Lương=greenx, Liên hệ/Ngân hàng=teal, Giấy tờ/Đoàn thể=violet, Công việc=amberx). Tab-body sections: family=violet, đào tạo=teal, kỹ năng=greenx, công tác=amberx, hợp đồng=brand. ⚠️ accent palettes chỉ có stop 50/100/500/600/700 — KHÔNG -800 → head dùng -700 (else Tailwind v4 silent no-class). Reusable: Avatar(hash 5 gradients + dim), CountBadge, Card(+accent), Field(+accent/mono/icon/full). ALL 5 satellite CRUD + 15 satellite api endpoint (+ top-level del + 3 reads) + 3 query keys preserved verbatim (grep+tsc verified, layout/style-only). fe-admin NOT mirrored (separate pass).

Anti-slop catches + rubric verdicts

  • LoginPage (S47): rubric PASS. Anti-generic ✓ (brand #1F7DC1 NOT default-blue, no emoji, lucide-ready, purposeful palette). Fix applied: subtitle "Đăng nhập để tiếp tục" text-slate-500text-slate-600 (borderline ~4.6:1 over translucent card → solid ~7.5:1, FD5 contrast floor). 1-line, no layout shift, on-scale (FD1). Screenshots: /tmp/fd2-login-shots/login-{before,after}-{mobile-375,desktop-1440}.png.
    • ⚠️ fe-admin parity follow-up: same subtitle likely text-slate-500 in fe-admin LoginPage — apply same bump next fe-admin touch (did NOT touch fe-admin this run; scope-disciplined).
    • Minor noted (NOT fixed, out of bounded scope): 2 blur-3xl blobs barely visible at 1440 = render cost ~0 payoff; eyebrow tracking-[0.2em] heavy. Candidates if login redesign requested.

Lessons / anti-patterns (value_protect — GIỮ L1 bất-kể tuổi)

  • Anti-truncation #53 (atomic-Write): file lớn (>1000 LOC) = ONE atomic Write cả-file (lands-fully-or-errors, no half-break) + EMIT change-list + build-verdict TRƯỚC khi vào FD2 screenshot loop (2× truncate liên-tiếp CÙNG điểm rig-entry — S55/S58, em-main proxy recover). Truncate giữa-run → build ×2 verify.
  • Tailwind v4 accent-stop: teal/violet/amberx/greenx CHỈ có stop 50/100/500/600/700 — KHÔNG -800 → head/value dùng -700 (brand -800 OK). Dùng -800 = silent no-class (build PASS, phải soi dist CSS). Rail pseudo cần before:content-[''] else ::before không render box.
  • Authed-rig gotcha #3: dev-rig chặn authed ProtectedRoute (API HTTPS :5443 ⟂ vite proxy HTTP → login fail kẹt /login) → verify authed pages qua DEPLOY prod login thật, đừng vật-lộn dev-rig. Public /login chụp trực-tiếp OK.
  • Lucide alias-dodge: icon từ export-aggregation-line (SendHorizonal) / mojibake token (网络Placeholder autocomplete) → dùng proven-safe export (Send), node-check icons trước build.

Activity log

  • S79 (2026-06-19) PE list fe-user → DECOUPLE chọn/mở-rộng (anh chốt, annotated screenshot): pages/pe/PurchaseEvaluationsListPage.tsx. S78 cũ: bấm dòng = mở overlay full-bleed, 2 panel giữa/phải LÀ placeholder vĩnh viễn. S79 đổi: bấm dòng = inline 3-panel detail "như cũ" (8e68ed1) + thêm nút "Xem mở rộng" mỗi dòng → overlay. Mechanism = param thứ-2 ?expand=1 cạnh ?id: overlay render CHỈ khi id && expand; inline detail (panel giữa <PeDetailTabs readOnly onBack=closeDetail> + panel phải <PeWorkflowPanel readOnly={!pendingMe} onApproved>) render khi id && !expand. Handlers: selectRow=set id+clear expand (NHƯNG <lg/innerWidth<1024 → set expand=1 đi thẳng overlay vì 3-panel không vừa) · expandRow=id+expand=1 · collapseFocus(Thu gọn/Esc/backdrop)=setParam('expand',null) GIỮ id (về inline, KHÔNG về list) · closeDetail(← Đóng/del)=clear id+expand → list · onApproved=clear cả hai → list (phiếu rời inbox). Overlay gate đổi hasSelectionoverlayActive=id&&expand; Esc gọi collapseFocus (không closeFocus). Nút "Xem mở rộng" = Maximize2 (lucide, import THÊM) icon-btn 7×7 góc phải-trên mỗi leaf row, opacity-0 group-hover/row:opacity-100 + opacity-100 khi selected, title+aria-label+brand focus-ring; leaf <button> cũ bọc trong <div className="group/row relative"> + pr-9 chừa chỗ nút. ALL logic VERBATIM (grep ✓): 3 queryKey (approval-workflows-v2-filter/pe-list/pe-detail) · 3 endpoint (/inbox+paged+detail) · del mutation (onSuccess giờ clear id+expand thay setParam) · yearGroups 4-tầng tree memo · PeUrgentChips pill · search/filter/SLA/localStorage-expand. PurchaseEvaluationDetailPage route GIỮ. slide+a11y S78 giữ NGUYÊN (mount-rAF×2, role=dialog/aria-modal, focus-trap Tab-vòng, body scroll-lock, motion-reduce). npm run build (tsc -b strict + vite v8) PASS 0 TS err, 570ms (3 warning pre-existing). FD2: static layout-shell harness (backend :5443 down → authed dev-rig blocked gotcha#3) — Playwright 6 shot × 3 state: (a) inline 3-panel desktop1920+laptop1280 = list+PeDetailTabs giữa+PeWorkflowPanel phải, dòng-chọn có nút Maximize2; (b) overlay 1920+1280 = full-bleed phiếu+panel cạnh; (c) mobile390 list (1-col, nút mở-rộng) + mobile390 overlay (stack). Read tất 6 PNG — đúng hết, no overflow, pill+tree intact. Harness/driver/shots ĐÃ XÓA. Rubric PASS. fe-admin NOT mirrored (implementer-frontend SHA-mirror riêng), no BE, no commit. Tag [s79, pe-decouple-select-expand, expand-param, maximize2-row-btn, inline-restore-8e68ed1, lg-straight-overlay, collapse-keeps-id, logic-verbatim, build-pass, harness-fd2].
  • S69 (2026-06-17) OfficeDashboardPage.tsx fe-user — E-Office landing, PURO HomePage, COMPOSES 3 shared widgets: built pages/office/OfficeDashboardPage.tsx (~400 LOC) over EXISTING data hooks of 4 modules. Read-first: 3 ui widgets (exact prop sigs) + 4 source pages (ProposalsListPage/WorkflowAppsListPage/ItTicketsPage/MeetingCalendarPage) to harvest queryKey+endpoint + types + App.tsx routes. Reused hooks verbatim → shared TanStack cache, ZERO new API/BE: GET /proposals (+inboxOnly query for needs-my-action) · /leave|ot|travel-requests (merged, countByStatus client-side) · /it-tickets · /meeting-bookings (today window). Layout = PageHeader(brand) + lg:grid-cols-3 [LEFT col-span-2 = 4 WidgetCards w/ KpiCard filter-chip bodies | RIGHT col-span-1 = "Công việc của tôi" hero+MetricRows + "Thao tác nhanh" 3 buttons], 1-col <lg. Routing insight (verified App.tsx): đơn-từ + ticket have NO standalone /new route (creation in-page) → quick-actions point at landings /workflow-apps/leave + /it-tickets (only /proposals/new is a real create route) so no link hits the * "chưa build" fallback. Per-widget graceful states: WidgetError(retry)/WidgetSkeleton(pulse, motion-reduce)/empty — never blocks. a11y full (role=button+Enter/Space+focus-ring on clickables, reduced-motion). npm run build (tsc -b strict + vite v8) PASS 0 TS err, 434ms (only pre-existing @import-order + chunk-size + INEFFECTIVE_DYNAMIC_IMPORT warnings — none from new file). Routing/menu NOT wired (next agent; page not yet in App.tsx). fe-admin NOT mirrored. FD2 authed-screenshot SKIP (ProtectedRoute + rig gotcha #3 — verify via deploy). Tag [s69, office-dashboard, compose-3-widgets, reuse-hooks-shared-cache, no-new-be, routing-existing-only, build-pass].
  • S69 (2026-06-17) Văn phòng số / Đơn từ fe-user RE-SKIN (PURO + HRM visual lang) — 2 file, CONSUMES shared ui (parallel fan-out, 7 agents same app): surgical re-skin (KHÔNG rewrite) pages/office/WorkflowAppsListPage.tsx + WorkflowAppDetailPage.tsx (:kind-driven leave/ot/travel/vehicle via KIND_CONFIG). LIST: swap @/components/PageHeader(constrained)→@/components/ui/PageHeader(eyebrow "Văn phòng số · Đơn từ" / title từ KIND_CONFIG / icon per-kind / accent teal) + status filter = ROW 4 ui/KpiCard (Tất cả teal / Đã gửi duyệt amberx / Trả lại violet / Đã duyệt greenx, grid-cols-2 sm:grid-cols-4) + slate table chrome (thead uppercase 11px slate-500, hover teal-50). Filter là CLIENT-SIDE view — added useState<StatusFilter> + 2 useMemo (counts + visibleItems) DERIVED over already-fetched items; KHÔNG touch query/endpoint/queryKey/navigation (page chỉ fetch page:1 như cũ, không có filter-state sẵn nên thêm view-layer = thuần presentation; empty-state phân biệt "chưa có data" vs "không có đơn ở trạng thái này"). DETAIL: ui/PageHeader teal + 4 section dùng local Card(accent-rail pseudo before:bg-{x}-500 + icon-chip) + Field(label uppercase text-{x}-700, value text-brand-800) — copy idiom HRM EmployeesListPage (KHÔNG import HRM, helper local riêng). Accent gán: Thông tin=teal · Số dư phép=greenx · Quy trình=violet · Ý kiến=brand. Status badge giữ WORKFLOW_APP_STATUS_BADGE. Drop raw "⚠️" emoji trong over-budget banner→text thuần (anti-slop FD3). ALL data logic VERBATIM (grep-verified): 2 query [endpoint,id]+['approval-workflows-v2',applicableType] (key/endpoint/enabled y nguyên) · 3 mutation pinWorkflow(PUT /workflow)/submit(POST /submit)/action(POST /{k}) body+onSuccess+invalidate identical · 3 state + flags isDraft/isInWorkflow/hasWorkflow + mọi onClick/nav target bất biến. gotcha Tailwind-v4 stop: dùng CHỈ -50/-500/-700 cho teal/violet/amberx/greenx (no -800) — border-l-greenx-500/bg-amberx-50/text-amberx-700 đều stop tồn tại (index.css verified). Self-caught: SendHorizonal (KpiCard "Đã gửi duyệt" icon) export-aggregation-line trong lucide d.ts → đổi Send (proven-safe export) tránh alias-risk. Self-review: mọi import resolve, 0 unused local (noUnusedLocals strict) — Info/Wallet/GitBranch/MessageSquareText đều dùng. KHÔNG run npm build (em main builds central, 7-agent parallel interference) + KHÔNG modify ui/index.css (other agents edit). FD2 authed-screenshot SKIP (ProtectedRoute + rig gotcha #3 — verify via deploy). fe-admin NOT mirrored. Tag [s69, eoffice-donutu-reskin, puro-hrm-visual, consume-ui-pageheader-kpicard, client-side-filter-view, logic-verbatim, no-build-parallel-fanout, lucide-alias-dodge].

Older Activity (S66/S65/S58/S55/S47 · 2026-06-02→16) → archive/2026-06.md verbatim · map archive/_INDEX.md · distill archive/2026-06.gist.md. Curated @S96 first-overflow (L1 26129B > autoinject-cap 25600B, H17-loop strike-1).