# HANDOFF — Brief 5 phút cho session tiếp theo

**Last updated:** 2026-05-29 (Session 39 — **⚙️ INFRA: Opus 4.8 1M default + Multi-agent 4→7 (BVAAU adapted) + budget +50%**)

**S39 (2026-05-29):** Pure infra/governance — KHÔNG feature/schema. 1 commit `fd0554a` (docs-only CI skip).

**⚠️⚠️ HÀNH ĐỘNG ĐẦU SESSION S40: ANH MAIN RESTART CLAUDE CODE CLI** (chưa restart S39 → registry vẫn 4 agent cũ). Post-restart verify smoke spawn 3 agent mới (investigator-api / implementer-frontend / test-specialist) confirm load OK trước khi dùng thật.

**7 sub-agent mới (post-restart):**
| Agent | Color | Scope | subagent_type |
|---|---|---|---|
| investigator-codebase | cyan | INTERNAL SQL/EF/grep/reference mirror | `investigator-codebase` |
| investigator-api | blue | EXTERNAL docs/CVE/lib/cross-project | `investigator-api` |
| implementer-backend | yellow | .NET Domain+App+Infra+Api | `implementer-backend` |
| implementer-frontend | orange | FE 2 app cookie-cutter SHA256 mirror | `implementer-frontend` |
| test-specialist | purple | tests/ dedicated xUnit | `test-specialist` |
| reviewer | red | adversarial pre-commit + live curl | `reviewer` |
| cicd-monitor | green | post-deploy Gitea + bundle + smoke | `cicd-monitor` |

