# S65 (2026-06-16) — HRM go-live + Hồ sơ Nhân sự master-detail (giống NamGroup) + Department hierarchy + PE Link hồ sơ

> ~6 deploy prod-verified Run #289→#295. Anh + anh Kiệt FDC UAT realtime (kiểu rapid-loop như S59). Workflow fan-out chạy THẬT lần đầu.

## Bối cảnh
Anh `/session-start` → chuỗi yêu cầu realtime từ screenshot eoffice + Zalo anh Kiệt FDC:
1. Screenshot eoffice "Public cho tất cả User module NHÂN SỰ".
2. "Trang trí toàn bộ giao diện cho đẹp, font/màu sắc" (đơn điệu).
3. Screenshot NamGroup HRM "bố trí hồ sơ nhân sự giống thế này".
4. Zalo anh Kiệt: "mục E Link hồ sơ (NAS hyperlink) dưới mục D" + "Dự trù PRO → Ngân Sách PRO".
5. "Sao không workflow fan-out ra?" (meta — HMW-mode ON).
6. "Gốc SOLUTION COMPANY rồi fan-out xuống phòng ban".
7. "Chữ đen đơn điệu đổi lại + panel phòng ban trên/User dưới + trang trí màu".
→ `/session-end`.

## Done — 6 deploy (Run #289→#295)

### #289 (`4004481`) — Public Hồ sơ Nhân sự cho mọi role (BE seed, em main solo)
- `SeedAllRolesHrmProfileReadPermissionsAsync` (DbInitializer): grant CanRead `Hrm`+`Hrm_HoSo` 13 role, gọi **SAU** `RevokeTemporarilyHiddenModulesAsync` (S58) để thắng revoke. **Upgrade-only** (nâng false→true trên row prod đã bị revoke set false — KHÔNG skip-existing như nhánh non-Pe; tránh NO-OP class S58). Giữ ẩn Dashboard NS + 6 `Hrm_Config*`.
- **Insight:** `EmployeesController` dùng policy-based `[Authorize(Policy="Hrm_HoSo.Read")]` (resolve qua `MenuPermissionHandler` đọc thẳng permission matrix) → grant CanRead mở **luôn cả menu lẫn API** trong 1 phát, không dính gotcha #44 (mở menu nhưng controller chặn cứng). FE 0 đổi (menu BE-driven).
- 🟥 reviewer PASS 7/7 (ordering · upgrade-path · scope · read-only · no-regression · idempotent · no-write-path). 🟩 cicd: non-admin GET /employees **200** (was 403), POST/PUT/DELETE **403**, DB 13 role CanRead=1.

### #290 (`c98030f`, 14 file) — Redesign màu foundation fe-user (🩷 designer)
- `index.css`: accent palette teal/violet/**amberx**/**greenx** (đặt tên né trùng built-in Tailwind) stop 50/100/500/600/700 + utilities `.app-gradient-brand`/`.card-accent`/`.icon-chip`/`.stat-value` + heading 600→700 + `.label-eyebrow` brand-600.
- primitives: Button gradient primary/danger · Input/Select/Textarea focus-glow mạnh · Dialog title-bar gradient · Label brand-600. **variant/size keys STABLE** (build PASS = type-contract intact, đòn bẩy TS strict).
- shell: Layout stripe + logo cap · PageHeader title lớn/đậm + accent bar · TopBar gradient hairline · DataTable thead gradient brand. brand #1F7DC1 + Be Vietnam Pro GIỮ.
- ⚠️ designer return bị cắt #53 (đang dựng showcase) → em main recover-disk: 14 file thuần className + 2 color-map (`contract/PE` phase color -700→-800), self-review toàn bộ + build PASS.

### #292 (`0f44d97` + `6ce5803` test-fix, Mig 51) — Department hierarchy + picker
- 🟨 implementer-backend: `Department.ParentId Guid?` **loose-Guid no-FK** (convention PE) + `GET /api/departments/tree` (`GetDepartmentTreeQuery`: load phẳng → đếm NV active GROUP BY `User.DepartmentId` [**recon catch: `EmployeeProfile` KHÔNG có DepartmentId**, link qua User Mig 11] → ráp cây in-memory + rollup TotalEmployeeCount + cycle-guard HashSet). Mig 51 `AddDepartmentParentId` (AddColumn+CreateIndex, no new table).
- em main A2: Create/Update command +ParentId + Update **cycle-guard** (chặn tự-làm-cha + đi ngược chuỗi cha gặp lại Id = vòng A→B→A).
- 🟧 implementer-frontend: picker "Phòng cha" fe-admin DepartmentsPage (Select + load-all query + exclude-self + cột "Thuộc"). Self-service org chart (anh chốt vs seed).
- ⚠️ **gotcha #65** (Run #291 FAIL CS7036): em build `Api.csproj` lẻ (không gồm tests) → push → CI `dotnet build slnx` bắt `CreateDepartmentCommand +ParentId` positional-required vỡ `MasterCatalogFilteredUniqueTests.cs:63` 4-arg. Fix +5th arg → Run #292 PASS. Lesson: đổi chữ ký BE → build full slnx trước push.