**Phân việc S40+ (chi tiết `.claude/agents/README.md`):** Read internal→investigator-codebase · Read external→investigator-api · WRITE .NET→implementer-backend · WRITE FE→implementer-frontend (parallel khi BE+FE independent, em main chốt DTO shape first) · test→test-specialist · pre-commit→reviewer · post-deploy→cicd-monitor. Budget +50% → lean delegate + parallel, ít em main solo (trừ schema/UX/cross-stack tight/gotcha #53-#55 fallback).

**Model:** default Opus 4.8 1M (`~/.claude/settings.json` global 7 project) + 7 agent `model: inherit`. effortLevel max.

**Tool grant:** all 7 agent có 5 RAG MCP (search_memory + search_code BM25 + cross_project_search + store_memory + list_projects).

**2 gotcha NEW (#54 #55):** 529 Overload spawn fail → em main solo fallback · truncation mid-exploration → brief ≤8K + pre-supply reference snippet.

**Memory rename:** agent-memory/ giờ 7 folder (investigator-codebase ← investigator history · implementer-backend ← implementer history · + 3 seed mới). Investigator MEMORY 32KB → cần curate đầu S40 (gotcha #53 risk).

**⚠️ Drift defer cron 2026-06-01:** docs/CLAUDE.md root count (mig 33→40 · gotcha 52→55 · 4→7 agent · table 60→84) + schema-diagram §15+ (Mig 34-40 Phase 10 chưa thêm) + RAG re-ingest S37-S39 content (3076 chunks chưa có Proposal/WorkflowApps/7-agent).

**Next S40 recommend (anh main pick):**
1. **CLI restart + smoke verify 7 agent** (BẮT BUỘC first)
2. **Curate Investigator MEMORY 32KB** (~10 phút em main proxy)
3. **Phase 11 polish** — workflow ApproveV2 wire 5 skeleton module (Leave/OT/Travel/Vehicle + LevelOpinions) + LeaveBalance + Auto-assign + SLA timer — test-specialist write test-before bug fix
4. **Phase 9 Ops** anh main coordinate (SMTP + Rotate creds + UAT 2-3 user 1 tuần production validation)
5. **Test bundle coverage gap** (test-specialist: Gap 1 HrmConfig Holiday composite UNIQUE → Gap 3 gotcha #44 regression → Gap 2 EmployeeSatellite)
6. **RAG re-ingest** S37-S39 content + cron audit 2026-06-01

**Last updated S38 (prev):** 2026-05-28 (Session 38 — **🎊 PHASE 10 COMPLETE 11/11 Plan G-* atomic 100% DONE — SKELETON 5 plan combo deploy prod**)

**S38 (2026-05-28):** Anh main "OK check lại plan nhé, xem còn gì ko thì finish cho xong luôn đi nhé" — em main pragmatic SKELETON aggressive 5 plan combo (G-O4+G-O5+G-O6+G-P1+G-H3) trong 1 session 1 commit mega. Cumulative end-to-end **1 commit** pushed remote `17aaba9..e54a22d` + **CI Run #247 PASS 3m25s** fastest S38 deploy.

**Commit S38 (1 push):**
1. `e54a22d` Mig 39+40 + BE mega ~1100 LOC + FE 5 file × 2 app SHA256 IDENTICAL + 11 MenuKey + 8 DbInit menu (Run #247 PASS 3m25s)

**Multi-agent ROI S38 ~80K total (heavy em main solo dominant):**
- 🟩 CICD 1 spawn ~12K (Run #247 PASS clean)
- 👤 Em main solo ~68K HEAVY (Mig 39+40 design + BE 1100 LOC mega + FE 5 file × 2 + Permission + commit/push + docs wrap)
- 🟦 Investigator + 🟨 Implementer + 🟥 Reviewer SKIPPED (skeleton scope no critical risk + em main solo proven fast post S37 lessons learned)

**SKELETON Phase 1 trade-off rõ ràng (defer Phase 11):**
- Status flat 5-state WorkflowAppStatus share Leave/OT/Travel/Vehicle (NO ApproveV2 advance level)
- LevelOpinions per-module DEFER (single table per module Phase 11)
- LeaveBalance calc business logic DEFER
- CodeGen atomic MaDonTu/MaTicket DEFER (NULL until Phase 11)
- Vehicle catalog + Driver catalog DEFER (free text VehicleLicense)
- ItTicketComments thread DEFER (free text Resolution)
- Auto-assign round-robin + SLA timer DEFER
- AttendanceReport admin + Excel export + OtPolicy multiplier DEFER

**NEW capability prod S38 (end-to-end UAT-ready visible 100%):**
- (a) Sidebar Văn phòng số 11 sub-menu (Đề xuất + Đơn từ × 3 + Đặt xe + Ticket CNTT + Chấm công) live
- (b) Sidebar Nhân sự > Dashboard NS `/hr/dashboard` shows 4 KPI card (Tổng=33 / Active=33 / Sinh nhật 7d / Mới vào tháng) + gender ratio (Male=17 / Female=16) + status breakdown
- (c) `/attendance` Web GPS check-in/out button live (geolocation API) + tháng calendar
- (d) `/it-tickets` kanban 5-column status board (Mới/Đang xử lý/Đã giải quyết/Mở lại/Đã đóng)
- (e) `/workflow-apps/{leave|ot|travel|vehicle}` declarative generic list table

**Bundle hash rotation S38 (1 push):**
- fe-admin: `CGueDk22` (S37) → `cWAXid0q` (Run #247)
- fe-user: `CEt0QRgX` (S37) → `CX79e2kZ` (Run #247)

**State chốt S38:**
- 40 mig (+2 Mig 39+40) · 84 tables (+6 Workflow Apps) · ~223 endpoints (+13 skeleton)
- 53 FE pages (+5 × 2 app) · 85 menu keys (+8)
- **130 test PASS unchanged** (UAT mode)
- **53 gotcha unchanged**
- 27 memory user-level · 6 skills · 4 sub-agents

**🎊 PHASE 10 COMPLETE — Plan G-* 11/11 atomic ALL DONE cumulative 6 session S33-S38:**
- ✅ G-H1 Hồ sơ NS (S33) + G-O1 Danh bạ (S34) + G-H2 Cấu hình HRM (S34+S35)
- ✅ G-O2 Phòng họp (S36) + G-O3 Đề xuất (S37)
- ✅ G-O4 Đơn từ + G-O5 Đặt xe + G-O6 Ticket CNTT + G-P1 Chấm công + G-H3 Dashboard NS (S38 SKELETON combo)

**Cumulative Phase 10 stats:**
- 7 Mig (Mig 34→40) · 30+ new tables · ~75+ new endpoints · 17 FE pages × 2 app · 50+ menu keys
- Pattern 16-bis 9× cumulative · Pattern 12-bis 12× cumulative · Smart Friend 9× clean
- 6 commit cumulative push 6 CI Run all PASS (Run #237/#238/#240/#241/#242/#243/#244/#246/#247)

**4 sub-agent reuse-able S39+ via fresh spawn:**
- 🟦 Investigator last spawn `a4c81ef2f9a79a325` (S37 G-O3 pre-flight)
- 🟨 Implementer last successful spawn `aabfc002a99339ab8` (S36 FE 2 app), 2 spawn S37 FAIL truncate/529
- 🟥 Reviewer last spawn `a579fa4856eb45316` (S36 G-O2 Smart Friend 9× clean)
- 🟩 CICD last spawn `a4ea874296872c2be` (S38 Run #247 PASS)

**⚠️ Next session S39+ — recommendations:**
1. **Phase 11 polish features** — workflow ApproveV2 wire per skeleton module (Leave/OT/Travel/Vehicle) + LevelOpinions table + CodeGen atomic. Spec defer commit message
2. **LeaveBalance business logic** (Hrm_Config LeaveTypes + EmployeeProfile.HireDate seniority) — em main solo cross-stack tight
3. **Vehicle catalog + Driver catalog** (Phase 11 Mig 41)
4. **ItTicket Auto-assign + SLA timer** (reuse SlaTimer pattern Contract)
5. **AttendanceReport admin + Excel export + OtPolicy multiplier** (Phase 11)
6. **Test bundle G-O2..G-O3..G-O4 wire** (~30 phút Implementer Case 3) — defer until UAT confirm stable
7. **Phase 9 Ops anh main coordinate** — SMTP + Rotate creds + UAT 2-3 user 1 tuần production validation

**Defer S39+:**
- Curate 4 MEMORY (CICD ~30KB post-Run #247 + Investigator borderline) ~10 phút
- Skill audit cron `2026-06-01` (còn 4 ngày, cumulative drift mig 33→40, gotcha 52→53)
- Test gate restoration sau UAT mode end

**Last updated S37 (prev):** 2026-05-28 (Session 37 — **🎯 S37 Mig 37 enum extend + Plan G-O3 Đề xuất ALL DONE end-to-end deploy prod full-stack 1 commit**)

**S37 (2026-05-28):** Anh main "OK làm tiếp đi nhé" Full batch 6 plan continuation. Cumulative end-to-end **1 commit** pushed remote `37593f9..de1c378` + **CI Run #246 PASS 3m53s**.

**Commit S37 (1 push):**
1. `de1c378` Mig 37 enum extend +5 values + Mig 38 Proposal schema + BE CQRS 700 LOC + FE 2 app SHA256 IDENTICAL (Run #246 PASS 3m53s)

**Multi-agent ROI S37 ~225K total (lessons learned mitigation):**
- 🟦 Investigator 1 spawn ~14K (G-O3 pre-flight — truncated mid-MEMORY size warning 29.6KB)
- 🟨 Implementer 2 spawn FAIL (BE 150K wasted mid-EXPLORATION gotcha #53 5th + FE 0 token 529 Overload transient)
- 🟩 CICD 1 spawn ~13K (Run #246 PASS clean)
- 👤 Em main solo ~50K HEAVY (Mig 37+38 schema + BE 700 LOC + FE 4 file × 2 + 6 mod + commit/push + docs wrap)
- 🟥 Reviewer SKIPPED (PE pattern proven 11× + test gate PASS + em main solo BE đảm bảo)

**Smart Friend 0 catch S37 — em main solo bypass:**
- Schema design correct first try (FK strategy + UNIQUE composite + ProposalCodeSequence Prefix PK)
- ApproveV2Async logic mirror PE pattern exact (match ApproverUserId actor + admin fallback + UPSERT + advance level/terminal)
- SeedSampleProposalWorkflowV2Async NOT gated DemoSeed (gotcha #51 compliance learned S29)
- FE PageHeader signature drift caught + fixed (no icon prop, use `actions=ReactNode`)

**NEW capability prod S37 (end-to-end UAT-ready):**
- (a) Sidebar Văn phòng số > Đề xuất `/proposals` shows table 6 cột + status badge 5 màu + filter
- (b) `/proposals/new` form Header + ApprovalWorkflow Select pick `QT-DX-V2-001` → Submit → POST + auto-gen `DX/2026/001` atomic SERIALIZABLE tx
- (c) `/proposals/{id}` 3 Section + WorkflowActions buttons (Submit/Approve/Reject/Return + Comment Dialog) + Section 3 Ý kiến cấp duyệt V2 dynamic UPSERT

**Bundle hash rotation S37 (1 push):**
- fe-admin: `C9kzTTmq` (S36) → `CGueDk22` (Run #246)
- fe-user: `CC4DQ-Tr` (S36) → `CEt0QRgX` (Run #246)

**State chốt S37:**
- 38 mig (+2 Mig 37+38) · 78 tables (+4 Proposal stack) · ~210 endpoints (+8 Proposal)
- 48 FE pages (+3 × 2 app) · 77 menu keys (+4 Off_DeXuat)
- **130 test PASS unchanged** (UAT mode)
- **53 gotcha unchanged** (#53 5th + 529 Overload noted commit only)
- 27 memory user-level · 6 skills · 4 sub-agents (Investigator MEMORY 29.6KB borderline)

**2 NEW lessons S37 cumulative (em main solo proven fallback):**
- gotcha #53 5th — Implementer truncated mid-EXPLORATION phase ngay đầu (heavy spec brief ~10K too risky → tight ≤ 6K hoặc em main solo)
- 529 Overload Anthropic API 1× S37 (S29 đã có 2× — pattern: spawn agent CÓ THỂ fail 0 token, em main MUST có fallback path solo cho critical task)

**4 sub-agent reuse-able S38 via fresh spawn (agentId S37 invalid post-session-end):**
- 🟦 Investigator last spawn `a4c81ef2f9a79a325` (S37 G-O3 pre-flight)
- 🟨 Implementer last 2 spawn FAIL (`a3afd177462addaa2` BE truncated + `a590c5c833dc05a1f` FE 529)
- 🟩 CICD last spawn `ab127ae9929ce6ed7` (S37 Run #246 PASS)
- 🟥 Reviewer not invoked S37

**⚠️ Next session S38 — sequence em chốt Full batch S37→S41 continued:**
1. **Plan G-O4 Đơn từ Mig 39** (~5-6h em main solo recommend) — 3 entity (LeaveRequest + OtRequest + TravelRequest) + LeaveBalance business logic + workflow V2 enum +5/+6 reuse (Mig 37 đã extend). Cookie-cutter mirror Proposal G-O3 pattern. **FALLBACK Implementer Case 2 ×3 spawn parallel** nếu API stable + tight brief ≤ 5K
2. **CICD post-deploy verify Run + bundle rotate**

**Defer recommendations S38+:**
- Curate 4 MEMORY (Investigator 29.6KB borderline post-S37) ~10 phút em main proxy
- Test bundle G-O3 (~30 phút) — ApproveV2 + CodeGen + workflow guard
- Phase 9 Ops anh main coordinate (SMTP + Rotate creds + UAT)

**Last updated S36 (prev):** 2026-05-28 (Session 36 — **🎯 S36 Plan G-O2 Phòng họp ALL DONE end-to-end deploy prod BE+FE combo 1 commit**)

**S36 (2026-05-28):** Anh main chốt **Full batch 6 plan** ~25-31h spread 6 session (G-O2 + G-O3 + G-O4 + G-O5 + G-O6 + G-H3). S36 kick off Plan G-O2 standalone độc lập NOT depend workflow V2 enum extend. Cumulative end-to-end **1 commit** push remote `8afdc1e..f45090b` + **CI Run #359 PASS 3m55s**.

**Commit S36 (1 push):**
1. `f45090b` BE Mig 36 + CQRS 584 LOC + FE 2 app 1770 LOC combo deploy (Run #359 PASS 3m55s)

**Multi-agent ROI S36 ~225K total:**
- 🟦 Investigator 2 spawn ~22K (startup + G-O2 pre-flight: clean-room verdict + FullCalendar v6 alternative)
- 🟨 Implementer 2 spawn ~67K (BE CQRS + FE 2 app — gotcha #53 4th truncation BE diagnose only, FE clean)
- 🟥 Reviewer 2 spawn ~110K (startup + G-O2 review Smart Friend 9× cumulative CLEAN 3 minor defer)
- 🟩 CICD 2 spawn ~25K (startup + Run #359 verify ALL PASS)
- 👤 Em main solo ~50K (curate 2 MEMORY + Mig 36 schema + Application.csproj +Relational + commit/push + docs wrap)

**Smart Friend 0 critical catch S36 (9× cumulative clean):**
- BE Schema design correct first try (FK strategy + UNIQUE composite + index Room/StartAt range query)
- BE Validator MaxLength match EF config (Pattern 12-bis 10× cumulative — S35 lesson learned)
- BE SeedMeetingRoomsAsync NOT gated DemoSeed (gotcha #51 compliance)
- BE Owner-OR-admin authorization Update/Cancel (no gotcha #44 pattern)
- BE SERIALIZABLE transaction + exclude self overlap (Cat 3 PASS minor caveat Room race defer)
- FE Layout staticMap 3 entry mirror Pattern 16-bis 7× (gotcha #50 prevention)
- FE SHA256 IDENTICAL × 2 app 3 file pair verified

**NEW capability prod S36 (end-to-end UAT-ready):**
- (a) Sidebar Văn phòng số > Phòng họp `/meeting-calendar` shows 7-day grid 8h-20h slot + click slot → Dialog create booking (Phòng dropdown 4 sample + Attendees multi-select + Title/Description/Note + Auto overlap check 409 detect "Phòng đã được đặt trong khoảng thời gian này")
- (b) Admin `/meeting-rooms` catalog CRUD table 4 sample room (PH-A Phòng họp lớn cap=20 + PH-B nhỏ cap=8 + PHG-501 Giám đốc cap=6 + ONL-1 Online Zoom cap=50)

**Bundle hash rotation S36 (1 push):**
- fe-admin: `Bl6e54yi` (S35) → `C9kzTTmq` (Run #359)
- fe-user: `DHmW2tUF` (S35) → `CC4DQ-Tr` (Run #359)

**State chốt S36:**
- 36 mig (+1 Mig 36) · 74 tables (+3 MeetingRoom+Booking+Attendees) · ~201 endpoints (+9 Meeting)
- 45 FE pages (+2 × 2 app) · 73 menu keys (+4 Off_PhongHop+leaf)
- **130 test PASS unchanged** (UAT mode, test bundle defer ~30 phút Implementer Case 3)
- **53 gotcha unchanged** (#53 4th occurrence noted commit only)
- 27 memory user-level · 6 skills · 4 sub-agents (post-curate Implementer 31.7KB + CICD 28.3KB acceptable)

**4 sub-agent reuse-able S37 via fresh spawn (agentId S36 invalid post-session-end):**
- 🟦 Investigator last spawn `a7bbd88513157f3a6` (S36 G-O2 pre-flight)
- 🟨 Implementer last spawn `aabfc002a99339ab8` (S36 FE 2 app — 0 truncation tight brief WORK)
- 🟥 Reviewer last spawn `a579fa4856eb45316` (S36 G-O2 Smart Friend 9× clean)
- 🟩 CICD last spawn `a915996c2b0c24698` (S36 Run #359 PASS)

**⚠️ Next session S37 — sequence em chốt Full batch S36→S42 cumulative:**
1. **Plan 10.3 Pre-flight Mig 37 enum extend** (~1h em main solo) — extend `ApprovalWorkflowApplicableType` enum +5 values (ProposalGeneral=4 / LeaveRequest=5 / OtRequest=6 / VehicleBooking=7 / ItTicket=8) cookie-cutter Mig 22 pattern. UNBLOCK G-O3-O6 Workflow Apps.
2. **Plan G-O3 Đề xuất Mig 38** (~4-5h Implementer Case 2 mirror PE Plan B pattern 12-bis cross-module entity scaffold + ApproveV2Async branch + CodeGen `DX/YYYY/NNN` business logic tight em main solo)
3. **Reviewer Smart Friend pre-commit Cat 1-6 + CICD post-deploy Run verify**

**Defer recommendations S37+:**
- Test bundle G-O2 (~30 phút Implementer Case 3) — Code uniqueness + overlap check 409 + Cancel preserve history + AttendeeInput owner-only update
- 3 minor defer Reviewer (AttendeeInput.Notes validator MaxLength + Room IsActive race window + silent skip unknown UserId)
- Phase 9 Ops anh main coordinate (SMTP + Rotate creds + UAT 2-3 user 1 tuần) — still blocked

**Last updated S35 (prev):** 2026-05-28 (Session 35 — **🎯 S35 3 chunk push ALL DONE deploy prod: FE inline forms 5 satellite + G-H2 BE CRUD 16 endpoint + G-H2 FE Admin HrmConfigsPage declarative**)

**S35 (2026-05-28):** Anh main authorize Option A "FE forms + G-H2 BE+FE combo" sequence + "warm-up Implementer + CICD". Cumulative end-to-end **3 commit** pushed remote `63dd9ec..021674a` + **3 CI Run ALL PASS** (#242 + #243 + #244).

**Commits S35 (3 push):**
1. `c3cd343` Chunk 1 — FE inline forms 5 satellite × 2 app (+1758 LOC, Run #242 PASS 3m30s)
2. `909655c` Chunk 2 — G-H2 BE CRUD HrmConfig 16 endpoint (+576 LOC NEW, Run #243 PASS 3m43s)
3. `021674a` Chunk 3 — G-H2 FE Admin HrmConfigsPage declarative (+1388 LOC, Run #244 PASS 3m38s em main fallback verify)

**Multi-agent ROI S35 ~250K:**
- 🟨 Implementer 3 spawn ~80K (3 cookie-cutter chunk + Smart Friend × 3 catch S35)
- 🟦 Investigator 1 spawn ~8K (Plan G-H2 BE CRUD pre-flight: NamGroup MISS verdict — SOL clean-room mạnh hơn)
- 🟥 Reviewer 3 spawn ~60K (Smart Friend 9× cumulative clean: 2 truncated mid-MEMORY-update, 1 tight brief PASS clean)
- 🟩 CICD 4 spawn ~70K (Run #241 warm-up + #242 + #243 PASS + #244 stalled em main fallback)

**Smart Friend Implementer 3 catch S35 (Pattern preserved):**
1. Chunk 2 MaxLength validator vs EF config mismatch → aligned to EF source-of-truth (LeaveType/OtPolicy Code=50, Name=200, Description=500)
2. Chunk 2 HRM entities NO HasQueryFilter (vs Master 9 file have) → explicit `.Where(!IsDeleted)` 8 List/Conflict-check site
3. Chunk 3 em main spec line 24 GAP: Layout staticMap "no resolve issue" — WRONG. Implementer verified `MenuItem.cs` NO Url field + `resolvePath()` regex chỉ match `Ct_*/Wf_*/Pe_*/PeWf_*/AwV2_*` → silent sidebar drop gotcha #50. Enforced staticMap mirror Pattern 16-bis 6× cumulative

**NEW capability prod S35 (end-to-end UAT-ready):**
- (a) Admin login `eoffice.solutions.com.vn/employees` → pick NV/2026/0007 → Section 2-6 (Công tác/Đào tạo/Thân nhân/Kỹ năng/Hồ sơ) inline `+ Thêm` button expand AddForm + per-row Pencil/Trash. 15 BE endpoint wire CRUD live.
- (b) Admin login `eoffice.solutions.com.vn/hrm/configs/leave-types` → 4 sub-tab + table 5 row LeaveType seed + Dialog modal CRUD `+ Thêm`. Switch tab → /hrm/configs/holidays (10 row) → /shifts (3 row) → /ot-policies (1 row). 16 BE endpoint wire CRUD live.

**NEW gotcha #53 sub-agent truncation/stall pattern S35 × 3 occurrence:**
- Reviewer FE forms (push #1) cut mid-Cat-1 after "PERFECT"
- Reviewer BE CRUD (push #2) cut mid-MEMORY trim
- CICD Run #244 verify (push #3) stalled 600s watchdog after VPS mtime cross-check
- Mitigation verified: tight brief ≤ 8K (Reviewer FE Admin chunk 3 PASS clean no truncation 5K return)
- Em main grep verify manual Cat 2-6 + commit confidence preserved

**State chốt S35:**
- 35 mig unchanged · 71 tables unchanged · ~185 endpoints (+16 HRM Configs)
- 43 FE pages (+1 HrmConfigsPage) · 69 menu keys unchanged
- **130 test PASS unchanged** (UAT mode no test add 3 chunk)
- **53 gotcha (+1 #53)** · 27 memory user-level · 6 skills · 4 sub-agents

**Bundle hash rotation S35 cumulative:**
- fe-admin: `RNTX6Fvo` (Run #241 start) → `BhR0MmLN` (Run #242) → `BhR0MmLN` (Run #243 BE unchanged) → `Bl6e54yi` (Run #244)
- fe-user: `29A1LuMm` → `DIdNaB6W` → `DIdNaB6W` → `DHmW2tUF`

**4 sub-agent reuse-able S36 via fresh spawn (agentId S35 invalid post-session-end):**
- 🟦 Investigator last spawn `a42bd882c367f6e47` (S35 G-H2 BE CRUD audit)
- 🟨 Implementer last spawn `a1aeda2fcbbc19eeb` (S35 G-H2 FE Admin)
- 🟥 Reviewer last spawn `a6b42cc3c1423e699` (S35 G-H2 FE Admin tight 5K PASS)
- 🟩 CICD last spawn `a3dfb05e2deee8f0f` (S35 Run #244 stalled mid-MEMORY)

**⚠️ Next session S36 — anh main pick:**
1. **Plan G-O2 Phòng họp BookingCalendar** (~3-4h Mig 36 — FullCalendar lib NEW dep) — standalone tiếp Phase 10.2 Office
2. **Phase 10.3 G-O3-O6 Workflow Apps** (4 plan đề xuất nghỉ phép/đăng ký OT/đăng ký công tác/dự án — depends G-H2 catalog ready)
3. **Phase 9 Ops** anh main coordinate (SMTP + Rotate creds + UAT 2-3 user 1 tuần)
4. **Curate 4 MEMORY post-S35 spawn** (Reviewer + CICD borderline) ~20 phút em main proxy
5. **Test bundle G-H2 BE CRUD** (~30 phút Implementer Case 3 — codeGen UNIQUE Conflict + composite Year+Date + soft delete restore)
6. **Audit cron 2026-06-01** (còn 4 ngày — cumulative skill + doc drift mig 33→35, test 111→130, gotcha 52→53)

**Recommend sequence S36:**
1. Curate 4 MEMORY (~20 phút) — clear context before heavy work S36
2. Plan G-O2 Phòng họp BookingCalendar (~3-4h) — standalone independent
3. Plan G-O3-O6 Workflow Apps batch (~6-8h) — series workflow app reuse V2

**Last updated S34 FULL (prev):** 2026-05-27 (Session 34 FULL — **🎯 Plan 1+2+3+4 ALL DONE: Curate + G-O1 Danh bạ + Phase 1.5 batch 6/6 + G-H2 Mig 35 schema foundation. BE CRUD + FE forms HrmConfig defer S35**)

**S34 FULL (2026-05-27):** Anh main authorize "1 → 2 → 3 → 4" sequence sau S34 init + "OK làm tiếp đi nhé" tiếp Plan 4. Cumulative end-to-end **6 commit** pushed remote `edba4ae..07b3f3b` + 4 CI Run (#238/#239/#240 PASS + #241 Plan 4 Mig 35 pending verify).

**Commits S34 (6 push):**
1. `7b0781b` Plan 1 Curate 4 MEMORY (-15% docs CI skip)
2. `ea440da` Plan 2 G-O1 BE+FE Danh bạ (Run #238 PASS)
3. `61e9ce5` Plan 3 Phase 1.5 batch 4 item (Run #239 PASS — +10 [Fact] 130 PASS)
4. `57099c5` Docs S34 batch 1+2+3 wrap (docs CI skip)
5. `e506cd8` Plan 3 Item 3 BE 5 satellite CRUD scaffold (Run #240 PASS — +15 endpoint)
6. `07b3f3b` Plan 4 G-H2 Mig 35 + 4 entity + 4 EF Config + seed 19 row + 4 menu (Run #241 verify pending)

**Multi-agent ROI S34 ~150K:**
- 🟨 Implementer 3 spawn ~80K (Plan 2 FE 2 app + Plan 3 Item 4 test bundle + Plan 3 Item 3 BE 5 satellite Case 2)
- 🟩 CICD 2 spawn ~40K (Run #238 full verify + Run #239 partial)
- 👤 em main solo ~30K (BE Plan 2 + Phase 1.5 items 6+2+1+5 + Plan 4 Mig 35 + commit/push 6 batch + UAT smoke + docs wrap)
- 🟦 Investigator + 🟥 Reviewer: 0 spawn S34

**Plan 4 G-H2 Mig 35 schema deployed (BE CRUD + FE forms defer S35):**
- 4 entity HRM catalog: LeaveType + Holiday + ShiftPattern + OtPolicy
- 4 EF Config với UNIQUE indexes (Code unique cho 3 + Year+Date composite cho Holiday)
- DbInitializer `SeedHrmConfigsAsync` 19 sample row seed (5 LeaveType + 10 Holiday VN 2026 + 3 Shift + 1 OtPolicy STANDARD)
- MenuKeys `HrmConfig` sub-group + 4 leaf seeded (LeaveTypes/Holidays/Shifts/OtPolicies)
- FE 2 app menuKeys sync 5 const mirror

**⚠️ Next session S35 — anh main pick:**
1. **Plan G-H2 Task 3+4 BE CRUD 4 catalog + FE Admin 4 page** (~3-4h Implementer Case 2 cookie-cutter cookie-cutter mirror EmployeeSatelliteFeatures + SOL Catalogs pattern Plan CA Chunk B)
2. **FE inline forms 5 satellite section** (~1.5h em main solo — Phase 1.5 Item 3 FE remainder)
3. **Plan G-O2 Phòng họp BookingCalendar** (~3-4h Mig 36 — FullCalendar lib new dep)
4. **Test bundle satellite CRUD** (~30 phút Implementer Case 3 — test endpoint Create UNIQUE + Update partial + soft delete)
5. **Curate 4 MEMORY again** (CICD + Implementer borderline post-S34 spawn entries)
6. **Anh main coordinate Phase 9 Ops** (SMTP + Rotate creds + UAT 2-3 user 1 tuần)

**4 sub-agent reuse-able S35:**
- 🟦 Investigator `afaf6d52a6a59a844` (idle S34)
- 🟨 Implementer `a5b7154286dad03f5` (last spawn Plan 3 Item 3 BE 5 satellite — NEW agentId S34)
- 🟥 Reviewer `a0aa13093d14f3bca` (idle S34, no spawn S34)
- 🟩 CICD `a520665a07ea4e8c4` (last spawn Plan 3 partial)

**Last updated S34 batch 1+2+3 (initial wrap):** 2026-05-27 (Session 34 — **🎯 Plan 1 Curate + Plan 2 G-O1 Danh bạ + Plan 3 Phase 1.5 batch (5/6) deploy prod end-to-end. Plan 4 G-H2 + Item 3 Satellite defer S35**)

**S34 (2026-05-27):** Anh main authorize "1 → 2 → 3 → 4" sequence sau S34 init 10 mục report. Cumulative end-to-end 3 commit pushed remote `edba4ae..61e9ce5` + 2 CI Run PASS (#238 G-O1 3m30s + #239 Phase 1.5 3m48s).

**Multi-agent ROI S34 ~95K:**
- 🟨 Implementer 2 spawn ~50K (G-O1 FE 2 app cookie-cutter mirror SHA256 IDENTICAL × 2 + Phase 1.5 Item 4 test bundle Case 3 +10 [Fact])
- 🟩 CICD 2 spawn ~40K (Run #238 Plan 2 G-O1 full verify + Run #239 Plan 3 batch partial → em main fallback manual smoke)
- Em main solo ~5K direct UAT smoke admin vs nv.test gotcha #44 verify
- 🟦 Investigator + 🟥 Reviewer: 0 spawn S34 (chưa cần — Implementer Case 2+3 + em main solo đủ)

**Plan 1 Curate 4 MEMORY** (~30 phút em main proxy):
- CICD: 32.9KB → 27.3KB → 30.9KB post-Run #238 entry (curate again next session)
- Implementer: 30.5KB → 24.6KB → 28KB post-Plan 3 entry
- Investigator: 26KB → 21.8KB ✅
- Reviewer: 28.5KB → 25.7KB ✅
- 4 archive q3.md mới (Reviewer q2.md vì lần đầu có q2)

**Plan 2 G-O1 Danh bạ nội bộ (~1.5h) — NEW capability prod:**
- `GET /api/directory` LEFT JOIN Users + EmployeeProfiles + Departments
- Sidebar Văn phòng số > Danh bạ → /directory card grid 34 user
- Avatar PALETTE 6 màu + filter dept dropdown + search name/email/phone/code
- Email mailto + phone tel click-to-call + Ext: badge
- `Off` + `OffDanhBa` MenuKeys Order=29 root + Order=1 leaf

**Plan 3 Phase 1.5 batch 5/6 item:**
- Item 6 menuKeys × 2 app FULL sync mirror BE 28 key cumulative ✅
- Item 2 UpdateEmployeeProfileCommand 3 bool→bool? safe partial update ✅
- Item 1 EmployeesController per-action policy Hrm_HoSo.{Read|Create|Update|Delete} ✅
- Item 4 test bundle +10 [Fact] 120→130 PASS ✅
- Item 5 UAT smoke nv.test 403 /api/employees (gotcha #44 ACTIVE) + 200 /api/directory ✅
- ⏸️ Item 3 Satellite CRUD endpoints + FE forms (~2-3h heavy) — DEFER S35

**Plan 4 G-H2 Cấu hình HRM** (~3-4h Mig 35) — **DEFER S35** (sequence cuối — em main reach realistic capacity)

**4 sub-agent reuse-able next session via SendMessage:**
- 🟦 Investigator `afaf6d52a6a59a844` (idle S34, last activity S33 t1 NamGroup audit)
- 🟨 Implementer `ad60dcfc154bdbbc4` (NEW S34 test bundle spawn agentId, last activity Plan 3 Item 4)
- 🟥 Reviewer `a0aa13093d14f3bca` (idle S34, last activity S33)
- 🟩 CICD `a520665a07ea4e8c4` (NEW S34 Run #239 verify partial agentId — may continue via SendMessage)

**⚠️ Next session S35 — anh main pick:**
1. **Item 3 Satellite CRUD endpoints + FE forms** (~2-3h) — round out Phase 1.5 G-H1 Phase 1
2. **Plan 4 G-H2 Cấu hình HRM** (~3-4h Mig 35 LeaveTypes + Holidays + ShiftPatterns + OtPolicy 4 catalog)
3. **Plan G-O2 Phòng họp BookingCalendar** (~3-4h Mig 36 — depends FullCalendar lib new dep)
4. **Curate 4 MEMORY again** (CICD 30.9KB + Implementer 28KB borderline post-S34 spawn entries)
5. **Anh main coordinate Phase 9 Ops hard blockers** (SMTP + Rotate creds + UAT 2-3 user 1 tuần)

**Recommend sequence S35:**
1. Item 3 Satellite CRUD (~2h) — close Phase 1.5 properly trước khi mở Cấu hình HRM
2. Plan 4 G-H2 (~3h) — prerequisite cho G-O3-O6 Workflow Apps
3. Curate 4 MEMORY (~15 phút mid-session) nếu over 25KB

**Last updated S33 (prev):** 2026-05-26 (Session 33 — **🎯 Plan B G-H1 Hồ sơ Nhân sự FOUNDATION DEPLOYED PROD + Plan C B-Wrap +9 tests + Drift patch**)

**S33 (2026-05-26):** Anh main authorize "Plan cha B và Plan cha C" + "OK commit, push" + "Pending Plan B G-H1 Phase 2 làm đi nhé". Cumulative end-to-end 7 commit pushed remote `5400983..79a8343` + 2 CI Run PASS (#350 Phase 1 + #237 Phase 2). **Phase 10.1 G-H1 UNBLOCK 8/11 module sau** (G-H2/G-O1-O6/G-H3/G-P1 ready depend EmployeeProfile data).

**Multi-agent ROI S33:**
- 🟦 Investigator 2 spawn ~20K: S33 startup audit (MEMORY size + RAG verify 2/3 PASS) + Plan B Task 1 NamGroup TblNhanVien* 10 bảng audit (NOT 8 anh estimate) + field map 70 cols main + 5 satellite proposal
- 🟨 Implementer 4 spawn ~110K Case 2 cookie-cutter: Plan B Task 3 BE entity 17 file + Task 4 BE CQRS 3 file + Task 5 FE 2 app 12 file SHA256 IDENTICAL + Plan C BW1-BW7 3 test file
- 🟥 Reviewer 3 spawn ~60K Smart Friend 6× clean: S33 startup drift severity + Plan C BW pre-commit + Plan B Phase 2 pre-commit (0 critical/major, 3 minor defer Phase 1.5)
- 🟩 CICD 3 spawn ~60K: S33 startup HEALTHY + Run #350 Phase 1 PASS 3m38s (Mig 34 + 33 EmployeeProfiles seeded + endpoint smoke + gotcha #51 verified) + Run #237 Phase 2 PASS 3m50s (5 NEW employee endpoint + Hrm menu seeded + bundle rotate × 2)

**Em main solo S33:** Task 2 Mig 34 schema design (4 decision: 5 satellite/Skill polymorphic/DiaChi dual-write/MaNhanVien NV/YYYY/NNNN) + Task 3b DbInitializer SeedDemoEmployeeProfilesAsync ~90 LOC (NOT gated DemoSeed flag per gotcha #51) + Task 6 Permission menu MenuKeys+SeedMenuTreeAsync 2 edit + 3 commit batch + push.

**NEW capability prod (UAT-ready):**
- Sidebar menu "Nhân sự" > "Hồ sơ Nhân sự" mở /employees
- 33 EmployeeProfile sample seeded `NV/2026/0001..0033` (1 admin + 30 demo + 2 catalog.manager)
- Sample: `NV/2026/0007 BOD 1 - Director / Ban Giám đốc / Active / hireDate=2021-08-01`
- CreatePage form Header minimal (UserId picker + status + birthday + gender + phone + hire date)
- 6-section inline collapsible detail (Cơ bản/Công tác/Đào tạo/Thân nhân/Kỹ năng/Hồ sơ)
- Phase 1 read-only satellite, edit Header defer Phase 1.5

**Implementer truncation pattern observation 2/3 (NEW lesson S33):**
- Task 3 + Task 4 heavy scaffold ~50+ tool uses → MEMORY update mid-sentence cutoff
- Task 5 clean (smaller scaffold ~10 file under same budget)
- Functional work complete despite truncation (em main verify via file existence + build + test post-spawn)
- Mitigation cross-project: Implementer split heavy task ≥30 file → 2 phase (scaffold + MEMORY update separate budget reserve)
- Em main proxy MEMORY append next session (Pattern 12-bis foundation S29 still in place, no critical knowledge loss)

**4 MEMORY status post-S33 (CURATE priority next session):**
- CICD: 32.9 KB OVER 25KB hard threshold (32.9-25=7.9 KB excess)
- Implementer: 30.5 KB OVER
- Reviewer: 28.5 KB borderline
- Investigator: 26 KB borderline
- Cumulative ~118 KB / 100 KB rolling budget
- Recommend dedicated curate session next time (~30 phút Plan A3 mirror S32 pattern)

**Defer Phase 1.5 backlog (per Reviewer 3 minor + CICD recommend):**
1. Per-action policy `Hrm_HoSo_View/Create/Edit/Delete` wire (gotcha #44 mitigation)
2. Convert 3 bool field UpdateCommand → `bool?` safe partial update
3. Satellite CRUD endpoint (WorkHistory/Education/FamilyRelation/Skill/Document) + edit forms FE
4. Test bundle Plan B Phase 2 (Create UNIQUE conflict + List filter + codeGen race)
5. UAT smoke non-admin role verify silent 403 catch
6. fe-admin menuKeys.ts add Bg_*/Catalog* sync với fe-user (drift not blocking)

**⚠️ Next session S34 — anh main pick:**
1. **Phase 1.5 backlog** (6 item above) — 1-2h dedicated session
2. **Phase 10.1 G-H2** Cấu hình HRM (Mig 35 LeaveTypes + Holidays + ShiftPatterns + OtPolicy 4 catalog) ~3-4h
3. **Phase 10.2 G-O1** Danh bạ nội bộ (reuse Users + EmployeeProfile + Departments, 1 endpoint + FE card grid) ~2-3h — easiest standalone, no workflow
4. **Curate 4 MEMORY sub-agent** (~30 phút) trước khi heavy work khác — avoid context bloat
5. **Update CLAUDE.md state numbers** drift S33 (mig 33→34, table 60→67, test 111→120) bundle batch 2026-06-01 audit cycle

**Recommend sequence S34:**
1. Curate 4 MEMORY (~30 phút) first — clear context for next plan
2. G-O1 Danh bạ (~2h) standalone easy win
3. G-H2 Cấu hình HRM (~4h) prerequisite cho G-O3-O6 workflow apps

**S32 (2026-05-26):** Anh main yêu cầu plan kỹ NamGroup 11 module port → SOL (NHÂN SỰ 3 + VĂN PHÒNG SỐ 6 + CÁ NHÂN 1). 4 quyết định AskUserQuestion: FULL 11 module + single schema `dbo` Mig 34-42 + reuse Workflow V2 enum +5 values + chunk per-module Plan riêng. Document Phase 10 backlog detailed migration-todos.md với 10 Plan G-* atomic (G-H1 + G-H2 / G-O1 + G-O2 / G-O3-O6 / G-H3 + G-P1). Stats target Phase 10 end: 33→42 mig, 60→85 tables, 148→250 endpoints, 38→60 FE pages, 14→18 AppRoles, 60→110 permission menu, 111→200 tests.

**Phase 9 stabilize batch done sequential anh main "finish hết":**
- **Plan A2** ✅ RAG re-ingest 2949→2988 chunks (166 files, 75.7s). Gotcha #52 rerank 0.516→0.906 verify.
- **Plan A3** ✅ MEMORY curate 3 agents OVER 25KB: Implementer 38.4→27.5KB (Patterns 1-19+12-bis+16-bis foundation preserved, 5 verbose S25-S29 archived q2) + Investigator 27.7→19KB (4 verbose S25-S26-S29 archived q2) + CICD 27→24.2KB (Run #231 PARTIAL detail archived q2). Reviewer 24.39KB unchanged (self-curated S32 startup earlier).
- **Plan B-Wrap** ✅ Spec BW1-BW7 documented detailed migration-todos D-Bis section. Implementation **defer dedicated session ~2h** chốt anh main S32 (test infra IChangelogService + IContractCodeGenerator mocks first-time).
- **Plan C1** ✅ Ops status update: backup-sql.ps1 ready (register Task Scheduler manual cmd documented). win-acme cert api.solutions.com.vn notAfter `2026-07-23` (not 2026-06-18 stale). SMTP + rotate creds BLOCKED chờ anh main coordinate.

**4 commits S32 push remote (all docs-only CI skip per #41):**
1. `b832f43` Phase 10 backlog Plan G + 4 sub-agent S32 startup MEMORY entries
2. `cce0963` Phase 9 stabilize batch (Plan A3 curate + B-Wrap spec + C1 ops)
3. + 2 earlier `f938bf5` (S31 docs patch) + `1e1c9a2` (S31 RAG baseline)

**4 sub-agent reuse-able next session via SendMessage (if tool available):**
- 🟦 Investigator `afaf6d52a6a59a844`
- 🟨 Implementer `ab23cc322b5d495c0`
- 🟥 Reviewer `a0aa13093d14f3bca`
- 🟩 CICD Monitor `a505a02d84fc1fabe`

**⚠️ Next session S33 — Phase 10.1 G-H1 Hồ sơ NS kick off:**
1. Spawn Investigator pre-flight audit NamGroup `TblNhanVien*` 8 bảng (`D:\Dropbox\CONG_VIEC\NAMGROUP\SOURCECODE_CÔNG_TY\NAMGROUP\NAMGROUP.Server\Data\Entities\`) + map fields → SOL EmployeeProfile schema (~30 phút)
2. Em main design Mig 34 `AddEmployeeProfiles` schema (1 main + 5 satellite: WorkHistory/Education/FamilyRelation/Skill/Document)
3. Implementer Case 2 cookie-cutter BE entity scaffold 6 entity + EF Config + DbInitializer seed 30 demo profile mirror 30 users
4. Implementer Case 2 mirror PE handler BE CQRS + 6 endpoint controller
5. Implementer Case 2 cookie-cutter mirror PE FE 2 app EmployeesPage 3-panel + EmployeeDetailTabs (6 section)
6. Em main solo Permission menu `Hrm_HoSo*` + Permission Matrix seed
7. Reviewer pre-commit + CICD Monitor post-deploy verify

**Anh main coordinate Phase 9 ops trong meantime:**
- SMTP host/user/pass → appsettings.Production.json
- Rotate creds plan approve cycle (admin + 30 demo + SA + vrapp + JWT secret + Gitea runner)
- Register Task Scheduler SQL backup `ssh vietreport-vps "Register-ScheduledTask -TaskName SolutionErpBackupDaily ..."`
- UAT 2-3 user 1 tuần

**Last updated S31 (prev):** 2026-05-26 (Session 31 — **🎯 RAG v1.3 Baseline PASS + retrieval.py fix**)

**S30 (2026-05-26):** Setup RAG Framework v1.3 governance + eval framework. Bootstrap 2949 chunks (164 files). Golden set v1.1 14 queries. Baseline v1.0=0.455 FAIL. v1.1 tentative=0.364 (MCP stale — vector broken). Commit b223466 (docs-only, CI skip).

**S31 (2026-05-26):** Diagnose vector search broken: qdrant-client 1.18 removed search() → AttributeError silently swallowed. Fix retrieval.py: search()→query_points().points. Re-run eval: **recall@5=1.000 (11/11) PASS** avg_rerank=0.847. Quality gate PASS. Gotcha #52 added. STATUS/HANDOFF updated.

**⚠️ S31 CRITICAL caveat (resolved S32):** CLI restart required — retrieval.py fix in AI_INFRA source but MCP server still loaded old code. ✅ S32 confirmed live (RAG query rerank 0.906 verify gotcha #52 hit post-CLI-restart + S32 re-ingest 2988 chunks).

**Last updated S29 (prev):** 2026-05-22 (Session 29 **FINAL wrap** — **🎯 2 big plans END-TO-END deployed prod: Plan CA + Plan B Contract V2**). 20 commits cumulative S29 push 4 CI Runs PASS (#229+#230 Plan CA + #231+#232 Plan B). **Plan CA Move "Cấu hình danh mục dùng chung" admin → eoffice**: 9 menu (Master + 4 leaf + Catalogs + 4 sub-catalogs) move + role mới `CatalogManager` + demo user `catalog.manager@solutions.com.vn / CatalogMgr@2026`. 5 chunks A+B+C+D+D2 + Hotfix 1 (resolvePath staticMap silent sidebar drop — anh UAT screenshot catch). **Plan B Contract V2 wire mirror PE Mig 22-26**: COEXIST V1+V2 (7 V1 contract giữ behavior). 9 chunks A1+A2+B+C+B2+E1+D+E2+E3 + Hotfix Reviewer (ApplicableType=Contract guard MAJOR security catch) + Hotfix CICD (SeedSampleContractWorkflowV2 OUT of DemoSeed gate — V2 path BLOCKED prod nếu gated — **gotcha #51 NEW INFRASTRUCTURE vs DEMO seed**). Mig 32+33 prod applied. Sample `QT-HD-V2-001` seeded post Hotfix CICD. fe-admin/fe-user bundle 4× rotate cumulative S29. **Multi-agent ROI S29 ~565K total**: Investigator 2 spawn ~40K + Implementer Case 2 5 spawn ~82K (1 stopped mid-task E3) + Reviewer 4 spawn ~355K (Smart Friend MAJOR catch 2 lần) + CICD Monitor 3 spawn ~90K (2 fail 529 transient em main fallback manual + 1 CRITICAL catch). Em main solo ~150K coordinate + Chunk B/B2 cross-stack + E3 finish + 2 hotfix re-edit. **Smart Friend pattern proven 4× cumulative S22 #44 + S25 #48 + S29 Reviewer #ApplicableType + S29 CICD #DemoSeed**. **MCP RAG tools added 4 agent definitions** (commit `b51fc94`) — search_memory + cross_project_search — anh restart CLI hot-reload S30+. **Test gate:** 111/111 PASS preserved (UAT mode skip per chunk, V2 ApproveV2Async 150 LOC NO test cover — defer test bundle Plan B Wrap session). **8 patterns NEW saved S29** (xem STATUS.md). **NEW capability prod end-to-end:** (1) Anh login Drafter fe-user → `/contracts/new?type=1` → Workspace dropdown pick `QT-HD-V2-001` → submit ChoDuyet → CCM `binh.le` approve → Service ApproveV2Async UPSERT opinion + advance → terminal gen mã HĐ + Phase=DaPhatHanh (mirror PE V2 flow). (2) Anh login `catalog.manager@solutions.com.vn / CatalogMgr@2026` → fe-user sidebar Master + 4 leaf + Catalogs + 4 sub-catalogs CRUD full. **Pending S30+**: anh UAT verify end-to-end V2 contract flow + restart CLI MCP RAG load + curate 4 MEMORY (Investigator 25.2KB + Implementer 35.4KB + Reviewer ~22KB + CICD 24.9KB — Implementer over hard threshold, dedicate curate session) + test bundle Plan B (regression test ApproveV2Async + ApplicableType validation). **Stats final S29:** 33 mig (+2) · 60 tables (+1) · ~148 endpoints (+1) · 38 FE pages · 111 test · 51 gotcha (+1 #51) · 25 memory · 6 skills · 4 sub-agents · 14 AppRoles (+1) · 34 active prod users (+1) · 7 V1 contracts + V2 ready · 4× bundle rotate.)

**Last updated S28 prev:** 2026-05-22 (Session 28 **FINAL wrap** — **🎯 S28 cumulative t1→t6 + Layer A governance apply + 4 sub-agent flush MEMORY DONE**. Bro broadcast Layer A governance active 2026-05-22 (3-Layer distributed: A project-local active / B shared_global defer / C infrastructure anh pqhuy). **Quên rule cũ "mọi tương tác PHẢI store_memory" — ABANDONED** (em main S28 t2 over-reach self-authorize cross-project rule → caught S28 t4 → scope-down về SOLUTION_ERP self-discipline). Em main apply Layer A policy local: **4-category default** (decision/lesson/pattern-proven-≥2×/session-wrap) + skip list nguyên + 5 SOLUTION_ERP-specific adjustment + tag schema mandatory `[type, phase, bc-or-module]` + phase enum 11 (current phase-9 UAT) + BC/module 8 domain + 5 cross-cutting + optional prefix 8 + source_path convention `solution_erp/<area>/<topic>-<date>` + eval ritual weekly Friday 5 metric từ 2026-05-29 → end-trial 2026-06-05 + 10 golden query draft. **4 sub-agent flush MEMORY DONE post-S27 model:inherit fix** — Investigator (aeda4 alive) + Implementer (a2a2f spawn) + Reviewer (a9e94 spawn) + CICD Monitor (a8098 spawn) — registry hot-reload verified, Recent activity entry S28 wrap góc nhìn từng con preserved cumulative S1-S27 narrative untouched. **6 chunk stored RAG S28 t2-t6** with tags schema mandatory format. **+1 memory user-level `feedback_interaction_logging_rag.md` SCOPE-DOWN S28 t4** (lesson authority boundary + implicit consent risk). **Hit rate verify 3 query**: Plan B Contract V2 rerank 0.84 ✓ + gotcha #48 rerank 0.84/0.82 ✓ + per-NV 10-surface checklist ✓ — cumulative > 0.7 threshold pass. **State chốt S28:** 31 mig · 59 tables · ~146 endpoints · 35 FE pages · **111 test pass** · 49 gotcha · **25 memory user-level (+1)** · 6 skills · 4 sub-agents verified · **3,462 RAG chunks SOLUTION_ERP** (+6 chunk S28). **0 commit remote S28** (all local docs + memory + RAG store — anh chốt push thủ công). Plan B Contract V2 wire kick-off PRIORITY HIGH S29+ — 8-chunk pre-allocated 🟦 Investigator pre-flight audit + 🟨 Implementer Case 2 mirror PE V2 (4 chunk) + 👤 Chủ trì Solo Service ApproveV2Async + 🟥 Reviewer pre-commit Smart Friend + 🟩 CICD Monitor post-deploy Run verify. Plan AI Phase 5 distributed bootstrap 4 project khác (NamGroup/DH/Ashico/Vipix) đã DONE — total 18,532 chunks indexed cross 5 project. VIPIX hosting governance docs `D:\Dropbox\CONG_VIEC\VIPIX_MULTISITE_PROJECT\docs\governance\RAG-LAYER-A-PROJECT-LOCAL.md` v1.1 (em đọc + apply scope SOLUTION_ERP). 2-week trial monitoring 2026-05-22 → 2026-06-05 weekly Friday eval — Friday 2026-05-29 first checkpoint.)

**Last updated S28 t1 initial:** 2026-05-22 (Session 28 turn 1 — **🎯 S28 startup 9-step + RAG ROI reality check em main verdict + cross-agent perspective update**. Anh restart Claude Code CLI sau S27 wrap → **sub-agent registry hot-reload OK** verified qua smoke test spawn Investigator (agentId `a2462ca503ed38b0b`, 16.3 KB MEMORY loaded, 3 skills attached: contract-workflow + permission-matrix + ef-core-migration, model `inherit` 1M Opus parent). RAG status verified: **3,460 chunks SOLUTION_ERP indexed** (cao hơn S26 wrap 2,628 — re-index ngầm) + 6 MCP tools (`mcp__rag-unified__*`) loaded, Qdrant NSSM Service Running PID 4476. **Bro hỏi "cảm nhận rõ lợi ích RAG ko?"** → em main verdict thẳng thắn (anti-suger-coat): **CÓ LỢI marginal short-term (5-10% time saving), TRANSFORM long-term cross-project (chưa khai phá vì 4 em main project khác chưa bootstrap).** 3 case WIN rõ S28 start: (1) Carry over Plan B Contract V2 state — query `Plan B mirror PE pattern` → rerank **0.84** instant 5 hits (saved ~8 phút vs đọc 3-4 session log); (2) Coverage gap detection — query `subagent setup pitfalls` → **0 hits** → phát hiện S27 NEW content chưa index, re-bootstrap pending pydantic fix; (3) Cross-project pattern verify — `cross_project_search` tool sẵn sàng (chưa dùng vì 4 dự án khác chưa bootstrap). **Reality check — RAG KHÔNG thay được blanket auto-load:** STATUS.md (40K) + HANDOFF.md (30K, đỉnh 88K full) + 4 MEMORY 200 lines/spawn (~25K) + CLAUDE.md (~10K) = ~150-200K blanket KHÔNG cắt được, RAG chỉ supplement khi cần truy cập historical specific. **3 nhược điểm cảm nhận thật:** (a) Bootstrap lag — S27 NEW content (4 file `feedback_subagent_setup_pitfalls + skill-audit-late + 4 MEMORY post-curate + rag-onboarding-guide`) chưa index sau 1 ngày; (b) Effective coverage gap — Plan B query rerank 0.84 excellent vs S27 content 0 hit miss; (c) Token cost rerank-2.5 ~$0.05/1K query. **4 case verdict — RAG worth it?** Solo project blanket đủ ⚠️ Marginal (5-10% saving) | Cross-session retrieve specific historical ✅ Tốt (1 query vs 10 phút find) | Cross-project pattern reuse 5 dự án 🚀 **TRANSFORM** (chưa dùng được vì 4 dự án khác chưa bootstrap) | Heavy refactor Plan B Contract V2 wire ✅ Tốt (instant Mig 22-26 pattern lookup). **Bottom line:** RAG hiện tại đáng giữ cho infrastructure đã đầu tư S26-S27 (60.9s bootstrap + NSSM Service auto-start + Custom Dashboard 7 panel), **nhưng nếu redo từ đầu** sẽ recommend bro **đợi bootstrap 5 dự án rồi mới claim "RAG transform"** — solo project chỉ saving marginal. **Pattern reusable cross-project — RAG ROI evaluation framework:** 1 dev solo + 1 project < 1M tokens → ❌ Skip RAG (blanket sufficient) · 1 dev solo + 1 project > 1M tokens → ⚠️ Marginal (5-10% saving) · 1 dev solo + N project > 5M tokens → ✅ Worth it (cross-project transform) · N dev team + 1 project → ✅ Worth it (shared knowledge) · N dev team + N project enterprise → ✅ Mandatory (Sourcegraph-tier scale). Avoid over-hype RAG cho solo 1 project. **Update multi-layer 6 file:** memory user-level `feedback_rag_hybrid_pattern.md` append §S28 t1 ROI reality check (~120 LOC NEW section) + 4 agent MEMORY Recent activity entry góc nhìn từng con (🟦 Investigator: pre-flight audit cross-session retrieve Plan B-class · 🟨 Implementer: Pattern proven retrieve cumulative 19 Pattern foundation · 🟥 Reviewer: gotcha historical context cross-ref #44+#48+#49 · 🟩 CICD Monitor: Run history cross-reference #186-#222 cumulative + 10-surface-point per-NV checklist promoted foundation) + STATUS.md + HANDOFF.md headers. **Bro sẽ cross-check** 4 agent có hiểu consistent context + verdict mới không (next test: spawn 4 agent với same prompt "cảm nhận RAG ROI sau S27?" + compare consistency). Stats unchanged: 31 mig · 59 tables · ~146 endpoints · 35 FE pages · **111 test pass** · 49 gotcha · **24 memory user-level (no new entry, only append §S28 t1 to existing feedback_rag_hybrid_pattern)** · 6 skills · 4 sub-agents (registry hot-reload verified post-S27 fix) · 3,460 RAG chunks + 6 MCP tools active. **0 commit remote S28 t1** (all docs + memory update + retrospective opinion — anh chốt push thủ công sau approve).)

**Last updated S27:** 2026-05-22 (Session 27 **chốt cuối** — **🎯 Memory Curate + RAG Manual Control + Multi-agent Setup Pitfalls Fix**. **Trigger:** Anh share VIPIX `docs/guides/multi-agent-pitfalls.md` (lesson VIPIX 2026-05-22 em main solo cả buổi vì agents KHÔNG load) → em audit SOLUTION_ERP setup phát hiện **CHÍNH XÁC CÙNG PATTERN broken**: 4 file `.claude/agents/*.md` dùng `model: claude-opus-4-7` (200K silent fallback) + non-standard `effort: max` field → registry chưa load cả session S27. Em main solo 8 task vì KHÔNG có lựa chọn delegate (2/8 lẽ ra Implementer Case 1+2: A3.1 5 PS scripts cookie-cutter + F2 4 file mechanical fix). **Fix applied:** 4 file `model: inherit` + remove `effort: max`. **Pending:** Anh restart Claude Code CLI để hot-reload (pitfall #1 - no hot-reload session đang chạy). **Memory user-level NEW:** `feedback_subagent_setup_pitfalls.md` 235 lines cross-project pitfall checklist (VIPIX + SOLUTION_ERP evidence). **Plan A.3 RAG Manual Control + Upgrade Option 4b NSSM:** 7 PS scripts `D:\.claude-rag\scripts\` (start/stop/status/dashboard/boot/install-service/fix-service-start ASCII-only) + 6 `$PROFILE` aliases + Startup folder shortcut `rag-boot.lnk` open Dashboard sau login + Custom MCP Dashboard HTML `dashboard.html` 19 KB 7 panels with SNAPSHOT warning banner (was 13.3 KB pre-NSSM). **S27 post-wrap upgrade Option 4a → Option 4b NSSM Windows Service** (anh phản biện "Qdrant nên auto-start như PostgreSQL/SQL Server"): Download NSSM 2.24 ~351 KB (retry 1× sau 503 transient) → `install-service.ps1` elevated register service Qdrant + AppDirectory + log paths + rotation 10MB + Start SERVICE_AUTO_START + AppExit Default Restart 3s delay + DisplayName "Qdrant Vector DB (RAG Unified)". **Bug WAL lock conflict** khi pre-existing manual qdrant.exe held WAL → service Paused → `fix-service-start.ps1` elevated recovery (Stop-Service Force + kill orphan qdrant.exe + Start-Service + verify). Service Running PID 4476 RAM 101.8 MB confirmed. Auto-start boot-time trước login + auto-restart on crash + survive logout. 4 PS scripts updated to Service cmdlet (Start-Service/Stop-Service/Get-Service). Dashboard removed meta refresh + add prominent SNAPSHOT warning banner + Service status panel. **Plan A.4 RAG Onboarding Guide:** `docs/guides/rag-onboarding-guide.md` **42.5 KB / 682 lines / 14 sections + §A2 + §A3 NEW** (post-NSSM upgrade S27). 13 sections base + §12 upgraded Option 4b NSSM với install/fix scripts + §A2 cấu trúc chuẩn em main pioneer + **§A3 Context loading Hybrid pattern (~242 lines NEW post-wrap)** — 8 sub-sections covering: Cách A defensive vs B aggressive decision gate / Layer 1 blanket auto-load checklist 200-280KB / Layer 2 RAG retrieve decision tree 7-branch / Token budget 5 utilization zones 1M Opus / 6 MCP tools examples Vietnamese+English / em main daily workflow morning+EOS+monthly / 7 anti-patterns cross-project / context monitoring + end session protocol. Cho 4 em main project khác self-onboard + apply workflow optimization từ session 1. **Plan F1 Qdrant Web UI fix:** UAT bro catch `localhost:6333/dashboard` 404 → root cause Qdrant Windows binary zip không bundle Web UI static, log warn `Static content folder for Web UI './static' does not exist`. Em pioneer S26 chỉ download `qdrant-x86_64-pc-windows-msvc.zip` 28.3 MB binary, miss `dist-qdrant.zip` 6.59 MB v0.2.12 separate. **Fix:** Download + extract `D:\.claude-rag\qdrant-bin\static\` + flatten dist subfolder + restart → HTTP 200 "UI | Qdrant" ✓. **Memory Curate 4 MEMORY -60%:** cicd-monitor 72.4→15.8 KB (-78%) + implementer 38.8→22.0 KB + investigator 34.9→16.3 KB + reviewer 34.5→17.9 KB. Total 180.6→72.0 KB. Archive 115 KB preserved verbose entries (rule §6.5 compliance). 4 archive files: `archive/2026-05-runs.md` + `archive/2026-05-q1.md` × 3 agent. 10-surface-point per-NV checklist promoted to cicd-monitor MEMORY foundation. Gotcha #48 SQLite tie-break promoted to cicd-monitor MEMORY foundation. **Audit drift sớm §6.4+§9.4** (trigger +8 gotcha vượt threshold): 5 file patched (4 `26 migration → 31` + 1 `41 bẫy → 49`) + audit log `docs/changelog/skill-audit-2026-05-late.md`. **State chốt S27:** 31 mig (no schema) · 59 tables · ~146 endpoints · 35 FE pages · **111 test pass unchanged** (UAT mode defer) · 49 gotcha (gotcha #48/#49 pending docs add) · **24 memory user-level (+1: feedback_subagent_setup_pitfalls)** · 6 skills · 4 sub-agents (fixed pending CLI restart) · +5 PS scripts · +1 custom Dashboard HTML · +2 docs files (onboarding + audit log late) · **0 commit remote S27** (all local + memory + docs - anh chốt push thủ công sau approve). **Patterns reusable cross-project S27:** (1) Multi-agent setup pitfall checklist cross-project, (2) PS scripts ASCII-only discipline + CSS-styled badges thay emoji, (3) Qdrant Windows 2-step setup (binary + Web UI static separate), (4) Custom Dashboard PS generator pattern, (5) Memory curate proxy pattern khi registry empty, (6) Audit log file separate khi drift sớm.)

**Last updated S26 FINAL:** 2026-05-21 (Session 26 **FINAL wrap** — **🎯 Anh chốt 2 decisions architectural CUỐI sau commit `bf93abd`**: **(1) Approach B distributed RAG bootstrap** — em main SOLUTION_ERP chỉ build infrastructure + docs, KHÔNG bootstrap hộ project khác. 4 em main NamGroup/DH/Ashico/Vipix tự setup khi mở session project đó (Cognition boundary, sensitivity per project, self-ownership re-bootstrap). **(2) Option 4a manual PowerShell scripts** thay Task Scheduler auto-start — anh OWN control qua `rag-start`/`rag-stop`/`rag-status`/`rag-dashboard` aliases trong `$PROFILE` + Qdrant dashboard built-in FREE `http://localhost:6333/dashboard`. **Bug fix bootstrap.py glob `extra_corpus`** — `import glob.glob(recursive=True)` thay Path manual parsing (em main fix sau khi test query "feedback_rag_distributed_ownership" return 0 hit → user-level memory KHÔNG được scan đúng). Re-bootstrap với fix: **2,421 → 2,628 chunks** (+207 chunks user-level memory 23 file). Cache SHA256 hit perfect — 2nd bootstrap 6.5s vs initial 60.9s = **10× speedup**. **+1 memory user-level NEW** `feedback_rag_distributed_ownership.md` ~200 lines documenting 2 decisions + setup-once scale-distributed pattern reusable cross-project (RAG + MCP + future shared tools). **Rerank verify S26 content:** 5/6 query hit rerank 0.498-0.926 (em main project X build infra docs query hit BEST 0.926 — semantic search Anthropic Contextual Retrieval working PERFECT). **Pending S27+ updated:** Phase 5 SKIP em (distributed approach) · Phase 6 Option 4a 4 PowerShell scripts (start/stop/status/dashboard) + `$PROFILE` aliases (~30 phút em viết) · Phase 7 `rag-onboarding-guide.md` 13 section (11 outline cũ + §12 monitoring `rag-status` + §13 distributed Approach B explanation) (~40 phút em viết). 4 em main project khác tự setup ~10 phút mỗi project khi anh mở session.)

**Last updated S26 initial wrap:** 2026-05-21 (Session 26 chốt cuối lần đầu — **🎯 Plan AG/AG2/AG3/AG4/AG5/AG6 cumulative 6 commits Plan AG series `0bf6c7e..d99069a` push remote + Plan AI RAG global MCP infrastructure setup**. **Trigger:** Bro UAT 2026-05-21 screenshot phàn nàn UI Duyệt NCC PE List "đám rừng" flat list + đề xuất Outlook-style folder tree theo dự án + dưới năm + NCC. 6 plan UI iteration cumulative: **Plan AG** Phase 1 PE List tree view 2-level Project > Gói thầu > PE (🟦 Investigator audit 5Q ~30K + 🟨 Implementer Case 2 cookie-cutter mirror 2 app ~16K commit `0bf6c7e` 2 file +346/-116 LOC SHA256 IDENTICAL + 🟥 Reviewer pre-commit ~25K PASS 0 blocker + 🟩 CICD Monitor Run #222 ~12K bundle hash 2 app rotate verified `C8TvDy7r→CWHIdoFo` admin + `BvcWrq2z→Bg2FNeIz` user). **Plan AG2** (`c5429c0`) simplify 1-level (drop tầng gói thầu per bro feedback "gói thầu thì ko cần thiết phải treedow") + widen Panel 1 340px → 400px. **Plan AG3** (`fbad4a9`) drop single-PE flat branch consistent UI (mọi dự án dù 1/N phiếu đều render `<details>`). **Plan AG4** (`2bf0118`) bổ sung Người tạo + Phòng ban tạo PE card — BE+FE cross-stack 8 file (DTO `PurchaseEvaluationListItemDto` +4 fields DrafterUserId/DrafterName/DepartmentId/DepartmentName + 3 projection JOIN Users+Departments LEFT (ListHandler/InboxHandler/ApprovedHandler) + FE PeListItem type +4 + UI card 👤 Drafter · Phòng ban inline) + dotnet test 111/111 PASS unchanged. **Plan AG5** (`083b601`) extend tree 3-level Project > Năm > NCC > PE (selectedSupplierName fallback "(Chưa chọn NCC)" cho PE chưa DaDuyet, sort vi locale + năm DESC + PE createdAt DESC). **Plan AG6** (`d99069a`) compact card 3-row gọn đẹp (drop Type "Duyệt NCC" label redundant + combine mã phiếu/time + Drafter/Dept/HĐ inline + py-2.5 → py-2). Tất cả 6 commit verified IDENTICAL SHA256 hash mirror 2 app §3.9 + npm build × 2 PASS 0 TS err. **Plan AI RAG global MCP setup** (User-level Approach A — 1 dev 5 dự án cùng máy localhost share infrastructure): Phase 0 pre-flight (Python 3.11.3 + Voyage API key set User scope length 46 + 5 project paths detected Claude Desktop config epitaxy: SOLUTION + NAMGROUP + DAI_Y_DUOC + ASHICO + VIPIX_MULTISITE) + Phase 1 Qdrant v1.18.0 Windows native binary running PID 67240 port 6333 (KHÔNG Docker, anh chốt no Docker) + Phase 2 FastMCP server ~1100 LOC Python 9 file (server.py 6 tool handlers + lib/embed.py Voyage wrapper + lib/retrieval.py 3-layer pipeline + lib/projects.py auto-detect cwd + lib/chunking.py Anthropic Contextual Retrieval prepend + lib/watcher.py + bootstrap.py CLI + README.md) + Phase 3 register MCP user-level (`claude mcp add -s user -e VOYAGE_API_KEY=$VK -- rag-unified python ...` — trick `--` separator để terminate variadic `-e` flag) — "rag-unified: ✓ Connected" + Phase 4 bootstrap SOLUTION_ERP corpus 126 files → **2,392 chunks indexed 60.9s** (~484K Voyage tokens = 0.24% free tier 200M/month) + verify search 4 query thực tế recall 0.641-0.879 rerank score. **Stack final RAG:** Voyage-4-large embedding (1024-dim) + Voyage rerank-2.5 cross-encoder + Qdrant Windows native + SQLite FTS5 BM25 (unicode61 + diacritics remove) + Reciprocal Rank Fusion (k=60) + FastMCP 3.3.1 stdio + Anthropic Contextual Retrieval prepend "From <doc> > <heading>:" + git-sync embeddings snapshot pattern (Cursor lesson). 6 MCP tools exposed: search_memory · cross_project_search · search_code · store_memory · promote_to_shared · list_projects. **+1 file MD onboarding** `docs/guides/multi-agent-setup-guide.md` xuất cho 4 dự án future áp dụng pattern 1 em main + 4 sub-agent + RAG share infrastructure. Multi-agent ROI S26: 🟦 Investigator 2 spawn (Plan AG audit 5Q + Plan AI RAG distribution research 4 study cases Cursor/Cline/Continue/Sourcegraph) ~70K + 🟨 Implementer 1 spawn (Plan AG Case 2) ~16K + 🟥 Reviewer 1 spawn (Plan AG pre-commit) ~25K + 🟩 CICD Monitor 1 spawn (Run #222) ~12K. Em main solo Plan AG2-AG6 (5 plan polish UAT iteration) + Plan AI Phase 0-4 (RAG infra setup ~1.5h). **State chốt S26:** 31 mig (no schema) · 59 tables · ~146 endpoints · 35 FE pages · **111 test pass unchanged** (UAT defer test-after per §7) · 49 gotcha unchanged · 23 memory user-level unchanged · 6 skills · 4 sub-agents · **+1 docs/guides/multi-agent-setup-guide.md** + **+1 RAG infrastructure** `D:\.claude-rag\` ready 5 project. 3 patterns reusable cross-project (S26 NEW): (1) Pattern 19 Implementer — HTML native `<details>/<summary>` + Tailwind named groups `group/proj`+`group/year`+`group/sup` + localStorage Set<string> cho hierarchical 3-level tree UI no Accordion lib, (2) RAG User-level Global MCP — 1 server localhost serve N project + per-project `.claude/rag.json` config (Approach A — 1 dev solo scenario), (3) Qdrant Windows native binary deployment — no Docker overhead, appropriate cho solo dev không enterprise scale. **Memory FLAG continued S26:** cicd-monitor MEMORY ~74KB (+2KB từ S25), critical curate session URGENT next (archive Run #186-#221 verbose → `archive/2026-05-runs.md`). **Pending follow-up Plan AI:** Phase 5 bootstrap 4 project còn lại (NamGroup/DH Y Dược/Ashico/Vipix) ~30 phút mỗi project + Phase 6 file watcher + Windows Task Scheduler auto-start Qdrant. Tests baseline 111 preserve cross 6 commit (test debt cumulative S22+1 + S25 + S26 bug fix not added — UAT mode defer per §7).)

**Last updated S25:** 2026-05-19 (Session 25 chốt cuối — **🎯 Plan AB→AF cumulative 7 commits push `e23f51c..506cada` + 7 CICD Runs (#215 FAIL → #216-#221 PASS) — Lịch sử visibility critical fix UAT iteration**. Trigger: Bro UAT 2026-05-19 phát hiện 2 bug critical phiếu PE/2026/A/032: Bug 1 Budget Adjust không show "Lịch sử thay đổi" (BE log OK + FE filter strict TraLai-only loại) + Bug 2 Return Mode 4 mode KHÔNG log Changelog (`ApplyReturnModeAsync` miss `db.PurchaseEvaluationChangelogs.Add()`). **Plan AB Chunk A** (`cdfd542`, 🟨 Implementer Case 1 spawn ~12K): BE refactor `ApplyReturnModeAsync` Drafter early return → common path + single `Changelog.Add()` cover 4 mode uniform với `modeName` switch enum + `actorName` resolve via `userManager.FindByIdAsync` mirror `LogTransitionAsync` pattern. FE `PeDetailTabs.tsx` × 2 app `HistoryTab` filter extend (PE_ENTITY_HEADER=1 + summary 'ngân sách' + Workflow summary 'Trả lại'). 🟥 Reviewer pre-commit verify ~22K PASS 0 critical/major/minor (1 minor V1 legacy fallback non-block). Run #215 FAIL test_infra 2/53 — Plan M edge case tests SQLite frozen-clock tie-break (gotcha #48 NEW): 2 Changelog rows cùng `CreatedAt` → `OrderByDescending.FirstAsync()` pick wrong → ContextNote null. **Plan AB Chunk A2** (`8c05947`, em main solo): Test filter `Where(Summary.Contains("Chuyển phase"))` pick đúng LogTransition entry. 111/111 PASS. Run #216 PASS. **Plan AC** (`a734bf2`, em main solo): BE capture pre-call Step/Level → Reject branch add `PurchaseEvaluationApprovals.Add()` với Decision=Reject + Comment from-pos + mode summary. BE line 472 enrich Approve comment prefix `[Duyệt vượt cấp tới Cấp cuối]` khi skipToFinal=true. FE × 2 app ApprovalsTab thêm `decisionBadge(decision, toPhase)` helper (Duyệt emerald / Trả lại amber / Từ chối rose). Run #217 PASS, bundle rotate × 2 app. **Plan AC2** (`25837b6`, em main solo, Option 2A bro chốt): FE merge view ApprovalsTab fetch changelogs + reconstruct synthetic Reject rows từ Workflow+Reject Changelog (filter EntityType=5 + Summary "→ TraLai"/"→ TuChoi" OR ContextNote "Trả về"/"không lùi được") + dedupe `approverUserId + timestamp 5s bucket` cùng real Reject rows + merge sort by approvedAt. KHÔNG DB write, reversible historical recovery. Run #218 PASS, 8 Workflow entries verified, bundle rotate × 2 app. **Plan AD** (`0aaf2df`, em main solo, Option A bro chốt): Drop fromPhase→toPhase badges (visual confusion 3/4 mode Reject giữ ChoDuyet — gotcha #49 NEW) + add `extractNextTargetHint(decision, toPhase, comment)` helper regex parse `Chuyển phase X → Y` + keyword detect "Trả về"/"vượt cấp"/"Cấp cuối" → "→ Cấp Y" / "→ Trả về Người chỉ định (Bước X Cấp Y)" / "→ Vượt cấp tới Cấp cuối" / "→ Đã duyệt hoàn tất". Cleanup unused `PurchaseEvaluationPhaseColor` import. Mirror × 2 app §3.9. Run #219 PASS, bundle rotate × 2 app. **Plan AE** (`9ea62be`, em main solo, preventive batch fix): BE 9 `Changelog.Add()` sites add `UserName = currentUser.FullName ?? currentUser.Email` (Budget Adjust + Create PE + Update PE Header + Detail Insert/Update/Delete + Quote Insert/Update/Delete + Select Winner). ICurrentUser đã có FullName/Email từ JWT — KHÔNG inject userManager mới. `replace_all=true` 1 pass cover 8 sites cùng indent + 1 site manual fix indent 16 spaces inside if block. 111/111 PASS local. Run #220 PASS, bundle UNCHANGED (BE-only commit). **Plan AF** (`506cada`, em main solo, Option A bro chốt): FE ApprovalsTab + HistoryTab build `userMap useMemo` từ embedded PeDetailBundle data: `drafterUserId+drafterName + approvals[].approverUserId+approverName + approvalFlow.steps[].levels[].approvers[].userId+fullName + levelOpinions[].signedByUserId+signedByFullName + departmentOpinions[].userId+userName` — no extra `/api/users` fetch (admin permission). `resolveActorName(a) / resolveUserName(l)` helper: trust entry.userName non-empty → userMap.get(entry.userId) → 'Hệ thống' fallback. Mirror × 2 app §3.9. Run #221 PASS, bundle rotate × 2 app. **Multi-agent ROI S25:** 🟦 Investigator 1 spawn (~28K Bug 1+2 audit) + 🟨 Implementer 1 spawn (~12K Plan AB Chunk A Case 1) + 🟥 Reviewer 1 spawn (~22K Plan AB pre-commit) + 🟩 CICD Monitor 7 spawns (Run #215 catch test gate fail + Run #216-#221 PASS verify, ~70K cumulative). Em main solo 5 plans AC→AF cross-stack reasoning + UAT iteration. Total cumulative agent cost ~132K · em main ~210K. **State chốt S25:** 31 mig (no schema) · 59 tables · ~146 endpoints · 35 FE pages · **111 test pass unchanged** (UAT defer test-after per §7) · **49 gotcha (+2: #48 SQLite tie-break + #49 dual-phase UI confusion)** · **23 memory user-level (+2 NEW: feedback_fe_merge_synthetic_audit + feedback_fe_usermap_fallback)** · 6 skills · 4 sub-agents active. **Memory CRITICAL FLAG:** cicd-monitor MEMORY ~72KB strongly over 50KB hard threshold — DEDICATED CURATION SESSION REQUIRED next (archive Run #186-#210 + S22-S24 verbose entries to `archive/2026-05-runs-186-210.md`). Investigator 32KB / Implementer 35.7KB / Reviewer 32.3KB cũng over 25KB threshold — recommend curate cùng session với cicd-monitor. **8 patterns reusable saved S25:** (1) Multi-Changelog SQLite tie-break (gotcha #48) → test discriminator, (2) CICD catch UAT skip test risk → resumed local verify BE refactor > 100 LOC, (3) Test fix Option A over BE refactor preservation, (4) Capture pre-call mutation state cho audit row from-position, (5) FE merge synthetic rows từ Changelog reversible recovery (Plan AC2), (6) Drop misleading dual-phase badges + semantic next-target hint (Plan AD), (7) Changelog UserName preventive systemic batch fix 9 sites (Plan AE), (8) FE userMap fallback từ embedded domain data no extra API (Plan AF). Bro UAT verify cumulative deploy live PE/2026/A/032 — historical entries pre-Plan AE nay resolve user names qua userMap fallback.)

**Last updated S24:** 2026-05-15 (Session 24 chốt cuối — **🎯 Plan AA cumulative 7 commits push `a1a910f..ee0902a`** — User Workflow Matrix view + Sidebar widen + 4 polish iteration UAT feedback. Phase 1 Plan AA core (3 commit `ee776d5..ac2c859`): 🟦 Investigator Pre-A 5Q audit (~32K) + 👤 Chủ trì Chunk A BE+Layout solo (gotcha #44 đã fix permanent từ S18 → chỉ ADD `IsUserSelectable bool?` filter + DbInitializer INSERT-OR-UPDATE-Order idempotent shift existing prod rows + MenuKeys `Pe_DuyetNcc_WfView` helper + sidebar widen `w-72 xl:w-80` × 2 app + revert Plan U truncate × 5 sites) + 🟨 Implementer Case 2 Chunk B FE (~14K, WorkflowMatrixViewPage + types + App.tsx route ~270 LOC) + 🟥 Reviewer cumulative pre-commit (~25K, PASS 0 blocker + fe-admin build bonus 1926 modules) + 🟩 CICD Monitor Run #210 verify (4/4 wire end-to-end: filter live + menu Order shift + bundle hash rotate 2 app + Mig 31 unchanged). Phase 2 polish iteration UAT feedback (4 commit `da218f1..ee0902a`): 👤 Chủ trì Solo 4× CSS/UX polish — (1) `da218f1` hotfix container px-6→px-2 (content sát sidebar) + (2) `4d60598` redesign v1 panel-per-NV mirror admin Designer color coding (drop table 11 cột symbol khó hiểu) + (3) `fbbd361` redesign v2 HTML table rowSpan tận dụng full width (4 cột: Bước/Cấp/NV/Quyền duyệt grid 2-col 7 label) + (4) `ee0902a` wrap fix label dài về đầu hàng pattern hanging-indent reverse CSS (flex→block + inline-block icon + inline text + absolute ChevronDown) + text smaller text-[12px] leading-snug. Patterns reusable cross-project: (a) gotcha #44 relax pattern PROVEN cross-stack reuse, (b) DbInitializer INSERT-OR-UPDATE-Order idempotent re-deploy, (c) Tailwind JIT palette array (STEP_PALETTE 5 màu + LEVEL_PALETTE 5 màu cycle), (d) HTML table rowSpan flat row builder helper, (e) hanging-indent reverse via inline-block icon. Stats: 31 mig · 59 tables · **~146 endpoints (+1 GET filter param)** · **35 FE pages (+1 WorkflowMatrixViewPage)** · **111 test** (no change, baseline confirmed post-Plan AA) · 47 gotcha · 21 memory · 6 skills · 4 sub-agents. Multi-agent ROI S24: ~175K total (Inv 32K + Imp 14K + Rev 25K + CICD 12K + 3 flush agents ~12K + em main ~80K) = ~28% solo equiv. CICD verify Run #210 PASS. **Implementer MEMORY ~31.5KB > 25KB threshold** → recommend curate next session archive S20-S22 old entries.)

**Last updated S23 t12:** 2026-05-15 (Session 23 turn 12 chốt cuối — **🎯 S23 cumulative 11 plan + 32 commits**. Plan K (Mig 31 F2 refactor 9c) → L (UAT bug 5c) → M (F1 edge case 4c) → N (per-NV lookup 1 site 2c) → O (4 sites cascade 2c) → P (Controller body record 1c) → Q (FE banner 1c) → R (cleanup phiếu/wf 1c) → S (wipe ALL wf 1c) → T (DemoSeed flag 2c) → U (sidebar truncate 1c). 4 sub-agents: Investigator 5 spawn + Implementer 5 spawn + Reviewer 2 spawn + CICD Monitor 10 verify runs PASS. Memory +1 NEW `feedback_demo_seed_flag_disable.md` + `feedback_per_nv_permission_scope.md` reinforced 10 wire surface points (point 9 lookup discrimination + point 10 Controller body record mirror). Cleanup cumulative R+S+T5 ~720 rows wiped + DbInitializer flag persist. Stats: 31 mig · 59 tables · ~145 endpoints · **111 test** · 47 gotcha · **21 memory** (+1) · 6 skills · **0 PE + 0 demo workflow + flag persist** UAT permanent clean slate. 32 commits push `eb106f2..86d8806`. Plan B Contract V2 wire HIGH next.)

**Last updated S23 t10:** 2026-05-15 (Session 23 turn 10 — **🔧 Plan T: Disable auto re-seed demo data + final DELETE — UAT permanent clean slate**. Bro phát hiện sau Plan R+S: 4 phiếu `[DEMO]-A/B` + workflows TỰ ĐỘNG RE-SEED sau IIS recycle do DbInitializer 5 demo seed methods. Plan T fix root cause: `appsettings.json` add `DemoSeed:Disabled=true` (Dev override false) + `DbInitializer.cs` check flag → skip 5 method. Run #207 PASS deploy applied flag. T5 sqlcmd DELETE 7 rows + cascade. T6 force IIS recycle verify NO re-seed: PE=0 + V2=0 + V1=0 preserved. DemoSeed flag PROVEN active end-to-end. Cumulative Plan R+S+T: ~677 rows wiped + DbInitializer re-seed permanent disable. Stats: 31 mig · 59 tables · ~145 endpoints · 111 test · 47 gotcha · 20 memory · 6 skills · **0 PE + 0 workflow + flag persist** UAT permanent clean slate.)

**Last updated S23 t9:** 2026-05-15 (Session 23 turn 9 — **🧹 Plan S: Wipe ALL workflows — UAT clean slate hoàn toàn**. Bro chốt sau Plan R: xóa hết 4 workflows demo cũ còn lại (V2 ghim + V1 active đều seed cumulative). Backup Plan R reuse. Execute DELETE ALL ApprovalWorkflows + PurchaseEvaluationWorkflowDefinitions. Post-state: **0/0 workflow + 0 cascade Steps/Levels/Approvers**. BE smoke 5/5 endpoints 200, KHÔNG crash. Total cumulative cleanup Plan R+S: ~670 rows wiped (35 PE + 17 V2 + 4 V1 + 600 child). Hậu quả: user phải đợi admin Designer seed workflow mới. Stats: 31 mig · 59 tables · ~145 endpoints · 111 test · 47 gotcha · 20 memory. Database UAT clean slate hoàn toàn.)

**Last updated S23 t8:** 2026-05-15 (Session 23 turn 8 — **🧹 Plan R: Cleanup destructive prod database**. Bro chốt sau Plan P+Q wire OK: "xóa hết phiếu test + quy trình ko ghim". Investigator pre-flight audit ~64K confirm scope 28 PE active + 7 soft + 15 V2 unghim + 2 V1 inactive. Backup mandatory `SolutionErp_pre_cleanup_2026-05-15.bak` 18.5MB. Hard-DELETE 52 rows + ~600 cascade child (PE child + workflow Steps+Levels+Approvers). Post-cleanup: **0 PE · 2 V2 ghim · 2 V1 active**. Smoke verify 3/3 endpoints 200 — BE healthy. Stats: 31 mig · 59 tables · ~145 endpoints · 111 test · 47 gotcha · 20 memory · 6 skills. UAT clean slate. Bro test workflow fresh.)

**Last updated S23 t6:** 2026-05-15 (Session 23 turn 6 — **🎯 Plan P HOTFIX: Controller TransitionPeBody record missing 3 fields — bug ROOT CAUSE thực sự F1+F2 fail**. CICD Monitor Plan O Run #202 catch CRITICAL caveat: Controller `TransitionPeBody:267` MISSING ReturnMode + ReturnTargetUserId + SkipToFinal. Investigator audit FE confirm: FE × 2 app mirror SEND ĐÚNG 7 fields qua `api.post()` — BE drop tại Controller body record + mediator.Send line 70. Bug present 2 ngày prod từ Mig 28 deploy 2026-05-13 → F1+F2 wire fail từ FE side. Plan N + Plan O fix lookup sites nhưng controller bug block flow trước khi đến lookup. Plan P fix BE-only ~10 LOC: TransitionPeBody +3 field default null/false + mediator.Send pass 7 fields + using import WorkflowReturnMode namespace. **111/111 PASS unchanged**. Pattern reinforced: Controller body record MUST mirror Command record fields. Multi-agent ROI: Investigator avoid cross-stack fix sai + CICD Monitor catch root cause invaluable chain. Stats: **31 mig** · 59 tables · ~145 endpoints · 34 FE pages · **111 test** · 47 gotcha · 20 memory · 6 skills.)

**Last updated S23 t5:** 2026-05-15 (Session 23 turn 5 — **🎯 Plan O HOTFIX: 4 lookup sites cùng pattern per-NV cascade — Plan N chỉ catch 1/5 sites**. Bro UAT sau Plan N: Actor NV Test trong OR-of-N click "Trả lại Người chỉ định" → "Không phải lượt bạn" mặc dù đúng slot. Em main grep audit phát hiện 5 lookup sites cùng bug, Plan N chỉ catch 1. Plan O fix 4 sites còn lại: Service.cs:201 (EnsureCanRejectV2Async = bug bro), Service.cs:248 (ApplyReturnModeAsync read Allow flag), DetailFeatures.cs:72 (F3 guard), Features.cs:311 (F4 AdjustBudget guard). ApplyReturnModeAsync signature +`Guid? actorUserId` 4th param + caller TransitionAsync:94 update. 3 regression test mới `PurchaseEvaluationPerNvLookupRegressionTests`. **111/111 PASS** (+3 từ 108). Pattern reinforced 5 sites checklist + audit grep `FirstOrDefault.*Order ==`. Bug 2 (F2 chỉ đến Phan Văn Chương) defer follow-up — F2 logic đúng, verify workflow v14 DB. Stats: **31 mig** · 59 tables · ~145 endpoints · 34 FE pages · **111 test (+3)** · 47 gotcha · 20 memory · 6 skills.)

**Last updated S23 t4:** 2026-05-15 (Session 23 turn 4 — **🎯 Plan N HOTFIX: BE per-NV lookup site discrimination — bug critical UAT block**. Bro UAT screenshot phát hiện admin Designer tick 7 flag TRUE cho NV Test (UAT V2) nhưng dialog Duyệt/Trả lại KHÔNG có F1+F2+F3+F4 options. Investigator audit verify Hypothesis B: `PurchaseEvaluationFeatures.cs:765` `FirstOrDefault(Order==X)` thiếu `ApproverUserId == currentUser.UserId` discriminator. Schema Mig 29 OR-of-N: 4 row cùng Order → handler luôn lấy row đầu DB (Lê Văn Bính, Drafter only), bỏ qua admin tick per-NV. Bug PRESENT 2 ngày prod từ Mig 29 deploy 2026-05-13 — chỉ bộc lộ khi lần đầu admin tick selectively. Fix 5 LOC: thêm match `ApproverUserId == currentUser.UserId` + fallback row đầu cho admin/non-approver. Test regression `GetPurchaseEvaluationCurrentLevelOptionsTests` 2 method: 4 actor distinct flag profile + admin fallback. **108/108 PASS** (+2 từ 106). Multi-agent ROI: Investigator catch root cause 1 spawn ~80K — em main solo fix nhanh. Pattern reinforced: per-NV admin opt-in flag wire checklist **9 surface points** (thêm point 9 handler lookup site discrimination). Stats: **31 mig** · 59 tables · ~145 endpoints · 34 FE pages · **108 test (+2)** · 47 gotcha · 20 memory (1 entry reinforced) · 6 skills.)

**Last updated S23 t3:** 2026-05-15 (Session 23 turn 3 — **🎯 Plan M: Fix F1.OneLevel/OneStep edge case Bước 1 → giữ ChoDuyet + FE label phase rename**. 3 commits Plan M `c2042ef..4dd6f9c` local (chưa push, chờ Reviewer verdict). Bro UAT post-Plan L deploy: "Hiện logic cũ là khi trả lại 1 cấp hoặc chỉ định hoặc edit là trạng thái draft → thay đổi lại". Investigator audit confirm 4 mode F1.OneLevel/Assignee + F2 + F3 + F4 main path đã giữ ChoDuyet đúng (Mig 28-31 cumulative). Edge case F1.OneLevel ở Bước 1 Cấp 1 + F1.OneStep ở Bước 1 còn fallback Drafter (Phase=TraLai) — gap "draft" bro phát hiện. Plan M fix: reset (0, 1) giữ Phase=ChoDuyet + audit log "không lùi được" + SLA reset 7d (no-op effective). M3 FE label phase Phase=TraLai badge "Trả lại" → "Cần chỉnh sửa lại" (rõ end-user). M2 add 2 edge case tests **106/106 PASS** (+2 từ 104). F1.Drafter mode 4 GIỮ NGUYÊN Phase=TraLai semantic (explicit role mode). Multi-agent ROI evidence: Investigator catch fact code main path đã đúng (avoid em main spam fix sai) + Implementer Case 2+3 cookie-cutter 2 spawns + Reviewer pre-commit pending. Memory user-level update 2 entry. Stats: **31 mig** · 59 tables · **~145 endpoints** · 34 FE pages · **106 test (+2)** · 47 gotcha · 20 memory (2 entry reinforced) · 6 skills · 4 sub-agents. CHƯA push remote.)

**Last updated S23 t1:** 2026-05-14 (Session 23 turn 1 — **🎯 Plan K Mig 31 F2 refactor sang per-Approver-slot — DONE 9 commits `56868bf..0062fcb` pushed remote**. Verify result K9 + K11: Mig 31 prod TOP 1 ✅, Levels.AllowApproverSkipToFinal col added ✅, Users.AllowDrafterSkipToFinal col dropped ✅, zombie PATCH /allow-skip-final → 404 ✅, bundle hash rotated 2/2 (admin `CpI5OL8n→CRsX6cFo`, user `d064StNa→X7qb4Zl4`) ✅, 33 active users preserved ✅, AwLevelDto `allowApproverSkipToFinal` field PRESENT (13 keys, default False opt-in) ✅. CICD Monitor K9 catch CRITICAL wire gap: `AwLevelDto` admin DTO miss field — em main K2 + Reviewer K2 cùng miss audit `ApprovalWorkflowV2AdminFeatures.cs`. K10 hotfix ~15 LOC 1 file (AwLevelDto + ToDto + CreateAwLevelInput + entity init) pushed `0062fcb`. Run #195 success → K11 self-verify field present. Pattern lesson: per-NV admin opt-in flag wire **8 surface points** required (NOT 6 — admin overview DTO + Create input là 2 gap em main miss S22+5 và S23 t1). Memory `feedback_per_nv_permission_scope.md` cần add wire checklist gotcha S23 t2+.

**S23 t1 prev:** 2026-05-14 (Session 23 turn 1 — **🎯 Plan K Mig 31 F2 refactor sang per-Approver-slot — DONE 8 commits Plan K `56868bf..<latest>`**. Bro phát hiện inconsistency S22: F1+F3+F4 đều per-slot ở Designer, F2 lone wolf ở User Management (Plan D S22 wire) + bro chốt đổi semantic F2: Drafter from Nháp → Approver during ChoDuyet skip thẳng Cấp cuối. Plan K 8 chunk: pre-A slot label rename "#NV {order}" → ApproverFullName + K1 Mig 31 schema swap (drop Users + add Levels, NO BACKFILL Option A) + K2 Service ApproveV2Async +skipToFinal 8th param APPROVE STEP branch + DTO 7th Allow* field + K3 Designer 7th checkbox + banner rewrite + K5 zombie endpoint cleanup (PATCH /users/{id}/allow-skip-final + Command/Handler/DTO/UI all backout Plan D S22) + K6 Workspace × 2 app DROP Drafter checkbox + ADD Approver toggle Dialog amber warning + K7 tests 104/104 PASS regression (3 deleted Drafter F2 + 3 added Approver F2 cancel out) + K8 docs cumulative. Multi-agent ROI: 🟦 Investigator K0 pre-flight + 🟨 Implementer 4 spawns (pre-A + K1 + K3 + K5 + K7 Case 2+3) + 🟥 Reviewer K2 pre-commit catch zombie endpoint Major + 👤 Chủ trì K0-bis sqlcmd + K2 cross-stack reasoning + K6 UX flow + K8 docs. 4 prod user lose AllowDrafterSkipToFinal=true value per Option A (admin re-config qua Designer). Reviewer K2 PASS 0 critical, 2 Major + 2 Minor flagged → K5 + K8 resolved. New pattern caught: "Transient sentinel zombie" anti-pattern (K1 sentinel-false patch + chunk scope shift → endpoint NoOp swallow silent). Per-NV admin opt-in flag pattern proven **3× cumulative** (Mig 29 F1+F3 + Mig 30 F4 + Mig 31 F2) — pattern ALSO applies cho refactor existing scope, KHÔNG chỉ greenfield. Memory `feedback_per_nv_permission_scope.md` reinforced S23 t1. State final: **31 mig (+1 Mig 31)** · 59 tables · **~145 endpoints (-1 backout)** · 34 FE pages · **104 test PASS unchanged** · 47 gotcha · 20 memory · 6 skills · 4 sub-agents (Investigator 1 spawn + Implementer 4 spawn + Reviewer 1 spawn + CICD pending K9). CHƯA push remote — chờ bro confirm K9 spawn CICD Monitor verify.)
**S22 chốt cuối:** 2026-05-13 2300 (Session 22 CHỐT cuối cùng — **bro chốt directive Thứ 9 BẮT BUỘC delegate sub-agent**. 14 commits pushed remote `3d725c4..2b9788d` + (this final). CICD Monitor Run #193 PASS verified all live endpoints + Mig 30 prod + bundle hash rotated. State final: **30 mig · 104 test · 47 gotcha (#47 revised informational) · 19 memory · 6 skills · 4 sub-agents · 33 active users prod**. Retrospective S22: em main solo 6/10 task lẽ ra delegate được — vi phạm directive Thứ 9. **Forward S23+ rule:** BẮT BUỘC spawn sub-agent khi ACCEPT criteria match (Implementer Case 1/2/3/5, Investigator pre-flight, Reviewer pre-commit, CICD Monitor post-deploy). Em main solo CHỈ khi schema/UX/architecture decision + cross-stack tight coupling + bug fix reasoning chain.)
**S22 chốt v1:** 2026-05-13 2200 (Session 22 CHỐT — **bro confirm sub-agent solution OK**. 11 commits pushed remote `3d725c4..b04a11a`. CICD Monitor Run #188 PASS verified. State final: **30 mig · 104 test · 47 gotcha (+1 #47) · 19 memory · 6 skills · 4 sub-agents · 33 active users prod**. KHÔNG còn pending push. Plan G S21-S22 evidence: Trial Week 1 → Week 2 sub-agent ROI confirmed. Bro test UAT 4 flag pattern + view file PDF + Section Điều chỉnh ngân sách.)
**S22 prev:** 2026-05-13 1800 (Session 22 — Plan C + D + E done, Plan F ABORTED pre-flight fail. 5 commits local `60efeed..HEAD` chưa push. Plan D F2 toggle UI (BE+FE Admin). Plan C task 4 + 1-3 — 19 unit test mới (+5 reg #44 + 7 ReturnMode + 7 Guard). Plan E strict V2 scope List + Detail (remove UAT loose `|| ApprovalWorkflowId != null`). Plan F ABORTED — pre-flight prod sqlcmd reveal Contract entity HOÀN TOÀN V1 chưa wire V2 + 4 PE V1-only + 23 PE V1+V2 mix. Defer F sau Plan B Contract V2 wire. State: **29 mig · 103 test (+19) · 46 gotcha · 19 memory · 6 skills · 4 sub-agents (em main solo S22)**. 5 commits pending push `3d725c4..HEAD`.)
**S21 CHỐT CUỐI:** 2026-05-13 1530 (Session 21 CHỐT CUỐI — 5 turn cumulative `3a34831..c0af9e0` 12 commits pushed remote, CICD Monitor verify 2/2 run PASS. Gotcha #46 mới (Gitea API path/cache stale). 2 memory user-level mới: `feedback_ef_migration_backfill_reorder` + `feedback_per_nv_permission_scope`. 3 agent Inv/Imp/Rev cập nhật MEMORY ghi recent activity S21 t3-t5 em main solo. State final: **29 mig · 84 test · 46 gotcha · 19 memory · 6 skills · 4 sub-agents (3 seeds + 1 cicd 2-run)**. KHÔNG còn pending push. Plan G Trial Week 1 evidence: CICD spawn 2/2 PASS (green), cost ~110-120K under 150K budget, CI time 3-3.5min stable. Pending Plan C test-after bundle defer sau UAT 2-3 lần ổn.)
**S21 turn 5:** 2026-05-13 1400 (Session 21 turn 5 — **🎯 Refactor Allow* sang PER-NV (Mig 29). 4 chunk per-commit `0366946` (A BE+Mig 29) → `63234b2` (B FE Admin Designer per-Level 5 checkbox) → `5ccb2a7` (C FE eOffice mirror 2 app rename) → this Chunk D Docs. **F1+F3** 5 flag MOVED xuống `ApprovalWorkflowLevels` (per slot Approver). **F2** MOVED xuống `Users` (per-Drafter). Mig 29 4-stage: ADD 5 Levels + 1 Users + BACKFILL bulk SQL preserve admin config S21 t4 + DROP 6 workflow column. Service refactor đọc `currentLevel.Allow*` + `drafterUser.AllowDrafterSkipToFinal`. DTO `AwLevelDto +5`, `PeDetailBundle.workflowOptions → currentLevelOptions + drafterAllowSkipToFinal`. FE Admin Designer 5 checkbox per Level slot inline (drop section workflow-level). 84 test PASS. CHƯA push remote — chờ bro confirm.**)
**S21 turn 4:** 2026-05-13 1200 (Session 21 turn 4 — **🎯 F1+F2+F3 PE Workflow advanced options (Mig 28) — 5 chunk per-commit `0294693` (A schema) → `c56024b` (B BE) → `a508564` (C FE Admin) → `d27caaf` (D FE eOffice) → this (E Docs). **F1** 4 mode Trả lại admin tick stick (1 Cấp / 1 Bước / Người chỉ định / Người soạn thảo) — 3 mode đầu giữ Phase=ChoDuyet lùi pointer (peer review chain), mode Drafter giữ Phase=TraLai clear pointer (S17 backward compat). **F2** Drafter skip thẳng Cấp cuối — workflow tick + Workspace checkbox dynamic. **F3** Approver edit Section 2 (Hạng mục/NCC/Báo giá) khi workflow tick + actor match CurrentLevel.ApproverUserId + audit ghi PurchaseEvaluationChangelog. Mig 28 thêm 6 bit column lên `ApprovalWorkflows` (DEFAULT 1 cho AllowReturnToDrafter backward compat, 5 còn lại 0). BE Service extend signature 3 optional param (returnMode/returnTargetUserId/skipToFinal). Helper `EnsureEditableForDetailsAsync` mới gating Detail/Quote/Supplier CRUD theo Drafter scope OR F3 Approver scope + audit changelog Update/Delete (trước đây silent). FE Admin Designer "Cấu hình nâng cao" section 6 checkbox 3 group. FE eOffice 3 changes mirror 2 app. UAT mode skip dotnet test mỗi chunk, npm build × 2 app pass mỗi chunk. CHƯA push remote — chờ bro confirm.**)
**S21 turn 3:** 2026-05-12 2100 (Session 21 turn 3 — **🔴 BUG FIX CRITICAL "Trả về nhưng hệ thống vẫn duyệt" PE workflow (gotcha #45 mới). 3 chunk per-commit: `de00887` (BE Chunk A guard + 3 test) + `4b29d00` (FE Chunk B fix 2 app mirror) + this Chunk C Docs. Root: `PeWorkflowPanel.tsx` `isReject` payload (L64-66) thiếu nhánh TraLai → button "← Trả lại" gửi `decision: 1` (Approve) thay vì `2` (Reject) khi target=TraLai(98) → BE skip Reject branch → enter APPROVE STEP → `ApproveV2Async` UPSERT opinion "đã duyệt" + advance Cấp tiếp theo. Inconsistency phụ: dialog `isSendBack` (L247-248) cùng pattern thiếu TraLai → dialog title sai `'✓ Duyệt → Trả lại'` + KHÔNG amber warning. Severity CRITICAL — data integrity issue khó rollback (BE đã `SaveChangesAsync`). Test-before §7 BẮT BUỘC: viết test reproduce → confirm FAIL (BE đi sâu vào ApproveV2Async throw "Phiếu chưa pin workflow") → thêm BE guard early throw ConflictException khi `target ∈ {TraLai, TuChoi} && decision != Reject` → confirm PASS. 3 regression test (Throws TraLai+Approve, Throws TuChoi+Approve consistency, happy path Reject+TraLai). Tổng `dotnet test SolutionErp.slnx` 84 PASS (58 Domain + 26 Infra = +3 from 81 baseline). `npm run build` × 2 app pass. Stats: 27 mig (no change) · 59 tables · ~142 endpoints · 34 FE pages · **84 test (+3)** · **45 gotcha (+1 #45)** · 17 memory · 6 skills · 4 sub-agents seeds-only. Em main solo S21 t3 — bug fix reasoning chain cross BE/FE Implementer REFUSE per multi-agent rule (decision tree: tightly coupled BE+FE+test). CHƯA push remote — chờ bro confirm sau Chunk C wrap.**)
**S21 turn 2:** 2026-05-12 1800 (Session 21 turn 2 — **🎯 RAG Hybrid setup planning + Cách A validation deep dive. 2 commit (`1f8e9af` plan save 1223 LOC + this chốt). KHÔNG implement, plan only — defer chờ bro confirm 5 dự án future. Decision chốt: Cách A defensive (giữ blanket 120K em main + RAG retrieve) over Cách B aggressive (cắt 60-70% blanket). Industry-validated cross 4 Anthropic blog + 5 community tools (Cursor/Continue/Cline/Aider). Stack: Voyage-3-large + Qdrant + FastMCP + Streamlit dashboard. Multi-agent cost reality: 4 agents → ~520K cumulative blanket → heavy session ~560K (Cách A) vs ~700K (lazy). 3-layer pattern Phase 1-3 rollout (embeddings + BM25 + reranking, ~70% → ~92% recall). Stats: +1 memory entry (`feedback_rag_hybrid_pattern`) +1 plan file (`rag-setup-plan.md` 1500 LOC). Sub-agents vẫn 4 seeds-only, em main solo session.**)
**S21 turn 1:** 2026-05-12 0030 (Session 21 turn 1 — **🎯 Add con thứ 4 cicd-monitor (Path A — post-deploy verifier). 1 commit `f1c61c9` pushed `36e21c8..f1c61c9 main -> main`. CI skipped per path filter (3 file `.md`). Cost reality update: ~750K spawn (3 → 4 agents) · ~1.35M heavy / ~700K optimized. Stats: 4 sub-agents seeds-only · 16 memory · 27 mig · 59 tables · ~142 endpoints · 81 test · 44 gotcha · 6 skills unchanged. KHÔNG flush 3 agent MEMORY.md (chưa spawn work — em main solo). Trial Week 1 kick-off S21 turn 2+ Plan B Contract V2 wire mirror PE pattern.**)

## 🎯 Plan B Contract V2 wire — sub-agent role pre-allocated (S23+ kick off priority)

Per directive Thứ 9 BẮT BUỘC delegate. Workflow forward:

| Chunk | Owner | Justification |
|---|---|---|
| **Pre-flight audit Contract entity V1 state** | 🟦 **Investigator** | Read-only scan `Contract.cs` + `ContractDeptApproval.cs` + sqlcmd 7 V1 contract — Case "audit > 5 files" |
| **Chunk A Mig 31 schema** (Contract +ApprovalWorkflowId +CurrentApprovalLevelOrder) | 🟨 **Implementer Case 2** | Cookie-cutter mirror PE Mig 23 pattern proven 1× S17. Spec deterministic |
| **Chunk B Service ApproveV2Async branch** | 👤 **Chủ trì Solo** | Cross-stack reasoning chain — Service branch theo pin field + state machine integration |
| **Chunk C Mig 32 ContractLevelOpinions + Service UPSERT hook** | 🟨 **Implementer Case 2** | Mirror PE Mig 26 + service hook pattern (`feedback_service_hook_vs_endpoint`) |
| **Chunk D FE ContractCreatePage Workspace Select V2** | 🟨 **Implementer Case 2** | Mirror PeCreate Workspace pattern × 2 app (rule §3.9) |
| **Chunk E FE ContractDetailContent Section 5 V2** | 🟨 **Implementer Case 2** | Mirror PE Section 5 LevelOpinionsSectionV2 × 2 app |
| **Pre-commit each chunk review** | 🟥 **Reviewer** | Heavy diff + cross-stack — Smart Friend guard active |
| **Post-deploy verify Run** | 🟩 **CICD Monitor** | Bundle hash + Mig 31+32 prod + smoke endpoint mới |

Estimated: 5-6 chunk, ~700+ LOC. Em main lead Chunk B (cross-stack reasoning) + coordinate. 4 chunk delegate Implementer (Case 2 cookie-cutter mirror).

---

## 🎯 Session 21 chốt cuối — 5 turn timeline (2026-05-12 → 2026-05-13)

| Turn | Date/Time | Topic | Commits | CICD verify |
|---|---|---|---|---|
| t1 | 2026-05-12 0030 | Add cicd-monitor sub-agent (4th, Path A) | `f1c61c9` `3a34831` (2) | n/a (docs+config only) |
| t2 | 2026-05-12 1800 | RAG Hybrid setup planning Cách A | `1f8e9af` `0a3b747` (2) | n/a (docs only) |
| t3 | 2026-05-12 2100 | 🔴 Fix gotcha #45 PE button "Trả lại" mismatch + 3 regression test | `de00887` `4b29d00` `6d30ba4` (3) | bundled with t4 push |
| t4 | 2026-05-13 1200 | F1+F2+F3 PE Workflow advanced options Mig 28 (workflow-level) | `0294693` `c56024b` `a508564` `d27caaf` `eea86fd` (5) | 🟩 Run #186 PASS 3m32s |
| t5 | 2026-05-13 1400 | Refactor Allow* sang per-NV Mig 29 (UAT feedback "cấu hình cho từng người") | `0366946` `63234b2` `5ccb2a7` `c0af9e0` (4) | 🟩 Run #187 PASS 3m18s |

**Cumulative 12 commits S21 pushed remote `3a34831..c0af9e0`.** No pending push.

### Major schema evolution

- S21 t4 Mig 28 +6 column `ApprovalWorkflows` (workflow-level Allow*)
- S21 t5 Mig 29 → REFACTOR: +5 column `ApprovalWorkflowLevels` (per-Approver slot) + 1 column `Users` (per-Drafter F2) + BACKFILL bulk SQL + DROP 6 column `ApprovalWorkflows`

### Pattern reusable saved memory (2 entry mới user-level)

- `feedback_ef_migration_backfill_reorder` — EF auto drop-then-add WRONG cho data preservation, manual reorder ADD→BACKFILL SQL→DROP discipline. Cross-project pattern.
- `feedback_per_nv_permission_scope` — Multi-role workflow flag split scope theo role context (Approver per-slot Level vs Drafter per-User global), KHÔNG gắn parent workflow-level. Decision tree role → table mapping.

### Gotcha mới (gotchas.md count +1: 45 → 46)

- `#46 Gitea Actions API` — path `/actions/tasks` not `/actions/runs` (Gitea v1 naming khác GitHub), cache stale ~2 min → cross-check VPS file mtime. Discovery từ CICD Monitor Run #186 + #187.

### Plan G Trial Week 1 evidence (multi-agent ROI)

| Spawn | Run | Cost | Verdict | Catch | Notes |
|---|---|---|---|---|---|
| 🟩 CICD Monitor #1 | Run #186 (S21 t4 deploy) | ~12K | PASS | 0 fail (green) | Gitea API discovery |
| 🟩 CICD Monitor #2 | Run #187 (S21 t5 deploy) | ~110K | PASS | 0 fail (green) | Schema refactor verify all 5 stage |
| 🟦 Investigator | seeds-only | 0 | n/a | n/a | Em main solo S21 t3-t5 (cross-stack) |
| 🟨 Implementer | seeds-only | 0 | n/a | n/a | REFUSE 3× per criteria #3/#4 — correct |
| 🟥 Reviewer | seeds-only | 0 | n/a | n/a | Em main self-review build+test |

CICD Monitor ROI: 100% green = 0 catch but 100% peace of mind, automated verify thay em nhớ verify thủ công (recurring blind spot pattern S20). 2 bonus gotcha learnings saved cross-session.

### Pending S22+ (cumulative carry)

- **Plan C test-after bundle** — defer commit riêng sau UAT 2-3 lần ổn:
  - Service `ApplyReturnModeAsync` 4 mode read level.Allow* (per-NV)
  - Service skipToFinal read user.AllowDrafterSkipToFinal (per-Drafter)
  - Helper `EnsureEditableForDetailsAsync` 3 scenario read level (Drafter scope / Approver match / Approver mismatch → Forbidden)
  - Regression #44 silent 403 còn nợ (S18 — vi phạm test-before bug fix)
- **User Management F2 toggle UI** — BE column `Users.AllowDrafterSkipToFinal` sẵn, FE chỉ cần thêm 1 checkbox vào UserEdit dialog (~30 phút). Defer khi admin UAT request.
- **Plan B Contract V2 wire Mig 30+31** — chưa kick off (PE V2 + per-NV pattern đã proven, ready mirror sang Contract).
- **Plan D-F-H-I** unchanged (carry from S21 t2 HANDOFF tree).

### Audit định kỳ unchanged

- Lần gần: 2026-05-04 manual
- Lần kế: **2026-06-01** combined audit (skill + doc drift)
- Drift sau S21: Mig 27→29 (+2), gotcha 44→46 (+2), memory 16→19 (+3), test 81→84 (+3). Threshold KHÔNG kích hoạt audit sớm.

---

## TL;DR Session 21 turn 5 — Refactor Allow* sang PER-NV (Mig 29 drop Mig 28)

User feedback sau UAT S21 t4 deploy: "Cấu hình cho từng người nhé (chứ ko phải là cho toàn bộ quy trình duyệt), thêm table vào SQL luôn để cấu hình cho dễ."

→ Refactor 6 Allow* options từ workflow-level (Mig 28) sang per-NV scope:
- **F1 (4 mode Trả lại) + F3 (Edit Section 2)** = 5 flag MOVE xuống `ApprovalWorkflowLevels` (per slot Approver — cùng table với ApproverUserId).
- **F2 (AllowDrafterSkipToFinal)** MOVE xuống `Users` (per-Drafter user, admin config ở User Management page).

### Q&A clarify (2 lượt)

| Câu | User chốt |
|---|---|
| Scope "từng người" | **Per-Level**: 5 flag (4 F1 + 1 F3) gắn slot Designer. F2 per-Drafter user. |
| Mig 28 cũ xử lý sao | **Migrate bốc → per-NV bulk + drop**: copy workflow → all Levels của workflow, set TRUE cho Drafter user từng dùng workflow F2, rồi drop 6 column workflow. |

### Chunk A — BE schema + Service refactor (`0366946`)

Mig 29 `RefactorAdvancedOptionsToPerLevelAndDrafterUser` — 4-stage (EF
auto-generated drop-then-add đã REORDER manual):

1. **ADD 5 column** trên `ApprovalWorkflowLevels` (AllowReturnOneLevel/OneStep/
   ToAssignee/ToDrafter[default true]/AllowApproverEditDetails)
2. **ADD 1 column** trên `Users` (AllowDrafterSkipToFinal default false)
3. **BACKFILL bulk SQL** (preserve admin config Mig 28):
   ```sql
   -- Levels: copy workflow.Allow* → all Levels của workflow
   UPDATE l SET l.AllowReturnOneLevel = w.AllowReturnOneLevel, ...
   FROM ApprovalWorkflowLevels l
   INNER JOIN ApprovalWorkflowSteps s ON s.Id = l.ApprovalWorkflowStepId
   INNER JOIN ApprovalWorkflows w ON w.Id = s.ApprovalWorkflowId;

   -- Users: SET TRUE cho user từng Drafter PE link workflow Allow=true
   UPDATE u SET u.AllowDrafterSkipToFinal = 1
   FROM Users u WHERE EXISTS (
     SELECT 1 FROM PurchaseEvaluations pe
     INNER JOIN ApprovalWorkflows w ON w.Id = pe.ApprovalWorkflowId
     WHERE pe.DrafterUserId = u.Id AND w.AllowDrafterSkipToFinal = 1
   );
   ```
4. **DROP 6 column** workflow-level (Mig 28 cleanup)

Domain entity refactor:
- `ApprovalWorkflow.cs` — REMOVE 6 Allow* (S21 t4 Mig 28 cũ)
- `ApprovalWorkflowLevel.cs` — ADD 5 Allow* (F1 + F3)
- `User.cs` — ADD 1 Allow* (F2 AllowDrafterSkipToFinal)

Service refactor `ApplyReturnModeAsync`:
- Resolve currentLevel slot (CurrentWorkflowStepIndex + CurrentApprovalLevelOrder)
- Read 5 Allow* từ `currentLevel.AllowXxx` thay vì workflow
- Drafter mode validate AllowReturnToDrafter của currentLevel
- V1 legacy phiếu → fallback Drafter tự động

DRAFTER trình refactor:
- Permission check moved → `drafterUser.AllowDrafterSkipToFinal`
- `userManager.FindByIdAsync(actorUserId)` get Drafter user entity
- Admin bypass unchanged

Helper `EnsureEditableForDetailsAsync`:
- Read `level.AllowApproverEditDetails` thay vì workflow
- Error message rõ "Cấp Approver hiện tại (Bước X / Cấp Y)"

DTO refactor:
- `AwLevelDto +5 Allow*` (admin Designer GET per-Level)
- `AwDefinitionDto -6 Allow*` (no longer workflow-level)
- `CreateAwLevelInput +5 Allow*` (admin Designer POST per-Level)
- `ApprovalWorkflowOptionsDto` chỉ còn 5 flag (F2 separate field)
- `PurchaseEvaluationDetailBundleDto`:
  - RENAME `WorkflowOptions` → `CurrentLevelOptions`
  - ADD `DrafterAllowSkipToFinal bool`

GetPe handler populate:
- currentLevelOptions = 5 Allow* của Cấp hiện tại (null nếu V1 / no pointer)
- drafterAllowSkipToFinal = User.AllowDrafterSkipToFinal từ DrafterUserId

### Chunk B — FE Admin Designer (`63234b2`)

ApprovalWorkflowsV2Page.tsx:
- Types: `LevelDto +5 Allow*`, `DefinitionDto -6 Allow*`, `EditLevelEntry +5 Allow*`
- Helper `makeDefaultLevelEntry(order, userId)` factory với 4 false + AllowReturnToDrafter=true
- `copyFromDefinition` propagate 5 Allow* từ Levels cũ
- REMOVE section "Cấu hình nâng cao" workflow-level (amber bg 6 checkbox)
- REPLACE với info banner violet ngắn "ⓘ Cấu hình quyền duyệt riêng cho từng NV..."
- Mỗi Level entry (NV row) ADD inline panel amber-50/30 5 checkbox grid-cols-2

fe-user KHÔNG mirror (Designer admin-only).

F2 cần UX riêng ở User Management page → defer commit sau (BE field đã sẵn,
FE chỉ thêm 1 toggle UserEdit dialog khi admin UAT request).

### Chunk C — FE eOffice (`5ccb2a7`) mirror 2 app

Types:
- `ApprovalWorkflowOptions` REMOVE allowDrafterSkipToFinal (5 flag)
- `PeDetailBundle`:
  - RENAME `workflowOptions → currentLevelOptions`
  - ADD `drafterAllowSkipToFinal: boolean`

PeWorkflowPanel.tsx:
- RENAME local `wfOptions → levelOptions`, source `evaluation.currentLevelOptions`

PeDetailTabs.tsx:
- F3 approverEditMode: read `currentLevelOptions?.allowApproverEditDetails`
- F2 allowSkipToFinal: read `drafterAllowSkipToFinal` (per-user)

Backward compat:
- Backfill Mig 29 preserve admin config S21 t4
- Phiếu V1 legacy → currentLevelOptions=null → fallback chỉ Drafter mode
- drafterAllowSkipToFinal TRUE chỉ cho user từng Drafter PE link workflow Allow=true

### Chunk D — Docs (this commit)

- `docs/database/schema-diagram.md §14`: title "Mig 22-29, S17-21" + add 5 column
  Level inline comment + add 1 column User Mig 29 block
- `docs/STATUS.md` Last updated S21 t5 + 28→29 mig
- `docs/HANDOFF.md` TL;DR S21 t5 đầy đủ (file này)
- Session log riêng

### State chốt S21 turn 5

| Metric | Trước (S21 t4) | Sau (S21 t5) | Δ |
|---|---|---|---|
| DB tables | 59 | 59 | 0 |
| **Migrations** | 28 | **29** | **+1** (Mig 29 refactor per-NV) |
| Endpoints | ~143 | ~143 | 0 (same body, different schema) |
| FE pages | 34 | 34 | 0 |
| Unit tests | 84 | 84 | 0 (UAT defer test-after §7) |
| Gotchas | 45 | 45 | 0 |
| Memory | 17 | 17 | 0 |
| Skills | 6 | 6 | 0 |
| Sub-agents | 4 seeds-only | 4 seeds-only | 0 |
| **Commits S21 t5** | — | **4** | `0366946` → `63234b2` → `5ccb2a7` → this |

### Pending — defer / next session

- **User Management page F2 toggle**: thêm checkbox "Cho phép gửi PE thẳng Cấp cuối"
  vào UserEdit dialog (BE column sẵn, FE 1 toggle nhỏ — defer khi admin UAT request)
- **Test-after carry** (Plan C bundle): Service ApplyReturnModeAsync 4 mode +
  EnsureEditableForDetailsAsync 3 scenario read-from-level + skipToFinal read-from-user

---

## TL;DR Session 21 turn 4 — F1+F2+F3 PE Workflow advanced options (Mig 28)

User request 3 tính năng mới trong PE V2 Workflow:
- **F1** 4 mode Trả lại admin stick: 1 Cấp / 1 Bước / Người chỉ định / Người soạn thảo
- **F2** Drafter gửi thẳng Cấp cuối (skip mọi Bước/Cấp trung gian)
- **F3** Approver chỉnh sửa Section 2 (Hạng mục + NCC + Báo giá) khi đang duyệt

### Q&A clarify chốt scope (2 lượt AskUserQuestion)

- **F1 "1 bậc"** = cả 2 mode (admin chọn 1 Cấp HOẶC 1 Bước HOẶC cả 2 stick)
- **F1 "Người chỉ định"** = Approver pick runtime từ list NV đã ký (PE.LevelOpinions)
- **F1 behavior** = 3 mode đầu giữ Phase=ChoDuyet lùi pointer (peer review chain). Mode Drafter giữ Phase=TraLai S17 fallback.
- **F2 skip** = chỉ skip tới Level cuối (CEO) — Dropdown 2 option "Gửi tuần tự" vs "Gửi thẳng Cấp cuối"
- **F2+F3 admin enable** = cả 2 cần admin tick per workflow (audit nghiêm)
- **F3 approver perm** = mọi approver Cấp đang active (currentLevel match)
- **F3 scope** = Section 2 only (Hạng mục + NCC + Báo giá), KHÔNG đụng PE Header, KHÔNG reset workflow
- **Test** = test-after UAT default Phase 9 (skip dotnet test mỗi chunk, npm build × 2 app pass)

### Chunk A — Mig 28 + Domain (`0294693`)

`ApprovalWorkflow.cs` thêm 6 bool field:
- `AllowReturnOneLevel` / `AllowReturnOneStep` / `AllowReturnToAssignee` (default false)
- `AllowReturnToDrafter` (default **TRUE** — backward compat S17)
- `AllowDrafterSkipToFinal` / `AllowApproverEditDetails` (default false)

EF config `ApprovalWorkflowConfiguration` thêm 6 `HasDefaultValue` match Mig 28 DEFAULT.

Mig 28 `AddAdvancedOptionsToApprovalWorkflows`:
- 6 AddColumn bit NOT NULL DEFAULT 0/1
- 3-file rule complete (mig.cs + Designer.cs + Snapshot.cs)
- Apply LocalDB Dev + Design

### Chunk B — BE Service + handlers + DTOs (`c56024b`)

**Service interface + impl** `TransitionAsync` thêm 3 optional param (backward compat):
- `WorkflowReturnMode? returnMode` (enum {OneLevel=1, OneStep=2, Assignee=3, Drafter=4})
- `Guid? returnTargetUserId` (required khi mode=Assignee)
- `bool skipToFinal`

REJECT branch extend với helper `ApplyReturnModeAsync` switch 4 mode:
- OneLevel: lùi 1 Cấp cùng Step. Bước 1 Cấp 1 → fallback Drafter.
- OneStep: lùi sang Bước trước Cấp cuối. Bước 1 → fallback Drafter.
- Assignee: tìm Step+Level match `ApproverUserId == returnTargetUserId`.
- Drafter: Phase=TraLai clear pointer (S17 behavior).
- 3 mode đầu giữ ChoDuyet + reset SLA 7d.
- Admin bypass workflow.Allow* flag check.
- Non-admin → throw ConflictException nếu flag disabled.

DRAFTER trình branch extend với F2 skipToFinal:
- Workflow.AllowDrafterSkipToFinal required (non-admin)
- Set CurrentWorkflowStepIndex = Steps.Count-1 + CurrentApprovalLevelOrder = max Level
- Audit comment append "[Drafter gửi thẳng Cấp cuối]"

**Helper edit guard** `EnsureEditableForDetailsAsync` mới (PurchaseEvaluationDraftGuard class):
- Drafter scope: DangSoanThao OR TraLai
- F3 Approver scope: ChoDuyet + workflow.AllowApproverEditDetails + actor match CurrentLevel.ApproverUserId
- Admin bypass workflow flag check

**8 handler switch** sang helper mới + inject ICurrentUser khi cần:
- Detail Add/Update/Delete + Quote Upsert/Delete (5 handler — replace EnsureDraftAsync)
- Supplier Add/Update/Remove (3 handler — bonus security fix, trước đây hoàn toàn KHÔNG có phase guard!)
- Update/Delete handler trước đây silent → thêm changelog `PhaseAtChange + UserId + Summary` (append `[Approver edit khi đang duyệt]` khi phase=ChoDuyet)

**Command DTO + DTOs**:
- `TransitionPurchaseEvaluationCommand` +3 optional field
- `ApprovalWorkflowOptionsDto` NEW sub-record (6 Allow* flag)
- `PurchaseEvaluationDetailBundleDto` +WorkflowOptions field
- `AwDefinitionDto` +6 Allow* (admin Designer GET)
- `CreateAwDefinitionCommand` +6 Allow* param (admin Designer POST)

### Chunk C — FE Admin Designer (`a508564`)

`ApprovalWorkflowsV2Page.tsx` Designer modal thêm section "Cấu hình nâng cao" 3 sub-group:

1. Mode Trả lại 4 checkbox:
   - Trả về 1 Cấp trước (peer review chain trong cùng Bước)
   - Trả về 1 Bước trước (Cấp cuối Bước trước nhận lại)
   - Trả về Người chỉ định (pick runtime từ NV đã ký)
   - Trả về Người soạn thảo (default checked = backward compat S17)

2. Drafter skip: 1 checkbox "Cho phép Drafter gửi thẳng Cấp cuối"

3. Approver edit: 1 checkbox "Cho phép Approver chỉnh sửa Section 2"

Styling: container amber-50/30 border distinct với Steps section. Helper text [10px] dưới label. Headers uppercase tracking.

DTO types + state defaults từ cloneFrom (giữ config version trước) hoặc S17 fallback (chỉ AllowReturnToDrafter=true). POST body propagate 6 flag → BE Create handler set entity.

fe-user KHÔNG mirror (Designer admin-only).

### Chunk D — FE eOffice (`d27caaf`) mirror 2 app

Types `purchaseEvaluation.ts`:
- `ApprovalWorkflowOptions` type
- `WorkflowReturnMode` const-object
- `PeDetailBundle` +workflowOptions field

`PeWorkflowPanel.tsx` F1 Trả lại radio picker:
- State `returnMode` (default Drafter) + `returnTargetUserId`
- Dialog Trả lại render 1-4 radio mode enabled theo wfOptions.Allow*
- Assignee mode → submodal Select pick từ levelOpinions (NV đã ký), dedupe by userId
- Banner amber rounded dưới mô tả hành vi mode chọn
- Mutation payload +returnMode +returnTargetUserId khi isTraLaiAction

`PeDetailTabs.tsx` F2 Drafter skip:
- State `skipToFinal` + `allowSkipToFinal` từ workflowOptions
- submitForApproval mutationFn accept opts.skipToFinal
- Workspace action bar: checkbox violet "Gửi thẳng Cấp cuối (skip trung gian)" conditional
- Confirm dialog message + button label dynamic theo skipToFinal

`PeDetailTabs.tsx` F3 Approver edit Section 2:
- useAuth import + compute `approverEditMode` (phase=ChoDuyet + workflowOptions.allowApproverEditDetails + actor match)
- `itemsReadOnly = readOnly && !approverEditMode` → ItemsTab nhận
- Banner violet "ⓘ Bạn được phép chỉnh sửa..." khi approverEditMode + readOnly (Duyệt menu)
- InfoTab / NccSelectorRow / BudgetFieldRow GIỮ strict isEditablePhase (Header + Section 3, KHÔNG trong F3 scope)

### Chunk E — Docs (this commit)

- `docs/database/schema-diagram.md §14` cập nhật title "Mig 22-28, S17-21" + thêm 6 column Allow* trong Core block với inline comment F1/F2/F3
- `docs/STATUS.md` Last updated S21 t4 + count 27→28 mig + UAT defer test count unchanged 84
- `docs/HANDOFF.md` TL;DR S21 t4 đầy đủ (file này)
- `docs/changelog/sessions/2026-05-13-1200-s21-turn4-pe-workflow-advanced-options.md` session log

### State chốt S21 turn 4

| Metric | Trước (S21 t3) | Sau (S21 t4) | Δ |
|---|---|---|---|
| DB tables | 59 | 59 | 0 |
| **Migrations** | 27 | **28** | **+1** (Mig 28 6 column Allow*) |
| Endpoints | ~142 | ~143 | +1 (extend transitions body) |
| FE pages | 34 | 34 | 0 (Designer extend section) |
| **Unit tests** | 84 | **84** | 0 (UAT defer test-after §7) |
| Gotchas | 45 | 45 | 0 |
| Memory entries | 17 | 17 | 0 |
| Skills | 6 | 6 | 0 |
| Sub-agents | 4 seeds-only | 4 seeds-only | 0 |
| **Commits S21 t4** | — | **5** | (`0294693` → `c56024b` → `a508564` → `d27caaf` → this) |

### Pending — Test-after (Plan C carry)

Per `feedback_uat_skip_verify` Phase 9 default: viết test sau UAT 2-3 lần ổn.
Test scope candidate (test-after-uat commit riêng):
- Service `ApplyReturnModeAsync` 4 mode happy path (OneLevel/OneStep/Assignee/Drafter)
- Service skipToFinal happy path + AllowDrafterSkipToFinal=false → ConflictException
- `EnsureEditableForDetailsAsync` 3 scenario: Drafter scope / Approver match / Approver mismatch → Forbidden

Bundle với Plan C existing (test #44 silent 403 + test V2 ApproveV2Async + Mig 25/27 PATCH).

---

## TL;DR Session 21 turn 3 — Bug fix CRITICAL "Trả về nhưng hệ thống vẫn duyệt" (gotcha #45)

User UAT 2026-05-12 21:00 screenshot button labeled `← Trả lại` trong PE Workflow Panel (menu "Duyệt"), mô tả hành vi: nhấn vào nhưng phiếu KHÔNG về phase TraLai — ngược lại tiến qua Cấp tiếp theo. User mô tả: "Trả về nhưng hệ thống vẫn duyệt".

### Diagnose (em main solo, no agent spawn)

3 chỗ inconsistency cùng pattern trong `PeWorkflowPanel.tsx` (× 2 app fe-admin + fe-user):

| # | Location | Logic | Bug? |
|---|---|---|---|
| 1 | L205-207 button `isSendBack` | include TraLai → label `← Trả lại` ĐÚNG | ✅ no bug |
| 2 | L64-66 payload `isReject` | thiếu nhánh TraLai → gửi `decision: 1` (Approve) | 🔴 BUG ROOT |
| 3 | L247-248 dialog `isSendBack` | thiếu nhánh TraLai → dialog title fallback `'✓ Duyệt → Trả lại'` + no amber warning | 🔴 BUG phụ |

BE `PurchaseEvaluationWorkflowService.TransitionAsync`:
- L51 `if (decision == Reject)` branch → đúng cho decision=Reject.
- L97 `APPROVE STEP` branch khi decision=Approve + fromPhase=ChoDuyet → ApproveV2Async UPSERT opinion + advance Cấp.
- → FE gửi `decision=1` (do bug `isReject`) → BE đi vào nhánh APPROVE thay vì REJECT → phiếu approve mặc dù user định trả lại.

### Chunk A — BE defense-in-depth + 3 regression test (`de00887`)

**Test-before §7 BẮT BUỘC:** Viết test reproduce bug TRƯỚC fix.

```csharp
// Sau line 48 (set isAdmin/isSystem), trước REJECT branch (L51)
if ((targetPhase == PurchaseEvaluationPhase.TraLai
        || targetPhase == PurchaseEvaluationPhase.TuChoi)
    && decision != ApprovalDecision.Reject)
{
    throw new ConflictException(
        $"Transition tới {targetPhase} BẮT BUỘC decision=Reject (nhận {decision}). " +
        "Báo lỗi caller — payload mismatch giữa target phase và decision.");
}
```

Boundary protection cho mọi caller tương lai (API client / mobile / cron retry). Guard KHÔNG xoá khi FE fix — defense-in-depth.

3 test file `tests/SolutionErp.Infrastructure.Tests/Services/PurchaseEvaluationWorkflowServiceGuardTests.cs`:
- `TransitionAsync_TargetTraLai_WithApproveDecision_Throws_AndDoesNotMutateState` — reproduce bug, expect `ConflictException` "*TraLai*Reject*"
- `TransitionAsync_TargetTuChoi_WithApproveDecision_Throws_AndDoesNotMutateState` — consistency cover TuChoi
- `TransitionAsync_TargetTraLai_WithRejectDecision_SetsPhaseTraLai` — happy path control (Reject branch vẫn đúng)

+ `NoOpNotificationService` stub reusable cho future PE service tests (avoid `INotificationService` real DI complexity).

Run test → 2 FAIL (reproduce bug, BE đi sâu vào ApproveV2Async throw "Phiếu chưa pin workflow") + 1 PASS (happy path). Thêm BE guard → 3 PASS. Tổng `dotnet test SolutionErp.slnx` 84 PASS (+3 from 81 baseline).

### Chunk B — FE fix mirror 2 app (`4b29d00`)

3 chỗ × 2 app = 6 edits:

```typescript
// Chỗ 1: isReject payload (L64-66)
const isReject = target === PurchaseEvaluationPhase.TuChoi
  || (target === PurchaseEvaluationPhase.DangSoanThao
      && evaluation.phase !== PurchaseEvaluationPhase.DangSoanThao)
  || (target === PurchaseEvaluationPhase.TraLai             // ← THÊM
      && evaluation.phase !== PurchaseEvaluationPhase.TraLai)

// Chỗ 3: dialog isSendBack (L247-248)
const isSendBack = (target === PurchaseEvaluationPhase.DangSoanThao
                    || target === PurchaseEvaluationPhase.TraLai)   // ← THÊM
  && evaluation.phase !== PurchaseEvaluationPhase.DangSoanThao
  && evaluation.phase !== PurchaseEvaluationPhase.TraLai             // ← THÊM
```

Chỗ 2 (button label `isSendBack` L205-207) đã đúng từ S17, KHÔNG đụng.

Verify: `npm run build` × 2 app pass (fe-user 17.91s + fe-admin 6.71s, 0 TS6 err).

### Chunk C — Docs (this commit)

- `docs/gotchas.md` +#45 PE button label vs decision payload mismatch (~120 dòng narrative + 2 commit cross-ref + pattern reusable + phòng tránh tương lai)
- `docs/gotchas.md` checklist debug +entry 22 quick lookup
- `docs/STATUS.md` Last updated S21 t3 + count 81→84 test + 44→45 gotcha
- `docs/HANDOFF.md` TL;DR S21 t3 narrative đầy đủ (file này)
- `docs/changelog/sessions/2026-05-12-2100-s21-turn3-fix-tra-lai-bug45.md` session log mới

### Pending (carry from S21 turn 2)

Plans A-I unchanged. Plan C1 (test regression gotcha #44 silent 403 S18) vẫn còn nợ — không bundle với S21 t3 fix (scope khác, ưu tiên unblock UAT bug critical trước).

### Audit cadence

- Lần gần nhất: 2026-05-04 manual trễ 4 ngày
- Lần kế: **2026-06-01** combined audit
- Drift sau S21 t3: 44→45 gotcha (+1) + 81→84 test (+3) + 17→17 memory (no new) + 6 skills unchanged

### State chốt S21 turn 3

| Metric | Trước (S21 t2) | Sau (S21 t3) | Δ |
|---|---|---|---|
| DB tables | 59 | 59 | 0 |
| Migrations | 27 | 27 | 0 |
| Endpoints | ~142 | ~142 | 0 |
| FE pages | 34 | 34 | 0 |
| **Unit tests** | 81 | **84** | **+3** (PE guard) |
| **Gotchas** | 44 | **45** | **+1** (#45) |
| Memory entries | 17 | 17 | 0 |
| Skills | 6 | 6 | 0 |
| Sub-agents | 4 seeds-only | 4 seeds-only | 0 |
| **Commits S21 t3** | — | **3** | (`de00887` + `4b29d00` + this) |

---

## TL;DR Session 21 turn 2 — RAG Hybrid setup planning (Cách A chốt + 3-layer pattern)

User clarify 5 dự án future > 1M MD tokens → cuộc thảo luận deep ~15 turn về RAG infrastructure. Em main solo (no SOLUTION_ERP sub-agent spawn), delegate 2 lần claude-code-guide agent research Anthropic + community practice.

### Q&A deep dive 10 topics

1. RAG fundamentals + Vector DB role (Qdrant)
2. Embedding "AI nhúng" + Voyage AI cost mechanics ($0.18/M tokens)
3. Multi-project shared architecture (5 projects → single Qdrant + per-collection)
4. Audit procedure 3-tier (weekly auto + monthly deep + quarterly major)
5. UI/UX Streamlit dashboard 7 pages design
6. Cách A defensive (giữ blanket 120K) vs Cách B aggressive (cắt 60-70%)
7. Reasoning depth comparison: lazy 60% → A 90% → B 75-80%
8. Industry validation: Anthropic + Cursor + Continue + Cline + Aider all hybrid
9. Multi-agent cost reality: 8-10× multiplier, ~520K cumulative blanket 5 entities
10. 3-layer hybrid pattern (Anthropic Contextual Retrieval Sept 2024)

### Quyết định chốt — Cách A vs Cách B

**Chọn Cách A** (defensive hybrid):
- Blanket: GIỮ NGUYÊN 120K em main + RAG retrieve supplement
- Sub-agent spawn baseline: ~80-100K each (4 agents = ~400K cumulative)
- Heavy session billed: ~560K (saving -20% vs lazy 700K)
- Quality recall: ~85% (vs Cách B 75-80% do fragmentation)

**Why Cách A** (bro priority chốt):
- ✅ Em main control flow strong (state ownership direct, response fast)
- ✅ Decision quality 90% (multi-source cohesive reasoning)
- ✅ Wall-clock per task -20% (12 phút vs Cách B 16 phút)
- ✅ Risk-averse (graceful fallback blanket nếu RAG fail)
- ✅ Multi-agent leverage cache 70-90% hit common queries
- ✅ Industry-validated (Anthropic + Cursor + Continue + Cline + Aider)

### 3-layer hybrid Phase rollout (Anthropic Contextual Retrieval)

| Phase | Layers | Recall | Cost/mo |
|---|---|---|---|
| Phase 1 (Week 1-4) | Vector embedding only (Voyage-3-large) | ~70% | ~$1.50 |
| Phase 2 (Month 2) | + BM25 hybrid (bm25s free local) | ~78% | ~$1.50 |
| Phase 3 (Month 3) | + Voyage rerank-2 + Contextual prefix | ~92% | ~$4-5 |

### Stack validated cross-industry

- Voyage AI embedding (Anthropic partner, multilingual 26 lang)
- Qdrant local (Rust binary, "leading agent memory backend 2026")
- FastMCP Python (official Anthropic SDK)
- SQLite event log + Streamlit dashboard 7 pages
- Pre-commit hook re-index delta

### Multi-agent cost reality (Anthropic warn 8-10× multiplier)

```
Per entity blanket Cách A:
  Em main: ~120K
  4 sub-agents × ~100K spawn = 400K cumulative
  Total: ~520K cumulative billed (not single context window)

Heavy session 4-agent spawn:
  Lazy: ~700K effective billed
  Cách A: ~560K (-20% from multi-agent shared cache)
```

### Plan I NEW — RAG Setup Implementation (defer)

**Trigger:** Bro confirm 5 dự án path + stack + pilot choice + Voyage API key + disk cleanup 5-8GB.

**Schedule:** Dedicated session 10-14h weekend (per `feedback_drastic_refactor_scope`).

**Phase rollout:**
- Phase 1 single project pilot 4-week trial
- Phase 2-3 upgrade incremental conditional on Phase 1 success
- Cost realistic: ~$2-5/month total cho 5 projects

### Deliverables

- ✅ `docs/rag-setup-plan.md` (commit `1f8e9af` 1223 LOC + extend S21 t2 ~300 LOC = ~1500 LOC final)
- ✅ Memory `feedback_rag_hybrid_pattern.md` (NEW cross-project reusable)
- ✅ MEMORY.md index +1 entry
- ✅ Session log this chốt
- ⏭ Implementation defer chờ trigger

### Em main solo S21 turn 2 (no SOLUTION_ERP sub-agent spawn)

3 spawn này session — KHÔNG phải 4 SOLUTION_ERP sub-agents:
- claude-code-guide × 2 (generic agent for Anthropic + industry research)
- 4 SOLUTION_ERP sub-agents (Inv/Imp/Rev/CICD) vẫn seeds-only

### State chốt S21 turn 2

| Metric | Trước | Sau | Δ |
|---|---|---|---|
| DB tables | 59 | 59 | 0 |
| Migrations | 27 | 27 | 0 |
| Endpoints | ~142 | ~142 | 0 |
| FE pages | 34 | 34 | 0 |
| Unit tests | 81 | 81 | 0 |
| Gotchas | 44 | 44 | 0 |
| **Memory entries** | 16 | **17** | **+1** (RAG hybrid pattern) |
| Skills | 6 | 6 | 0 |
| Sub-agents | 4 seeds-only | 4 seeds-only | 0 |
| **Commits S21 cumulative** | 2 | **4** | **+2** |
| **Plan files** | 0 | **1** (`rag-setup-plan.md`) | **+1** |

---

## TL;DR Session 21 turn 1 — Add cicd-monitor (4th sub-agent, Path A chốt)

User chốt Path A sau pre-flight Plan G Trial Week 1 (S20 wrap đã setup 3 agents Inv/Imp/Rev): thêm sub-agent thứ 4 chuyên post-deploy verify (Gitea Actions poll + bundle hash × 2 app + sqlcmd mig prod + endpoint smoke). **Trade-off chốt:** +~150K spawn extra mỗi run, đổi lại catch deploy ship fail tự động — recurring blind spot pattern em main solo S20 quên verify ~30% push.

### Em main solo S21 turn 1 (no agent spawn)

1 turn từ S20 wrap chiều/đêm 2026-05-11 22:00 sang sáng/đêm 2026-05-12. Em main solo qua context paste + Write file. **3 agents (Inv/Imp/Rev) KHÔNG spawn**, vẫn seeds-only state. cicd-monitor mới setup file (Write), chưa spawn work.

### Deliverables (1 commit `f1c61c9`, 456 +/-23 LOC)

| File | Loại | LOC |
|---|---|---|
| `.claude/agents/cicd-monitor.md` | NEW system prompt (~7KB) | ~200 |
| `.claude/agent-memory/cicd-monitor/MEMORY.md` | NEW seed (~5KB) | ~150 |
| `.claude/agents/README.md` | UPDATE 4-agent architecture | +~80 / -23 |
| `feedback_multi_agent_setup.md` (user-level memory) | UPDATE 3 → 4 agents | +12 / -6 |

### Q&A chốt scope (Path A vs B)

- **Path A (chọn):** cicd-monitor READ tier (~150K/spawn) — catch fail tự động
- **Path B (bỏ):** Em main thêm checklist verify manual (~0 cost) — phụ thuộc memory + recurring miss
- **Decision rationale:** Recurring blind spot S20 ~30% push quên verify → tự động hóa worth +150K trade-off

### CI skipped (gotcha #41 path filter)

3 file `.md` → match `paths-ignore: '**/*.md'` → CI không trigger → prod IIS không thay đổi. Đây là **expected behavior** — agent infrastructure là local Claude Code only, KHÔNG cần deploy lên prod IIS. Khi spawn agent đọc file `.md` từ local filesystem.

### State chốt S21 turn 1

| Metric | Trước S21 | Sau S21 t1 | Δ |
|---|---|---|---|
| DB tables | 59 | 59 | 0 |
| Migrations | 27 | 27 | 0 |
| Endpoints | ~142 | ~142 | 0 |
| FE pages | 34 | 34 | 0 |
| Unit tests | 81 | 81 | 0 (no test added) |
| Gotchas | 44 | 44 | 0 |
| Memory entries | 16 | 16 | 0 (update existing entry only) |
| Skills | 6 | 6 | 0 |
| **Sub-agents** | **3 seeds-only** | **4 seeds-only** | **+1 cicd-monitor green** |
| Commits S21 | — | **1** | (`f1c61c9`) |

### Plan G (Multi-agent Trial 4-week) update post-S21 t1

- ✅ **Setup phase complete**: 3 agents S20 t12 + 1 agent S21 t1 → 4 sub-agents seeds-only ready
- ⏳ **Week 1 pending**: Plan B Contract V2 wire Mig 28+29 — kick-off Session 21 turn 2+
- ⏳ **Pass criteria updated**: Rev catch ≥ 2 wire bugs + **CI/CD Monitor catch ≥ 1 deploy ship fail** (bundle hash unchanged / mig drift) + saving ≥ 25% Case 1+2 + Max 20× quota comfortable

### Next session priority unchanged (carry from S20 wrap)

- **Plan B** (HIGH) Contract V2 wire — Trial Week 1 kick-off với 4 agents
- **Plan C** (HIGH) Test gap fill (B4 silent 403 + Mig 25/26/27 PATCH) — bundle Chunk E Plan B
- **Plan D** Hard blockers ops (UAT/SMTP/creds/backup)
- **Plan E** Phân quyền strict V2 + drop legacy V1
- **Plan F** Audit định kỳ 2026-06-01 (chưa đến — KHÔNG tự chạy)

### Cost reality update

| Metric | Trước (3 agents) | Sau (4 agents) |
|---|---|---|
| Spawn setup total | ~564K | **~750K** (+150K cicd-monitor) |
| Heavy session | ~1.2M (~6× solo) | **~1.35M (~6.5× solo)** |
| Optimized cached | ~600K (~3× solo) | **~700K (~3.5× solo)** |

Max 20× plan absorbs ~3.5× solo cost comfortable.

---

## TL;DR Session 20 WRAP (turns 1-12 chốt 2026-05-11)

User UAT live iteration liên tục — 12 turns trong 1 ngày (sáng-trưa-chiều-tối-đêm). 14 commit cumulative.

### 3 chủ đề lớn

1. **PE Detail UI restructure** (turns 1-5 + 6 + 8-10): User yêu cầu 3 polish UX core + 4 polish nhỏ.
   - Turn 1-5 wrap commit `9dee00d→f2f01f4→f8e5675`: Section reorder (Hạng mục lên #2 + auto-seed 1 row từ gói thầu) → Nested grid HangMucCard NCC expand (drop SuppliersTab dead code) → Section Ý kiến gộp đồng cấp 1 box / Step
   - Turn 6 `f568945`: Manual budget "Nhập tay" drop tên field, chỉ giữ số tiền + VND format
   - Turn 8-10: NCC palette 5-màu cycle + Winner icon ✓ đậm + hover transition + AddSupplier auto-fill master data 4 field
2. **Admin menu eOffice management** (turn 7): Mig 27 `IsVisible + DisplayLabel` cột MenuItem + PATCH `/api/menus/{key}` + NEW `MenuVisibilityPage` ~210 LOC + fe-user Layout filter !isVisible + render `displayLabel || label`. Admin sidebar luôn dùng Label gốc (Q2=b).
3. **Infrastructure** (turns 11-12):
   - Turn 11 responsive 4-tầng pattern cho laptop nhỏ → memory `feedback_responsive_laptop_breakpoint.md`
   - Turn 12 SETUP 3 sub-agents (Investigator + Implementer + Reviewer) + em main coordinator → memory `feedback_multi_agent_setup.md`

### Stats cumulative

| Metric | Trước S20 | Sau S20 | Δ |
|---|---|---|---|
| DB tables | 59 | 59 | 0 |
| Migrations | 26 | **27** | +1 (Mig 27 menu visibility) |
| Endpoints | ~141 | **~142** | +1 (PATCH /menus/{key}) |
| FE pages | 33 | **34** | +1 (MenuVisibilityPage) |
| Menu keys | ~60 | **~61** | +1 (MenuVisibility) |
| Unit tests | 81 | 81 | 0 (Phase 9 UAT defer §7) |
| Gotchas | 44 | 44 | 0 |
| Memory entries | 14 | **16** | +2 (responsive t11 + multi-agent t12) |
| Skills | 6 | 6 | 0 |
| Sub-agents | 0 | **3** | +3 (Inv + Imp + Rev seeds) |
| Commits S20 | — | **14** | (`9dee00d` → `ae1814c`) |

### Multi-agent state chốt session

3 sub-agents vừa setup turn 12 → **seeds-only state, chưa spawn work**. KHÔNG có findings để flush cross-agent learnings ở session này.

**Trial Week 1 sẽ kick off Session 21:**
- Investigator pre-flight: audit PE V2 schema patterns (Mig 22-27) + Permission flow → spec Contract V2
- Implementer Chunk A-E (Mig 28 ALTER Contract + Mig 29 ContractLevelOpinions + Service ApproveV2Async + Controller + FE mirror)
- Reviewer pre-commit verify gotcha #42 (V1/V2 dual schema branch)
- Em main: architecture decisions + scope refusals + final synthesize

### Memory entries mới capture S20

1. `feedback_responsive_laptop_breakpoint.md` (t11) — 4-tầng pattern: sidebar w-60 xl:w-72 + workspace 2-panel lg:260 xl:320 + Section padding xs/sm responsive + Card flex-wrap. Phân biệt `lg` vs `xl` breakpoint quan trọng cho laptop nhỏ.
2. `feedback_multi_agent_setup.md` (t12) — Decision gate 6-criteria. Anthropic + Cognition hybrid. Implementer ACCEPT/REFUSE strict rules. Windows MAX_PATH pitfall (drop isolation worktree). NAMGROUP s41-s43 ROI curve.

### Pending Session 21+ (cumulative carry over)

**Plan cha B (HIGH priority) — Contract V2 wire Mig 28+29:** mirror PE pattern S17-S19 + S20 turn 7. Audit-reuse memory `feedback_audit_reuse_before_clone` áp dụng — discriminator `ApplicableType.Contract=3` đã chung 80% với PE schema V2. 6-task plan:
- Task 1: Mig 28 ALTER `Contract.ApprovalWorkflowId? + CurrentApprovalLevelOrder?`
- Task 2: Mig 29 CREATE `ContractLevelOpinions` (mirror PE Mig 26 UNIQUE + FK Cascade/Restrict)
- Task 3: `ContractWorkflowService.ApproveV2Async` branch + UPSERT opinion
- Task 4: `ContractCreatePage` Workspace Select V2 (validate ApplicableType=3)
- Task 5: Pin V2 mặc định cho ContractType qua Designer (admin)
- Task 6: `ContractDetailContent` Section "Ý kiến cấp duyệt" V2 dynamic mirror S20 Chunk C

**Plan cha C (HIGH priority) — Test coverage gap fill (§7):**
- Test regression B4 silent 403 S18 (HIGH — vi phạm rule §7 test-before bug fix)
- Test V2 Service wire `ApproveV2Async` UPSERT opinion (Mig 26) + Section gộp render (S20 t1-5 Chunk C)
- Test Mig 25 PATCH `/user-selectable` endpoint
- Test PATCH `/api/menus/{key}` Mig 27 (mới)

**Plan cha D — Hard blockers ops (chờ user/ops):** UAT thật 1 tuần / SMTP / Rotate creds / SQL backup schedule / win-acme fix / remove `.huypham.vn` binding

**Plan cha E — Phân quyền strict V2 + drop legacy V1:**
- List/Inbox/Detail filter actor scope (V2 đã đúng — `ResolveV2InboxIdsAsync`)
- Drop tables V1 sau UAT confirm: WorkflowDefinitions/Steps/Approvers + column RejectedAtStepIndex/RejectedFromPhase
- Mig 30 drop Mig 15 PurchaseEvaluationDepartmentOpinions cleanup

**Plan cha F — Audit định kỳ 2026-06-01:** skill stale (`ef-core-migration` "21" → 27 / `dependency-audit-erp` 41 → 44) + `schema-diagram` §16-21 + memory consolidate xem có duplicate

**Plan cha G NEW — Multi-agent trial 4 tuần (Week 1-4):** evaluate ROI keep/tune/archive 3 sub-agents

### Audit cadence

- Lần gần nhất: 2026-05-04 manual trễ 4 ngày
- Lần kế: **2026-06-01** combined audit
- Drift sau S20: Mig 27 + 1 menu key + +2 memory entries + 3 sub-agents NEW + (no gotcha new). `ef-core-migration` skill "21 migration" stale → thực 27 sau S20 t12

## TL;DR Session 20 turn 7 — Admin Ẩn/Hiện + Đổi tên menu eOffice (Mig 27)

User UAT live yêu cầu thêm tính năng admin quản lý menu eOffice (fe-user) — Ẩn/Hiện + Đổi tên hiển thị. Hỏi xác nhận "chưa có?" → đúng, chưa có. User clarify **Q2=b "edit hiển thị bên ngoài, chỉ của eOffice thôi"** → admin sidebar luôn dùng Label gốc, DisplayLabel CHỈ áp render fe-user.

### Q&A chốt scope

- Q1=**a** Global (không per-role — permission matrix đã handle)
- Q2=**b** DisplayLabel chỉ fe-user, admin sidebar giữ Label gốc
- Q3=**a** Giữ USER_HIDDEN_KEYS hardcode + tầng IsVisible dynamic combine
- Q4=ok UAT iteration skip test, vẫn npm build mỗi chunk

### Chunk A (`2ea2d27`) — Schema + Migration 27

Domain MenuItem +IsVisible bool=true +DisplayLabel string?(200). EF config HasDefaultValue(true) + HasMaxLength(200). Migration 27 AddVisibilityAndDisplayLabelToMenuItems (2 AddColumn) — 3-file rule. Apply LocalDB Dev + Design qua --connection override.

### Chunk B (`ef394f8`) — BE API

DTO MenuNodeDto + MenuItemDto +isVisible +displayLabel. GetMyMenuTreeQueryHandler pass through (KHÔNG filter server-side — 2 FE app tự quyết render). NEW UpdateMenuItemCommand + Validator (Key required + DisplayLabel max 200) + Handler (whitespace → null). MenusController +PATCH /api/menus/{key} [Authorize Policy="Permissions.Update"].

### Chunk C (`059bfcb`) — FE Admin MenuVisibilityPage

Domain MenuKeys +MenuVisibility. DbInitializer +leaf System/MenuVisibility (Eye, Order=94). FE Admin: types/menu.ts mirror, lib/menuKeys.ts +const, Layout resolver +/system/menu-visibility, App.tsx +Route. NEW pages/system/MenuVisibilityPage.tsx ~210 LOC:
- PageHeader + description nhắc admin sidebar dùng Tên gốc
- 4 StatCard (Tổng / Hiển thị / Đã ẩn / Đã đổi tên)
- Search box (key | label | displayLabel)
- Table 5 cột: Key mono + parentKey ↳ / Tên gốc / Input "Tên hiển thị" inline (placeholder "Mặc định: {label}") / Toggle button emerald-Eye / amber-EyeOff / Lưu (khi dirty) + Khôi phục (khi custom)
- onSuccess invalidate `['menus','all']` + `['my-menu']` → live update

### Chunk D (`1ed6530`) — fe-user Layout filter + render

types/menu.ts mirror. Layout.tsx:
```tsx
function filterForUser(nodes: MenuNode[]) {
  // 2 tầng: hardcode USER_HIDDEN_KEYS + dynamic !isVisible
  return nodes.filter(n => !USER_HIDDEN_KEYS.has(n.key) && n.isVisible !== false)
              .map(n => ({ ...n, children: filterForUser(n.children) }))
}
function effectiveLabel(n) { return (n.displayLabel?.trim()) || n.label }
```
Replace 3 callsite `{node.label}` → `{effectiveLabel(node)}`. **fe-admin Layout KHÔNG đụng** — render Label gốc + show hết menu (Q2=b).

### Pending S21+ (cumulative carry over)

1. Test V2 Service wire + Section gộp + B4 silent 403 + Mig 25 PATCH user-selectable
2. **Contract V2 wire (Mig 28+29 mirror PE)** — biggest pending
3. Phân quyền strict V2 + drop legacy V1 + Mig 15 cleanup
4. Test PATCH /api/menus/{key} validate (Mig 27)
5. Skill `permission-matrix` thêm section "menu visibility" — defer cron audit 2026-06-01
6. UX verify trong UAT: admin ẩn menu cha → children có ẩn theo không (FE filter chỉ check `!n.isVisible` per-node)

### Hard blockers ops (carry)

UAT thật / SMTP / Rotate creds / SQL backup schedule / win-acme fix / remove `.huypham.vn`

## TL;DR Session 20 — PE Detail UI restructure 3 yêu cầu UX user (previous)

Note: TL;DR Session 20 PE Detail UI giữ nguyên dưới (rule §6.5 KEEP narrative). Session 20 turn 7 này thêm trên cùng (admin menu visibility — chủ đề khác).

 (4 chunk `9dee00d`→`2bba851`→`f2f01f4`→Docs). FE-only restructure (1 hook BE nhẹ auto-seed Detail). Q1=a giữ Section "Chọn NCC TP" riêng / Q2=a NCC shared + 1 hạng mục demo / Q3=a chỉ hiện NV đã ký / Q4 public luôn (skip dotnet test, vẫn npm build × 2 app mỗi chunk vì rename/remove function). Chunk A: BE CreatePE handler thêm 1 PurchaseEvaluationDetail mặc định (NoiDung=TenGoiThau, ThanhTienNganSach=Budget.TongNganSach||BudgetManualAmount||0) + FE reorder section Hạng mục lên #2. Chunk B: ItemsTab restructure list HangMucCard 1 card / hạng mục với expand panel chứa NCC inline table 8 cột (NCC/Liên hệ/Điều khoản TT/File báo giá/ĐG chưa VAT/ĐG có VAT/Thành tiền/Action). Click cell quote → QuoteDialog reuse. Drop SuppliersTab function ~134 LOC dead code, giữ 2 dialog + SupplierAttachmentsCell. Section 4 NCC tham gia gộp vào Section 2 → 4 section final (Thông tin/Hạng mục nested/Chọn NCC TP thắng thầu/Ý kiến). Chunk C: LevelOpinionsSectionV2 forEach step → 1 StepOpinionsBox (replace grid-cols-2 N approvers). Header "Bước N — Tên" + dept badge + "X/Y đã duyệt" counter. Body filter signed opinions sort levelOrder asc + signedAt asc → StepOpinionEntry per signed (tên + Cấp badge + admin override badge + timestamp + comment). NV chưa duyệt KHÔNG hiển thị. KHÔNG đụng Mig 26 schema. Drop LevelOpinionBox function. 81 test pass unchanged (UAT defer).**)

## TL;DR Session 20 — PE Detail UI restructure 3 yêu cầu user UX

User UAT live feedback: "Logic khá OK rồi, điều chỉnh giao diện chỗ Duyệt NCC 1 tý". 3 yêu cầu cụ thể:

1. Hạng mục đưa lên phía trên + auto-tạo 1 hạng mục từ gói thầu (tên = TenGoiThau, giá trị = ngân sách)
2. NCC expand dưới hạng mục (tầng 1 = hạng mục, tầng 2 = NCC, thông tin nhập trên grid)
3. Section Ý kiến: gộp comment đồng cấp cùng Phòng → 1 ô / bước (chỉ hiện NV đã duyệt)

### Q&A clarify trước code (chốt scope)

- **Q1=a**: Giữ Section "Chọn NCC TP thắng thầu" riêng (rõ UX, không gộp dropdown winner vào nested grid)
- **Q2=a**: NCC shared cross-hạng mục (như schema PE.Suppliers hiện tại) — "nhưng hiện chỉ cần 1 hạng mục trước tiên" → đơn giản scope Chunk B
- **Q3=a**: CHỈ hiển thị NV đã ký (KHÔNG show placeholder "— chưa duyệt")
- **Q4 public luôn demo**: Phase 9 UAT iteration skip `dotnet test` mỗi chunk, vẫn chạy `npm run build` × 2 app mỗi chunk (rule UAT skip verify exception cho rename/remove function — đã catch TS6133 SuppliersTab + SupplierAttachmentsCell)

### Chunk A (`9dee00d`) — BE auto-seed Hạng mục + FE reorder section

**BE — `PurchaseEvaluationFeatures.cs` `CreatePurchaseEvaluationCommandHandler`:**

```csharp
var defaultBudgetValue = linkedBudgetTotal ?? request.BudgetManualAmount ?? 0m;
var defaultDetail = new PurchaseEvaluationDetail
{
    PurchaseEvaluationId = entity.Id,
    GroupCode = "01",
    GroupName = "Hạng mục chính",
    NoiDung = request.TenGoiThau,
    DonViTinh = "gói",
    KhoiLuongNganSach = 1m,
    KhoiLuongThiCong = 1m,
    DonGiaNganSach = defaultBudgetValue,
    ThanhTienNganSach = defaultBudgetValue,
    Order = 1,
};
db.PurchaseEvaluationDetails.Add(defaultDetail);
// + Changelog Insert audit
```

`linkedBudgetTotal` mới: nếu PE link Budget, fetch `Budget.TongNganSach` (computed sum BudgetDetails). Nếu không link, fall back `BudgetManualAmount`. Nếu cả 2 null → 0.

**FE — Reorder section** (mirror fe-admin + fe-user, Chunk A intermediate state):
1.Thông tin / **2.Hạng mục (lên #2)** / 3.Chọn NCC / 4.NCC tham gia / 5.Ý kiến.

Verify: `dotnet build SolutionErp.slnx` 0 warn / 0 err.

### Chunk B (`2bba851`) — Nested grid Hạng mục → NCC expand

Restructure `ItemsTab` thành list `HangMucCard` (1 card / 1 hạng mục, expanded=true mặc định cho 1 hạng mục demo).

**HangMucCard structure:**

```
┌──────────────────────────────────────────┐
│ ▼ 01 · Tên hạng mục          KL ĐG TT NS │ ← Header row
│ ──────────────────────────────────────── │
│ NCC tham gia (3)  [+ Thêm NCC]           │ ← Sub-header
│ ┌──────────────────────────────────────┐ │
│ │ NCC │ Liên hệ │ ĐK │ File │ giá ... │ │ ← Inline NCC table
│ ├──────────────────────────────────────┤ │
│ │ NCC X │ ... │ ... │ ... │ ... │     │ │
│ └──────────────────────────────────────┘ │
└──────────────────────────────────────────┘
```

Header card: GroupCode + NoiDung + 3 stat (KL/ĐG ngân sách/TT ngân sách) + NS link Δ (nếu có Budget link) + Pencil/Trash actions + ▼/▶ toggle expand. Expand body: NCC inline table 8 cột (NCC / Liên hệ / Điều khoản TT / **File báo giá** / ĐG chưa VAT / ĐG có VAT / Thành tiền / Action).

**Tương tác:**
- Click cell quote (chưa VAT / có VAT / Thành tiền) → mở `QuoteDialog` cũ (reuse)
- `+ Thêm NCC` button trong expand panel → `AddSupplierDialog` cũ (reuse)
- `✏` icon mỗi NCC row → `EditSupplierDialog` cũ
- `✓` icon → `setWinner` mutation, row + cell ăn theo màu emerald
- `🗑` icon disabled khi NCC là winner hoặc đã có quote (giữ logic cũ)
- `SupplierAttachmentsCell` nhúng vào cell "File báo giá" — full CRUD upload/download/delete file

**Drop dead code:**
- Function `SuppliersTab` xóa hoàn toàn (~134 LOC) — replace bằng `HangMucCard` expand panel
- Bỏ Section 4 "NCC tham gia" cũ trong main render PeDetailTabs (gộp vào Section 2)

**Section layout cuối** (4 section):
1. Thông tin gói thầu
2. Hạng mục + Báo giá NCC (nested expand)
3. Chọn NCC / TP thắng thầu
4. Ý kiến cấp duyệt

Verify: `npm run build` × 2 app pass (sau khi catch TS6133 SuppliersTab unused → drop + SupplierAttachmentsCell unused → restore vào cột "File báo giá").

### Chunk C (`f2f01f4`) — Section Ý kiến gộp đồng cấp cùng Phòng

**FE-only mirror 2 app**. KHÔNG đụng Mig 26 schema (vẫn UPSERT 1 row / Level trong `PurchaseEvaluationLevelOpinions` qua `ApproveV2Async` Service). Chỉ thay đổi render layer.

**Trước (S19 LevelOpinionsSectionV2):**
```
forEach step:
  div.grid-cols-2:
    forEach level:
      forEach approver:
        <LevelOpinionBox /> (1 box / NV)
          - Cấp N — Tên NV
          - "Đã duyệt" emerald badge or "— chưa duyệt" italic gray
          - comment text
          - admin override badge nếu signedBy !== approver
```

**Sau (Chunk C):**
```
forEach step:
  <StepOpinionsBox stepOrder stepName departmentName totalApprovers opinions={stepOpinions} />
    - Header: "Bước N — Tên" + dept badge emerald + "X/Y đã duyệt" counter
    - Body:
      - empty → "— Chưa có ý kiến duyệt." italic gray
      - else → list <StepOpinionEntry opinion /> per signed opinion
              (sort levelOrder asc, signedAt asc)
```

`StepOpinionEntry`:
- Header trái: ApproverFullName + "Cấp N" badge slate + admin override badge amber nếu có
- Header phải: emerald rounded-full timestamp "✓ DD/MM/YYYY HH:mm"
- Body: comment text whitespace-pre-wrap

NV chưa duyệt KHÔNG hiển thị (Q3=a) — chỉ 1 box / Step thay vì N box / NV như cũ.

**Drop dead code:**
- Function `LevelOpinionBox` xóa (~50 LOC) — replace bằng `StepOpinionsBox` + `StepOpinionEntry`

Verify: `npm run build` × 2 app pass.

### Pending Session 21+ (carry over từ HANDOFF S19 + còn nguyên)

1. **Test V2 Service wire** (Chunk B Service hook S19 + Section gộp Chunk C S20) — defer chờ UAT user confirm + có sample data Production. Domain test ApproveV2 + UPSERT opinion match logic + Admin override match firstLevel + comment empty placeholder + render gộp Step.
2. **Test regression B4 silent 403 S18** (HIGH §7 priority — vi phạm rule "test-before bug fix") — per-action `[Authorize(Policy=...)]` ApprovalWorkflowsV2Controller.
3. **Test Mig 25 PATCH `/user-selectable`** endpoint (admin scope hẹp, MED).
4. **🎯 Contract V2 wire (Mig 27/28 mirror PE pattern)** — biggest pending Plan. Audit-reuse memory áp dụng:
   - Mig 27: `Contract.ApprovalWorkflowId` Guid? + `CurrentApprovalLevelOrder` int?
   - Mig 28: `ContractLevelOpinions` mirror PE Mig 26 (UNIQUE composite, FK Cascade/Restrict)
   - `ContractWorkflowService.ApproveV2Async` mirror PE branch
   - `ContractCreatePage` Workspace Select V2 (validate ApplicableType=Contract=3)
   - Pin V2 mặc định cho ContractType (admin Designer)
   - `ContractDetailContent` Section "Ý kiến cấp duyệt" V2 dynamic (mirror S20 Chunk C — 1 box / Step)
5. **Phân quyền strict V2** — vẫn loose UAT (mọi authenticated user thấy mọi phiếu V2). Sau confirm flow:
   - List = Drafter + approver any-Step + Admin
   - Inbox = chỉ approver Cấp hiện tại (V2 đã đúng — `ResolveV2InboxIdsAsync`)
   - Detail = same as List
   - Cũng giải quyết được bug "/inbox loose trả phiếu Nháp" → sau khi strict, B1 FE filter S18 có thể relax
6. **Drop legacy V1 cleanup** sau khi không còn phiếu pin `WorkflowDefinitionId`:
   - Drop tables `WorkflowDefinitions` + `WorkflowSteps` + `WorkflowStepApprovers` + PE versions
   - Mig 29+ cleanup drop column `RejectedAtStepIndex` + `RejectedFromPhase` deprecated S17
   - Drop `ApproveV1LegacyAsync` branch trong Service
7. **Drop Mig 15 cho V2 phiếu** sau UAT confirm — Mig 30 cleanup drop bảng `PurchaseEvaluationDepartmentOpinions` + entity. Hoặc giữ cả 2 backward compat (Q3 user chốt phiếu MỚI dùng V2, V1 cũ giữ legacy không migrate).
8. **schema-diagram §16 PE Level Opinions V2** + §17-21 Mig 18-21 — defer cron audit 2026-06-01.
9. **Skill `ef-core-migration`** frontmatter "21 migration" stale (thực 26) — defer cron audit 2026-06-01.

### Hard blockers ops (carry over từ Session 19)

- UAT thật 1 tuần với 2-3 user (Drafter/CCM/BOD)
- SMTP config → Email outbox (BLOCKED chờ user cấp host/user/pass)
- Rotate creds (admin + 30 demo + SA + vrapp + JWT secret + Gitea runner token)
- Schedule SQL backup daily — `scripts/backup-sql.ps1` chưa schedule Task Scheduler
- Remove binding cũ `.huypham.vn` sau verify stable
- win-acme scheduled task "unhealthy" — auto-renew fix trước 2026-06-18

### Audit định kỳ

- Lần gần nhất: 2026-05-04 (manual trễ 4 ngày) — log `docs/changelog/skill-audit-2026-05.md`
- Lần kế: **2026-06-01** combined audit (skill + doc drift). Drift hiện tại unchanged từ S19 (S20 không thêm migration / gotcha mới):
  - `ef-core-migration` "21 migration" stale (thực 26)
  - `dependency-audit-erp` count gotcha 41 stale (thực 44)
  - `schema-diagram` §16 PE Level Opinions V2 + §17-21 Mig 18-21 pending
- Cron Claude SDK KHÔNG fit monthly (auto-expire 7d, memory `feedback_cron_monthly_limitation`) — manual trigger khi đến ngày hoặc user nói "audit MD" / "kiểm tra docs"

## TL;DR Session 19 — PE Section 5 V2 dynamic theo Workflow + Mig 26

User feedback Section 5 hiện CỨNG 4 box (Mig 15 PheDuyet/CCM/MuaHàng/SmPm từ Phase 8) → cần ĐỘNG theo Workflow V2 đã pin với phiếu. Spec rõ: forEach Step (Phòng) → forEach Level (Cấp) → forEach NV → 1 OpinionBox với ý kiến + tên người. "Bước 1 phòng A có 2 NV → 2 box ngang hàng".

User chốt 5 câu Q&A trước code (capture trong session log):
- Q1=**1B**: Comment khi NV nhấn Duyệt trong Workflow Panel auto sync sang OpinionBox của NV đó (Section 5 read-only summary). KHÔNG có form input rời.
- Q2=**2A+Admin**: Chỉ NV chính chủ duyệt được. Admin override → lưu SignedByUserId=Admin.Id, FE banner "Admin <name> duyệt thay" khi SignedByUserId !== Level.ApproverUserId.
- Q3=**V2 hết**: Phiếu V1 legacy → fallback render Mig 15 4 box CỨNG readOnly cho data history (KHÔNG drop Mig 15 — giữ data cũ).
- Q4=**4C + bonus**: Phase=DaDuyet/TuChoi → khoá hoàn toàn. Admin có quyền duyệt thay. Comment empty/whitespace → ghi "(duyệt — không ý kiến)" placeholder.
- Q5=**5A**: Layout group theo Step (header "Bước N — Phòng X" badge emerald + hint số người duyệt) + grid-cols-2 cho N approvers (wrap nếu N>2).

### Polish 3 button Hành động (873e7a1) — đầu session

Session 18 turn cuối user đã review screenshot "Hành động: Trả lại / Hủy/Từ chối / Duyệt → Chờ CCM" — yêu cầu rút gọn label + 3 màu khác nhau + bold. PeWorkflowPanel.tsx (mirror 2 app):
- Label: "← Trả lại (về Drafter sửa)" → **"← Trả lại"** | "✗ Hủy / Từ chối" → **"✗ Từ chối"** | "✓ Duyệt → Chờ CCM" → **"✓ Duyệt"**
- Phase đích vẫn hiện qua tooltip title khi hover
- 3 màu: Duyệt = emerald (positive) · Trả lại = amber (request changes) · Từ chối = red (terminal)
- font-medium → font-bold

### Chunk A (`77a3058`) — Domain entity + EF + Mig 26

`PurchaseEvaluationLevelOpinion : AuditableEntity`:
- (PEId, ApprovalWorkflowLevelId) UNIQUE composite
- Comment nvarchar(2000)
- SignedAt datetime2 (luôn có khi UPSERT)
- SignedByUserId Guid (NV chính chủ HOẶC Admin override)
- SignedByFullName nvarchar(200) — denorm tránh user xóa/đổi tên

EF: FK Cascade Pe + Restrict Level. SignedByUserId KHÔNG nav (denorm only).
Migration 26 `AddPeLevelOpinionsForV2`: 1 CREATE TABLE + 2 FK + 2 index. 3-file rule commit đủ. Apply LocalDB SolutionErp_Dev OK (Mig 25 + 26 catchup).

Verify: dotnet build pass + dotnet test 81 pass.

### Chunk B (`90baa8e`) — Service V2 hook + DTO + GET include

Service `ApproveV2Async` sau khi log approval → UPSERT row LevelOpinion cho Cấp hiện tại:
```csharp
var matchingLevel = pendingLevelGroup
    .FirstOrDefault(l => actorUserId.HasValue && l.ApproverUserId == actorUserId.Value)
    ?? pendingLevelGroup.First();  // Admin override fallback first
var actorFullName = await ResolveActorFullNameAsync(actorUserId, isSystem, ct);
var existingOpinion = await db.PurchaseEvaluationLevelOpinions
    .FirstOrDefaultAsync(o => o.PurchaseEvaluationId == evaluation.Id
        && o.ApprovalWorkflowLevelId == matchingLevel.Id, ct);
var normalizedComment = string.IsNullOrWhiteSpace(comment)
    ? "(duyệt — không ý kiến)"
    : comment.Trim();
// UPSERT: if existing → update; else → Add new
```

Reject (Trả lại / Từ chối) KHÔNG sync. Multi-NV cùng Cấp OR-of-N: match level theo ApproverUserId (NV chính chủ). Admin = fallback first; FE banner "Admin duyệt thay".

Helper `ResolveActorFullNameAsync` lookup denorm SignedByFullName từ Users (fallback "(System)" / "(unknown)").

DTO `PurchaseEvaluationLevelOpinionDto` 15 fields:
- LevelId, StepOrder, StepName, StepDepartmentId, StepDepartmentName
- LevelOrder, LevelName, ApproverUserId, ApproverFullName
- Comment, SignedAt, SignedByUserId, SignedByFullName

GET handler Include LevelOpinions + `BuildLevelOpinionsAsync` JOIN ApprovalWorkflows.Steps.Levels + Departments + Users → denorm DTO. Empty list cho phiếu V1 / V2 chưa có cấp duyệt → FE fallback message.

Verify: dotnet build pass + dotnet test 81 pass.

### Chunk C (`6e913b3`) — FE Section 5 V2 dynamic mirror 2 app

Type `PeLevelOpinion` (15 field) + `PeDetailBundle.levelOpinions[]`.

Section 5 PeDetailTabs conditional:
```tsx
{evaluation.approvalWorkflowId
  ? <LevelOpinionsSectionV2 ev={evaluation} />
  : <DepartmentOpinionsSection ev={evaluation} readOnly={opinionsReadOnly} />}
```

`LevelOpinionsSectionV2`:
- Empty state khi `flow null` / `0 steps` → message "Workflow chưa cấu hình hoặc chưa có cấp duyệt nào"
- forEach `step` → header "Bước N — <step.name>" + dept badge emerald + hint "(N người duyệt)" nếu totalApprovers > 1
- Body grid-cols-2 cho `step.levels.flatMap(level => level.approvers.map(approver => <LevelOpinionBox/>))`
- Lookup opinion theo (stepOrder, levelOrder, approverUserId) match levelOpinions[]

`LevelOpinionBox` read-only:
- Title "Cấp N — <ApproverFullName>"
- Badge amber "⚠ Admin <SignedByFullName> duyệt thay" khi `signedByUserId !== approverUserId`
- Badge emerald "✓ Đã duyệt" khi opinion tồn tại
- Empty: "— chưa duyệt" italic gray
- Footer: timestamp signedAt format vi-VN

Workspace mode hint amber giữ "Ý kiến + chữ ký auto đồng bộ khi NV duyệt phiếu — vào menu Duyệt để ký."

Mirror fe-admin + fe-user (rule §3.9).

Verify: npm run build × 2 pass · 0 TS error.

### Stats Δ Session 19

| | Trước S19 | Sau S19 |
|---|---:|---:|
| Migrations | 25 | **26** (+1 Mig 26) |
| DB tables | 58 | **59** (+1 PeLevelOpinions) |
| API endpoints | ~141 | ~141 (no new — UPSERT auto qua Service hook) |
| FE pages | 33 | 33 (modify existing only) |
| Test pass | 81 | 81 (no change — UAT defer test §7) |
| Gotchas | 44 | 44 |
| Memory entries | 14 | 14 |
| Skills | 6 | 6 |
| Commits | (after S18) | **+4** (873e7a1 + 77a3058 + 90baa8e + 6e913b3 + Chunk D Docs) |

## ⚠️ Điều quan trọng cho Session 20+

1. **Test V2 Service wire mới (Chunk B Service hook)** — defer khi UAT user UAT confirm + có sample data Production. Domain test ApproveV2 + UPSERT opinion match logic + Admin override match firstLevel + comment empty placeholder.

2. **Drop Mig 15 cho V2 phiếu (cleanup sau UAT confirm)** — sau khi không còn phiếu V2 dùng `PurchaseEvaluationDepartmentOpinions` (tất cả phiếu V2 chỉ dùng Mig 26 LevelOpinions). Mig 27 cleanup drop bảng + entity. Phiếu V1 legacy giữ Mig 15. Hoặc giữ cả 2 để backward compat.

3. **Migrate phiếu V1 cũ sang V2 (data migration)** — admin tool chuyển `ApprovalWorkflowId` từ null → V2 workflow phù hợp + clear `WorkflowDefinitionId`. Hiện chưa làm (Q3 user nói chuyển V2 hết = phiếu MỚI dùng V2, phiếu V1 cũ giữ legacy không migrate — đơn giản hơn).

4. **Contract V2 wire (Mig 27 hoặc 28) + Section 5 dynamic Contract** — mirror PE Mig 26 pattern: thêm `Contract.ApprovalWorkflowId` + `CurrentApprovalLevelOrder` (Mig 27) + `ContractLevelOpinions` (Mig 28) + Service `ApproveV2Async` mirror PE + ContractDetailContent Section 5 V2. Audit-reuse pattern memory `feedback_audit_reuse_before_clone` áp dụng.

5. **Phân quyền strict V2** — vẫn loose UAT. Sau confirm V2 flow (S19 Section 5 + S18 polish OK):
   - List = Drafter + approver any-Step + Admin
   - Inbox = chỉ approver Cấp hiện tại (V2 đã đúng)
   - Detail = same as List

6. **schema-diagram §16 PE Level Opinions V2** — thêm khi Chunk D update. Mig 22-25 V2 schema vẫn defer cron audit 2026-06-01.

7. **Skill `ef-core-migration` frontmatter "21 migration" stale** (thực 26). Defer cron audit 2026-06-01.

---

## TL;DR Session 18 — PE V2 polish + Clone B + 4 bug fix UAT

User UAT live tiếp Session 17, 7 batch nhỏ + 1 feature lớn (Mig 25). Áp memory `feedback_uat_skip_verify` (skip dotnet test mỗi chunk, push ngay) + lesson `0ae3fe2`: rename/remove → BẮT BUỘC `npm run build`.

### B1 (`aaa1c6c`) — Pe Duyệt filter cứng "Đã gửi duyệt"

User: "Duyệt bỏ cái trạng thái đi, chỉ load những trạng thái 'Đã gửi duyệt' là đc."

- Bỏ dropdown "Tất cả trạng thái" khỏi UI khi `pendingMe=true`, thay bằng hint amber "Lọc cố định: Đã gửi duyệt (phiếu đang chờ duyệt)"
- Filter cứng client-side: `getPeDisplayStatus(p.phase) === DaGuiDuyet` — loại Nháp/Trả lại/Đã duyệt/Từ chối
- Header count dùng `rows.length` khi `pendingMe` (inbox không paged)
- Workaround BE `/inbox` loose UAT có thể trả phiếu Nháp (phân quyền strict V2 pending Session 19+)
- Mirror fe-admin + fe-user `PurchaseEvaluationsListPage.tsx`

### B2 (`917446d`) — HistoryTab filter Trả lại / Gửi duyệt lại

User: "Lịch sử thay đổi: chỉ bắt các dòng thay đổi khi trả lại và gửi duyệt lại thôi nhé, không cần bắt trạng thái duyệt và các thay đổi trước khi trả lại."

- FE filter trong `PeDetailTabs.HistoryTab`, BE giữ audit data đầy đủ (reversible nếu user đổi ý / cần audit trail compliance)
- Logic giữ:
  - Workflow transition về TraLai (`phaseAtChange === 98`)
  - Workflow transition từ TraLai (summary chứa `"TraLai →"`)
  - Mọi thay đổi nội dung (Header/Detail/Supplier/Quote/Attachment) khi `phaseAtChange === 98`
- Bỏ: workflow Approve cùng cấp (Cấp 1→2→DaDuyet), sửa khi phase=Nháp/ChoDuyet ban đầu
- Empty state: "Chưa có lịch sử trả lại / gửi duyệt lại"

### B3 (`937eb24`) — Clone V2 cho B (DuyetNccPhuongAn)

User: "Quy trình chọn thầu phụ - NCC → Duyệt NCC đúng. Plan kế hoạch clone toàn bộ updates sang Duyệt NCC và Giải pháp."

Audit reuse trước thay vì duplicate. Phát hiện 80% đã chung:
- Schema V2 (Mig 22-24) qua `ApplicableType` enum
- BE Service `ApproveV2Async` không hardcode type
- App CQRS / API `/approval-workflows-v2?applicableType=N` dynamic
- FE Designer `ApprovalWorkflowsV2Page` có `TYPE_CODE_TO_INT` cả 3 type
- Layout regex `^AwV2_(.+)$` match dynamic typeCode
- App.tsx route `/system/approval-workflows-v2/:typeCode` dynamic

Chỉ thiếu cho B: **menu key + sample seed** (3 file ~60 LOC).

- `MenuKeys.cs` +const `ApprovalWorkflowDuyetNccPhuongAnV2 = "AwV2_DuyetNccPhuongAn"` + add vào `All[]`
- `DbInitializer.SeedMenusAsync` +leaf "Duyệt NCC và Giải pháp (Mới)" dưới root ApprovalWorkflowsV2 (Order=2 cạnh leaf A Order=1)
- `DbInitializer +SeedSampleApprovalWorkflowsV2Async` (idempotent — skip nếu admin đã tạo workflow B nào, hoặc thiếu test user `nv.test`/Phòng CCM): seed `QT-DN-PA-V2-001 v01` 1 Bước Phòng CCM × 1 Cấp NV test
- `fe-admin/lib/menuKeys.ts` +`AwV2_DuyetNccPhuongAn`

KHÔNG migration / Service / Designer page mới. Memory `feedback_audit_reuse_before_clone.md` capture pattern.

User feedback "OK khá tốt, 1 phát chạy luôn :))" sau verify → confirm approach.

### B4 (`f77ea38`) — Fix silent 403 ApprovalWorkflowsV2Controller

Triệu chứng: Drafter `nv.test` Workspace tạo phiếu B → dropdown "Quy trình duyệt" empty mặc dù Admin Designer thấy 2 version (v01 sample + v02 admin clone).

Root cause: Class-level `[Authorize(Policy = "Workflows.Read")]` → non-admin role 403 Forbidden khi GET `/api/approval-workflows-v2`. TanStack Query catch error không hiện UI → dropdown rỗng silent.

Fix:
- Class-level đổi `[Authorize]` only (any authenticated user)
- GET endpoint inherit class policy — Drafter list workflow để pick read-only, không nhạy cảm
- POST + DELETE giữ `[Authorize(Policy = "Workflows.Create")]` admin-only Designer

Pattern reusable cho Contract V2 Mig 26 sau.

### B5 (`a9c0857`) — Fix sidebar highlight queryMatches transient keys

Triệu chứng: Ở leaf "Danh sách" `/purchase-evaluations?type=1`, click chọn 1 phiếu → URL thành `?type=1&id=abc` → leaf bị mất highlight box (gotcha #34 cũ tái phát theo cách khác).

Root cause: `queryMatches` exact-set equality — target `{type}` (1 key) vs current `{type, id}` (2 keys) length mismatch → no match → leaf unhighlight.

Fix: `TRANSIENT_QUERY_KEYS = {id, q, editHeader, page, phase, awId}` — strip trước khi compare. Mọi key navigation identity (`type`, `pendingMe`, `mode`) check exact-set như cũ.

Edge cases verified:
| URL hiện tại | Target leaf | Match |
|---|---|---|
| `?type=1&id=abc` | Danh sách `?type=1` | ✓ giữ highlight |
| `?type=1&pendingMe=1` | Danh sách `?type=1` | ✗ distinct (không cross-highlight Pending) |
| `?type=1&phase=10` | Danh sách `?type=1` | ✓ giữ highlight (filter dropdown) |
| `?type=1&pendingMe=1&awId=xyz` | Duyệt `?type=1&pendingMe=1` | ✓ giữ highlight |

Mirror fe-admin + fe-user `Layout.tsx`.

### B6 (`2a53107`) — Mig 25 IsUserSelectable + Designer pin toggle + bỏ "(clone)"

User feedback xem Admin Designer: "Bỏ chữ Clone đi nhé, ghi v02, v03... là đủ rồi. Thêm cho tao nút stick để chọn các quy trình nào mà User đc select bên ngoài khi tạo phiếu."

**Bỏ "(clone)":** Designer auto-fill `name = cloneFrom.name` (bỏ ` (clone)` suffix). Version số đã đủ phân biệt.

**Pin toggle "Cho user chọn":**

- **Migration 25** `AddIsUserSelectableToApprovalWorkflows`:
  ```sql
  ALTER TABLE ApprovalWorkflows ADD IsUserSelectable bit NOT NULL DEFAULT 0;
  -- Backfill (giữ behavior cũ — active workflows vẫn pickable):
  UPDATE ApprovalWorkflows SET IsUserSelectable = 1 WHERE IsActive = 1;
  ```
- **Domain** `ApprovalWorkflow.IsUserSelectable` — independent với `IsActive`, multiple versions có thể cùng selectable (admin có thể "ghim" nhiều version cho user pick).
- **App CQRS:**
  - `AwDefinitionDto` +field `IsUserSelectable`
  - `CreateAwDefinitionCommand` Handler set default `true` cho version mới (mirror IsActive default)
  - New `SetAwUserSelectableCommand(Guid Id, bool IsUserSelectable)` + Handler — toggle
- **API** `PATCH /api/approval-workflows-v2/{id}/user-selectable` policy `Workflows.Create` (admin only)
- **DbInitializer** `SeedSampleApprovalWorkflowsV2Async` +`IsUserSelectable = true`
- **FE Designer** (`fe-admin/ApprovalWorkflowsV2Page.tsx`):
  - `DefinitionDto` +`isUserSelectable`
  - Badge amber "📌 Cho user chọn" cạnh badge IsActive/Archived khi `isUserSelectable === true`
  - Button "📌 Ghim cho user / 🚫 Bỏ ghim" trong action group + mutation `toggleSelectable` (call PATCH endpoint, invalidate query)
- **FE Workspace** (cả fe-admin + fe-user `PeWorkspaceCreateView.tsx`):
  - approvalWorkflows query `.filter(w => w.isUserSelectable)` — chỉ workflows admin đã ghim hiện trong dropdown user

### B7 (`32a8d4d`) — Cleanup orphan zip files

`.claude.zip + docs.zip` từ harness session start lỡ tay vào `git add -A` ở B6 commit. Untrack + add `*.zip` rule `.gitignore`.

### Stats Δ Session 18

| | Trước S18 | Sau S18 |
|---|---:|---:|
| Migrations | 24 | **25** (+1) |
| DB tables | 58 | 58 (Mig 25 chỉ ALTER cột) |
| API endpoints | ~140 | **~141** (+1 PATCH user-selectable) |
| FE pages | 33 | 33 (modify existing only) |
| Test pass | 81 | 81 (no change — UAT feature defer test §7) |
| Gotchas | 43 | **44** (+1 silent 403) |
| Memory entries | 13 | **14** (+1 audit reuse pattern) |
| Skills | 6 | 6 (no add) |
| Commits | (after S17) | **+7** |

## ⚠️ Điều quan trọng cho Session 19+

1. **Contract V2 wire (Mig 26) — pending dedicated session.** Pattern audit-reuse áp dụng: phần lớn đã chung. Mirror PE pattern:
   - Thêm `Contract.ApprovalWorkflowId` + `CurrentApprovalLevelOrder` (Mig 26)
   - `ContractWorkflowService.ApproveV2Async` mirror PE pattern
   - `ContractCreatePage` Workspace Select V2
   - Pin V2 mặc định cho ContractType
   - Permission GET endpoint đã permissive (Session 18 fix), không cần đụng

2. **Phân quyền strict V2 V2** — hiện loose UAT (mọi authenticated thấy mọi phiếu V2). Sau confirm flow:
   - List = Drafter + approver any-Step + Admin
   - Inbox = chỉ approver Cấp hiện tại (V2 đã đúng — `ResolveV2InboxIdsAsync`)
   - Detail = same as List
   - Cũng giải quyết được bug "/inbox loose trả phiếu Nháp" — sau khi strict, B1 FE filter có thể relax nếu BE đã filter đúng

3. **Drop legacy V1 cleanup** sau khi không còn phiếu pin `WorkflowDefinitionId` (V1):
   - Drop tables `WorkflowDefinitions` + `WorkflowSteps` + `WorkflowStepApprovers` + PE versions
   - Mig 27 cleanup drop column `RejectedAtStepIndex` + `RejectedFromPhase` deprecated S17
   - Drop `ApproveV1LegacyAsync` branch trong Service

4. **Test V2 wire** (defer khi UAT confirm + có sample data) — Domain test `ApproveV2Async` match logic + transient TraLai entry → Cấp 1 reset.

5. **`feedback_audit_reuse_before_clone` memory** — áp dụng cho mọi "clone X sang Y" / "thêm type Z mới" sau này. List "đã chung" vs "còn thiếu" trước khi propose plan.

6. **Sample seed B sample** chạy với check `hasAnyB` — sau UAT có thể remove sample seed (admin đã tạo workflow thật). Hoặc giữ làm fallback. Idempotent skip nếu admin có workflow B → không clobber.

---

## TL;DR Session 17 — PE V2 schema end-to-end

## TL;DR Session 17 — PE V2 schema end-to-end

User chốt sau Session 16 (drastic refactor flat Mig 21 vẫn sai intent): **viết lại schema riêng + thêm Menu "Duyệt NCC (Mới)"** với cấu trúc explicit:

```
Mã Quy trình - Tên Quy trình
  * Bước 1 - Phòng A
    * Cấp 1 - NV X        ← 1 user CỤ THỂ qua ApproverUserId
    * Cấp 2 - NV Y
  * Bước 2 - Phòng B
    * Cấp 1 - NV Z
```

Khác Mig 21: mỗi Cấp = 1 NV chính xác, KHÔNG OR-of-many group Dept+PositionLevel/Role/User.

**4 commit (3 chunk per-commit + docs):**

### Chunk A (`c847dc0`) — Domain + EF + Mig 22 + Menu

- Domain `ApprovalWorkflowsV2/ApprovalWorkflow.cs` — 3 entity (ApprovalWorkflow + Step + Level) + enum `ApprovalWorkflowApplicableType` (DuyetNcc=1 / DuyetNccPhuongAn=2 / Contract=3)
- EF `ApprovalWorkflowConfiguration.cs` — UNIQUE (Code, Version), FK Cascade Step→Workflow + Level→Step, FK Restrict Department + ApproverUserId
- ApplicationDbContext +3 DbSet
- **Migration 22** `AddApprovalWorkflowsV2` — 3 CREATE TABLE + 1 UNIQUE + 4 INDEX. Applied cả `_Design` + `_Dev` LocalDB
- DbInitializer SeedMenusAsync: +menu `ApprovalWorkflowsV2` root dưới System (icon Workflow) + leaf `AwV2_DuyetNcc` (icon FileCheck, label "Duyệt NCC (Mới)")
- MenuKeys.cs +2 const trong All array

### Chunk B (`f6047d5`) — Application CQRS + API

- `Application/ApprovalWorkflowsV2/ApprovalWorkflowV2AdminFeatures.cs`:
  - `GetAwAdminOverviewQuery(ApplicableType?)` — load 3-level Include + dept/user names map
  - `CreateAwDefinitionCommand` + Validator — auto-increment Version theo Code, deactivate active version cùng ApplicableType
  - `DeleteAwDefinitionCommand` — UAT helper unconditional (chưa pin)
  - DTO AwDefinition/AwStep/AwLevel + AwTypeSummary
- IApplicationDbContext +3 DbSet
- `Api/Controllers/ApprovalWorkflowsV2Controller` — route `/api/approval-workflows-v2`, GET ?applicableType=N | POST | DELETE/{id}, reuse policy `Workflows.Read` + `Workflows.Create`

### Chunk C (`2781c7e`) — FE Designer

- `fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx` (~480 LOC)
  - Overview cards Active+History per ApplicableType
  - DefinitionCard read-only: Bước (badge phòng emerald) → Cấp (badge violet C1/C2 + tên NV + email)
  - Designer dialog: Mã/Tên/Mô tả + Add/Remove Step + reorder (chevron up/down) + Add/Remove Level + Select Phòng + Select NV duyệt
  - Validate: mỗi Step ≥1 Level, mỗi Level phải có ApproverUserId
  - Auto-assign code mặc định theo type: `QT-DN-V2-001` / `QT-DN-PA-V2-001` / `QT-HD-V2-001`
- Layout.tsx resolver +ApprovalWorkflowsV2 root → `/system/approval-workflows-v2`, +AwV2_<TypeCode> leaf → `/system/approval-workflows-v2/<code>`
- App.tsx +2 route
- menuKeys.ts +2 const sync với BE

### Chunk D — Docs

STATUS + HANDOFF + project_solution_erp.md memory.

### Chunk E (UAT iteration, 9 commit) — Designer fix + State machine + Service wire + UX

User UAT iter Designer V2 phát hiện multiple issues + chốt spec dần qua state diagram. Per memory `feedback_uat_skip_verify.md` UAT mode iterate nhanh:

| Commit | Tóm tắt |
|---|---|
| `9712778` | Designer iter 1 lock 3 cấp/bước (sai intent) |
| `f3bea3c` | Designer iter 2 đúng intent: max 3 cấp × N NV/cấp + sequential gating C2/C3 disabled khi prev empty + filter NV theo Phòng + no-dup same level. Validator BE strict |
| `ff21120` | State machine 5 trạng thái Nháp/ĐãGửiDuyệt/TrảLại/TừChối/ĐãDuyệt. TraLai = Phase RIÊNG (không revert DangSoanThao + không jump-back). PE/Contract/Budget Phase enum + Policy + Service Reject branch → TraLai. 4 test mới TraLai entry point |
| `0a40c65` | **Mig 23** `AddApprovalWorkflowIdToPurchaseEvaluation` — pin V2 vào PE entity. Workspace Select bắt buộc workflow lúc create. Validate ApplicableType match PE.Type |
| `b41484b` | **Mig 24** `AddCurrentApprovalLevelOrderToPe` + Service V2 wire — `ApproveV2Async` iterate Steps/Levels group by Order = Cấp (OR-of-N approvers) match `actor.Id ∈ ApproverUserId`. Synthetic Policy `ForV2Schema()` cho FE nextPhases |
| `d814429` | DTO CurrentApproval + banner "Đến lượt bạn" / "Không phải lượt bạn" + button Duyệt forward disabled khi V2 + actor không trong cấp + tooltip "chỉ {NV X / Y} duyệt được". Trả lại + Từ chối vẫn enabled |
| `9e63e2d` `d250ae4` `74745a7` | List/Inbox V2-aware (`ResolveV2InboxIdsAsync` precompute IDs). 2 dropdown filter Quy trình + Trạng thái (chỉ ở Duyệt). Inbox endpoint nhận `approvalWorkflowId` |
| `ac41d5e` | SQL `clean-transactional-uat.sql` — clean prod (9 PE + 11 HĐ + 19 Notif xóa) giữ master. Run qua SSH VPS `.\SQLEXPRESS` |
| `de0f38d` | Panel 3 thay 4 phase cards bằng flow workflow thực tế: Bước (icon ✓/●/○) → Cấp (label "đang chờ"/"đã duyệt" + tên NV). DTO `ApprovalFlow` full snapshot với Status Done/Current/Pending |

**Stats final Session 17:** 24 migration (+3), 58 DB tables (+3), ~140 endpoints (+5), 81 test pass (+4).

**Test user UAT** (tạo qua API admin):
- Email: `nv.test@solutions.com.vn` / Pass: `TestUser@123456` / Role: Drafter / Phòng: CCM

## ⚠️ Điều quan trọng cho Session 18+

1. **Contract V2 wire CHƯA làm** — chỉ PE wire xong (Mig 23-24). Session sau mirror pattern PE → Contract:
   - Thêm `Contract.ApprovalWorkflowId` + `CurrentApprovalLevelOrder` (Mig 25)
   - Update `ContractWorkflowService` thêm `ApproveV2Async` branch
   - Update Workspace Select V2 trong `ContractCreatePage`
   - Pin V2 thành mặc định cho Contract types

2. **Phân quyền strict V2** — hiện loose UAT (mọi authenticated user thấy phiếu V2). Sau confirm flow OK:
   - List: Drafter + bất kỳ approver any-Step + Admin
   - Inbox: chỉ approver Cấp hiện tại (V2 đã đúng — `ResolveV2InboxIdsAsync`)
   - Detail: same as List

3. **Drop legacy V1 sau UAT** — khi không còn phiếu nào pin `WorkflowDefinitionId`:
   - Drop tables `WorkflowDefinitions` + `WorkflowSteps` + `WorkflowStepApprovers` + PE versions
   - Cleanup migration: drop column `RejectedAtStepIndex` + `RejectedFromPhase` (deprecated từ Session 17)
   - Drop `ApproveV1LegacyAsync` branch trong Service

4. **Admin role bypass** — hiện code `if (!isAdmin && !isSystem)` skip approver check. By design cho UAT + emergency override. Nếu prod cần audit override → option C trong Session 17 thảo luận: thêm flag `IsAdminOverride=true` trong approval row + banner đỏ trên detail.

5. **81 test pass** — Domain WorkflowPolicyTests + PurchaseEvaluationPolicyTests + BudgetPolicyTests đã update cho TraLai entry point. KHÔNG có test cho V2 Service wire (defer khi UAT confirm + có sample data).

---

## TL;DR Session 16 (08/05 — Drastic refactor flat workflow EXECUTE)

Resume từ Session 15 defer plan. Per memory `feedback_drastic_refactor_scope.md`: dedicated session, fresh context, conservative buffer.

**Spec:** Workflow flat list (Phòng × Cấp × Approvers). Mỗi step = 1 (Phòng × Cấp). Service iterate steps OrderBy Order, advance pointer. Phase enum simplify ChoDuyet=10. Pin WorkflowDefinitionId.

**2 chunk per-commit (5-6 chunk plan rút gọn vì BE tightly coupled):**

### Chunk A (`dbb0089`) — All BE: Domain + Mig 21 + Service + Tests

**Domain entities:**
- Phase enum (PE + Contract): + ChoDuyet=10 generic intermediate. Legacy 2-6 + 98 deprecated (giữ enum cho data cũ).
- WorkflowStep + DepartmentId Guid? FK Restrict + PositionLevel int?
- PurchaseEvaluation/Contract + CurrentWorkflowStepIndex int? + RejectedAtStepIndex int?
- DROP class WorkflowStepInnerStep + nav (PE + Contract)
- DROP *DepartmentApproval.InnerStepId column

**EF Configurations:**
- DROP InnerStep config (PE + Contract) → table dropped
- WorkflowStep config + DeptId/PositionLevel + FK Restrict
- DepartmentApprovals: restore simple unique non-filtered (Mig 19/20 filtered split reverse)

**ApplicationDbContext:** DROP DbSet<*WorkflowStepInnerStep> × 2

**Migration 21** `RefactorWorkflowToFlatModel` GỘP:
- 4 ALTER (PE/Contract +CurrentStepIndex +RejectedAtStepIndex)
- 2 ALTER (WorkflowStep +DepartmentId +PositionLevel) PE + Contract
- DROP TABLE × 2 (PEWorkflowStepInnerSteps + WorkflowStepInnerSteps Mig 18+20)
- DROP COLUMN × 2 (*DeptApproval.InnerStepId)
- DROP filtered indexes Mig 19/20
- RESTORE simple UNIQUE (TargetId, Phase, Dept, Stage) non-filtered × 2

**Service rewrite (PE + Contract WorkflowService.TransitionAsync):**
- DangSoanThao → ChoDuyet (Drafter trình, init idx=0)
- ChoDuyet → ChoDuyet (advance idx per approve)
- ChoDuyet → DaDuyet/DaPhatHanh (idx ≥ steps.Count → terminal, gen mã HĐ Contract)
- ChoDuyet → DangSoanThao (Trả lại — save RejectedAtStepIndex)
- ChoDuyet → TuChoi (Từ chối — khoá vĩnh viễn)
- Resume Drafter (DangSoanThao + RejectedAtStepIndex≠null) → ChoDuyet jump-back
- Match approver: actor.Dept == step.Dept AND actor.PositionLevel >= step.PositionLevel (OR-of-many cùng cấp/dept) OR Approvers.Kind=User|Role match
- Admin role bypass policy

**App CQRS:** WorkflowStepDto + WorkflowStepInput drop InnerStep, add DepartmentId/DepartmentName/PositionLevel (PE + Contract mirror).

**Tests:**
- DROP `PeNStageApprovalTests.cs` (6) + `ContractNStageApprovalTests.cs` (6) + `PeTwoStageApprovalTests.cs` (7) — legacy
- UPDATE `PeWorkflowAdminTests` signature for new flat input
- **96 → 77 test pass** (-19 legacy)

**3-file rule** Mig 21 commit đủ (.cs + Designer + Snapshot).

### Chunk B (`88a5be1`) — FE Designer + types

**PeWorkflowsPage + WorkflowsPage rewrite (~210 LOC each):**
- Drop InnerStepDto + EditInnerStep types
- Drop PHASE_OPTIONS (auto-assign ChoDuyet=10 behind scenes)
- StepDto + EditStep + departmentId, departmentName, positionLevel
- Designer step UI rewrite: Tên + Phòng Select + Cấp Select + SLA + Approvers (Role/User optional fallback). Drop InnerSteps sub-section.
- DefinitionCard view: badge Phòng emerald + Cấp NV/PP/TP violet
- Save payload: phase=10 (ChoDuyet)
- Hint amber: "User cùng Phòng + Cấp ≥ step → duyệt được (OR-of-many)"

**types/purchaseEvaluation.ts (fe-admin + fe-user mirror):** + ChoDuyet=10 enum + label "Đang duyệt" + color amber. Legacy 2-6 + 98 keep.

**Chunk C (FE PeWorkflowPanel) SKIPPED** — existing UI compatible (workflow.nextPhases BE-driven, 3-button Trả lại/Từ chối Session 14 reuse với target=DangSoanThao/TuChoi pattern).

### Verify

- ✅ dotnet build SolutionErp.slnx 0 error
- ✅ dotnet ef database update Mig 21 LocalDB applied OK
- ✅ dotnet test 77 pass (54 Domain + 23 Infra)
- ✅ npm build fe-admin + fe-user pass

### Cumulative sau Session 16

| | Trước S16 | Sau S16 |
|---|---:|---:|
| BE LOC | ~15800 | ~15500 (-300 service simplified) |
| Migrations | 20 | **21** |
| DB tables | 57 | **55** (-2 InnerStep tables) |
| Tests | 96 | **77** (-19 legacy N-stage/2-stage) |
| FE pages | 32 | 32 (rewrite existing 2 designer) |

## ⚠️ CẢNH BÁO Session 17+

1. **UAT live test** — workflow flat ready. Tạo new workflow definition qua `/system/pe-workflows/:typeCode` với 3 phòng × N cấp setup. Verify Drafter trình → cấp 1 phòng A → cấp 2 phòng A → cấp 1 phòng B → ... → DaDuyet flow.

2. **Old PE/HĐ pinned legacy workflow definitions** (phase=ChoPurchasing/ChoCCM/etc) — service rewrite chỉ handle ChoDuyet=10 + DangSoanThao/DaDuyet. Old data ở phase 2-6 sẽ stuck (admin manual transition required). Recommend: data migration script convert old workflow → new flat model (defer).

3. **Approver explicit (Role/User Approvers list)** — fallback nếu user không match Dept+PositionLevel của step. Cho phép user external (không thuộc dept) duyệt qua Role match (vd Admin) hoặc User explicit.

4. **Bypass cấp dưới cùng dept** — User TP với CanBypassReview=true cùng dept và PositionLevel cao hơn step.PositionLevel → duyệt qua. KHÔNG batch upsert NV+PP rows như Mig 18 N-stage trước (đơn giản hóa: 1 step approve = 1 row).

5. **N-stage tests dropped** — 19 test legacy (Mig 18, 20 N-stage + Mig 16 2-stage). Có thể viết test mới cho flat workflow flow nếu UAT phát sinh bug. Defer.

6. **Sample data N-stage seed** task vẫn pending (Session 14). Block trên DesignTime vs Runtime DB gotcha + DbInitializer seed flow.

7. **Budget N-stage** vẫn defer (cần versioned WF migration trước).

8. **schema-diagram §17-21 update** defer cron audit 2026-06-01.

## TL;DR Session 15 (07/05 — Tooltip diagnose + drastic refactor DEFER)

User UAT live screenshot báo button "Lưu & Gửi Duyệt" KHÔNG hoạt động + suy đoán "trùng ID" giữa các phiếu.

**Diagnose (commit `835cc7f`):**
- Root cause: button silent disabled khi `evaluation.workflow.nextPhases` không có forward phase (chỉ TuChoi/TraLai). Cause khả năng: workflow definition pinned thiếu adjacent step → `policy.NextPhasesFrom(DangSoanThao)` return empty.
- Improvement: tooltip + dialog hiển thị reason rõ ràng:
  - `submitDisabledReason` text: "Phiếu đã ở phase X — chỉ Bản nháp/Trả lại mới sửa+gửi" / "Workflow không có phase tiếp theo từ X. Liên hệ admin kiểm tra cấu hình"
  - Button title attribute → hover show reason hoặc forward phase label
  - Dialog confirm show forward phase explicit ("Sẽ chuyển sang Chờ Purchasing")
- Mirror fe-admin + fe-user. Build pass cả 2. KHÔNG đụng BE — chỉ FE diagnostic UX.
- "Trùng ID" KHÔNG phải bug FE — `PurchaseEvaluationWorkspacePage` URL state đúng, mỗi PE row unique GUID + MaPhieu. Suy đoán user do button silent.

**Plan drastic refactor → DEFER:**

User confirm "bỏ phase enum hoàn toàn, dùng ChoDuyet=10 đơn nhất + currentStepIndex tracking" — refactor workflow từ phase-based + InnerStep nested model sang flat WorkflowStep model (mỗi step = Phòng × Cấp + Approvers users).

Edit working tree 12 files (Domain entities + EF Configurations + DbContext):
- Phase enum +ChoDuyet=10, legacy values 2-6 deprecated
- WorkflowStep +DepartmentId, +PositionLevel
- Drop class WorkflowStepInnerStep + nav (PE + Contract)
- PE/Contract +CurrentWorkflowStepIndex int?, +RejectedAtStepIndex int?
- *DepartmentApproval drop InnerStepId column
- EF Configurations: drop InnerStep config + nav, restore simple unique non-filtered
- DbContext: drop DbSet<WorkflowStepInnerStep> × 2

Reality check scope realistic ~8-10h:
1. Domain + EF + DbContext (~50min) ✓ done in working tree
2. PolicyRegistry rewrite PE+Contract (~45min)
3. App CQRS DTOs rewrite (~45min)
4. Service rewrite PE+Contract (~2-3h)
5. Tests rewrite — drop 12 N-stage tests + update remaining (~1.5h)
6. Migration 21 + LocalDB apply + verify (~30min)
7. FE Designer rewrite (~1.5h)
8. FE PeWorkflowPanel + workflow timeline (~1h)
9. Docs/Skill update (~45min)

Vượt session boundary + risk session deep ~30 commits → **REVERT working tree** về `835cc7f` clean state. Test 96 pass intact.

**Decision memorized:** add memory `feedback_drastic_refactor_scope.md` — drastic refactor cần dedicated session, scope estimation conservative (2x buffer), tránh mid-session big refactor.

## ⚠️ CẢNH BÁO Session 16+

1. **Drastic refactor flat workflow chưa làm — DEFER** với plan chi tiết. Khi resume:
   - Plan kỹ 6 chunk per-commit
   - Buffer 2x estimate (~16h thực tế)
   - Tests rewrite biggest risk
   - Hoặc fall back Approach Y (FE Designer flat UI giới hạn 5 phòng) ROI 1-2h nếu user OK trade-off
2. **Task 2 sample data seed N-stage** vẫn pending (block trên DesignTime vs Runtime DB gotcha + DbInitializer seed flow)
3. **schema-diagram §17-19 Mig 18-20** vẫn defer cron audit 2026-06-01
4. **Hard blockers Ops** giữ nguyên 6 task

## TL;DR Session 14 (07/05 — PE 3-button approval workflow)

User chỉ thị thay 2-button approval (Duyệt + Reject mơ hồ) bằng **3 hành động rõ ràng** cho approver:
- **Duyệt** = forward phase tiếp theo (decision=Approve)
- **Trả lại** = về DangSoanThao + Drafter sửa (decision=Reject + target=DangSoanThao). Smart reject pattern Mig 16 + clear N-stage rows + Drafter resume jump-back tới phase đã reject.
- **Từ chối** = phase=TuChoi (decision=Reject + target=TuChoi). Phiếu khoá vĩnh viễn (17 handler Mig 16 lock edit). Drafter phải tạo phiếu mới.

**1 commit (`0d77698`):**

- **Domain `PurchaseEvaluationPolicy.cs`**: NccOnly + NccWithPlan thêm `(X → TuChoi)` transition cho mọi phase trung gian (ChoPurchasing/ChoDuAn/ChoCCM/ChoCEODuyetPA/ChoCEODuyetNCC) với roles của phase đó. FromDefinition expand: mỗi step (trừ DangSoanThao) thêm (step.Phase → TuChoi) với roles step.
- **Service** `PurchaseEvaluationWorkflowService.TransitionAsync` — Reject branch tách 2 case:
  ```
  if (decision == Reject) {
    if (target != TuChoi) {  // Trả lại
      RejectedFromPhase = fromPhase
      target = DangSoanThao  // force
      clear N-stage rows tại fromPhase
    }
    // else target=TuChoi: giữ nguyên, KHÔNG set RejectedFromPhase, KHÔNG clear
  }
  ```
- **FE PeWorkflowPanel (admin + user mirror)**: render 3 button rõ:
  - "✓ Duyệt → <label phase>" brand
  - "← Trả lại (về Drafter sửa)" red khi `target=DangSoanThao && fromPhase != DangSoanThao`
  - "✗ Hủy / Từ chối" red khi `target=TuChoi`
  - Decision logic FE: `isReject = target=TuChoi || isSendBack`
- **Dialog confirm**: title rõ theo loại, Cancel case warning đỏ "Phiếu sẽ bị khoá hoàn toàn", SendBack case hint amber "Phiếu về DangSoanThao, Drafter sửa rồi trình lại — workflow tự jump tới phase này".
- **Tests update + add 1**:
  - rename `Reject_Sets_RejectedFromPhase_And_Forces_DangSoanThao` → `Reject_To_DangSoanThao_Sets_RejectedFromPhase_TraLai` (change target TuChoi→DangSoanThao)
  - NEW `Reject_To_TuChoi_Locks_Permanently_No_RejectedFromPhase` (Phase=TuChoi + RejectedFromPhase null)
  - update `NStage_Reject_Clears_InnerStep_Rows_At_Phase` target → DangSoanThao
- **95 → 96 test pass** (+1 Từ chối).

**Task 2 sample seed in-progress (defer Session 15+):**

- Phát hiện gotcha: `DesignTimeDbContextFactory` hardcoded `SolutionErp_Design` connection — `dotnet ef database update` từ session 12-13 thực ra apply lên `_Design` DB, KHÔNG phải `_Dev` runtime DB. Khi user run API → DbInitializer auto-MigrateAsync apply lên `_Dev`.
- Đã apply Mig 9-20 lên `SolutionErp_Dev` qua `dotnet ef database update --connection`.
- Start API để DbInitializer auto-seed 30 demo user. API exit 255 sớm khi log buffer full (~100 lines) — seeding dở dang (chỉ seed Roles, chưa Users).
- Defer Task 2 cho session sau. Cần: user manual `dotnet run` API 1 lần hoàn thành seed, hoặc Claude resume với output redirect to file.

## ⚠️ CẢNH BÁO Session 15+

1. **Phase TraLai = 98 (orphan)** — Domain enum đã có từ S11+++++++ nhưng KHÔNG dùng (user chốt Session 14 không cần phase trung gian, chỉ DangSoanThao + TuChoi). Để giữ enum không phá nhưng ko wire vào policy → orphan. Có thể remove migration sau (không gây hại).

2. **Task 2 sample seed pending**: 2 việc:
   - Update `Users.PositionLevel` (1=NV, 2=PP, 3=TP) cho 30 demo user đã seed (mapping email pattern: `tpb./ccm./pro./fin./act./equ./hra.` → 3 (TP), `pp.*` → 2 (PP), `nv./qs./` else → 1 (NV), `bod./pm./admin./huy.duong/chau.le` → null)
   - INSERT N-stage WorkflowDefinition v2 cho DuyetNcc với InnerSteps (PRO + CCM × 3 cấp NV/PP/TP)

3. **DesignTime vs Runtime DB distinction**: dev có 2 DB:
   - `SolutionErp_Design` — `dotnet ef migrations add/update` (DesignTimeDbContextFactory hardcode)
   - `SolutionErp_Dev` — runtime API (appsettings.Development.json)
   Khi muốn apply migrations lên Dev: `dotnet ef database update --connection "...SolutionErp_Dev..."` hoặc `dotnet run` API (DbInitializer auto-MigrateAsync).

4. **Budget N-stage defer** — chưa wire vì Budget chưa có versioned WorkflowDefinition. Cần migration `AddBudgetVersionedWorkflow` + `AddBudgetWorkflowInnerSteps`. User quyết riêng (feature mở rộng module lớn).

5. **schema-diagram §17-19 Mig 18-20** chưa update — defer cron audit 2026-06-01.

## TL;DR Session 13 (07/05 — Mirror N-stage workflow sang Contract)

User chỉ thị mirror N-stage từ PE (Mig 18+19) sang Contract. Budget defer (cần migration `AddBudgetVersionedWorkflow` trước — hardcoded `BudgetPolicy.Default` chưa có WorkflowDefinition entity).

**5 chunk per-commit** (Chunk E skipped — WorkflowsController auto-bind record qua [FromBody]):

- **Chunk A (`951ffa3`)** Domain `WorkflowStepInnerStep` + nav WorkflowStep.InnerSteps + ALTER ContractDeptApproval.InnerStepId + EF config + **Migration 20** GỘP 1 (CREATE TABLE + ALTER + DropIndex old + Recreate filtered legacy/N-stage)
- **Chunk B (`04cf2a0`)** Application CQRS DTO/Input/Validator/Handler mirror PE Chunk B (default null backward compat)
- **Chunk C (`e247b67`)** ContractWorkflowService refactor — load InnerSteps eager + reject clear N-stage rows + dept block split hasInnerSteps→N-stage / else→legacy 2-stage
- **Chunk D (`7c0772a`)** ContractNStageApprovalTests 6 test mirror PE + helper SeedWorkflowDefinitionAsync 2 step adjacent + FakeChangelogService + FakeContractCodeGenerator stubs. **89→95 test pass**
- **Chunk E SKIP** — auto-bind no code change
- **Chunk F (current)** FE WorkflowsPage Designer extend InnerSteps sub-section mirror PeWorkflowsPage + Docs

**Verify:** dotnet build pass + dotnet ef database update Mig 20 LocalDB applied + dotnet test 95 pass + npm build fe-admin pass.

**Cumulative sau Session 13:**

| | Trước S13 | Sau S13 |
|---|---:|---:|
| BE LOC | ~15300 | ~15700 (+400) |
| Migrations | 19 | **20** (+Mig 20) |
| DB tables | 56 | **57** (+1 WorkflowStepInnerSteps) |
| DB columns mới | — | +1 (ContractDeptApproval.InnerStepId) |
| API endpoints | ~134 | ~134 (no change — auto-bind) |
| FE pages | 32 | 32 (extend existing WorkflowsPage) |
| Tests | 89 | **95** (+6 Contract N-stage) |
| Commits | (after S12) | **+5** (A→F per-chunk, E skipped) |

## ⚠️ CẢNH BÁO Session 14+

1. **Budget N-stage defer** — Budget chưa có versioned WorkflowDefinition entity (hardcoded `BudgetPolicy.Default`). Để mirror N-stage Budget cần migration `AddBudgetVersionedWorkflow` trước (4 bảng `BudgetWorkflowDefinitions/Steps/StepApprovers/InnerSteps` + `Budget.WorkflowDefinitionId?`). Defer cho user quyết riêng — feature mở rộng module Budget lớn.

2. **PE-only deploy first** — Contract N-stage giờ đã có nhưng PE đã được test trước. UAT cả 2 module song song để verify pattern reusable.

3. **Sample data N-stage** — pending (Task 2). Khi user yêu cầu sẽ seed PositionLevel cho 30 demo user + 1 N-stage workflow definition active cho DuyetNcc + 1 cho ContractType (ví dụ HopDongThauPhu).

4. **TraLai BE wire** — pending (Task 4). Phase TraLai = 98 enum đã có ở Session S11+++++++ FE side, nhưng BE workflow service chưa wire button "Trả lại" cho approver. Khi user yêu cầu sẽ thêm decision `SendBack` (or reuse Reject với target=TraLai) + Service logic + FE button trong Workflow Panel.

5. **schema-diagram §17 Mig 20** chưa update — defer cron audit 2026-06-01.

6. **Skill ef-core-migration Mig 20 row** chưa add — defer cron audit.

7. **Skill contract-workflow N-stage cross-ref** chưa add — defer cron audit.

## TL;DR Session 12 (07/05 — N-stage workflow approval per phase × dept × cấp)

User yêu cầu mở rộng từ 2-stage Mig 16 sang N-stage cấu hình động: 1 phase (WorkflowStep cha) có thể cấu hình chuỗi InnerSteps con theo Department × PositionLevel sequential. Mỗi phòng có NV → PP → TP duyệt thứ tự, có thể bypass cùng dept khi role cao + CanBypassReview.

**6 câu spec defaults chốt:**
- Q1 enum `PositionLevel { NhanVien=1, PhoPhong=2, TruongPhong=3 }` + `User.PositionLevel int?`
- Q2 Sequential pure (Order asc, mỗi inner step = 1 cấp duyệt)
- Q3 TP có CanBypassReview → skip NV+PP cùng dept (audit IsBypassed=true cho cấp dưới)
- Q4 Smart reject về DangSoanThao + RejectedFromPhase + clear N-stage rows tại phase reject
- Q5 PE first (Contract+Budget mirror sau khi PE stable UAT)
- Q6(a) Designer UI 1 sub-section "Cấp duyệt nhỏ trong phòng" drag-list per step

**6 chunk per-commit:**

- **Chunk A (`13ab533`)** Domain + Migration 18 — enum PositionLevel + entity InnerStep + ALTER User.PositionLevel + ALTER PEDeptApproval.InnerStepId + EF config Cascade/Restrict + 3-file rule
- **Chunk B (`0e56bd0`)** Application CQRS DTO — extend PeWorkflowStepDto + InnerStepDto + Validator + Handler atomic batch + UserDto +PositionLevel + SetUserPositionLevelCommand (default null backward compat existing PeWorkflowAdminTests)
- **Chunk C (`0c62e24`)** Service N-stage logic + **Migration 19** filtered unique (drop UNIQUE Mig 16 → recreate `WHERE InnerStepId IS NULL` legacy + new `WHERE InnerStepId IS NOT NULL` N-stage). Refactor TransitionAsync: load InnerSteps eager + reject clear rows + hasInnerSteps→N-stage / else→legacy 2-stage. Match firstPending Order asc, exact match upsert 1 row, bypass batch upsert NV+PP+TP audit IsBypassed
- **Chunk D (`3d76c6b`)** 6 test PE N-stage (FirstInner blocks / 3-level sequential pass / TP bypass skips / wrong dept 403 / reject clears rows / legacy fallback no inner). IdentityFixture extend +positionLevel arg. Helper SeedWorkflowDefinitionAsync 2 step adjacent. **83→89 test pass**
- **Chunk E (`83ffabd`)** API `PATCH /users/{id}/position-level` mirror SetBypassReview
- **Chunk F (current)** FE-Admin types/users.ts +positionLevel + PositionLevel const + Label/Short. PeWorkflowsPage Designer + sub-section InnerSteps drag-list { Phòng × Cấp + required } + button "+ Thêm cấp duyệt" emerald + departmentsList query + payload include. UsersPage column "Cấp" badge + cycle button. KHÔNG đụng fe-user (admin-only). Docs/Skill update.

**Verify:** dotnet build pass + dotnet ef database update Mig 18+19 LocalDB applied + dotnet test 89 pass + npm build fe-admin pass.

**Cumulative sau Session 12:**

| | Trước S12 | Sau S12 |
|---|---:|---:|
| BE LOC | ~14850 | ~15300 (+450 — Domain enum + entity + EF config + Service N-stage + App CQRS) |
| Migrations | 17 | **19** (+Mig 18 + Mig 19) |
| DB tables | 55 | **56** (+1 PEWorkflowStepInnerSteps) |
| DB columns mới | — | +2 (User.PositionLevel + PEDeptApproval.InnerStepId) |
| API endpoints | ~133 | **~134** (+1 PATCH /users/{id}/position-level) |
| FE pages | 32 | 32 (no change — extend existing 2 page) |
| Tests | 83 | **89** (+6 N-stage PE) |
| Commits | (after S11+++) | **+6** (A→F per-chunk) |

## ⚠️ CẢNH BÁO Session 13+

1. **N-stage chỉ áp PE first** — Contract + Budget vẫn 2-stage Mig 16 cũ. Mirror sau khi UAT PE 2-3 tuần ổn (giữ pattern, lặp lại Domain entity + Service logic + Tests).

2. **User.PositionLevel chưa seed cho 30 demo user hiện có** — admin phải set tay qua UsersPage cycle button. Hoặc seed migration sau (mapping Position text → PositionLevel int).

3. **Designer InnerSteps optional** — workflow definition KHÔNG có InnerSteps fallback logic 2-stage Mig 16 cũ. Backward compat 100%. Khi muốn enable N-stage cho 1 phase: clone version + add inner steps + Save.

4. **Bypass kế thừa cùng dept only** — TP với CanBypassReview chỉ skip NV+PP CÙNG dept của TP. KHÔNG cross-dept (NV.A bypass không skip NV.B).

5. **Reset N-stage rows khi reject** — clear chỉ rows tại phase reject. Phase trung gian khác vẫn giữ approvals nếu có. Resume sẽ jump tới RejectedFromPhase + cần re-approve N-stage tại phase đó.

6. **Wrong-level error message** — đã localize "phòng X cấp Y không khớp". UAT user cần feedback nếu confused.

7. **schema-diagram §15 Mig 18 + §16 Mig 19** chưa update — defer cho audit cron 2026-06-01 (small drift, không major).

8. **Skill `ef-core-migration` Mig 18+19 row** chưa add — defer audit 2026-06-01.

## TL;DR Session phase 2 (08/05 — B12-B14 polish iterate sau wrap-up `6e7a6db`)

User UAT live tiếp tục, áp rule strict verify khi rename/remove (lesson hotfix CI 0ae3fe2).

- **B12 (`378c993`)** PE detail polish 5 changes:
  - "Lưu" no-close (chỉ toast + invalidate sync, KHÔNG đóng workspace)
  - "Xóa phiếu" red bottom button CHỈ Bản nháp (soft-delete IsDeleted=true)
  - Bỏ header bar workspace mode "Sửa header"/"Xóa"/"Đóng" (chuyển hết xuống bottom action bar)
  - Section 4 column header: `s.supplierName` (master NCC name) thay `displayName`
  - Section 3 row chặn xóa NCC khi đã có quotes + tooltip "xóa báo giá trước"
- **B13 (`e320027`)** InfoTab auto re-edit + pencil active visual:
  - useEffect watch `[autoEdit, canEdit, ev.id, ...]` → re-trigger edit khi pencil click phiếu khác
  - Sync values từ ev mới (tránh stale state)
  - Pencil "sáng lên" active state (`bg-brand-100 + ring`) khi `editingRowId === p.id`
- **B14 (`d2306b8`)** QuoteDialog + winner column highlight + loading feedback:
  - Bỏ checkbox "Chọn NCC này cho hạng mục" (consolidate winner ở Section 2.a)
  - Winner column Section 4 matrix LUÔN xanh emerald (header `✓ ` prefix + cells full column)
  - QuoteDialog full overlay loading + spinner khi save có delay
  - NccSelectorRow inline spinner "Đang chọn NCC + sync cột giá Section 4…"

**Verify:** `npm run build` × 2 app pass · `dotnet test` 83 pass (KHÔNG regression). Push gitea OK.

## ⚠️ CẢNH BÁO Session 12+ (cập nhật)

Bổ sung từ wrap-up trước (xem TL;DR S10-11+++++++ phía dưới):

8. **`isSelected` per-quote BE field** — vẫn còn trong API payload nhưng UI ẩn. Nếu sau này muốn cho user chọn winner per-item (khác với winner per-phiếu) → re-enable checkbox QuoteDialog. Hiện tại chỉ gửi `existing?.isSelected ?? false` để giữ legacy data nếu có.

9. **Section 4 cell winner highlight** dùng `ev.selectedSupplierId === s.supplierId` (per-supplier check). Nếu BE workflow có multi-winner per-item (ít khả năng), cần điều chỉnh logic.

10. **Loading overlay QuoteDialog** chỉ áp QuoteDialog. Các dialog khác (AddSupplierDialog/EditSupplierDialog/DetailDialog) chỉ có button text feedback. Nếu UAT thấy delay → mở rộng pattern overlay sang các dialog khác.

11. **InfoTab re-edit useEffect deps** include 4 ev fields (tenGoiThau/diaDiem/moTa/paymentTerms). Có thể trigger re-set values nếu BE cập nhật ev (vd auto-save từ chỗ khác). Trade-off: chấp nhận để sync state. Nếu UAT thấy "values mất" → debug deps.

## TL;DR Session S10-11+++++++ (07/05 — PE Workspace UX overhaul đầy đủ)

User UAT live mode iterate liên tục. Áp rule [`feedback_uat_skip_verify`](../../../../Users/pqhuy/.claude/projects/D--Dropbox-CONG-VIEC-SOLUTION/memory/feedback_uat_skip_verify.md) skip dotnet test sau mỗi chunk, push ngay. Lesson hotfix CI `0ae3fe2`: rename/remove → BẮT BUỘC `npm run build` trước commit.

**11 batch deliverable** (23 commit total — xem STATUS Recently Done row đầu tiên cho narrative đầy đủ):

- **B1** PE Thao tác 2-panel workspace (S10)
- **B2** Migration 17 manual budget fields PE + HĐ (S11)
- **B3** BudgetFieldRow inline editor Section 2.b
- **B4** InfoTab inline edit Section 1 + PeListPanel pencil hover
- **B5** Workspace "new" sectioned create view 5 sections
- **B6** Danh sách view disable toàn bộ tương tác
- **B7** Workspace "new" lock Loại quy trình + payment preset Select
- **B8** PE display status meta (Bản nháp / Đã gửi duyệt / Trả lại / Đã duyệt / Từ chối)
- **B9** Phase TraLai = 98 + pencil always visible + edit gating
- **B10** Hotfix CI TS errors (forcedPhase rename + unused import)
- **B11** PE detail polish — NCC selector dropdown + Section 3 winner protect + Bottom action bar Lưu/Gửi Duyệt

## Stats sau wrap-up

| | Trước S10 | Sau S11+++++++ |
|---|---:|---:|
| BE LOC | ~14400 | ~14850 (+450 — Mig 17 + Domain TraLai + App CQRS) |
| Migrations | 16 | **17** (+1 Mig 17) |
| DB columns mới | — | +4 (PE+HĐ × Name+Amount manual budget) |
| FE pages | 31 | **32** (+1 Workspace 2-panel) |
| FE components mới | — | +5 (PeListPanel, PeHeaderForm, PeWorkspaceCreateView, BudgetFieldRow, NccSelectorRow inline) |
| PE phase enum | 8 | **9** (+TraLai = 98) |
| PE display status | — | **5** (gom phase chi tiết) |
| Tests | 83 | 83 (no test added — UAT iteration, áp rule §7 test-after defer) |
| Commits S10-S11+++++++ | — | **+23** |

## ⚠️ CẢNH BÁO Session 12+

1. **Workflow transition vào TraLai** — Domain enum đã có (98), FE label/color/badge sẵn. NHƯNG chưa wire button "← Trả lại" trong PeWorkflowPanel approver view + workflow service BE chưa transition logic vào TraLai. Khi UAT cần → thêm: button "Trả lại" trong Chuyển tiếp (hoặc Reject Dialog), workflow rule `Decision=3 (TraLai)` set phase=TraLai + ghi `RejectedFromPhase`. FE đã ready accept hiển thị TraLai phase.

2. **"Đã gửi duyệt" multi-phase filter** — display status "Đã gửi duyệt" gom 5 phase trung gian (ChoPurchasing/ChoDuAn/ChoCCM/ChoCEODuyetPA/ChoCEODuyetNCC). BE chưa hỗ trợ multi-phase param trong list endpoint → FE filter dropdown ẩn option này (chỉ exact-match Bản nháp/Đã duyệt/Từ chối). TODO: BE thêm `?phases=2,3,4,5,6` query param hoặc derive `displayStatus` field.

3. **`feedback_uat_skip_verify` exception** — khi commit có **rename/remove**, BẮT BUỘC `npm run build` 1 lần trước push (lesson hotfix `0ae3fe2`). Chỉ skip verify khi pure ADD (props, JSX, new file).

4. **Workspace + Danh sách + Duyệt** matrix:
   - Workspace (Pe_*_Create): 2-panel, Section 5 disabled, edit only Bản nháp+Trả lại, pencil bright/disabled, bottom bar 2 nút Lưu/Gửi Duyệt
   - Danh sách (Pe_*_List): 3-panel, **readOnly=true** toàn bộ (no edit/transition)
   - Duyệt (Pe_*_Pending): 3-panel, PeDetailTabs readOnly + PeWorkflowPanel có Chuyển tiếp + Section 5 nhập opinion (chỗ này hiện vẫn disabled do code path cũ — verify nếu UAT user cần sign opinions ở đây)

5. **Mig 17 columns nullable** — backward compat OK. Cả 2 (BudgetId + manual fields) cùng null acceptable. Validation BE chưa XOR.

6. **CreateContractFromEvaluation carry-forward** manual fields PE→HĐ — đã wire. Verify UAT.

7. **CI deploy status** — sau hotfix `0ae3fe2` các run từ commit `4c0625c` trở đi sẽ xanh + deploy. User UAT live trên prod ngay sau push.

## TL;DR Session 11++ housekeeping (07/05 — InfoTab inline edit + pencil hover)

User feedback after BudgetFieldRow: muốn thêm nút edit kế bên row trong Panel 1 list, click sáng nội dung Section 1 lên cho sửa header inline.

- ✅ **InfoTab inline edit** — display mode + button "✎ Sửa" góc phải Section 1. Editing mode: card border brand-200 + 4 input (Tên / Dự án locked / Địa điểm / Mô tả / Payment) + Save (PUT /pe/:id) / Hủy.
- ✅ **PeListPanel pencil hover** — icon absolute right-2 top-2 mỗi row, group-hover opacity-100. Click → callback onEditClick(id).
- ✅ **URL flag `?editHeader=1`** — set bởi pencil click → autoEditHeader prop chain xuống PeDetailTabs → InfoTab tự open editing mode mount-time.
- ✅ **fe-admin** Chunk 1 (`5a89dd2`) · **fe-user mirror** Chunk 2 (this).

**Defer (chưa làm):** Refactor workspace "new" mode wrap PeHeaderForm trong sectioned layout giống detail view (5 sections visible). PeHeaderForm hiện tại single-card đủ dùng — chỉ làm thêm khi user feedback rõ.

## TL;DR Session 11+ housekeeping (07/05 — inline budget editor)

User feedback after Session 11: muốn toggle + 2 fields hiển thị trực tiếp trong Section 2 "b. Ngân sách" của PeDetailTabs, không chỉ ở "Sửa header" page. Empty values cứ empty.

- ✅ **BudgetFieldRow component** (~125 LOC) thay FormRow tĩnh cũ ở `b. Ngân sách`. canEdit=`!readOnly && isDraft`. Save dùng existing PUT endpoint (KHÔNG cần BE thay đổi).
- ✅ **fe-admin** Chunk 1 (commit `19712d8`) · **fe-user mirror** Chunk 2 (commit `d5c6f12`).
- ✅ **Verify**: 2 build pass + 0 TS error.

UX:
- **Workspace + Danh sách + isDraft** → editable inline: toggle + Select OR 2 input + nút Lưu khi dirty
- **Duyệt + !isDraft** → display only (link card / manual values / "—" empty)
- **Empty state**: hiển thị "—" thay vì "(chưa link)" verbose (per user)

## TL;DR Session 11 (07/05 — Migration 17 manual budget fields)

**Output session 11** — UX improvement cho user nhập ngân sách không cần Budget entity approved:

- ✅ **Migration 17** `AddManualBudgetFieldsToPeAndContract` — 4 ALTER (PE + HĐ × BudgetManualName nvarchar(200) + BudgetManualAmount decimal(18,2)). Applied LocalDB OK. Mirror logic PE ↔ HĐ (Q3 user chốt).
- ✅ **BE** Domain 2 entity (PE + Contract) +2 property + EF config HasMaxLength/HasPrecision. App CQRS Create/Update commands + DTO + Validator + diff log + carry-forward CreateContractFromEvaluation pe→contract.
- ✅ **FE** Toggle checkbox "Nhập tay (không link)" cạnh Label Ngân sách trong 4 chỗ: PeHeaderForm (workspace + /new page wrap), ContractCreatePage NewForm + EditForm. Khi ON: hide Select, show 2 input field grid 2-col (Tên + Số tiền formatted VND). Khi OFF (default): Select Budget approved cũ. Auto-detect manual mode khi load existing có manual data + !budgetId. Display fallback ở PeDetailTabs Section 1 "b. Ngân sách" + Contract EditForm read-only branch.
- ✅ **5 chunk per-commit** (build + 83 test pass mỗi chunk): C1 Domain+Infra Migration 17 (`ecd5f7e`) · C2 App CQRS (`0f7901c`) · C3 FE-Admin (`bab5031`) · C4 FE-User mirror (`14f8d9d`) · C5 Docs (current).

**Validation Q2 chốt:** cả BudgetId + manual fields có thể cùng null (PE/HĐ chưa có ngân sách gì). KHÔNG XOR enforce — BE prefer link nếu có (Phase=DaDuyet guarantee), manual fallback only.

**KHÔNG đụng:** Budget entity / Phase=DaDuyet validation (giữ invariant "PE/HĐ link Budget approved only"). Manual fields chỉ là note/display, KHÔNG join với Budget.Details cho per-row comparison ở PE matrix Section 4 (cột "So với ngân sách" vẫn require ev.budgetId — không có detail rows để compare).

## ⚠️ CẢNH BÁO session tiếp (Session 12+)

1. **UAT manual budget flow** — toggle ON/OFF + save + reload + verify auto-detect mode. Đặc biệt edit existing PE/HĐ có manual data → toggle phải auto-checked. Edit existing có Budget link → toggle auto-unchecked.
2. **Section 4 PE matrix "So với ngân sách"** vẫn require Budget link — manual amount KHÔNG render comparison column. Document giới hạn này nếu UAT thắc mắc.
3. **`docs/database/schema-diagram.md`** chưa cập nhật count 16→17 migration + 4 column mới. Defer cho audit định kỳ 2026-06-01 (per §6.4 cron) — nhỏ enough không phải selective rewrite.
4. **CreateContractFromEvaluation** đã carry forward manual fields PE→Contract. Verify khi UAT: PE có manual budget → tạo HĐ từ phiếu → HĐ inherit luôn.
5. **No new test added** (rule §7 — feature mới = test-after, soak UAT 2-3 tuần ổn → viết happy path).
6. **Schema fields nullable** — không phá HĐ/PE cũ (legacy null OK). Backward compatible.

## TL;DR Session 10 (07/05 — PE workspace 2-panel)

**Output session 10** — restructure leaf "Thao tác" PE từ page Create header riêng sang workspace 2-panel mirror HĐ Thầu phụ pattern:

- ✅ **Spec chốt 5 câu trước code** — Q1 Panel 2 chỉ data entry (KHÔNG Workflow/Approvals/History); Panel 1 pure picker (no inline edit/delete); Q2 mirror HĐ Thầu phụ (sticky "+ Thêm mới" + new→edit Panel 2); Q3 leaf "Danh sách" + "Duyệt" giữ 3-panel hiện tại; Q4 route mới `/workspace`; Q5 Section 5 Ý kiến 4PB disable trong workspace (nhập khi duyệt).
- ✅ **Chunk 1 fe-admin** (commit `ee0d360`) — 3 file mới: `PeListPanel.tsx` (~180 LOC pure picker reuse-able + sticky "+ Thêm mới"), `PeHeaderForm.tsx` (~210 LOC extract header form từ CreatePage), `PurchaseEvaluationWorkspacePage.tsx` (~120 LOC 2-panel `[320px_1fr]`). 3 file sửa: `PeDetailTabs.tsx` thêm prop `mode?: 'detail' \| 'workspace'` + Section 5 hint amber khi workspace + force `opinionsReadOnly`. `Layout.tsx` resolver `Pe_*_Create` → `/workspace?type=N`. `App.tsx` route mới.
- ✅ **Chunk 2 fe-user mirror** (commit `ecf3c59`) — 6 file y hệt content (rule §3.9 duplicate có chủ đích).
- ✅ **Verify**: 2 build pass + dotnet test 83 vẫn pass mỗi chunk.

**KHÔNG đụng BE / migration / schema / endpoint.** Route `/new` cũ giữ tồn tại cho deep-link "Sửa header" button.

## ⚠️ CẢNH BÁO session tiếp (Session 11+)

1. **UAT live workspace** với anh Kiệt + 2-3 user — feature mới UX khác (page Create cũ thành Dialog implicit qua sticky button + transition new→edit auto).
2. **Section 5 Ý kiến 4PB hint banner amber** ở workspace mode chỉ là text gợi ý — KHÔNG block. User vẫn thấy existing opinions read-only. Nếu UAT thấy confused → có thể đổi thành collapsible section.
3. **Mobile: workspace KHÔNG có fallback** — màn hình `<lg` (< 1024px) Panel 2 ẩn (lg:block). Cần test mobile redirect → `/purchase-evaluations/:id` nếu UAT phát hiện. Hiện chỉ admin desktop dùng workspace.
4. **Pe_*_Pending vẫn /purchase-evaluations?pendingMe=1** — leaf "Duyệt" 3-panel (giữ Panel 3 Workflow + readOnly Section 5 cho phép sign). UAT verify Drafter trình → TPB duyệt thấy Section 5 enable đúng.
5. **PE WorkflowPanel duplicate ở 2 chỗ** — leaf "Danh sách" + "Duyệt" Panel 3 (3-panel). KHÔNG có ở "Thao tác" workspace. Verify route resolver active state highlight đúng (queryMatches helper từ session 3).
6. **Sửa header trong workspace** — vẫn navigate sang `/new?id=` (button "Sửa header" trong PeDetailTabs). Chưa wire inline edit qua Dialog. Nếu UAT yêu cầu → thêm prop `onEditHeader` cho PeDetailTabs trigger Dialog reuse `PeHeaderForm`.

## Housekeeping today (sau Session 9)

- ✅ **Audit định kỳ 2026-05** — combined skill + doc drift (commit `7dc0233`). 5 drift patch (count 77→83 + 52→55 bảng), 1 skill content patch (contract-workflow Phase 9 cross-ref). KHÔNG tạo skill mới. Log `docs/changelog/skill-audit-2026-05.md`.
- ✅ **Optional polish — fe-user Inbox PE section** (commit `332a90f`). useQuery `/purchase-evaluations/inbox` + Panel 1 chia 2 section sticky header (HĐ + PE). PE click → navigate page riêng.
- ✅ **User Manual 7 file rewrite compact** (commit `16c2c9c`). User feedback "ko cần quá đầy đủ chi tiết, cho end-user họ làm". Bỏ field validation table + error troubleshoot table + FAQ chi tiết. Giữ tổng quan ngắn + numbered steps đơn giản. ~86 KB / 7 file (cũ ~123 KB ↓30%). Setup `package.json` + `npm install docx@9.5.0` + `npm run gen:all`. Lần sau sửa: edit `_gen-*.js` → run gen:all.

**Cron audit định kỳ:** Claude SDK CronCreate auto-expire 7 days → KHÔNG fit monthly cadence. User cần setup OS Task Scheduler `.bat` script ping API hoặc manual trigger mỗi đầu tháng. Lần kế: 2026-06-01.

**User Manual style rule (mới chốt session này):** end-user docs BỎ field/error tables, FAQ chi tiết, phím tắt — GIỮ tổng quan ngắn + numbered steps + note/warn/tip critical. Phân biệt với rule §6.5 (KHÔNG cắt narrative) — đây là rule cho audience end-user, không phải agent dev đọc rationale.

## TL;DR Session 9 (04/05 — Chunk E-bis sau Session 8)

**Output session 9** — đóng tất cả Chunk E-bis defer từ session 8:

- ✅ **FE PE WorkflowPanel** — Section "Tiến trình duyệt 2-cấp phòng ban" group by phase × dept, highlight amber chờ TPB confirm, badge fuchsia bypass (cả fe-admin + fe-user).
- ✅ **FE UsersPage UserManager** — Column "Bypass" + button ShieldCheck toggle CanBypassReview, badge fuchsia khi enabled. UserDto thêm field.
- ✅ **HĐ 2-stage logic** — `ContractWorkflowService` thêm UserManager DI + mirror logic từ PE service. `ContractDepartmentApprovalFeatures.cs` List query. Endpoint `GET /contracts/{id}/department-approvals`. FE `WorkflowHistoryPanel` section mới.
- ✅ **Budget 2-stage logic** — `TransitionBudgetCommandHandler` thêm INotificationService + IDateTime DI + 2-stage. `BudgetDepartmentApprovalFeatures.cs` + endpoint + FE `BudgetWorkflowPanel` section.
- ✅ **6 test PE 2-stage** — `IdentityFixture` setup full Identity stack (DbContext SQLite + AddIdentityCore + AddRoles<Role>) reusable. 6 scenario: NV_Review_Blocks / TPB_Confirm_Allows / NV_Bypass / Admin_Skip / Reject_Sets / Resume_Jumps_Back.
- ✅ **Verify**: Build pass + 83 test pass mỗi commit (54 Domain + 29 Infra: 17 codegen + 6 PE WF Application + 6 PE 2-stage).
- ✅ **5 commit pushed** Gitea (E2 → E6).

## ⚠️ CẢNH BÁO session tiếp (Session 10+)

1. **UAT live ngay** với anh Kiệt + 2-3 user — feature 2-stage đầy đủ cả 3 module + UX.
2. **Tests Contract + Budget 2-stage skipped** — logic identical PE (cùng pattern, cùng entity shape). Pattern `PeTwoStageApprovalTests` reusable nếu UAT phát hiện regression riêng.
3. **Bypass toggle audit** — chưa log Changelog khi admin toggle CanBypassReview. Audit qua Identity standard column UpdatedAt only. Có thể cần thêm audit row riêng nếu UAT yêu cầu.
4. **Notify TPB cùng dept** dùng `UserManager.GetRolesAsync` filter `DeptManager` — verify production có user role DeptManager đúng (data already seeded).
5. **fe-user KHÔNG có UsersPage** — admin-only function. Bypass toggle chỉ ở fe-admin.
6. **3 endpoint mới List dept-approvals** PE/HĐ/Budget cùng pattern, reuse policy authz `*Read`.
7. **Cron audit định kỳ 2026-05-01** vẫn EMPTY (`No scheduled jobs`). Có thể recreate khi user yêu cầu.

## TL;DR Session 8 (04/05 — code lớn, 5 commit per-chunk)

## TL;DR Session 8 (04/05 — code lớn, 5 commit per-chunk)

**Output session 8** — đóng bug anh Kiệt + thêm 3 ràng buộc workflow:

- ✅ **Migration 16** `AddTwoStageDeptApprovalAndSmartReject` — 4 ALTER (3 RejectedFromPhase int + Users.CanBypassReview bit) + 3 CREATE TABLE (`Contract/PE/Budget DepartmentApprovals` UNIQUE (TargetId, Phase, Dept, Stage)) + 12 indexes.
- ✅ **Lock edit** 17 handler thêm guard Phase != DangSoanThao (Contract Detail × 15 qua helper, PE Detail × 5 qua helper mới, Budget Detail × 3 inline).
- ✅ **Smart reject + Resume** 3 module — Reject = lưu phase nguồn + force về DangSoanThao. Resume = jump straight tới phase đã reject (skip phase trung gian, bypass policy guard).
- ✅ **PE 2-stage logic** trong `PurchaseEvaluationWorkflowService` — TPB/CanBypass → Confirm; NV → Review only, BLOCK transition cho đến khi TPB confirm.
- ✅ **3 endpoint mới**: `GET /pe/{id}/department-approvals` (List), `PATCH /users/{id}/bypass-review` (toggle), Notify TPB cùng dept khi NV review.
- ✅ **Verify**: Build + 77 test pass mỗi commit. Migration applied LocalDB OK. Schema verified.
- ✅ **6 commit pushed** (2 docs S7 + 5 code S8).

## ⚠️ CẢNH BÁO session tiếp (Session 9+)

1. **Bug fix anh Kiệt** chỉ áp PE workflow. **HĐ + Budget 2-stage scope DEFER** cho khi UAT PE OK.
2. **FE Workflow Panel chưa update** — workflow vẫn block đúng (BE), nhưng UX chưa hiển thị 2-stage progress. User test sẽ thấy phase không đổi mà không hiểu tại sao "stuck". Phải UAT với hint cho user trước khi code FE.
3. **FE UserManager toggle CanBypassReview chưa làm** — tạm thời SET qua HTTP PATCH:
   ```
   PATCH /api/users/{userId}/bypass-review
   Authorization: Bearer <admin>
   { "canBypassReview": true }
   ```
4. **Test thực tế bug fix flow**:
   - Login `phuong.nguyen` (NV.PRO, role=Procurement, DeptId=PRO) tạo phiếu PE type A
   - Trình DangSoanThao → ChoPurchasing
   - phuong.nguyen click Duyệt phase ChoPurchasing → expect: phase KHÔNG đổi, có row Stage=Review
   - `tra.bui` (TPB.PRO, role=DeptManager) click Duyệt → expect: phase chuyển ChoCCM
5. **Notify TPB cùng dept** dùng `UserManager.GetRolesAsync` filter `DeptManager`. Best effort, fail OK.
6. **Cron audit định kỳ 2026-05-01** đã quá hạn 3 ngày, vẫn EMPTY runtime. Cần manual trigger.
7. **Smart reject test**: Reject phase ChoCCM → DangSoanThao + RejectedFromPhase=ChoCCM. Drafter sửa Detail + trình lại → jump straight tới ChoCCM (skip ChoPurchasing).
8. **Lock edit test**: HĐ ở Phase=DangGopY → cố sửa Detail → expect 409 Conflict "đã trình duyệt, không thể chỉnh sửa".

## TL;DR Session 6 (30/04 — không code, chỉ docs)

**Output session 6** — pure docs work, không thay đổi code/test:

- ✅ **Compact 3 file core**: STATUS -27%, HANDOFF -32%, migration-todos -35% (-288 dòng tổng). Archive 51 row Recently Done Phase 0-7 → `changelog/recently-done-archive-2026-04.md`.
- ✅ **Refresh 3 skill stale**: `form-engine` (Phase 2 MVP → Tier 3 feature-complete), `permission-matrix` (12 menu → ~60 + inheritance roots), `ef-core-migration` (24 DbSet → 52 bảng).
- ✅ **Rule mới §7 Timing test**: 1 bảng 5-row compact — feature mới = test-after, bug = test-before, critical algorithm = test-before, spec change = update test cũ, skip 5 loại.
- ✅ **Rule mới §6.4 Audit + compact MD định kỳ**: cadence + checklist + anti-pattern. Cross-ref §9.4 skill audit. KHÔNG rewrite toàn bộ.
- ✅ **77 test vẫn pass** (Domain 54 + Infra 23). 0 thay đổi code.
- ✅ **Cron 2026-05-01 fire mai** — combined audit (skill + doc drift) theo §6.4 + §9.4.

**Session 5 (29/04)** đóng gần hết feature gap + bật test gate:

### 🎯 Headline outcomes
- ✅ **Budget feature-complete** — FE 3-panel pages cả 2 app, BE đã sẵn từ session 4
- ✅ **PE/HD ↔ Budget integration** — form select Budget filter Phase=DaDuyet + Project, cột "So với ngân sách" ở PE matrix với delta indicator
- ✅ **PE Detail UI restructure** match form chính thức PHIẾU TRÌNH KÝ (4 section đánh số)
- ✅ **PE Workflow Designer admin UI** `/system/pe-workflows/:typeCode` versioned với clone/edit/+Role/+User
- ✅ **Ý kiến 4 phòng ban** — migration 15 + section sign-off 2x2 grid (Phê duyệt/CCM/MuaHàng/SM-PM)
- ✅ **Tests Phase 1-2** — 71 unit test pass + CI gate fail-fast (Domain policy + Infra code generator)

**Session 4 (28/04)** đã có:

### A. Module Ngân sách (Budget) BE — migration 14, +4 bảng, +11 endpoint

- 4 entity: `Budget` (Header) + `BudgetDetail` (flat row) + `BudgetApproval` (history) + `BudgetChangelog` (audit log).
- Enum `BudgetPhase` 5 state (DangSoanThao→ChoCCM→ChoCEO→DaDuyet + TuChoi).
- `BudgetPolicy.Default` hardcoded simple 3-step (Drafter→CCM→CEO) — chưa versioned (TODO khi user cần admin config UI).
- Mã `NS-YYYYMM-XXXX` Random.Shared (chưa atomic — TODO khi format chính thức).
- Link nullable: `Contract.BudgetId?` + `PE.BudgetId?` đã có FK + index, FE chưa wire form.
- Menu seed `Budgets` root + 3 leaf (Bg_List/Bg_Create/Bg_Pending) order=27 icon Wallet.
- Application: 11 CQRS handler ~340 LOC (Create/UpdateDraft/Transition/List/GetDetail/Delete + Detail CRUD auto-recompute TongNganSach + ListChangelogs).
- Api: `BudgetsController` 11 endpoint REST.
- **FE chưa làm — Priority 0 session 5.**

### B. 14 demo user Solutions thật

- PRO 5 (TPB tra.bui + 4 NV) + CCM 7 (TPB ngocanh.huynh + 6 NV) + ISO 1 (chau.le) + CEO 1 (huy.duong).
- Pwd `User@123456`. Reconcile pattern (gotcha #38 4-field rename).
- Tổng 30 user (16 sample cũ giữ + 14 Solutions thật mới).

**Tổng cumulative:** 52 DB tables, ~128 endpoints, 15 migrations, **41 gotchas (+3 CI fixes)**,
**77 unit test (+6 PE WF Application)**, 11+ commit session 5 push lên Gitea.

### 🆕 CI/CD optimize (29/04 tối)

- ✅ **Manual checkout bypass github.com** — fix #108/#109 transient TCP timeout 21s. Run #110 pass.
- ✅ **Path filter docs-only skip** — `paths-ignore` trong on:push, commit MD-only KHÔNG trigger CI (verify `512880c` → no run #113).
- ⏸️ **npm junction cache** — thử ở #111 fail `tsc not found`, rollback. Cần debug session sau (gotcha #40 doc rồi).
- ✅ **Tests Phase 3 mini (PE Workflow Designer)** — 6 test versioning logic. Total 77.

## ⚠️ CẢNH BÁO session tiếp (Session 7+)

1. **CI test gate active** — code → `dotnet test SolutionErp.slnx` local → commit → push. Test fail = NO deploy. Workflow user §7 rules.md.
2. **Timing test rule live (§7)**: feature mới = test-after, bug = test-before BẮT BUỘC, critical algorithm = test-before merge, spec change = update test cũ + code chung commit.
3. **Doc audit cadence live (§6.4)**: cuối phase compact, đầu tháng cron audit, KHÔNG rewrite toàn bộ. Cron fire **mai (2026-05-01)** — combined skill + doc drift theo checklist.
4. **Login email** `admin@solutionerp.local` / `Admin@123456` — domain default chưa đổi sang `@solutions.com.vn` (chỉ demo user rebrand).
5. **Chưa xóa binding cũ `.huypham.vn`** — vẫn fallback. Sau verify stable → `.\migrate-domains.ps1 -RemoveOld -SkipCert` trên VPS.
6. **win-acme scheduled task "unhealthy"** — fix trước cert expire 2026-06-18.
7. **Export phiếu PDF/Excel PE** — pending vô thời hạn (user nói không quan trọng).
8. **3 feature mới chưa test** (PE Opinion Upsert / Budget validate / Contract BudgetId carry) — đợi UAT phát sinh bug → áp rule §7 (regression test before fix). Hoặc soak 2-3 tuần ổn → viết happy path.
9. **G-084:** VPS shared VietReport — mọi reverse proxy mới phải `127.0.0.1` + bind loopback IPv4 explicit.

## ⭐ Skills (.claude/skills/) — PHẢI dùng khi task khớp

| Domain (3) | Ops (3) |
|---|---|
| `contract-workflow` — state machine + versioned WF | `dependency-audit-erp` — npm/dotnet CVE scan |
| `form-engine` — render docx/xlsx + PDF | `ef-core-migration` — EF migration + 3-file rule |
| `permission-matrix` — role × menu × CRUD | `iis-deploy-runbook` — 3 site IIS + win-acme + runner |

**Audit cron:** `0 9 1 * *` (9:00 AM ngày 1 mỗi tháng). Workflow: `docs/rules.md §9.4`. Lần kế: **2026-05-01**.

**Quy tắc:** KHÔNG bulk-clone repo skill 3rd party. Chỉ skill PROJECT-SPECIFIC. Đầy đủ: `docs/rules.md §9`.

## Ở đâu rồi?

| Phase | Trạng thái |
|---|---|
| 0 Draft | ✅ Done |
| 1 Alpha Core (foundation + đợt 2 CRUD + Permission) | ✅ Done |
| 2 Form Engine MVP + iter 2 (upload UI + .doc auto-convert + PDF export) | ✅ Done |
| 3 Workflow MVP (9 phase + code gen) + iter 2 (SLA job + attachment + notify) | ✅ Done |
| 4 Report MVP (Dashboard + Excel) + user-specific dashboard | ✅ Done |
| 5 Prep + 5.1 Security + Users Mgmt | ✅ Done |
| **5 Deploy prod** (3 domain HTTPS live) | ✅ Done |
| **Tier 3 (Attach + Realtime + Form builder + PDF + Versioned WF + Nested menu + Permission 3-panel)** | ✅ Done |
| **Skill governance** (6 skill project-level + audit cron 1/tháng) | ✅ Done |
| **3-panel List/Inbox/Thao tác + sidebar accordion + UserDashboard** | ✅ Done |
| **4-bảng data model** (Header + 7 Details + Workflow + Changelog audit) | ✅ Done |
| **Mã HĐ gen tại Create + backfill legacy** | ✅ Done |
| **4 master catalogs** (Units/Materials/Services/WorkItems) + datalist autocomplete | ✅ Done |
| **Roles VN labels (ShortName)** + Users dept/position + 13 demo users | ✅ Done |
| **RolesPage CRUD** `/system/roles` + custom role admin | ✅ Done |
| **7 demo HĐ varied phases** + details + comments + approvals workflow | ✅ Done |
| **User-kind approver runtime guard** + Warning 20% SLA | ✅ Done |
| **Edit detail row inline** (7 typed Update commands + EditRowDialog) | ✅ Done |
| **Master expand 15 NCC + 8 Project** + backfill demo HĐ diverse | ✅ Done |
| **Deps audit script** (`scripts/deps-audit.ps1`) | ✅ Done |
| **Module Duyệt NCC (tiền-HĐ) E2E** — 10 bảng + 2 workflow + Kế thừa HĐ | ✅ Done |
| **PE polish iter 2** — flat layout + per-NCC attachments + readOnly Duyệt + email rebrand | ✅ Done |
| **Domain rebrand huypham.vn → solutions.com.vn** — 3 subdomain + cert + CORS + FE bundle | ✅ Done |
| **Module Ngân sách BE + 30 user** — 4 bảng + 11 endpoint + workflow simple | ✅ Done (S4) |
| **Module Ngân sách FE** — 3-panel pages + Detail tabs + Workflow Panel cả 2 app | ✅ Done (S5) |
| **PE/Contract → Budget integration** — form Select + cột "So với ngân sách" PE matrix | ✅ Done (S5) |
| **PE Workflow designer admin UI** `/system/pe-workflows/:typeCode` | ✅ Done (S5) |
| **Ý kiến 4 phòng ban PE** — migration 15 + section sign-off 2x2 grid | ✅ Done (S5) |
| **Tests Phase 1-2 + CI gate** — 71 test (Domain policy + Infra code generator) | ✅ Done (S5) |
| **CI manual checkout bypass github.com + Path filter docs-only skip** | ✅ Done (S5) |
| **Tests Phase 3 mini** — 6 test PE Workflow Designer versioning (total 77) | ✅ Done (S5) |
| **Export phiếu PDF/Excel PE** — `IDocumentConverter` + template | 📝 Pending (không quan trọng) |
| **Tests Phase 3 full** — Application handlers (UpsertOpinion + Budget link validation, cần UserManager setup) | 📝 Pending |
| **Tests Phase 4-5** — API smoke + FE Vitest | 📝 Pending (làm khi cần) |
| **npm cache CI optimize** (debug junction Move-Item issue #40) | 📝 Pending |
| 9+ Post-launch (E-signature, Bravo/SAP, Mobile, AI) | 📝 Future |

## Run nhanh

```powershell
# Terminal 1 — API (auto seed 12 role + 9 dept + 5 supplier + 3 project + 8 template + 7 workflow definition + 28 ContractType menu + 7 workflow menu)
dotnet run --project src\Backend\SolutionErp.Api

# Terminal 2 — Admin FE
cd fe-admin && npm run dev  # → http://localhost:8082

# Terminal 3 — User FE
cd fe-user && npm run dev   # → http://localhost:8080
```

Login: `admin@solutionerp.local` / `Admin@123456`

## Quick sanity-check

**Admin (:8082):**
- `/dashboard` → "Của tôi" row 4 card + KPI cards + charts
- `/contracts` → list toàn bộ, filter phase/supplier/project
- `/contracts/new?type=5` → tạo HĐ Mua bán, pre-select type từ URL
- `/contracts/{id}` → timeline + action dialog + attachments drag-drop + WorkflowSummaryCard
- `/system/workflows` → 7-card landing (Thầu phụ/Giao khoán/NCC/Dịch vụ/Mua bán/NguyenTacNcc/NguyenTacDv)
- `/system/workflows/MuaBan` → DefinitionCard active + history + "Tạo phiên bản mới" modal với Steps + Approvers (+Role / +User)
- `/system/permissions` → 3-panel layout (Role list | Menu×CRUD matrix | Granted stats)
- `/system/users` → Users CRUD + assign roles
- `/forms` → upload .docx/.xlsx + render dialog Form↔JSON + Tải PDF

**User (:8080):**
- `/inbox?type=5` → HĐ Mua bán chờ role mình
- `/my-contracts?type=2` → HĐ Thầu phụ của tôi
- `/contracts/new?type=3` → tạo HĐ NCC
- Sidebar nested: 📄 Hợp đồng → expand 7 type → expand "HĐ Mua bán" → Danh sách / Thao tác / Duyệt

**Realtime check:**
- Login 2 tab (admin + user) → user tạo comment / transition → admin nhận toast + bell +1

## Cần làm kế tiếp

### 🔥 Priority 0 — Session 6 (Ops + UAT focus)

Đa số feature gap đã đóng. Còn lại chủ yếu Ops/UAT + optional polish.

**A. Hard blockers (chờ user / ops):**
1. **UAT thật 1 tuần với 2-3 user** — Drafter (QS/NV.PB) + CCM + BOD ghi bug/friction
2. **SMTP config** → bật Email outbox (BLOCKED chờ user cấp host/user/pass)
3. **Rotate credentials** — admin + 30 demo + SA + vrapp + JWT secret
4. **Schedule SQL backup daily** — `scripts/backup-sql.ps1` chưa schedule Task Scheduler

**B. PE feature gap còn lại:**
- **Export phiếu PDF/Excel** PE — pending (user nói không quan trọng lắm). Khi cần: tái dùng `IDocumentConverter` + template `PE-TrinhDuyet.docx`.

**C. Optional polish (làm khi UAT phát sinh):**
- Budget MaNganSach atomic sequence — chốt format → migration `AddBudgetCodeSequences` mirror Contract/PE
- Budget versioned workflow — admin config UI thay hardcoded `BudgetPolicy.Default`
- Payment terms PE tách field — JSON blob → 6 column riêng
- Auto-map PE Details → Contract per-type Details khi gen HĐ
- Matrix Quotes bulk paste từ Excel
- fe-user Inbox thêm section "Phiếu Duyệt NCC chờ tôi"

**D. Tests Phase 3-5 (làm khi gặp bug recurring để justify ROI):**
- Phase 3 — Application handler tests (CQRS + EF InMemory) ~15 test
- Phase 4 — API smoke tests (WebApplicationFactory) ~7 test
- Phase 5 — FE Vitest cho lib utility (queryMatches, fmtMoney) ~10 test

**Đã xong session 5 (check STATUS Recently Done):**
- ~~Module Ngân sách FE — 3-panel pages cả 2 app~~ ✅
- ~~PE/Contract → Budget integration + cột "So với ngân sách"~~ ✅
- ~~PE Detail UI restructure 4 section đánh số~~ ✅
- ~~PE Workflow Designer admin UI~~ ✅
- ~~Ý kiến 4 phòng ban (migration 15)~~ ✅
- ~~Tests Phase 1-2 + CI gate (71 test)~~ ✅

### A. Hard blockers (chờ user / ops)

1. **UAT thật 1 tuần với 2-3 user** — hard requirement từ roadmap. Kiến nghị:
   - User A: Drafter (QS/NV.PB) — tạo 3 HĐ mỗi type, đi hết 9 phase
   - User B: CCM — duyệt phase 6
   - User C: BOD — duyệt phase 7
   - Ghi bug / friction / đề xuất → backlog iter 2
2. **SMTP config** để bật Email outbox:
   ```json
   "Email": {
     "Host": "smtp.gmail.com",
     "Port": 587,
     "Username": "...",
     "Password": "...",
     "From": "noreply@solutionerp.local"
   }
   ```
   Khi có → thêm `MailKit`, `IEmailSender`, hook vào `NotificationService.CreateAsync` ngay trước khi enqueue realtime push.
3. **Rotate credentials** — SA SQL password, vrapp password, JWT secret prod, Gitea runner registration token
4. **Schedule SQL backup** — `schtasks /create /tn "SolutionErp Backup" /tr "powershell -File C:\...\scripts\backup-sql.ps1" /sc DAILY /st 03:00`

### A1. Định kỳ — Skill audit

**Cadence:** Mỗi đầu tháng (4 tuần). Lần audit kế tiếp: **2026-05-01**.

Workflow xem `docs/rules.md §9.4`. Tóm tắt:
1. Cross-check 6 skill hiện có với STATUS / gotchas / migration-todos
2. Check repo nguồn 3rd party (alirezarezvani/claude-skills) có gì mới
3. Update / archive / add skill nếu cần
4. Log vào `docs/changelog/skill-audit-2026-05.md`

Trigger: user nói "audit skill" hoặc tự chạy đầu Phase mới.

### B. Polish iterations (optional — khi UAT phát sinh)

- **Roles CRUD** — admin tạo custom role ngoài 12 hardcoded (`Domain.Identity.AppRoles`)
- **User-kind approver runtime** — data model `WorkflowStepApprover.Kind=User` + `AssignmentValue=userId` đã có, chỉ cần:
  ```csharp
  // ContractWorkflowService.TransitionAsync (bổ sung):
  var userApprovers = step.Approvers.Where(a => a.Kind == ApproverKind.User)
                                    .Select(a => Guid.Parse(a.AssignmentValue));
  if (userApprovers.Any() && !userApprovers.Contains(actorUserId))
      throw new ForbiddenException();
  ```
- **Grant `Workflows.Read` cho non-admin role** trong PermissionsPage → menu Wf_* auto-visible (inheritance đã có)
- **Warning notification 20% SLA** — job emit khi `SlaDeadline - now < sla * 0.2 && !SlaWarningSent`, set flag
- **Reject → DangSoanThao E2E test** với 3 role khác nhau
- **Deps scan CI** — `dotnet list package --vulnerable` + `npm audit --audit-level=high`

### C. Non-goals / parked

- E-signature (VNPT CA / FPT CA) — Phase 6
- Bravo/SAP import NCC — Phase 6
- Mobile app — Phase 6
- AI OCR scan HĐ — Phase 6+

## Lưu ý kỹ thuật quan trọng

**Đọc [`gotchas.md`](gotchas.md) (41 bẫy) trước khi:**

- Thêm package mới → .NET 10 compat (MediatR 14 fail → dùng 12.4.1)
- Debug TS enum error → dùng const-object pattern (`erasableSyntaxOnly`)
- Expression tree lỗi → tách switch ra ngoài LINQ
- Deploy Windows Feature (WebSockets, etc.) → unlock section ở applicationHost (gotcha #25)
- Workflow transition 403 → check `Contract.WorkflowDefinitionId` pin đúng không
- Migration lỗi → 3 file đầy đủ (Designer + Migration + Snapshot)

## Versioned workflow — quick ref

`Contract.WorkflowDefinitionId` pin tại Create. `ContractWorkflowService.LoadAsync` resolve order: pinned def → admin override → hardcoded Registry.For(type). Invariant: HĐ cũ giữ policy cũ qua FK Restrict. Detail: [`workflow-contract.md §7bis`](workflow-contract.md).

## File đang active

Cấu trúc thư mục + file map: [`PROJECT-MAP.md`](PROJECT-MAP.md). Git state: `git log --oneline -10`.

## Credentials + URLs

```
admin@solutionerp.local / Admin@123456    ← Admin (QTV)

Demo users — 30 user (User@123456):

  ── 16 sample @solutionerp.local (giữ cho test legacy) ──
  bod.huynh, bod.le              Tổng GĐ + Phó GĐ NĐUQ
  pm.nguyen                      GĐ Dự án FLOCK 01 (PM)
  ccm.tran, pro.pham, fin.do     TPB CCM/PRO/FIN
  act.vu, equ.bui, hra.dang      Kế toán trưởng / TPB EQU / TPB HRA
  qs.hoang, qs.ngo               QS công trường (NV.PB)
  nv.cao, nv.dinh, nv.truong     NV Cung ứng/Tài chính/CCM (NV.PB)
  bod.tran, pm.le                Bonus NĐUQ + PM thứ 2

  ── 14 Solutions thật @solutions.com.vn (session 4) ──
  PRO 5:  tra.bui (TPB) + phuong.nguyen, binh.lethanh, danh.huynh, dat.tran (NV)
  CCM 7:  ngocanh.huynh (TPB) + ha.dao, cuong.do, long.le, ha.nguyen,
                                dung.nguyen, anh.nguyen (NV)
  ISO 1:  chau.le
  CEO 1:  huy.duong

⚠ Rotate ALL passwords trước UAT thật
```

- API prod: https://api.solutions.com.vn — `/health/live`, `/health/ready`
- Admin FE prod: https://admin.solutions.com.vn
- User FE prod: https://eoffice.solutions.com.vn
- API dev: http://localhost:5443 — `/swagger` (Dev only)
- Admin FE dev: http://localhost:8082
- User FE dev: http://localhost:8080
- SQL dev: `(localdb)\MSSQLLocalDB` / `SolutionErp_Dev`
- SQL prod: `.\SQLEXPRESS` / `SolutionErp` / `vrapp` (⚠️ rotate)

## Đánh giá nhanh

**Tốt:**
- 3 domain HTTPS prod live, CI/CD xanh
- Tier 3 feature-complete: attachment, realtime, form builder (upload + DynamicForm + PDF), versioned workflow (admin-configurable per ContractType, pin per contract), nested menu per type, 3-panel permissions
- Clean-arch 3-project split đúng cho 2 cross-cutting service (realtime + document-converter)
- 26 gotchas tích lũy, 8 session log, 40 docs — agent onboard nhanh
- Invariant critical: "HĐ cũ giữ quy trình cũ" guaranteed by pinning (reference-based immutability, không snapshot copy)

**Rủi ro còn:**
- UAT thật chưa chạy → có thể phát hiện edge case missing
- SMTP chưa có → notification chỉ in-app (toast + bell), không email
- User-kind approver chưa enable guard runtime (designer cho pick, nhưng transition dùng Role fall-back)
- Credentials chưa rotate
- SQL backup chưa schedule Task Scheduler