### #293 (`318860a` Phase B + `5a0aaa4` PE) — Employee master-detail + PE Link hồ sơ
- **Phase B** (🩷 designer, EmployeesListPage 1201-LOC rewrite): 3-panel master-detail (cây tổ chức consume `/tree` + list + chi tiết **5 tab** Tổng quan/Thân nhân/Trình độ/Kinh nghiệm/Hợp đồng + avatar header). **Giữ 100% 5 satellite CRUD** (16 endpoint, grep-verified). Recon: HRM port từ NamGroup nên model+satellite ~90% sẵn — chỉ thiếu org-tree + re-skin. Designer self-caught `网络Placeholder` mojibake import.
- **PE** (🔥 **Workflow fan-out** `pe-hoso-link-rename-pro` BE∥FE→review): Mig 52 `AddHoSoLinkToPurchaseEvaluation` (PE +`HoSoLink string?` hyperlink NAS — anh Kiệt "copy link thư mục NAS paste vào, dạng hyperlink"; `<a target=_blank rel=noopener>` null-safe; Create/Update **+trailing-optional =null** → backward-compat 0 call-site break) + rename "Dự trù PRO"→"Ngân sách PRO" (row+badge) ×2 app SHA256-mirror.
- ⚠️ **Workflow: FE+reviewer return RỖNG** (#53 die-empty trong fan-out) → em main recover-disk + self-gate: build ×3 PASS + mirror SHA256 + grep rename → **bắt badge "DỰ TRÙ PRO" sót** (agent đổi row label 1120/1126 nhưng sót badge 1078) → vá ×2 app.

### #294 (`ec517f7`) — Gốc cây "SOLUTION COMPANY"
- Anh: "để SOLUTION COMPANY rồi fan-out xuống phòng ban". Gộp nút "Tất cả phòng ban" + list phẳng thành 1 node gốc công ty (chevron `companyOpen` mở mặc định + bấm tên = pickDept(null) tất cả + CountBadge tổng) chứa phòng ban TreeNode depth=1 toả xuống (cha-con giống NamGroup "Nam Group").

### #295 (`456c7a7`) — Employee refine 2-cột + tô màu (🩷 designer)
- Anh góp ý live (screenshot eoffice): (1) layout 2 cột giống NamGroup gốc — cột TRÁI dọc = cây tổ chức (trên) + danh sách NV (dưới) chồng nhau · cột PHẢI = chi tiết rộng; (2)+(3) chữ đen đơn điệu → tô màu accent panel chi tiết (icon-chip/heading-700/rail/nhãn brand-tint).
- Designer (return ĐẦY ĐỦ lần này) tự bắt 2 bug: accent `-800` không tồn tại palette → `-700`; rail `before:content` thiếu.

## Lessons / insight
- **Workflow fan-out THẬT lần đầu** (`feedback_workflow_fanout_reliability`): parallelism thật (BE∥FE file-disjoint) NHƯNG return-reliability kém (2/3 empty #53) + reviewer-stage không tin được → verify-heavy prod task em main VẪN tự gác = tương đương spawn lẻ. Chọn tool theo bản chất task.
- **gotcha #65:** build csproj con ≠ `dotnet build slnx` (gồm tests) → miss test-compile khi đổi chữ ký command. Trailing-optional `=null` (HoSoLink) backward-compat vs positional-required (ParentId) vỡ test.
- **Policy-based authz** (`Hrm_HoSo.Read` qua MenuPermissionHandler) = mở quyền 1 phát cả menu+API, tránh gotcha #44.
- HRM module port từ NamGroup nên Employee model+5-satellite ~90% sẵn → "giống NamGroup" = re-skin + org-tree, không build mới.

## State sau S65
- **Mig 52** (51 AddDepartmentParentId + 52 AddHoSoLink — cả 2 AddColumn-only) · **88 bảng** · **263 test** (45D+218I, unchanged test-after defer) · **65 gotcha** · **menu 53**.
- Bundle final: admin **`BDwV5d0X`** / user **`DbVv6rsf`** (Run #295).
- HARVEST: H2 GATE 🟡 2-MISS (PE-Workflow FE+reviewer empty) → 2 on-behalf record APPEND. H1 tooling CLEAN (roster/skill/plugin), docs count-flush Mig 50→52. cicd-monitor L1 **82KB over-cap P1** (next-session curate).

## Carry → next session
- Mirror Employee page (+ gốc SOLUTION COMPANY + màu) sang **fe-admin** (fe-user only).
- test-after: HoSoLink + Department.ParentId/cycle-guard + HRM-permission seed.
- 🔴 cicd-monitor 82KB curate-L2 P1 · ef-core SKILL Mig flush · docs/CLAUDE.md full · schema-diagram §16+ ERD (monthly).
- Ops anh (S58/S59 carry): tzutil · anh Chương typo · 5 staff password · gán CNTT.
