# STATUS — Snapshot hiện tại > **Update rule:** trước khi bắt đầu 1 task → ghi row vào `🔥 In Progress`. Xong → chuyển sang `✅ Recently Done`. **Last updated:** 2026-05-21 (Session 26 chốt cuối — **🎯 S26 cumulative wrap: 6 plan PE List tree view UI iteration + Plan AI RAG global MCP infrastructure setup**. **Plan AG/AG2/AG3/AG4/AG5/AG6** cumulative 6 commits Plan AG series `0bf6c7e..d99069a` push remote — UAT feedback bro Tra Sol 2026-05-21 screenshot "UI Duyệt NCC như đám rừng" → folder tree giống Outlook: **Plan AG** Phase 1 PE List tree view 2-level Project > Gói thầu > PE (Investigator audit 5Q + Implementer Case 2 cookie-cutter mirror 2 app + Reviewer pre-commit PASS 0 blocker + CICD Monitor Run #222 PASS bundle hash rotate). **Plan AG2** simplify 1-level (drop tầng gói thầu) + Panel 1 widen 340→400px. **Plan AG3** drop single-PE flat branch, consistent UI (mọi dự án dù 1/N phiếu đều render `
`). **Plan AG4** bổ sung Người tạo + Phòng ban tạo PE card — BE DTO `PurchaseEvaluationListItemDto` +4 fields (DrafterUserId/DrafterName/DepartmentId/DepartmentName) + 3 projection JOIN Users+Departments LEFT (ListHandler/InboxHandler/ApprovedHandler) + FE `PeListItem` type +4 fields + UI card render 👤 Drafter · Phòng ban. **Plan AG5** extend tree 3-level Project > Năm > NCC > PE (selectedSupplierName fallback "(Chưa chọn NCC)" cho PE chưa DaDuyet). **Plan AG6** compact card 3-row gọn đẹp (drop Type label redundant + combine mã phiếu/time + Drafter/Dept/HĐ inline). **Plan AI RAG global MCP setup** (User-level Approach A — 1 dev 5 dự án cùng máy localhost) — Phase 0 pre-flight + Phase 1 Qdrant v1.18.0 Windows native binary (KHÔNG Docker, PID 67240 port 6333) + Phase 2 FastMCP server ~1100 LOC Python 9 file (server.py + lib/embed.py voyage wrapper + lib/retrieval.py 3-layer pipeline + lib/projects.py auto-detect cwd + lib/chunking.py contextual prepend + lib/watcher.py) + Phase 3 register MCP user-level (`claude mcp add` với `--` separator trick) + Phase 4 bootstrap SOLUTION_ERP 126 files → **2,392 chunks indexed 60.9s** (~484K Voyage tokens = 0.24% free tier 200M/month). 4 test queries verified end-to-end recall: "gotcha #45 PE button TraLai" 0.641 / "admin opt-in per-NV" 0.836 / "EF migration backfill" 0.766 / "multi-agent setup" 0.879. Stack: Voyage-4-large embedding + Voyage rerank-2.5 + Qdrant Windows native + SQLite FTS5 BM25 + Reciprocal Rank Fusion + Anthropic Contextual Retrieval prepend + FastMCP 3.3.1 stdio. **+1 file MD onboarding `docs/guides/multi-agent-setup-guide.md`** xuất cho 4 dự án future (NamGroup/DH Y Dược/Ashico/Vipix detected từ Claude Desktop config epitaxy folder permission). Multi-agent execution S26: 🟦 Investigator 2 spawn (Plan AG audit 5Q ~30K + Plan AI RAG distribution research ~40K — 4 study cases Cursor/Cline/Continue/Sourcegraph) + 🟨 Implementer 1 spawn (Plan AG Case 2 cookie-cutter ~16K) + 🟥 Reviewer 1 spawn (Plan AG pre-commit ~25K PASS 0 blocker) + 🟩 CICD Monitor 1 spawn (Run #222 verify ~12K). Em main solo Plan AG2-AG6 + Plan AI Phase 0-4 (UI polish iteration + RAG infra setup — cost ROI thấp nếu spawn 5×). Stats final S26 chốt: **31 mig (no schema)** · 59 tables · ~146 endpoints · 35 FE pages · **111 test pass unchanged** (UAT defer test-after) · 49 gotcha unchanged · 23 memory user-level unchanged · 6 skills · 4 sub-agents · **+1 docs file `multi-agent-setup-guide.md`** + **+1 RAG infrastructure** `D:\.claude-rag\` (server + lib + qdrant-bin + data). Patterns reusable cross-project (3 NEW S26): (1) HTML native `
/` + Tailwind named groups `group/proj` + localStorage Set cho hierarchical tree UI when no Accordion lib available (Pattern 19 Implementer NEW), (2) RAG User-level Global MCP 1 server localhost serve N project (Approach A Plan AI), (3) Qdrant Windows native binary deployment (no Docker overhead — appropriate cho 1 dev solo scenario, không scale enterprise). **Memory FLAG**: cicd-monitor MEMORY ~74KB (+2KB từ S25), critical curate session URGENT next.) **Last updated S25:** 2026-05-19 (Session 25 chốt cuối — **🎯 S25 cumulative wrap: 7 plan + 7 commits Plan AB→AF + 7 CICD Runs (#215 FAIL → #216-#221 PASS)**. Push remote `e23f51c..506cada`. Bro UAT 2026-05-19 phát hiện 2 bug visibility critical PE/2026/A/032 + 4 follow-up polish UAT iteration: **Bug 1 Budget Adjust** không show "Lịch sử thay đổi" (BE log Header+Update OK nhưng FE filter strict TraLai-only loại) — **Bug 2 Return Mode 4 mode** không log Changelog (ApplyReturnModeAsync miss `db.PurchaseEvaluationChangelogs.Add()`) — **Bug 3 Lịch sử duyệt** missing Trả lại + Vượt cấp entries (Reject branch không add Approval row) — **Bug 4 dual-phase badges** gây nhầm "Đã gửi duyệt → Đã gửi duyệt" cho 3 mode Reject giữ Phase=ChoDuyet — **Bug 5 Changelog UserName** show "Hệ thống" thay vì user thật (9 sites BE miss UserName) — **Bug 6 historical name resolve** entries pre-deploy vẫn show empty (forward-only fix). **Plan AB Chunk A** (`cdfd542`) BE log return mode + FE filter relax × 2 app. **Plan AB Chunk A2** (`8c05947`) fix Plan M tests SQLite frozen-clock tie-break (gotcha #48 NEW — Run #215 caught + Run #216 PASS). **Plan AC** (`a734bf2`) BE Reject add Approval row + skipToFinal comment + FE Decision badge × 2 app. **Plan AC2** (`25837b6`) FE merge synthetic Reject rows từ Changelog historical (Option 2A bro chốt, reversible no-DB-touch). **Plan AD** (`0aaf2df`) Drop misleading dual-phase badges + parse next-target hint via regex `Chuyển phase X → Y` + comment keyword Trả lại/Vượt cấp (gotcha #49 NEW). **Plan AE** (`9ea62be`) BE preventive batch fix UserName 9 Changelog.Add sites (Budget Adjust + 8 sites: Create + Update + Detail CRUD + Quote CRUD + Select Winner). **Plan AF** (`506cada`) FE userMap fallback từ embedded domain data PeDetailBundle (drafter + approvals + approvalFlow + levelOpinions + departmentOpinions) — no extra API fetch admin permission. Multi-agent execution: 🟦 **Investigator** 1 spawn Plan AB Bug 1+2 audit ~28K + 🟨 **Implementer** 1 spawn Plan AB Chunk A ~12K Case 1 + 🟥 **Reviewer** 1 spawn Plan AB pre-commit ~22K PASS 0 blocker (1 minor non-block) + 🟩 **CICD Monitor** 7 spawns Run #215-#221 verify ALL PASS post-Plan AB Chunk A2 fix. **Em main solo từ Plan AC đến AF** (5 plan, cross-stack reasoning + UAT iteration borderline, Implementer/Reviewer KHÔNG re-spawn). Stats final S25 chốt: **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` Plan AC2 + `feedback_fe_usermap_fallback` Plan AF)** · 6 skills · 4 sub-agents active. Bundle hash 6× rotate × 2 app (#216-#221), Mig 31 unchanged toàn S25. Patterns reusable cross-project (8 NEW S25): (1) Multi-Changelog.Add SQLite tie-break test discriminator, (2) CICD catch UAT skip test risk for 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, (6) Drop misleading dual-phase badges + parse semantic hint, (7) Changelog UserName preventive systemic batch fix, (8) FE userMap fallback từ embedded domain data no extra API. **Memory CRITICAL FLAG**: cicd-monitor MEMORY ~72KB strongly over 50KB hard threshold — DEDICATED CURATION SESSION REQUIRED next (archive Run #186-#210 + S22-S24 verbose). Bro UAT verify ALL Plan AB→AF live deploy cumulative.) **Last updated S23 t12:** 2026-05-15 (Session 23 turn 12 chốt cuối — **🎯 S23 cumulative wrap: 11 plan + 32 commits cumulative**. Plan K (Mig 31 F2 refactor 9 commits) → L (UAT bug 5) → M (F1 edge case 4) → N (per-NV lookup 1 site 2) → O (4 sites cascade 2) → P (Controller body record 1) → Q (FE banner CSS 1) → R (cleanup phiếu/workflow 1) → S (wipe ALL workflows 1) → T (DemoSeed flag 2) → U (sidebar truncate 1). 4 sub-agents active throughout: 🟦 Investigator 5 spawn (K0 + N + P FE wire + R audit + L2 spawn) + 🟨 Implementer 5 spawn (K1/K3/K5/K7 + M2 + M3 Case 2/3) + 🟥 Reviewer 2 spawn (K2 pre-commit + M3 cumulative review) + 🟩 CICD Monitor 5 spawn (Run #195 K10 + #199 L + #200 M + #201 N + #202 O + #203 P + #204 Q + #207 T + #208 T5+T6 + #209 U — ALL PASS). **Memory user-level update cumulative S23:** `feedback_per_nv_permission_scope.md` reinforced 5 sections (S22+5 + S23 t1 + t3 + t4 + t5 + t6) → wire 10 surface points (point 9 lookup discrimination + point 10 Controller body record mirror). `feedback_uat_skip_verify.md` Plan L lesson Service refactor semantic test atomic. **+1 memory NEW** `feedback_demo_seed_flag_disable.md` cross-project Plan T pattern. **3 destructive cleanup cumulative Plan R+S+T5** ~720 rows wiped + DbInitializer permanent disable demo seed. **Stats final S23 chốt cuối:** 31 mig · 59 tables · ~145 endpoints · 34 FE pages · **111 test unchanged** · 47 gotcha · **21 memory entries** (+1 NEW Plan T flag pattern) · 6 skills · 4 sub-agents active · **0 PE + 0 demo workflow + DemoSeed flag persist** UAT permanent clean slate. Backup rollback: `vietreport-vps:C:\Backup\SolutionErp_pre_cleanup_2026-05-15.bak` 18.5MB. **32 commits S23** pushed remote `eb106f2..86d8806`. CI verify ALL PASS. Bro Designer setup workflow mới from scratch, KHÔNG còn auto re-seed contaminate. Plan B Contract V2 wire HIGH priority 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` + 2 V1 workflows + 1 V2 mẫu UAT TỰ ĐỘNG RE-SEED sau khi BE deploy commits Plan P+Q+R+S → IIS recycle → `DbInitializer.InitializeAsync` chạy lại 5 demo seed methods. Bro AskUserQuestion chốt Option A: disable demo seed vĩnh viễn qua flag config. Em main implement: `appsettings.json` add `"DemoSeed": { "Disabled": true }` + `appsettings.Development.json` override `false` cho dev test seed local + `DbInitializer.cs` add `using Microsoft.Extensions.Configuration` + `var demoSeedDisabled = config.GetValue("DemoSeed:Disabled")` + wrap 5 method conditional `if (!demoSeedDisabled)` (SeedWorkflowDefinitions V1 + SeedPurchaseEvaluationWorkflows V1 + SeedDemoContracts + SeedDemoPurchaseEvaluations + SeedSampleApprovalWorkflowsV2). KEEP: SeedRoles + SeedAdmin + SeedDepartments + SeedDemoUsers (30 UAT users) + SeedMenuTree + SeedAdminPermissions + SeedDemoMasterData (Supplier/Project) + SeedContractTemplates + SeedCatalogs + Backfill helpers. Note: `appsettings.Production.json` bị `.gitignore` (secrets), em set flag mặc định trong `appsettings.json` commit qua git — production inherit `true`. Run #207 sha=0b97840 PASS 3min24s → IIS deploy applied flag. T5 final DELETE: SCP `scripts/plan-t5-final-cleanup.sql` upload + sqlcmd -i 3 TRANSACTION DELETE → 4 PE + 1 V2 + 2 V1 = 7 rows + cascade child. T6 verify NO re-seed loop: `Restart-WebAppPool SolutionErp-Api` force IIS recycle → BE startup → wait healthy → sqlcmd verify: **PE=0 + V2=0 + V1=0** preserved (KHÔNG re-seed) + Users=33 + Suppliers=19 + Projects=9 + Contracts=7 (existing preserved). DemoSeed flag PROVEN active end-to-end. Total cumulative Plan R+S+T cleanup: ~677 rows wiped + DbInitializer re-seed disable vĩnh viễn. Stats final S23 t10: **31 mig** · 59 tables · ~145 endpoints · 34 FE pages · **111 test unchanged** · 47 gotcha · 20 memory · 6 skills · **0 PE + 0 workflow + flag disable demo seed** — UAT permanent clean slate. Bro Designer setup workflow mới from scratch, KHÔNG còn auto re-seed contaminate.) **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: "các cái demo quy trình cũ -> xóa hết luôn đi nhé". 4 workflows còn lại (2 V2 ghim + 2 V1 active) đều seed demo cumulative (`(mẫu UAT)` / `(clone)(clone)` / `(v01)` sample seed). Bro AskUserQuestion chốt Option A (Recommended): wipe ALL 4 workflows, UAT clean slate. Em main solo execute (Investigator audit Plan R đã cover scope precedent, Plan S = continuation cùng UAT cleanup phase). Backup rollback: Plan R backup `SolutionErp_pre_cleanup_2026-05-15.bak` 18.5MB vẫn capture full state pre-cleanup → reuse cho rollback. Script `scripts/plan-s-wipe-all-workflows.sql` 2 BEGIN/COMMIT TRANSACTION + SET QUOTED_IDENTIFIER ON. Execute via scp + sqlcmd -i: DELETE ALL ApprovalWorkflows (2 rows cascade Steps+Levels) + DELETE ALL PurchaseEvaluationWorkflowDefinitions (2 rows cascade Steps+Approvers). Post-state: **0 V2 + 0 V1 + 0 Steps + 0 Levels + 0 Approvers** (ALL workflow entities wiped). BE smoke verify 5/5 endpoints 200 (auth + PE list + V2 workflows + V1 workflows + users + menus) — KHÔNG crash startup (no Contract pin to V1 — chỉ PE dùng workflow, PE đã wipe Plan R). Hậu quả expected: user KHÔNG tạo được phiếu mới (Workspace Select empty) cho đến khi admin Designer seed workflow mới from scratch — UAT mode chấp nhận. Total cleanup cumulative Plan R + S: 35 PE + 17 V2 + 4 V1 + ~600 cascade child = **~670 rows wiped**, prod database UAT clean slate hoàn toàn. Stats final S23 t9: **31 mig** · 59 tables · ~145 endpoints · 34 FE pages · 111 test · 47 gotcha · 20 memory · 6 skills · **0 PE + 0 workflow** (database state mới hoàn toàn). Pending: admin Designer seed workflow mới + bro UAT test fresh.) **Last updated S23 t8:** 2026-05-15 (Session 23 turn 8 — **🧹 Plan R: Cleanup destructive prod database — phiếu test + workflow ko ghim**. Bro UAT confirm Plan P+Q wire OK + chỉ thị "OK Tao thấy tạm ổn rồi đấy, mày xóa hết các phiếu test cũ đi nhé, các quy trình cũ ko ghim cũng xóa hết đi. Cho gọn đẹp." 🟦 Investigator pre-flight audit prod ~64K spawn confirm scope: 28 PE active + 7 soft-deleted + 15 V2 workflows IsUserSelectable=false + 2 V1 workflows IsActive=false. FK Restrict gotcha: PE.ApprovalWorkflowId Restrict → phải hard-DELETE PE trước (soft-delete KHÔNG release FK). Entity extend `BaseEntity` (workflow) KHÔNG hỗ trợ soft-delete. Bro chốt Option A (Recommended) qua AskUserQuestion. Em main execute 6 steps qua sqlcmd ssh vietreport-vps: (1) BACKUP `SolutionErp_pre_cleanup_2026-05-15.bak` 18.5MB qua `scripts/plan-r-backup.sql` upload scp + sqlcmd -i (workaround SQL Express no COMPRESSION + no RESTORE VERIFYONLY permission cho vrapp). (2) Script `scripts/plan-r-cleanup.sql` 3 BEGIN/COMMIT TRANSACTION + SET QUOTED_IDENTIFIER ON (filtered index Mig 29+ require). (3) DELETE 35 PE rows (28 active + 7 soft-deleted, cascade ~446 child: 42 Details + 49 Suppliers + 64 Approvals + 238 Changelogs + 10 Attachments + 43 LevelOpinions). (4) DELETE 15 V2 unghim (cascade ~140 Steps+Levels). (5) DELETE 2 V1 inactive (cascade ~37 Steps+Approvers). Total: **52 rows + ~600 cascade child deleted**. Post-cleanup state: **0 PE · 2 V2 workflows ghim** (`QT-DN-V2-001 v16` + `QT-DN-PA-V2-001 v2`) · **2 V1 workflows active** (`QT-DN-A v3` + `QT-DN-B v1`). Smoke verify BE alive post-cleanup: 3/3 endpoints 200 (auth login + PE list + V2/V1 workflow list) → KHÔNG crash startup (Plan F precedent S22 avoid được — V1 active workflow giữ nguyên). Multi-agent ROI: Investigator catch FK Restrict gotcha + recommend backup mandatory + 3 Option compare — saved em main hard-delete without backup risk. Stats final S23 t8: **31 mig** · 59 tables · ~145 endpoints · 34 FE pages · 111 test unchanged · 47 gotcha · 20 memory · 6 skills · **0 PE phiếu test + 4 workflow ghim/active** (gọn đẹp UAT clean slate). Pending: bro UAT test workflow mới 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ự của F1+F2 fail**. 1 commit Plan P atomic. CICD Monitor Run #202 verify Plan O CRITICAL caveat catch: `PurchaseEvaluationsController.cs:267` `TransitionPeBody` record CHỈ có 3 fields (TargetPhase, Decision, Comment) — MISSING 3 fields có trong Command record `TransitionPurchaseEvaluationCommand` (ReturnMode + ReturnTargetUserId + SkipToFinal). `mediator.Send` line 70 cũng drop 3 field. → FE × 2 app SEND ĐÚNG 7 fields qua `api.post(/transitions)` body (Investigator verify FE wire OK 100%) → ASP.NET Core deserialization silently DROP 3 fields ở Controller layer → Handler nhận `ReturnMode=null` + `SkipToFinal=false` → fallback default Drafter mode + F2 không trigger. Bug present 2 NGÀY PROD từ Mig 28 deploy 2026-05-13 — gây TẤT CẢ 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 site). Fix Plan P: Controller body record +3 field default null/false + `mediator.Send` pass 7 fields + add `using SolutionErp.Application.PurchaseEvaluations.Services` cho WorkflowReturnMode enum import. ~10 LOC BE 1 file. dotnet test **111/111 PASS** (+0 từ 108 — không cần test mới vì Investigator confirm scope BE Controller only, FE wire đúng, Mig 28/31 Domain test đã cover handler logic). Multi-agent ROI: 🟦 Investigator audit FE 2 spawn ~58K confirm hypothesis BE-only scope, avoid em main fix sai cross-stack. 🟩 CICD Monitor Run #202 catch caveat critical lúc verify Plan O — invaluable bug catch chain. Pattern reinforced: **Controller body record MUST mirror Command record fields** khi Command thêm optional params — Mig 28 + Mig 31 cumulative thêm 3 fields nhưng Controller body record chưa update → silent drop bug. Stats final S23 t6: **31 mig** · 59 tables · ~145 endpoints · 34 FE pages · **111 test pass unchanged** · 47 gotcha · 20 memory · 6 skills · 4 sub-agents (Investigator FE wire + CICD Monitor verify spawns). CHƯA push remote — chờ CICD verify cuối.) **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 point 9 chỉ catch 1/5 sites**. 2 commits Plan O atomic + docs. Bro UAT 2026-05-15 sau Plan N deploy phát hiện tiếp: Actor NV Test (UAT V2) trong OR-of-N slot 4 NV click "Trả lại Người chỉ định" → toast "Không phải lượt bạn" mặc dù NV Test đúng trong slot. Em main audit grep `FirstOrDefault.*Order ==` toàn project — discovered 5 lookup sites total: Plan N catch 1 (GetPe handler), 4 sites khác CÙNG BUG còn nguyên: (1) `Service.cs:201` `EnsureCanRejectV2Async` actor match guard ← Bug bro UAT ROOT CAUSE, (2) `Service.cs:248` `ApplyReturnModeAsync` read Allow flag từ row đầu, (3) `DetailFeatures.cs:72` F3 `EnsureEditableForDetailsAsync` cùng bug, (4) `Features.cs:311` F4 `AdjustBudgetCommand` cùng bug. 👤 Chủ trì Solo fix 4 sites surgical ~30 LOC + add `Guid? actorUserId` param vào `ApplyReturnModeAsync` (4 param signature change) + caller `TransitionAsync:94` update + 3 regression test mới `PurchaseEvaluationPerNvLookupRegressionTests` (Actor non-first-row OR-of-N trả lại / Outsider không slot ForbiddenException / Actor có flag while others don't AllowsMode). dotnet test **111/111 PASS** (+3 từ 108 baseline Plan N). Pattern reinforced: **5 lookup sites checklist** sau Mig 29 OR-of-N — Plan N chỉ catch 1/5. Memory `feedback_per_nv_permission_scope.md` CRITICAL HOTFIX S23 t5 section updated với 5 sites enum + audit grep checklist. Bug 2 (F2 Duyệt thẳng chỉ đến Phan Văn Chương thay vì Nguyễn Văn Trường BOD) defer follow-up — F2 logic line 483-524 đã đúng, cần verify workflow v14 DB structure thực tế. Stats final S23 t5: **31 mig** · 59 tables · ~145 endpoints · 34 FE pages · **111 test pass (+3)** · 47 gotcha · 20 memory (1 entry CRITICAL HOTFIX section reinforced 2 lần) · 6 skills · 4 sub-agents (em main solo Plan O — bug fix reasoning chain cross 4 sites). CHƯA push remote — chờ CICD verify.) **Last updated S23 t4:** 2026-05-15 (Session 23 turn 4 — **🎯 Plan N: HOTFIX per-NV lookup site discrimination — BE bug critical UAT block**. 2 commits N1+N2 batch atomic + N4 docs. Bro UAT screenshot post-Plan M deploy: Admin Designer tick TRUE 7 flag cho NV Test (UAT V2) slot Bước 2 Cấp 1 workflow QT-DN-V2-001 v12/v13 (4 NV cùng Cấp) — actor login → dialog ✓ Duyệt KHÔNG có checkbox F2 skipToFinal + dialog ← Trả lại CHỈ 1 radio Drafter + KHÔNG có F3+F4 Edit options. 🟦 Investigator audit (~80K spawn) verify Hypothesis B: BE bug `PurchaseEvaluationFeatures.cs:765` `FirstOrDefault(l => l.Order == curLevelOrder)` thiếu discriminator `ApproverUserId == currentUser.UserId` — schema Mig 29 (S21 t5) refactor: 1 row per ApproverUserId (OR-of-N cùng Order) → handler luôn lấy row đầu DB (Lê Văn Bính — Drafter only), bỏ qua admin tick per-NV của actor. Bug PRESENT từ Mig 29 deploy 2026-05-13 nhưng chỉ bộc lộ khi lần đầu admin tick selectively per-NV. 👤 Chủ trì Solo fix N1 (5 LOC BE) + N2 regression test (~200 LOC test) + N4 docs — KHÔNG spawn Implementer vì bug fix reasoning chain. Fix: `var curLevel = curStep?.Levels.FirstOrDefault(l => l.Order == curLevelOrder && l.ApproverUserId == currentUser.UserId) ?? curStep?.Levels.FirstOrDefault(l => l.Order == curLevelOrder);` — actor match per-NV slot, admin/non-approver fallback row đầu (giữ behavior view detail). Test regression `GetPurchaseEvaluationCurrentLevelOptionsTests` 2 method: 4 actor distinct flag profile + admin fallback. dotnet test **108/108 PASS** (+2 từ 106). Pattern reinforced: **Per-NV admin opt-in flag** wire checklist phải bao gồm **9 surface points** (KHÔNG 8) — thêm point 9 "Handler lookup site `currentLevelOptions` MUST discriminate ApproverUserId". Cumulative Mig 29 + 30 + 31 đã wire 8 points đúng nhưng MISS point 9 — bug present 2 days production. Stats final S23 t4: **31 mig** · 59 tables · ~145 endpoints · 34 FE pages · **108 test pass (+2)** · 47 gotcha · 20 memory (1 entry reinforced narrative wire checklist 9 points) · 6 skills · 4 sub-agents (1 Investigator spawn). CHƯA push remote — chờ Reviewer/CICD verify.) **Last updated S23 t3:** 2026-05-15 (Session 23 turn 3 — **🎯 Plan M: Fix F1.OneLevel/OneStep edge case Bước 1 → giữ ChoDuyet (KHÔNG fallback Drafter) + FE label phase TraLai rename**. 3 commits Plan M `c2042ef..4dd6f9c` chuỗi M1→M3→M2: M1 (`c2042ef`) BE Service `ApplyReturnModeAsync` 2 edge case block (line 287-333) → reset (0, 1) giữ Phase=ChoDuyet thay vì fallback Phase=TraLai (semantic mới per bro chốt AskUserQuestion: "Cho phép nhưng vẫn giữ ChoDuyet, clear pointer thôi") + M3 (`508b17a`) FE × 2 app rename `PurchaseEvaluationPhaseLabel[98]` + `PeDisplayStatusLabel.TraLai` + 2 inline literal trong PeWorkflowPanel F1 dialog "Trả lại" → "Cần chỉnh sửa lại" (phase status badge, KHÔNG đụng action verb "← Trả lại" + mode picker "Trả về Người soạn thảo") + M2 (`4dd6f9c`) Tests add 2 edge case test `OneLevel_AtStep1Level1_ResetsToBuoc1Cap1_KeepsChoDuyet` + `OneStep_AtStep1_ResetsToBuoc1Cap1_KeepsChoDuyet` + helper `SeedWorkflowAsync` extend 2 param optional. Multi-agent execution: 🟦 Investigator audit pre-flight catch fact code main path đã đúng (F1.OneLevel/Assignee + F2 + F3 + F4 main path giữ ChoDuyet) chỉ edge case Bước 1 fallback sai + 🟨 Implementer 2 spawns Case 2+3 (M2 + M3) + 🟥 Reviewer pre-commit pending + 👤 Chủ trì M1+M4. Test final: **106/106 PASS** (+2 từ 104, 58 Domain + 48 Infra) — K7 Approver F2 NO cascade. F1.Drafter mode 4 GIỮ NGUYÊN Phase=TraLai semantic (explicit role mode). Memory user-level update 2 entry: `feedback_per_nv_permission_scope.md` reinforcement S23 t3 "edge case không lùi được KHÔNG fallback role khác" + `feedback_uat_skip_verify.md` Plan L lesson "Service refactor semantic BẮT BUỘC update test cùng commit". Stats final S23 t3: **31 mig** (no change) · 59 tables · **~145 endpoints** (no change) · 34 FE pages · **106 test pass (+2)** · 47 gotcha · 20 memory (2 entry reinforced) · 6 skills · 4 sub-agents. CHƯA push remote — chờ Reviewer verdict.) **Last updated S23 t1:** 2026-05-14 (Session 23 turn 1 — **🎯 Plan K: F2 refactor sang per-Approver-slot Mig 31** + UI consistency. 8 commits Plan K `56868bf..`: Chunk pre-A slot label rename → K1 Mig 31 schema swap (drop `Users.AllowDrafterSkipToFinal` + add `ApprovalWorkflowLevels.AllowApproverSkipToFinal` no BACKFILL) → K2 Service ApproveV2Async Approver F2 branch + DTO refactor → K3 Designer 7th checkbox + banner rewrite → K5 zombie endpoint cleanup + Reviewer Major #1+#2 + Minor #3+#4 → K6 Workspace × 2 app DROP Drafter + ADD Approver toggle → K7 tests regression 104/104 PASS (3 deleted + 3 added cancel) → K8 docs. Multi-agent execution: 🟦 Investigator K0 pre-flight + 🟨 Implementer K1+K3+K5+K7 (4 spawns Case 2+3) + 🟥 Reviewer K2 pre-commit (PASS 0 critical, 2 Major + 2 Minor) + 👤 Chủ trì K0-bis+K2+K6+K8. Bro decision: F2 semantic ĐỔI Drafter from Nháp → Approver during ChoDuyet skip thẳng Cấp cuối (mirror F3+F4 admin opt-in per slot) + setup ALL ở Workflow Designer (KHÔNG ở User Management). UI slot label "#NV {order}" → ApproverFullName. Stats final S23 t1: **31 mig (+1 Mig 31)** · 59 tables · **~145 endpoints (-1 backout /allow-skip-final)** · 34 FE pages · **104 test pass unchanged** (3 deleted + 3 added) · 47 gotcha unchanged · 20 memory (cumulative reinforce `feedback_per_nv_permission_scope` 3×) · 6 skills · **4 sub-agents active 4 spawns (Implementer Case 2+3) + 1 Reviewer**. 4 prod user lose `AllowDrafterSkipToFinal=true` value per Option A (admin re-config qua Designer). Pattern reinforced: per-NV admin opt-in flag 3× proven (Mig 29 F1+F3 + Mig 30 F4 + Mig 31 F2) — pattern ALSO applies cho refactor existing scope, KHÔNG chỉ greenfield. Plan B Contract V2 wire vẫn pending S23 t2+. CHƯA push remote — chờ bro confirm.) **S22 chốt cuối:** 2026-05-13 2200 (Session 22 CHỐT — **🎯 Bro chốt sub-agent solution OK**. 11 commits S22 pushed remote `3d725c4..b04a11a`. CICD Monitor Run #188 PASS verified. Highlights: Plan D F2 toggle + Plan C +20 test (test catch-up backlog) + Plan E strict V2 scope + Plan F ABORTED pre-flight (defer sau Plan B) + S22+1 fix disable 3 button bug + S22+2 seed 20 test user role-based naming + S22+3 rename users `{dept}.{level}@solutions.com.vn` + S22+4 attachment view inline PDF + Section "Điều chỉnh ngân sách" + S22+5 Mig 30 `AllowApproverEditBudget` per-NV opt-in (spec fix bro feedback). Stats final S22: **30 mig (+1 Mig 30)** · 59 tables · **~146 endpoints (+3)** · 34 FE pages · **104 test pass (+20: 5 reg #44 + 7 ReturnMode + 7 Guard + 1 V2 actor scope)** · **47 gotcha (+1 #47 paths-ignore agent-memory gap pending bro fix)** · 19 memory · 6 skills · **33 active users prod** (13 cũ + 20 mới role-based) · 4 sub-agents (CICD Monitor Run #188 PASS verify, 3 sub-agents seeds-only em main solo throughout S22). Pattern reinforced: per-NV admin opt-in flag 2× proven (Mig 29 + Mig 30) — anti-pattern default scope expansion. Memory `feedback_per_nv_permission_scope` reinforced S22+5 narrative.) **Last updated S22 prev:** 2026-05-13 1800 (Session 22 — Plan C + D + E done, Plan F ABORTED pre-flight fail. 5 commits local `60efeed`→`dbda37e`→`215b1e0`→`f149661`→Docs. Plan D BE+FE Admin User F2 toggle UI (PATCH /users/{id}/allow-skip-final + UsersPage column "Skip cuối" violet badge). Plan C task 4 5 reflection regression test gotcha #44 silent 403 ApprovalWorkflowsV2Controller policy split (catch class-level Policy regression). Plan C task 1-3 14 service test catch-up — ApplyReturnMode 4 mode per-NV Mig 29 + skipToFinal per-Drafter + EnsureEditableForDetails F3 gating. Plan E strict V2 scope List + Detail (remove UAT loose `|| ApprovalWorkflowId != null`, replace with V2 approver actor.UserId scope via ApprovalWorkflowLevels join). Plan F ABORTED — pre-flight prod sqlcmd reveal 23 PE + 7 Contract pin V1 + Contract entity HOÀN TOÀN V1 chưa wire V2 → drop V1 BE crash startup. Defer F sau Plan B Contract V2 + migrate 4 V1-only PE + UAT 2-3 tuần. Stats S22: 29 mig (0) · 59 tables · ~144 endpoints (+1) · 34 FE pages · **103 test pass (+19: 5 reg #44 + 7 ReturnMode + 7 Guard)** · 46 gotcha · 19 memory · 6 skills · 4 sub-agents (em main solo). Pattern reusable: SeedWorkflowAsync + SeedApproversAsync helper test infra + reflection Authorize regression test + pre-flight prod check pattern. CHƯA push remote — chờ bro confirm.) **S21 chốt cuối:** 2026-05-13 1530 (Session 21 chốt cuối — 5 turn cumulative pushed remote, CICD verified PASS 2/2 run, 1 gotcha mới #46 Gitea API path, 2 memory entry mới — `feedback_ef_migration_backfill_reorder` + `feedback_per_nv_permission_scope`. Session 21 5 turn timeline: t1 cicd-monitor add → t2 RAG planning Cách A → t3 fix gotcha #45 → t4 F1+F2+F3 Mig 28 → t5 refactor Allow* per-NV Mig 29. Stats final: 29 mig · 59 tables · ~143 endpoints · 34 FE pages · **84 test pass** · **46 gotcha (+2 từ S20: #45 PE button mismatch + #46 Gitea API)** · **19 memory entries (+3 từ S20: RAG + EF backfill + per-NV scope)** · 6 skills · 4 sub-agents (3 seeds-only + 1 cicd-monitor 2 runs PASS). 12 commits pushed `3a34831..c0af9e0`. Plan G Trial Week 1 evidence: CICD Monitor catch 0 fail vì green 2/2 run, cost 110-120K/run under 150K budget, 3-3.5min CI time stable. KHÔNG còn pending push. UAT mode skip dotnet test mỗi chunk, test-after Plan C bundle cho tất cả turn 3-5 sau UAT 2-3 lần ổn.) **S21 turn 5:** 2026-05-13 1400 (Session 21 turn 5 — **🎯 Refactor F1+F2+F3 sang PER-NV (Mig 29 drop Mig 28 column workflow-level). 4 chunk per-commit `0366946` (A BE schema+Service refactor) → `63234b2` (B FE Admin Designer 5 checkbox per-Level row) → `5ccb2a7` (C FE eOffice rename currentLevelOptions + drafterAllowSkipToFinal) → this Chunk D Docs. **F1+F3** 5 flag MOVED xuống `ApprovalWorkflowLevels` (per slot Approver, mỗi NV có riêng quyền duyệt). **F2** AllowDrafterSkipToFinal MOVED xuống `Users` (per-Drafter user, admin config ở User Management). Mig 29 4-stage: ADD 5 column Levels + 1 column Users + BACKFILL bulk SQL (copy workflow → all Levels, set TRUE cho Drafter user nào từng dùng workflow Allow) + DROP 6 column workflow `ApprovalWorkflows`. Service `ApplyReturnModeAsync` refactor đọc `currentLevel.AllowXxx` thay vì `workflow.AllowXxx`. Helper `EnsureEditableForDetailsAsync` read `level.AllowApproverEditDetails`. DRAFTER trình branch read `userManager.FindByIdAsync(actorUserId).AllowDrafterSkipToFinal`. DTO refactor: `AwLevelDto +5 Allow*`, `AwDefinitionDto -6 Allow*`, `CreateAwLevelInput +5 Allow*`, `PeDetailBundle.workflowOptions → currentLevelOptions + drafterAllowSkipToFinal`. FE Admin Designer drop section "Cấu hình nâng cao" workflow-level, replace 5 checkbox grid-cols-2 inline mỗi Level entry row (5 flag per slot). FE eOffice rename `wfOptions → levelOptions` đọc `currentLevelOptions`. Backward compat: backfill preserve admin config S21 t4. Test 84/84 PASS unchanged. Stats: **29 mig (+1) · 59 tables · ~143 endpoints · 34 FE pages · 84 test pass · 45 gotcha · 17 memory · 6 skills · 4 sub-agents seeds-only.**) **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`→`c56024b`→`a508564`→`d27caaf`→this Chunk E Docs. **F1** 4 mode Trả lại admin tick: "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 S17 fallback. **F2** Drafter skip thẳng Cấp cuối — workflow tick + Workspace checkbox dynamic confirm. **F3** Approver edit Section 2 (Hạng mục/NCC/Báo giá) khi workflow tick + actor match CurrentLevel + audit ghi PurchaseEvaluationChangelog. Mig 28 `ApprovalWorkflows +6 bool Allow*` (DEFAULT 1 cho AllowReturnToDrafter backward compat, 5 còn lại 0). BE Service `TransitionAsync` extend 3 optional param (returnMode/returnTargetUserId/skipToFinal) + helper `ApplyReturnModeAsync` switch 4 mode. Detail/Quote/Supplier helper `EnsureEditableForDetailsAsync` mới (kế thừa `EnsureDraftAsync` + add ChoDuyet+F3 branch + Admin bypass). FE Admin Designer "Cấu hình nâng cao" section 6 checkbox 3 group. FE eOffice 3 changes mirror 2 app: Trả lại radio picker 1-4 mode + Workspace skip checkbox violet + Section 2 itemsReadOnly approver banner. UAT mode skip dotnet test mỗi chunk (per `feedback_uat_skip_verify`), `npm run build` × 2 app pass mỗi chunk. Stats: **28 mig (+1)** · 59 tables · **~143 endpoints (+1 user-selectable patch existed)** · **34 FE pages (+1 Designer section)** · **84 test pass unchanged** (UAT defer test-after) · **45 gotcha unchanged** · 17 memory · 6 skills · 4 sub-agents seeds-only.**) **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). 2 chunk per-commit `de00887` (BE Chunk A) + `4b29d00` (FE Chunk B) + Chunk C Docs this. 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 ApproveV2Async UPSERT opinion "đã duyệt" + advance Cấp. Inconsistency phụ: dialog `isSendBack` (L247-248) cùng pattern thiếu TraLai → dialog title sai. Fix BE defense-in-depth + FE 3 chỗ × 2 app mirror rule §3.9. Test-before §7 BẮT BUỘC: 3 regression test mới (2 reproduce bug + 1 happy path control) — `dotnet test SolutionErp.slnx` 84 PASS (58 Domain + 26 Infra = +3). `npm run build` × 2 app pass. Stats: 27 mig (no change) · 59 tables · ~142 endpoints · 34 FE pages · **84 test pass (+3)** · **45 gotcha (+1 #45)** · 17 memory entries (no new) · 6 skills. Em main solo (no sub-agent spawn S21 t3 — bug fix reasoning chain cross BE/FE Implementer REFUSE per multi-agent rule).**) **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). Em main solo (no SOLUTION_ERP sub-agent spawn), delegate claude-code-guide × 2 research Anthropic + community practice. Decision chốt: Cách A defensive (giữ blanket 120K em main + RAG retrieve supplement) over Cách B aggressive (cắt 60-70% blanket). Industry-validated cross 4 Anthropic blog + 5 community tools (Cursor/Continue/Cline/Aider all hybrid). Stack: Voyage-3-large + Qdrant local + FastMCP Python + Streamlit dashboard 7 pages + SQLite event log. Multi-agent cost reality: 4 agents → ~520K cumulative blanket → heavy session ~560K (Cách A) vs ~700K (lazy), saving -20%. 3-layer pattern Phase 1-3 rollout (Layer 1 vector → Layer 2 +BM25 → Layer 3 +reranking, recall ~70% → ~92%). Stats: +1 memory entry (`feedback_rag_hybrid_pattern.md`) +1 plan file (`rag-setup-plan.md` 1500 LOC final). 4 sub-agents vẫn seeds-only. Plan I NEW deferred chờ bro confirm 5 dự án path + stack + Voyage API key + disk cleanup 5-8GB.**) **S21 turn 1:** 2026-05-12 0030 (Session 21 turn 1 — **🎯 Add con thứ 4 cicd-monitor (Path A — post-deploy verifier green READ tier). 1 commit `f1c61c9` pushed `36e21c8..f1c61c9 main -> main`, CI skipped per path filter (`**/*.md` paths-ignore docs-only). Trade-off: +~150K spawn extra mỗi run, đổi lại catch deploy ship fail tự động (bundle hash unchanged / mig drift prod / endpoint 500) — recurring blind spot pattern em main solo S20 quên verify ~30% push. Cost reality update: ~750K spawn setup (3 → 4 agents) · ~1.35M heavy session · ~700K optimized cached. Stats: 4 sub-agents seeds-only (+1 cicd-monitor green) · 16 memory entries (no new, update existing `feedback_multi_agent_setup.md` 3 → 4 agents narrative) · 27 mig · 59 tables · ~142 endpoints · 81 test unchanged · 44 gotcha unchanged · 6 skills unchanged. KHÔNG flush 3 agent MEMORY.md (chưa spawn work S21 t1 nên KHÔNG có findings — em main solo via context + Write file).**) **S20 wrap:** 2026-05-11 22:00 (Session 20 wrap turns 1-12 — **🎯 14 commit `9dee00d` → `ae1814c`. PE Detail UI restructure 3 yêu cầu (t1-5) + Manual budget drop tên (t6) + Mig 27 admin menu eOffice (t7) + NCC palette 5-màu cycle + Winner icon ✓ đậm + AddSupplier auto-fill master + Responsive laptop nhỏ 4-tầng pattern (t8-11) + Multi-agent infrastructure setup 3 sub-agents (t12). 27 mig (+1) · 59 tables · ~142 endpoints (+1) · 34 FE pages (+1) · 61 menu key (+1) · 81 test pass unchanged · 44 gotcha · 16 memory entries (+2) · 3 sub-agents NEW. Phase 9 UAT iteration mode.**) **S20 turn 7:** 2026-05-11 17:00 (Session 20 turn 7 — **🎯 Admin Ẩn/Hiện + Đổi tên menu eOffice (Mig 27). 5 chunk `2ea2d27`→`ef394f8`→`059bfcb`→`1ed6530`→Chunk E Docs. User Q2=b: DisplayLabel CHỈ áp fe-user, admin sidebar giữ Label gốc. Domain MenuItem +IsVisible(true) +DisplayLabel(200). Mig 27 AddVisibilityAndDisplayLabelToMenuItems. BE PATCH /api/menus/{key} [Authorize Policy=Permissions.Update]. NEW FE-admin MenuVisibilityPage ~210 LOC (table inline edit per-row + Save dirty + Khôi phục mặc định + Toggle Eye/EyeOff + 4 StatCard). fe-user Layout filterForUser 2 tầng (USER_HIDDEN_KEYS hardcode + !isVisible dynamic) + effectiveLabel(displayLabel || label) replace 3 callsite. fe-admin Layout KHÔNG đụng. +1 menu key MenuVisibility "Menu eOffice" leaf System Order=94. 27 mig, 59 tables, ~142 endpoints, 34 FE pages, 81 test pass (Q4 UAT defer).**) **S20 prev:** 2026-05-11 (Session 20 — **🎯 PE Detail UI restructure 3 yêu cầu user UX. 4 chunk per-commit `9dee00d` → `2bba851` → `f2f01f4` → (current Chunk D Docs).** Q1=a (giữ Section "Chọn NCC TP" riêng), Q2=a "1 hạng mục trước tiên" (NCC shared, demo 1 hạng mục), Q3=a (chỉ hiện NV đã ký), Q4 public luôn (skip dotnet test mỗi chunk theo memory `feedback_uat_skip_verify`, vẫn `npm run build` × 2 app mỗi chunk vì có rename/remove function). **Chunk A (`9dee00d`)**: BE `CreatePurchaseEvaluationCommandHandler` thêm 1 PurchaseEvaluationDetail mặc định khi tạo phiếu — GroupCode="01", GroupName="Hạng mục chính", NoiDung=TenGoiThau, DonGiaNganSach=ThanhTienNganSach=Budget.TongNganSach hoặc BudgetManualAmount fallback 0; Changelog Insert audit. FE reorder PeDetailTabs (mirror 2 app) 1.Thông tin / 2.Hạng mục (lên #2) / 3.Chọn NCC / 4.NCC tham gia / 5.Ý kiến. **Chunk B (`2bba851`)**: ItemsTab restructure thành list `HangMucCard` (1 card / 1 hạng mục, expanded=true mặc định cho 1 hạng mục demo). Header card: GroupCode + NoiDung + 3 stat (KL/ĐG/TT) + NS link Δ nếu có + Pencil/Trash actions + ▼/▶ toggle expand. Expand body: NCC inline table columns 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. Quote inline click cell → QuoteDialog cũ reuse. Add NCC + Sửa NCC reuse AddSupplierDialog/EditSupplierDialog cũ. Winner ✓ button mỗi NCC row. Drop function `SuppliersTab` (dead code ~134 LOC, replace bằng HangMucCard expand panel). Giữ AddSupplierDialog + EditSupplierDialog + SupplierAttachmentsCell (HangMucCard call lại). Section layout cuối: 1.Thông tin / 2.Hạng mục + Báo giá NCC (nested) / 3.Chọn NCC TP thắng thầu / 4.Ý kiến cấp duyệt — 4 section. **Chunk C (`f2f01f4`)**: Section Ý kiến restructure render layer (KHÔNG đụng Mig 26 schema — vẫn UPSERT 1 row / Level). LevelOpinionsSectionV2 forEach step → 1 `StepOpinionsBox` (replace grid-cols-2 cho N approver). Box header: "Bước N — Tên" + dept badge emerald + "X/Y đã duyệt" counter. Body: filter opinions theo step.order → sort levelOrder asc, signedAt asc → render `StepOpinionEntry` per signed opinion (tên NV + Cấp badge slate + admin override badge amber nếu có + emerald rounded-full timestamp + comment text). NV chưa duyệt KHÔNG hiển thị (Q3=a). Drop function `LevelOpinionBox` (replaced). Mirror fe-admin + fe-user. Verify build pass cả 2 app sau khi catch TS6133 `SuppliersTab` + `SupplierAttachmentsCell` unused (đã giải quyết: drop SuppliersTab, restore SupplierAttachmentsCell vào HangMucCard cột "File báo giá"). 81 test pass (no change — UAT defer)**) ## 📍 Phase hiện tại: **Phase 9 active — UAT V2 testing với user thật** — **59 DB tables, 29 migrations (Mig 28 +6 column workflow-level S21 t4 → Mig 29 refactor sang per-NV S21 t5 — 5 column trên ApprovalWorkflowLevels + 1 column trên Users + DROP 6 column workflow-level), ~143 API endpoints, 34 FE pages. 84 unit test pass** (58 Domain + 26 Infra = +3 PE guard S21 t3, UAT defer test-after per §7 cho S21 t4+t5). **46 gotcha (+1 S21 t5 chốt: #46 Gitea API path/cache)**. 30 demo user + 1 test user UAT. 6 skill. **5 trạng thái phiếu** (Nháp/Đã gửi duyệt/Trả lại/Từ chối/Đã duyệt). **2 Workflow schemas đồng tồn tại** post-Session 17 + Per-NV refactor S21 t5: (1) Mig 21 `WorkflowDefinition` flat (V1) — pin với PE/Contract cũ. (2) Mig 22-29 `ApprovalWorkflow` (V2) — pin với PE mới, Mig 29 các flag per-NV ở Levels + Users. Service PE branch theo `ApprovalWorkflowId`. **4 sub-agents:** 🟦 Investigator + 🟨 Implementer + 🟥 Reviewer (3 seeds-only S21 — em main solo cross-stack reasoning chain REFUSE) + 🟩 CICD Monitor (2 runs PASS, ~110-120K/run cost). 30 demo user + 1 test user UAT. 6 skill. **5 trạng thái phiếu** (Nháp/Đã gửi duyệt/Trả lại/Từ chối/Đã duyệt). **2 Workflow schemas đồng tồn tại** post-Session 17: (1) Mig 21 `WorkflowDefinition` flat (V1) — pin với PE/Contract cũ + match Dept+PositionLevel. (2) Mig 22-26 `ApprovalWorkflow` (V2) — pin với PE mới + match ApproverUserId 1-1, Steps/Levels group by Order, Bước (Phòng) > Cấp (N NV OR-of-N), Mig 25 +IsUserSelectable admin pin per version, **Mig 26 +PeLevelOpinions sign-off dynamic theo Level**. Service PE branch theo `ApprovalWorkflowId` set or null. Sau UAT chốt → migrate + drop V1 + Contract V2 wire. ### 🌐 Production URLs - https://api.solutions.com.vn — API (Let's Encrypt, auto-renew via win-acme) - https://admin.solutions.com.vn — Admin FE (HTTP→HTTPS auto-redirect) - https://eoffice.solutions.com.vn — User FE (HTTP→HTTPS auto-redirect) - https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp — Gitea repo + Actions - Default admin: `admin@solutionerp.local` / `Admin@123456` ⚠️ **RE-ROTATE sau login đầu** ## 🔥 In Progress — Session 7+ (Phase 9 active) ### A. Hard blockers (chờ user / ops) - [ ] **UAT thật 1 tuần** với 2-3 user (Drafter / CCM / BOD) — hard requirement Phase 5 roadmap - [ ] **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` đã sẵn, chưa schedule Task Scheduler - [ ] **Remove binding cũ `.huypham.vn`** sau verify stable: `ssh vietreport-vps ; .\migrate-domains.ps1 -RemoveOld -SkipCert` - [ ] **win-acme scheduled task "unhealthy"** — auto-renew fix trước 2026-06-18 ### B. Carry over feature gap - [ ] **Export phiếu PDF/Excel** PE — tái dùng `IDocumentConverter` + template `PE-TrinhDuyet.docx` (user pending — không quan trọng lắm) ### C. Optional polish (khi UAT phát sinh bug) - [ ] Budget MaNganSach atomic sequence (hiện Random.Shared → migration `AddBudgetCodeSequences`) - [ ] 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 — rule §7) - [ ] **Phase 3 full** — Application handler tests cần UserManager DI helper (PE Opinion Upsert, Budget validate, CreateContractFromEvaluation BudgetId carry) ~15 test - [ ] **Phase 4** — API smoke tests qua WebApplicationFactory ~7 test - [ ] **Phase 5** — FE Vitest cho lib utility (queryMatches, fmtMoney) ~10 test ### E. 2-stage dept approval — Chunk E-bis ✅ DONE (Session 9) - [x] **FE Workflow Panel** PE — section "Tiến trình duyệt 2-cấp phòng ban" group by phase × dept, highlight amber khi chờ TPB confirm - [x] **FE UserManager toggle** `CanBypassReview` — column "Bypass" badge fuchsia + button toggle ShieldCheck (UsersPage) - [x] **HĐ 2-stage** mở rộng — `ContractWorkflowService` thêm UserManager DI + 2-stage logic mirror PE + `ContractDepartmentApprovalFeatures.cs` + endpoint `GET /contracts/{id}/department-approvals` + FE WorkflowHistoryPanel section mới - [x] **Budget 2-stage** mở rộng — `TransitionBudgetCommandHandler` thêm INotificationService + IDateTime + 2-stage logic + `BudgetDepartmentApprovalFeatures.cs` + endpoint + FE BudgetWorkflowPanel section - [x] **Tests 2-stage** (6 test) — `IdentityFixture` setup full Identity stack + 6 test PE workflow service: NV review block / TPB confirm allow / NV bypass / Admin skip / Reject set / Resume jump-back. Pattern reusable. ### F. Audit định kỳ (cron tự fire) - [x] **2026-05-01** (audit 2026-05-04 manual trigger sau trễ 4 ngày) — combined audit log `docs/changelog/skill-audit-2026-05.md`. Cron Claude SDK KHÔNG fit monthly cadence (auto-expire 7d) → user setup OS Task Scheduler nếu cần auto-remind. - [ ] **2026-06-01** — combined audit kế. Trigger thủ công khi đến ngày hoặc user nói "audit MD" / "kiểm tra docs" / "định kỳ kiểm tra". ## ✅ Recently Done (newest on top) | Ngày | Ai | Task | Commit | |---|---|---|---| | 2026-05-15 | Claude | **🎯 SESSION 24 chốt cuối — Plan AA cumulative 7 commits (3 core + 4 polish UAT iteration)** — Plan AA Phase 1 core 3 commit (Chunk A BE+Layout + Chunk B FE matrix page + Chunk C Docs initial). Phase 2 polish 4 commit liên tục UAT feedback bro: `da218f1` hotfix px-6→px-2 (content sát sidebar) + `4d60598` redesign v1 panel-per-NV mirror admin Designer color coding (drop table symbol khó hiểu) + `fbbd361` redesign v2 HTML table rowSpan tận dụng full width (Bước/Cấp/NV/Quyền duyệt grid 2-col 7 label tiếng Việt nguyên văn) + `ee0902a` wrap fix sidebar label dài về đầu hàng (hanging-indent reverse CSS: flex→block + inline-block icon + inline text + absolute ChevronDown + text-[12px] leading-snug × 5 sites mirror 2 app). Patterns reusable saved: (1) Tailwind JIT palette array (5-color cycle Step + Cấp), (2) HTML table rowSpan flat row builder helper, (3) hanging-indent reverse CSS pattern. Multi-agent ROI: ~175K total (Investigator 32K Pre-A + Implementer 14K Chunk B + Reviewer 25K cumulative + CICD 12K Run #210 + 3 agent flush ~12K + em main ~80K) ≈ 28% solo equiv. **CICD Run #210 PASS 4/4 wire end-to-end**: IsUserSelectable filter live (admin no-filter / admin ghim / nv.test ghim all HTTP 200) + menu Pe_DuyetNcc_WfView present + Order shift idempotent 1-10 contiguous + non-admin permission Read OK + bundle hash rotate 2 app + Mig 31 unchanged. Pattern reinforced cumulative: gotcha #44 class-level Authorize relax PROVEN cross-stack reuse S18→S24 (Workflows endpoint từ admin-only sang any-auth read accessible). Test 111/111 PASS unchanged post-Plan AA (BE filter param + DbInitializer refactor no regression). Stats: 31 mig · 59 tables · **~146 endpoints (+1)** · **35 FE pages (+1 WorkflowMatrixViewPage)** · 111 test · 47 gotcha · 21 memory · 6 skills. **Implementer agent MEMORY ~31.5KB > 25KB threshold** → recommend curate next session archive S20-S22 old entries. | `ee776d5` (A BE+Layout) · `c667802` (B FE page) · `ac2c859` (C Docs init) · `da218f1` (px-2 hotfix) · `4d60598` (redesign v1 panel) · `fbbd361` (redesign v2 table rowSpan) · `ee0902a` (wrap fix label) · this Docs final | | 2026-05-15 | Claude | **🎯 SESSION 24 turn 1 — Plan AA User Workflow Matrix view + Sidebar widen revert Plan U truncate (3 commit)** — Bro UAT request: (1) sidebar Plan U S23 t11 truncate "..." → hiển thị đầy đủ label custom Mig 27, (2) thêm menu "Luồng duyệt" trên Danh sách hiển thị ma trận phân quyền workflow Designer ghim cho user xem (filter `IsUserSelectable=true` Mig 25). Q&A clarify 2 lượt: Q1 Permission strategy "Relax class-level [Authorize]" (đã fix gotcha #44 permanent từ S18 → CHỈ cần add filter param), Q2 Matrix layout "Table 2D full 7 Allow* cột". Plan 4 chunk: Pre-A (🟦 Investigator audit ~32K, confirm gotcha #44 + sidebar `w-72 xl:w-80` SAFE + Order strategy shift existing) + Chunk A (👤 Chủ trì Solo BE + Layout: MenuKeys `PurchaseEvaluationWorkflowView` helper + DbInitializer tree.Add LuongDuyet Order=2 + INSERT-OR-UPDATE-Order refactor idempotent + Permission seed 7 role + Handler `IsUserSelectable bool?` filter + Controller pass-through + sidebar `w-60 xl:w-72` → `w-72 xl:w-80` × 2 app mirror + revert Plan U truncate × 5 sites; commit `ee776d5` 6 files +73/-24) + Chunk B (🟨 Implementer Case 2 ~14K: WorkflowMatrixViewPage.tsx ~215 LOC + types/approvalWorkflowV2.ts ~55 LOC + App.tsx +route 2 LOC; commit `c667802` 3 files +305) + Chunk C (🟥 Reviewer cumulative ~25K: PASS 0 critical/major/minor blocker + Smart Friend guard active + fe-admin build verify bonus 1926 modules 740ms). Pattern reinforced: (1) gotcha #44 relax pattern PROVEN cross-stack reuse — Workflows endpoint từ admin-only sang any-auth read accessible. (2) DbInitializer INSERT-only → INSERT-OR-UPDATE-Order safe shift existing prod rows (idempotent re-deploy). (3) Plan U truncate revert pattern: widen sidebar + drop truncate, keep `min-w-0 flex-1` + `shrink-0` + `title` tooltip (no harm). **Surprise:** PE Workspace là **2-panel** không phải 3-panel — memory `feedback_responsive_laptop_breakpoint.md` stale claim → defer update memory. **3 commits push pending** (`ee776d5..`). Stats: 31 mig · 59 tables · ~146 endpoints (+1 query filter param) · **35 FE pages (+1)** · 111 test (unchanged UAT mode) · 47 gotcha · 21 memory · 6 skills · 4 sub-agents (3 spawn S24 t1). | `ee776d5` (A BE+Layout) · `c667802` (B FE page) · (this Docs C) | | 2026-05-12 | Claude | **🎯 SESSION 21 turn 2 — RAG Hybrid setup planning + Cách A validation deep dive (2 commit `1f8e9af` plan save + this chốt)** — Sau S21 turn 1 chốt cicd-monitor, 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 **claude-code-guide × 2** spawn agent research Anthropic + community practice. **Q&A deep dive 10 topics**: (1) RAG fundamentals + Vector DB Qdrant role, (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 (overview + drill-down + compare + audit + cost + change + admin), (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** (defensive hybrid: giữ blanket 120K em main + RAG retrieve supplement, sub-agent spawn baseline ~100K each, 4 agents = ~400K cumulative, heavy session billed ~560K saving -20% vs lazy 700K, quality recall ~85%) over **Cách B bỏ** (aggressive cut 60-70% vi phạm priority em main control flow strong + reasoning fragmented + UX latency +1-2s/state Q + risk severe RAG fail). **Why Cách A** (bro priority chốt): em main control flow strong preserve, decision quality 90% multi-source cohesive, wall-clock -20% (12 phút vs 16), risk-averse graceful fallback, multi-agent leverage cache 70-90%, industry-validated 9 sources. **3-layer hybrid Phase rollout**: P1 (W1-4) vector only Voyage-3-large recall ~70% $1.50/mo · P2 (M2) +BM25 bm25s free recall ~78% $1.50/mo · P3 (M3) +Voyage rerank-2 + Contextual prefix recall ~92% $4-5/mo. **Stack validated** cross-industry: Voyage AI embedding (Anthropic partner, multilingual 26 lang, $0.36 initial), Qdrant local (Rust 50MB, agent-native 2026 leader, ~3GB disk 5 project), FastMCP Python (official SDK, ~100 LOC), SQLite event log (5 tables + audit history), Streamlit 7 pages. **Plan I NEW deferred** — trigger bro confirm 5 dự án path + stack + pilot + Voyage API key + disk cleanup → dedicated session 10-14h weekend (per `feedback_drastic_refactor_scope` rule). **Deliverables**: `docs/rag-setup-plan.md` 1223 LOC commit `1f8e9af` + extend S21 t2 ~300 LOC = ~1500 LOC final, memory `feedback_rag_hybrid_pattern.md` cross-project reusable, session log this chốt, MEMORY.md index +1 entry. **CI skipped** path filter (`.md`). **4 sub-agents vẫn seeds-only** (KHÔNG spawn S21 turn 2 nên KHÔNG flush MEMORY.md per §6.5 KHÔNG add noise). Tests baseline 81 unchanged. | `1f8e9af` (plan save) · this chốt (commit final) | | 2026-05-12 | Claude | **🎯 SESSION 21 turn 1 — Add con thứ 4 cicd-monitor (Path A — post-deploy verifier green READ tier, 1 commit `f1c61c9`)** — User chốt Path A sau pre-flight Plan G Trial Week 1: thêm sub-agent thứ 4 chuyên post-deploy verify (Gitea Actions poll + bundle hash 2 app verify + sqlcmd mig prod = repo latest + endpoint smoke). Trade-off: +~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. **2 file mới**: `.claude/agents/cicd-monitor.md` (~7KB) — system prompt + 8-step workflow (verify push → poll Gitea API → fail log grep → live curl smoke → bundle hash × 2 app + verify changed → sqlcmd mig prod = repo latest → report PASS/FAIL/PARTIAL/TIMEOUT/SKIPPED-DOCS) + 5-stage report table + gotcha #25/#39/#40/#41/#44 cross-ref + skill `iis-deploy-runbook`/`dependency-audit-erp`/`ef-core-migration` preload + Anti-pattern 9 rules. `.claude/agent-memory/cicd-monitor/MEMORY.md` (~5KB seed) — recurring CI bug patterns + 5-stage checklist + baseline build/bundle metrics + bearer test pattern admin/nv.test. **1 file update repo**: `.claude/agents/README.md` — 4-agent architecture diagram (green slot mới) + decision tree (after push code + prod issue diagnose branches) + memory routine 4 SendMessage + skills preload 4 agents + cost reality table 564K → 750K spawn / 1.2M → 1.35M heavy / 600K → 700K optimized + trial workflow Week 1-3 CI/CD Monitor spawn integrated + pass criteria + catch ≥1 deploy ship fail. **Memory user-level update**: `feedback_multi_agent_setup.md` — title 3 → 4 sub-agents, decision tree +CI/CD Monitor invocation branches (after push + user prod issue), skills preload list +CI/CD Monitor (iis-deploy-runbook + dependency-audit-erp + ef-core-migration), cost table update + trade-off rationale (recurring blind spot ~30% push S20). **CI skipped**: all 3 file changed `.md` → match `paths-ignore: '**/*.md'` per gotcha #41 → no Gitea Actions run → no IIS deploy (expected — agent infra là local Claude Code, không cần present trên prod). Push success `36e21c8..f1c61c9 main -> main`. **3 (now 4) sub-agents vẫn seeds-only**: chưa spawn work nào — em main solo via context paste + Write file. KHÔNG flush 3 agent MEMORY.md (chưa spawn work = không findings, per §6.5 KHÔNG add noise entry). cicd-monitor MEMORY.md có entry "setup 2026-05-12" trong seed. Trial Week 1 kick-off ở Session 21 turn 2+ với Plan B Contract V2 wire Mig 28+29 candidate (mirror PE pattern S17-S19 proven 1×). Tests baseline 81 unchanged (no test added — docs-only commit). | `f1c61c9` (Setup cicd-monitor + README 4-agent + memory update) | | 2026-05-11 | Claude | **🎯 SESSION 20 turns 6 + 8-12 — PE polish (NCC palette + autofill + responsive) + Multi-agent setup (7 commit `f568945` → `ae1814c`)** — Sau turn 7 wrap-up Mig 27, user iterate 7 polish/feature lớn nhỏ. **Turn 6 (`f568945`)** Manual budget "Nhập tay" drop tên field — 3 file × 2 app mirror (BudgetFieldRow + WorkspaceCreateView + HeaderForm) bỏ Input "Tên" UI khỏi manual mode, BE save `budgetManualName: null` luôn, VND format `1.000.000` + suffix đ. **Turn 8 (`3ec7b5a`)** AddSupplier +Số tiền inline + NCC 5-màu palette + Winner badge "🏆 Trúng thầu" — AddSupplierDialog +prop detailId? +form thanhTien, sequential POST /suppliers (response {id}) → POST /quotes (nếu detailId + thanhTien > 0). NCC_PALETTES const 5 màu literal Tailwind (blue/purple/sky/teal/pink) cycle theo idx. Winner row override emerald-500 border-l + bg-emerald-100/70 + shadow-sm + ring-1 emerald-300 + badge rounded-full bg-emerald-600 text-white "🏆 Trúng thầu". **Turn 9 (`83aae8e`)** User feedback bỏ badge → revert icon ✓ stick cũ nhưng đậm hơn (text-base font-bold emerald-700) + tên NCC winner text-emerald-900 + hover transition (winner hover:bg-emerald-200/70, non-winner hover:bg-white/80 hover:shadow-sm). **Turn 10 (`66551db`)** AddSupplierDialog auto-fill từ master data khi chọn NCC dropdown — onChange lookup picked supplier, setForm ghi đè 4 field (contactName ← contactPerson / contactPhone ← phone / contactEmail ← email / note ← note). Hint emerald "✓ Đã tự điền từ Master". User vẫn override được. **Turn 11 (`6e338f7`)** Responsive cho laptop màn hình nhỏ 1280-1366px — 4-tầng pattern: sidebar fe-admin + fe-user `w-72` → `w-60 xl:w-72` (+48px lg) / PE Workspace 2-panel `lg:[320px_1fr]` → `lg:[260px_1fr] xl:[320px_1fr]` (+60px lg) / Section padding `px-5 py-4` → `px-3 py-3 sm:px-5 sm:py-4` (+16px xs) / HangMucCard `gap-3 p-3` → `flex-wrap gap-2 p-2 sm:gap-3 sm:p-3` (+8px xs). Net gain trên 1366px ~+132px width cho NCC table area. Memory `feedback_responsive_laptop_breakpoint.md` capture pattern. **Turn 12 (`ae1814c`)** SETUP MULTI-AGENT INFRASTRUCTURE 3 sub-agents (Investigator READ cyan + Implementer WRITE conditional yellow + Reviewer READ adversarial red) + em main coordinator. Pre-flight decision gate 6/6 ✅. Phase 1-4 execute: `.claude/agents/` 4 file (README ~9.7KB + investigator + implementer + reviewer) + `.claude/agent-memory/` 3 MEMORY.md seed (~6KB each). Customize SOLUTION_ERP: skills preload mỗi agent (reuse 6 skills hiện có) + bearer test (admin@solutions / nv.test@solutions) + prod UAT URL + Phase 9 UAT mode + DB Dev/Design distinct. Windows MAX_PATH pitfall handled — drop `isolation: worktree` khỏi implementer.md (project path 51 chars + Dropbox-managed nested overflow 260+ chars). Memory `feedback_multi_agent_setup.md` capture decision gate + ACCEPT/REFUSE criteria + NAMGROUP s41-s43 ROI reference. 3 agents **chưa spawn work** ở S20 turn 12 — seeds-only state. Trial Week 1 candidate Contract V2 wire Mig 28+29 (mirror PE pattern proven). **Stats cumulative S20:** 27 mig (+1 Mig 27 from turn 7) · 59 tables · ~142 endpoints (+1 PATCH /menus/{key}) · 34 FE pages (+1 MenuVisibilityPage) · ~61 menu key (+1) · 81 test pass unchanged · 44 gotcha unchanged · **16 memory entries (+2: responsive + multi-agent)** · 6 skills unchanged · **3 sub-agents NEW** · 14 commits S20. | `f568945` (t6) · `3ec7b5a` (t8) · `83aae8e` (t9) · `66551db` (t10) · `6e338f7` (t11) · `ae1814c` (t12) · (current Docs t13 wrap) | | 2026-05-11 | Claude | **🎯 SESSION 20 turn 7 — Admin Ẩn/Hiện + Đổi tên menu eOffice (Mig 27, 5 chunk `2ea2d27`→`ef394f8`→`059bfcb`→`1ed6530`→Chunk E Docs)** — User UAT yêu cầu "tính năng Ẩn Hiện và Đổi tên hiển thị của các Menu bên ngoài Office, làm trong Trang Admin Page". Hỏi xác nhận "chưa có" — đúng. User clarify Q2=b "edit hiển thị bên ngoài, chỉ của eOffice thôi" → admin sidebar luôn giữ Label gốc, DisplayLabel CHỈ áp fe-user. Q1=a global (không per-role), Q3=a giữ USER_HIDDEN_KEYS hardcode + tầng IsVisible dynamic combine, Q4 UAT skip test. **Chunk A** Domain MenuItem +IsVisible bool=true +DisplayLabel string?(200) + EF config + Migration 27 AddVisibilityAndDisplayLabelToMenuItems (2 AddColumn) — 3-file rule, apply LocalDB _Dev + _Design OK. **Chunk B** BE API: MenuNodeDto + MenuItemDto +isVisible +displayLabel (sau CRUD flags trước Children). GetMyMenuTreeQueryHandler pass through, KHÔNG filter server-side — 2 FE app tự quyết. UpdateMenuItemCommand + Validator + Handler (trim DisplayLabel whitespace → null). MenusController +PATCH /api/menus/{key} [Authorize Policy=Permissions.Update] body {isVisible, displayLabel}. **Chunk C** Domain MenuKeys +MenuVisibility const + All[] + DbInitializer +leaf "Menu eOffice" Icon=Eye Order=94 (Workflows shift 94→95). Manual seed Mig 27 LocalDB _Dev (INSERT MenuItems + Permissions Admin). FE Admin: types/menu.ts +isVisible +displayLabel, lib/menuKeys.ts +MenuVisibility, Layout resolver +/system/menu-visibility, App.tsx +Route. NEW pages/system/MenuVisibilityPage.tsx ~210 LOC: PageHeader + 4 StatCard (Tổng/Hiển thị/Đã ẩn/Đã đổi tên) + Search + 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). PATCH endpoint, invalidate ['menus','all'] + ['my-menu'] trigger live update sidebar. Row hidden bg-amber-50/40 highlight, custom label bg-brand-50/40. **Chunk D** fe-user types/menu.ts mirror. Layout.tsx filterForUser 2 tầng (USER_HIDDEN_KEYS structural + !isVisible dynamic). Helper effectiveLabel(n) = displayLabel?.trim() || label. Replace 3 callsite {node.label} → {effectiveLabel(node)}. USER_FIXED_TOP "__inbox" entry +isVisible:true cho type check pass. **fe-admin Layout KHÔNG đụng** — admin sidebar render Label gốc + show hết menu (user Q2=b). **Chunk E Docs (current)**. **Stats Session 20 turn 7**: 26→27 mig, 59 DB tables (no change), ~141→142 endpoints, 33→34 FE pages, ~60→61 menu key, 81 test pass (Q4 UAT defer), 44 gotcha (no new). Memory entries 14 (no new). | `2ea2d27` (A Mig 27) · `ef394f8` (B BE API) · `059bfcb` (C FE admin) · `1ed6530` (D FE user) · (current E Docs) | | 2026-05-11 | Claude | **🎯 SESSION 20 — PE Detail UI restructure 3 yêu cầu UX (4 chunk: 9dee00d→2bba851→f2f01f4→Chunk D Docs)** — User UAT live phản hồi "Logic OK rồi, điều chỉnh UI Duyệt NCC 1 tý": (1) Hạng mục lên trên + auto-tạo 1 row từ gói thầu, (2) NCC expand dưới hạng mục, (3) Section Ý kiến gộp đồng cấp cùng Phòng. Q&A clarify trước code (4 câu Q1=a/Q2=a "1 hạng mục"/Q3=a "chỉ hiện signed"/Q4 "public luôn demo thôi"). 4 chunk per-commit pattern `feedback_per_chunk_commit`. **Chunk A** BE `CreatePurchaseEvaluationCommandHandler` + INSERT 1 PurchaseEvaluationDetail mặc định + Changelog (GroupCode=01, NoiDung=TenGoiThau, ThanhTienNganSach=Budget.TongNganSach hoặc BudgetManualAmount fallback 0) + FE reorder PeDetailTabs section. **Chunk B** ItemsTab restructure list HangMucCard (1 card / hạng mục, expanded=true default cho demo 1 hạng mục). Header: GroupCode + NoiDung + 3 stat (KL/ĐG/TT) + NS link Δ + Pencil/Trash + ▼/▶ toggle. 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). Click cell quote → QuoteDialog reuse. Add NCC/Edit NCC reuse 2 dialog cũ. Winner ✓ button per row. Bỏ Section 4 "NCC tham gia" (gộp vào Section 2 nested) → 4 section final. Drop SuppliersTab function ~134 LOC dead code (replace bằng HangMucCard expand). Giữ AddSupplierDialog + EditSupplierDialog + SupplierAttachmentsCell (HangMucCard reuse). **Chunk C** Section Ý kiến gộp đồng cấp cùng Phòng. LevelOpinionsSectionV2 forEach step → 1 `StepOpinionsBox` (replace grid-cols-2 N approvers). Header: "Bước N — Tên" + dept badge emerald + "X/Y đã duyệt" counter. Body: filter opinions theo step.order → sort levelOrder asc + signedAt asc → render `StepOpinionEntry` per signed (tên NV + Cấp badge slate + admin override amber + timestamp emerald rounded-full + comment). NV chưa duyệt KHÔNG hiển thị (Q3=a). KHÔNG đụng Mig 26 schema (vẫn UPSERT 1 row / Level qua Service). Drop LevelOpinionBox function. Mirror fe-admin + fe-user mỗi chunk. **Verify**: dotnet build pass (Chunk A) + npm build × 2 app pass (Chunk B/C — catch TS6133 SuppliersTab unused + SupplierAttachmentsCell unused, fix re-add cột File báo giá vào nested table). **Test skip** Phase 9 UAT iteration (81 test pass unchanged). **Stats unchanged**: 26 mig, 59 DB tables, ~141 endpoint, 33 FE pages, 44 gotcha, 81 test. **Pending S21+**: Test regression B4 silent 403 (HIGH §7), Test V2 Service wire ApproveV2Async + Section gộp (Chunk C), Test Mig 25 PATCH user-selectable, Contract V2 (Mig 27/28 mirror PE), phân quyền strict V2, drop legacy V1 cleanup. | `9dee00d` (A) · `2bba851` (B) · `f2f01f4` (C) · (current D Docs) | | 2026-05-09 | Claude | **🎯 SESSION 19 — PE Section 5 V2 dynamic theo ApprovalWorkflowLevel + Mig 26 (4 commit: 873e7a1 polish 3 button + 77a3058/90baa8e/6e913b3/Chunk D Mig 26)** — User feedback Section 5 hiện CỨNG 4 box (PheDuyet/CCM/MuaHàng/SmPm Mig 15 từ Phase 8) → cần động theo Workflow V2 đã pin: forEach Step (Phòng) → forEach Level (Cấp) → forEach NV → 1 OpinionBox với ý kiến + tên người ý kiến. Bước 1 Phòng A có 2 NV → 2 box ngang hàng. **5 câu chốt spec trước code:** Q1=1B (gắn — Service auto sync khi duyệt, KHÔNG form input rời), Q2=2A+Admin (NV chính chủ + Admin override với SignedByUserId track actual signer), Q3=chuyển V2 hết (phiếu V1 legacy fallback Mig 15 4 box readOnly), Q4=4C+bonus (Phase=DaDuyet/TuChoi khoá; Admin có quyền duyệt thay; comment empty → "(duyệt — không ý kiến)" placeholder), Q5=5A (layout group Step header "Bước N — Phòng X" + grid-cols-2 cho N approvers). **3 chunk per-commit (memory `feedback_per_chunk_commit`):** Chunk A (`77a3058`) Domain entity `PurchaseEvaluationLevelOpinion : AuditableEntity` (PEId+LevelId UNIQUE composite, Comment nvarchar(2000), SignedAt datetime2, SignedByUserId Guid, SignedByFullName nvarchar(200) denorm) + EF config FK Cascade Pe + Restrict Level + ApplicationDbContext + IApplicationDbContext DbSet + **Migration 26** `AddPeLevelOpinionsForV2` (1 CREATE TABLE + 2 FK + 2 index — UNIQUE composite + IX LevelId). 3-file rule. Apply LocalDB SolutionErp_Dev OK. Chunk B (`90baa8e`) Service `PurchaseEvaluationWorkflowService.ApproveV2Async` sau line log approval → UPSERT row PurchaseEvaluationLevelOpinions cho Cấp hiện tại: match level theo `ApproverUserId == actorUserId` (multi-NV cùng Cấp OR-of-N), fallback first khi Admin override (FE detect SignedByUserId !== Level.ApproverUserId hiển thị "Admin duyệt thay"). Reject KHÔNG sync. Empty/whitespace comment → "(duyệt — không ý kiến)" placeholder. Helper `ResolveActorFullNameAsync(actorUserId, isSystem)` 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 `GetPurchaseEvaluationQueryHandler` Include LevelOpinions + helper `BuildLevelOpinionsAsync` JOIN ApprovalWorkflows.Steps.Levels + Departments + Users → denorm DTO list. Empty list cho phiếu V1 / V2 chưa có cấp duyệt → FE fallback. Chunk C (`6e913b3`) FE Section 5 V2 dynamic: type `PeLevelOpinion` + `PeDetailBundle.levelOpinions[]`. Section 5 conditional: `evaluation.approvalWorkflowId` set → `` (V2 dynamic), else `` readOnly fallback (V1 legacy giữ Mig 15 4 box). Component `LevelOpinionsSectionV2` group theo step.order: header "Bước N — " + dept badge emerald + hint "(N người duyệt)" khi totalApprovers > 1; body grid-cols-2 cho `step.levels.flatMap(level => level.approvers.map(approver => ))`; lookup opinion theo (stepOrder, levelOrder, approverUserId). `LevelOpinionBox` read-only: title "Cấp N — ", badge amber "⚠ Admin duyệt thay" khi override, badge emerald "✓ Đã duyệt", empty "— chưa duyệt" italic gray, footer timestamp signedAt format vi-VN. Workspace mode hint giữ amber "Ý kiến + chữ ký auto đồng bộ khi NV duyệt". Mirror fe-admin + fe-user (rule §3.9). Verify: dotnet build pass + dotnet test 81 pass + npm run build × 2 pass · 0 TS error. Chunk D docs (current) STATUS/HANDOFF/migration-todos/CLAUDE.md/schema-diagram §16 mới + session log. Phiếu V1 cũ KHÔNG migrate (giữ Mig 15 readOnly), drop sau UAT confirm. **Stats:** 26 mig (+1), 59 DB tables (+1), ~141 endpoints (no new), 33 FE pages, 81 test pass. Polish 3 button (873e7a1) Hành động đầu Session 19: rút gọn label "✓ Duyệt / ← Trả lại / ✗ Từ chối" + 3 màu khác nhau (emerald/amber/red) + font-bold cho cả 2 app. | `873e7a1` (3 button) · `77a3058` (Chunk A Mig 26) · `90baa8e` (Chunk B Service+DTO+GET) · `6e913b3` (Chunk C FE) · (current Chunk D Docs) | | 2026-05-08 19:45 | Claude | **🎯 SESSION 18 WRAP-UP — PE V2 polish + Clone B (DuyetNccPhuongAn) + 4 bug fix UAT + Mig 25 IsUserSelectable (7 commit `aaa1c6c` → `32a8d4d`)** — User UAT live tiếp Session 17, request chuỗi polish nhỏ + clone V2 cho type B. Áp memory `feedback_uat_skip_verify` (skip dotnet test mỗi chunk, push ngay) + lesson rename/remove → bắt buộc `npm run build`. **B1 (`aaa1c6c`)** Pe Duyệt (`?pendingMe=1`): bỏ dropdown "Tất cả trạng thái" + filter cứng client-side `getPeDisplayStatus === DaGuiDuyet` (loại Nháp/Trả lại/Đã duyệt/Từ chối). Hint amber "Lọc cố định: Đã gửi duyệt". Header count dùng `rows.length` (inbox không paged). Workaround BE /inbox loose UAT trả phiếu Nháp. Mirror fe-admin + fe-user. **B2 (`917446d`)** PeDetailTabs HistoryTab filter chỉ events Trả lại/Gửi duyệt lại: workflow transition về TraLai (phaseAtChange=98) + transition từ TraLai (summary chứa "TraLai →") + sửa nội dung khi phaseAtChange=TraLai. BE giữ audit data đầy đủ, chỉ FE filter (reversible). Empty state "Chưa có lịch sử trả lại / gửi duyệt lại". Mirror cả 2 app. **B3 (`937eb24`) Clone V2 cho B (DuyetNccPhuongAn)** — User chốt "Quy trình chọn thầu phụ - NCC → Duyệt NCC đúng. Clone toàn bộ updates sang Duyệt NCC và Giải pháp". Audit phát hiện 80% chung qua `ApplicableType` discriminator → chỉ thêm 3 file ~60 LOC: (a) `MenuKeys.cs` +const `ApprovalWorkflowDuyetNccPhuongAnV2` + add vào `All[]`. (b) `DbInitializer.SeedMenusAsync` +leaf "Duyệt NCC và Giải pháp (Mới)" dưới root ApprovalWorkflowsV2 + new method `SeedSampleApprovalWorkflowsV2Async` seed `QT-DN-PA-V2-001 v01` (1 Bước Phòng CCM × 1 Cấp NV test, idempotent). (c) `fe-admin/lib/menuKeys.ts` +`AwV2_DuyetNccPhuongAn`. KHÔNG migration / Service / Designer page (Layout regex `^AwV2_(.+)$` đã match dynamic, ApprovalWorkflowsV2Page có `TYPE_CODE_TO_INT` cả 3 type). Rút memory `feedback_audit_reuse_before_clone.md`. **B4 (`f77ea38`) Fix permission silent 403** — Drafter `nv.test` Workspace dropdown empty mặc dù seed OK. Root: class-level `[Authorize(Policy = "Workflows.Read")]` → non-admin 403, TanStack Query catch silent → UI empty không warning. Fix: class-level `[Authorize]` only (any authenticated). GET = list workflow read-only không nhạy cảm; POST + DELETE giữ `Workflows.Create` admin-only. Pattern reusable cho Contract V2 sau. **B5 (`a9c0857`) Fix sidebar highlight queryMatches** — Click phiếu trong leaf "Danh sách" → URL `?type=1&id=abc` → menu mất highlight (gotcha #34 cũ tái phát). Root: queryMatches exact-set equality {type} vs {type, id} length mismatch. Fix: `TRANSIENT_QUERY_KEYS = {id, q, editHeader, page, phase, awId}` strip trước compare. Edge case verified: Danh sách `?type=1` vs Pending `?type=1&pendingMe=1` distinct (không cross-highlight). Mirror cả 2 app Layout.tsx. **B6 (`2a53107`) Mig 25 + Designer pin toggle + bỏ "(clone)" + Workspace filter** — User feedback Admin Designer: bỏ "(clone)" auto-suffix khi clone version (version đã đủ phân biệt) + thêm pin toggle "Cho user pick lúc create phiếu" (multi-select, độc lập IsActive). Migration 25 `AddIsUserSelectableToApprovalWorkflows`: ALTER ApprovalWorkflows +`IsUserSelectable bit NOT NULL DEFAULT 0` + Sql backfill `UPDATE WHERE IsActive=1 SET 1` (giữ behavior cũ active workflow vẫn pickable). Domain ApprovalWorkflow +property. DTO AwDefinitionDto +field. CreateAwDefinitionCommand set default `true` cho version mới (mirror IsActive). New `SetAwUserSelectableCommand` + Handler. API `PATCH /api/approval-workflows-v2/{id}/user-selectable` policy `Workflows.Create`. DbInitializer SeedSampleApprovalWorkflowsV2Async +`IsUserSelectable=true`. FE Designer: `DefinitionDto` +field; badge amber "📌 Cho user chọn"; button "Ghim cho user / Bỏ ghim" + mutation `toggleSelectable`. Designer `name = cloneFrom.name` (bỏ ` (clone)` suffix). Workspace fetch filter `w.isUserSelectable === true` (cả fe-admin + fe-user). **B7 (`32a8d4d`)** Cleanup orphan `.claude.zip + docs.zip` từ harness session start, +`*.zip` rule .gitignore. **Cumulative Session 18:** 25 mig (+1), 58 tables (no new), ~141 endpoints (+1), 33 FE pages, **81 test pass** (no change — feature mới UAT defer test theo §7), 44 gotcha (+1 silent 403). Memory +1 entry. **Pending Session 19+:** Contract V2 wire (Mig 26 mirror PE), phân quyền strict V2, drop legacy V1 cleanup. | `aaa1c6c` (B1) · `917446d` (B2) · `937eb24` (B3) · `f77ea38` (B4) · `a9c0857` (B5) · `2a53107` (B6) · `32a8d4d` (B7) | | 2026-05-08 | Claude | **🎯 SESSION 17 WRAP-UP — PE Workflow V2 schema + Service wire end-to-end (13 commit `c847dc0` → `de0f38d`)** — User chốt sau Session 16 "Thấy vẫn không đúng" → viết lại schema riêng + thêm Menu "Duyệt NCC (Mới)" UAT. Cấu trúc rõ ràng: Quy trình > Bước (Phòng) > Cấp (NV cụ thể qua ApproverUserId). 3 chunk lớn: **Schema design + Designer** (Mig 22 — `c847dc0/f6047d5/2781c7e/12daa7f`): 3 entity ApprovalWorkflow/Step/Level + enum ApplicableType (DuyetNcc/DuyetNccPhuongAn/Contract). Designer page `/system/approval-workflows-v2/:typeCode` — iter 1 lock 3 cấp (`9712778`, sai intent) → iter 2 đúng intent max 3 cấp × N NV/cấp + sequential gating C2/C3 disabled khi cấp trước empty + filter NV theo Phòng + no-dup same level (`f3bea3c`). Validator BE Order∈{1,2,3} + HaveSequentialOrders + HaveNoDuplicateApproverInSameLevel. **State machine 5 trạng thái** (`ff21120`): Nháp→Đã gửi duyệt→Đã duyệt (terminal) | Trả lại (Phase riêng TraLai=98, KHÔNG revert DangSoanThao + KHÔNG jump-back) | Từ chối (terminal). Drafter từ TraLai sửa+gửi lại chạy LẠI từ Cấp 1 Bước 1 (Option A user chốt diagram). PE/Contract/Budget Phase enum +TraLai=98 + Policy + Service Reject branch trỏ → TraLai + bỏ smart-reject (RejectedAtStepIndex giữ DB column deprecated). 4 test mới TraLai entry point. **Pin V2 vào PE + Service wire** (Mig 23-24 — `0a40c65/b41484b`): PE.ApprovalWorkflowId Guid? + PE.CurrentApprovalLevelOrder int? + EF FK Restrict. CreatePurchaseEvaluationCommand+Validate ApplicableType match PE.Type. UpdateDraft cho phép sửa Phase=Nháp/TraLai. Workspace Select bắt buộc (filter ApplicableType=type). Service `ApproveV2Async` + `ApproveV1LegacyAsync` branch theo ApprovalWorkflowId set or null: V2 group Levels by Order = Cấp (OR-of-N approvers cùng cấp), match `actor.Id ∈ ApproverUserId`, advance levelOrder++ trong Step → idx++ + reset levelOrder=1 → DaDuyet. Synthetic Policy `ForV2Schema()` cho FE nextPhases (DangSoanThao/TraLai → ChoDuyet/TuChoi; ChoDuyet → ChoDuyet/TraLai/TuChoi). **UX V2-aware** (`d814429/9e63e2d/d250ae4/74745a7/de0f38d`): DTO `CurrentApproval { stepIdx, levelOrder, approvers[] }` + `ApprovalFlow { steps[]: { Order, Name, Dept, Levels[]: { Order, Approvers[], Status:Done/Current/Pending } } }`. Banner emerald "Đến lượt bạn" / amber "Không phải lượt bạn — chỉ {NV X / Y} duyệt được". Button Duyệt forward disabled khi V2 + actor không trong cấp + tooltip. Trả lại + Từ chối vẫn enabled (BE không gating reject theo cấp). Inbox V2-aware (`ResolveV2InboxIdsAsync` precompute Set IDs khớp actor.Id ∈ Cấp hiện tại). 2 dropdown filter "Quy trình duyệt" + "Trạng thái" (chỉ ở Duyệt sau user feedback, Danh sách giữ 1 dropdown trạng thái). Panel 3 thay 4 phase cards bằng flow workflow thực tế: Bước (icon ✓/●/○ + dept badge) → Cấp (icon nhỏ + label "đang chờ" / "đã duyệt" + tên NV). Phiếu V1 legacy fallback note. **Test setup** (`ac41d5e`): SQL `clean-transactional-uat.sql` xóa 9 PE + 11 HĐ + Budget + 19 Notif + reset CodeSequences trên prod, giữ master (Users/Suppliers/Projects/Departments/Workflows V1+V2). Tạo test user `nv.test@solutions.com.vn`/`TestUser@123456` (Drafter, Phòng CCM) qua API. **77→81 test pass** (+4 TraLai entry point Domain). FE rename "Bản nháp" → "Nháp" + ChoDuyet=10 + TraLai=98 thêm vào types/contracts.ts + types/budget.ts. **Pending session sau:** Contract V2 wire (mirror PE pattern), Budget V2 (defer xa hơn), phân quyền strict V2 (hiện loose UAT cho mọi authenticated user xem phiếu V2), drop legacy V1 sau khi UAT chốt + cleanup migration drop RejectedAtStepIndex/RejectedFromPhase. | 13 commit (xem `git log --since='2026-05-08'`) | | 2026-05-08 | Claude | **🎯 SESSION 16 — DRASTIC REFACTOR flat workflow Phòng × Cấp (Mig 21, 2 commit Chunk A+B)** — Resume từ Session 15 defer plan. User chốt "bỏ phase enum hoàn toàn, dùng ChoDuyet=10 đơn nhất + currentStepIndex tracking". Per memory `feedback_drastic_refactor_scope`: dedicated session với context fresh, scope conservative 2x buffer (~8-10h estimate, actual ~3h). **Chunk A (`dbb0089`)** — Domain enum simplify (DangSoanThao=1, ChoDuyet=10 NEW, DaDuyet=7, TuChoi=99; legacy 2-6 + 98 deprecated giữ cho data cũ). WorkflowStep + DepartmentId Guid? FK Restrict + PositionLevel int? (PE + Contract mirror). PE/Contract entity + CurrentWorkflowStepIndex int? + RejectedAtStepIndex int?. Drop class WorkflowStepInnerStep + nav (PE + Contract). Drop *DepartmentApproval.InnerStepId column. EF Configurations: drop InnerStep config + restore simple unique non-filtered (Mig 19/20 filtered split reverse). DbContext drop DbSet<*WorkflowStepInnerStep> × 2. **Migration 21** `RefactorWorkflowToFlatModel` GỘP: 4 ALTER cols (PE/Contract CurrentStepIndex+RejectedAtStepIndex) + 2 ALTER (WorkflowStep DeptId+PositionLevel) + DROP TABLE x 2 (PEWorkflowStepInnerSteps + WorkflowStepInnerSteps Mig 18+20) + DROP InnerStepId column x 2 (PE+Contract DeptApproval) + DROP filtered indexes x 2 + restore simple unique x 2. PE + Contract Service rewrite TransitionAsync: phase transitions DangSoanThao→ChoDuyet (Drafter trình init idx=0) / ChoDuyet→ChoDuyet (advance idx) / ChoDuyet→DaDuyet/DaPhatHanh (last step done) / ChoDuyet→DangSoanThao (Trả lại save RejectedAtStepIndex) / ChoDuyet→TuChoi (Từ chối khoá vĩnh viễn). Match approver: actor.Dept==step.Dept AND actor.PositionLevel>=step.PositionLevel (OR cùng cấp/dept) OR Approvers.Kind=User match OR Kind=Role match. Admin role bypass policy. Last step done → gen mã HĐ (Contract only). App CQRS WorkflowStepDto + WorkflowStepInput drop InnerStep, add DepartmentId/DepartmentName/PositionLevel (PE + Contract mirror). Tests rewrite: DROP `PeNStageApprovalTests.cs` (6) + `ContractNStageApprovalTests.cs` (6) + `PeTwoStageApprovalTests.cs` (7) — legacy N-stage/2-stage no longer applicable. UPDATE `PeWorkflowAdminTests` signature. **96 → 77 test pass** (-19 legacy). 3-file rule Mig 21 (.cs + Designer + Snapshot) commit đủ. **Chunk B (`88a5be1`)** — FE-Admin Designer rewrite (PeWorkflowsPage + WorkflowsPage): drop InnerStepDto + EditInnerStep types, drop PHASE_OPTIONS auto-assign ChoDuyet=10, StepDto + EditStep + departmentId/positionLevel, copyFromDefinition simplified, Designer step UI rewrite (Tên + Phòng Select + Cấp Select + SLA + Approvers Role/User optional fallback, drop entire InnerSteps sub-section), DefinitionCard view hiển thị badge Phòng emerald + Cấp NV/PP/TP violet, save payload phase=10. types/purchaseEvaluation.ts (fe-admin + fe-user mirror) + ChoDuyet=10 enum + label "Đang duyệt" + color amber. **Chunk C (FE PeWorkflowPanel) SKIP** — existing UI compatible (workflow.nextPhases driven by BE simplified policy), reuse 3-button Trả lại/Từ chối logic Session 14 hoạt động trên ChoDuyet phase tự động. **KHÔNG đụng** Service Notify pattern + Changelog pattern (giữ hành vi Mig 16). Verify: dotnet build pass + Mig 21 LocalDB applied + 77 test pass + npm build × 2 pass. Memory `feedback_drastic_refactor_scope.md` validated: dedicated session approach hoạt động đúng dự đoán. | `dbb0089` (A) · `88a5be1` (B) | | 2026-05-07 | Claude | **🎯 SESSION 15 — Tooltip diagnose "Lưu & Gửi Duyệt" + Plan drastic refactor flat workflow → DEFER** — User UAT live screenshot phiếu PE Bản nháp + báo "Lưu & Gửi Duyệt" KHÔNG hoạt động + suy đoán "trùng ID với phiếu khác". Chẩn đoán: button silent disabled khi `evaluation.workflow.nextPhases` không có forward phase (chỉ TuChoi/TraLai). FE chưa có visual feedback → user không biết. Improvement (commit `835cc7f`): compute `forwardPhase` once + add `submitDisabledReason` string giải thích reason (canEditPhase=false / readOnly / !forwardPhase với hint admin kiểm tra cấu hình quy trình) + button title attribute show reason hover hoặc forward phase label khi enabled + Dialog confirm show forward phase explicit "Sẽ chuyển sang Chờ Purchasing". Mirror fe-admin + fe-user. Build pass cả 2. **"Trùng ID" KHÔNG phải bug FE** — `PurchaseEvaluationWorkspacePage` URL state đúng (`+ Thêm mới` clear `id`, save set new), mỗi PE row unique GUID + MaPhieu. **Tiếp theo plan drastic refactor**: User chốt "bỏ phase enum hoàn toàn, dùng ChoDuyet=10 đơn nhất + currentStepIndex tracking" + workflow flat list (Phòng × Cấp × Users[]) thay InnerStep model. Surface 6 chunk plan + start Chunk A: edit Domain entities (Phase enum +ChoDuyet=10, WorkflowStep +DeptId/PositionLevel, drop InnerStep class+nav, PE/Contract +CurrentWorkflowStepIndex/RejectedAtStepIndex, *DeptApproval drop InnerStepId) + EF Configurations (drop InnerStep config + nav, restore simple unique non-filtered) + DbContext drop DbSets — 12 files trong working tree. Realize scope realistic ~8-10h (PolicyRegistry rewrite + 2 Service rewrite + App CQRS + 12 tests rewrite + Designer FE + Migration 21 + Docs) vượt session boundary + risk session context deep ~30 commits. **REVERT working tree** về `835cc7f` clean. Add memory `feedback_drastic_refactor_scope` decision rule: drastic refactor cần dedicated session, ước tính conservative (2x buffer), tránh mid-session big refactor. **Stats unchanged**: 96 test pass, 20 mig, 57 bảng. | `835cc7f` | | 2026-05-07 | Claude | **🎯 SESSION 14 — PE 3-button workflow Duyệt/Trả lại/Từ chối + Task 2 sample seed in-progress** — User chỉ thị thay 2-button approval bằng 3 hành động rõ ràng cho approver: **Duyệt** (forward), **Trả lại** (về DangSoanThao + Drafter sửa, smart reject Mig 16 + clear N-stage rows + Drafter resume jump-back), **Từ chối** (Phase=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/ChoCCM/ChoDuAn/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: target=TuChoi giữ nguyên (KHÔNG override + KHÔNG set RejectedFromPhase + KHÔNG clear N-stage); target khác (DangSoanThao) → smart reject (force DangSoanThao + RejectedFromPhase + clear N-stage). FE PeWorkflowPanel (admin + user mirror): render 3 button rõ ràng "✓ Duyệt → X" brand / "← Trả lại (về Drafter sửa)" red / "✗ Hủy / Từ chối" red. Decision logic: target=TuChoi || isSendBack → Reject (2), else Approve (1). Dialog confirm: title rõ + Cancel case warning red "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: rename `Reject_Sets_RejectedFromPhase_And_Forces_DangSoanThao` → `Reject_To_DangSoanThao_Sets_RejectedFromPhase_TraLai` (target từ TuChoi → DangSoanThao). NEW `Reject_To_TuChoi_Locks_Permanently_No_RejectedFromPhase`. Update `NStage_Reject_Clears_InnerStep_Rows_At_Phase` target → DangSoanThao. **95 → 96 test pass** (+1 Từ chối). **Task 2 sample seed in-progress**: dotnet ef database update applied Mig 9-20 lên LocalDB SolutionErp_Dev (trước đó chỉ Mig 1-8 vì DesignTimeDbContextFactory hardcoded SolutionErp_Design ≠ runtime SolutionErp_Dev — gotcha tooling distinction). API start để DbInitializer auto-seed 30 demo user nhưng exit 255 sớm khi log buffer full → seed dở dang. Defer Task 2 cho session sau (cần guidance: seed manual SQL hoặc manual API run + sample N-stage workflow def + Update PositionLevel cho 30 users existing). | `0d77698` | | 2026-05-07 | Claude | **🎯 SESSION 13 — Mirror N-stage workflow sang Contract (Mig 20, 5 commit per-chunk + skip Chunk E API)** — User chỉ thị mirror N-stage từ PE sang Contract. Budget defer (cần versioned WF migration trước, hardcoded BudgetPolicy hiện tại chưa có WorkflowDefinition). 5 chunk per-commit (build + ef + test pass mỗi chunk): **Chunk A (`951ffa3`)** Domain entity `WorkflowStepInnerStep` (Domain/Contracts/) + nav WorkflowStep.InnerSteps + ALTER ContractDepartmentApproval.InnerStepId Guid? + EF config FK Cascade Step / Restrict Dept+InnerStep + **Migration 20** `AddContractWorkflowInnerStepsAndAlterDeptApprovalUnique` GỘP 1 (CREATE TABLE WorkflowStepInnerSteps + ALTER InnerStepId + DropIndex old + Recreate filtered legacy `WHERE InnerStepId IS NULL` + new filtered N-stage `WHERE InnerStepId IS NOT NULL` + 3 IX + 3 FK). **Chunk B (`04cf2a0`)** Application CQRS DTO — `WorkflowStepInnerStepDto` + extend `WorkflowStepDto` + GetWorkflowAdminOverview Include InnerSteps + DeptNames map + `CreateWorkflowStepInnerStepInput` + `CreateWorkflowStepInput` extend (default null backward compat) + Validator child rules + Handler atomic batch insert. **Chunk C (`e247b67`)** ContractWorkflowService refactor mirror PE — load definition InnerSteps eager, reject branch clear N-stage rows tại fromPhase, dept approval block split hasInnerSteps→N-stage logic / else→legacy 2-stage. N-stage flow giống PE: yêu cầu actor có DeptId+PositionLevel, match firstPending Order asc + (exact level OR canBypass + level≥), exact upsert 1 row InnerStepId, bypass batch upsert NV+PP+TP cùng dept ≤ actor (audit IsBypassed cho cấp dưới), recheck stillPending → BLOCK + log "duyệt cấp X (còn Y pending)". **Chunk D (`7c0772a`)** Tests 6 N-stage Contract mirror PE pattern + helper SeedWorkflowDefinitionAsync 2 step adjacent (DangGopY + DangDamPhan) + SeedContractAsync với Project + Supplier seed + FakeChangelogService + FakeContractCodeGenerator stubs. Bug fix: legacy fallback test ban đầu fail (Standard policy DangGopY → DangDamPhan chỉ cho [Drafter, DeptManager], không Procurement) → switched phase pair sang DangKiemTraCCM → DangTrinhKy + role CostControl khớp. Total 89 → **95 test pass**. **Chunk E SKIP** — WorkflowsController auto-bind `[FromBody] CreateWorkflowDefinitionCommand` record qua JSON, no code change cần. **Chunk F (current)** FE-Admin types/users.ts đã có PositionLevel const từ Session 12 reuse. WorkflowsPage Designer extend mirror PeWorkflowsPage Chunk F: InnerStepDto + EditInnerStep types + copyFromDefinition include + departmentsList query + sub-section "Cấp duyệt nhỏ trong phòng" drag-list { Phòng × Cấp + required } + button "+ Thêm cấp duyệt" emerald + payload include Order asc. Empty state hint fallback 2-cấp legacy. KHÔNG đụng fe-user (admin-only). Docs/Skill update. **Backward compat 100%**: workflow Contract no InnerSteps → fallback legacy 2-stage Mig 16. Data legacy InnerStepId=null vẫn enforce unique cũ qua filtered index. Defer Budget mirror cho session sau (cần migration `AddBudgetVersionedWorkflow` trước). | `951ffa3` (A) · `04cf2a0` (B) · `e247b67` (C) · `7c0772a` (D) · (current F) | | 2026-05-07 | Claude | **🎯 SESSION 12 — N-stage workflow approval Phòng × PositionLevel cấu hình động (PE-only first, 6 commit per-chunk + Mig 18+19)** — User yêu cầu mở rộng từ 2-stage Mig 16 (NV.Review/TPB.Confirm) sang N-stage cấu hình động: mỗi WorkflowStep cha (= 1 phase) có thể cấu hình chuỗi InnerSteps con theo Department × PositionLevel với Order sequential. Spec defaults chốt 6 câu (PositionLevel int 1=NV/2=PP/3=TP, sequential pure, bypass cùng dept TP skip NV+PP, smart reject reset N-stage rows về DangSoanThao, PE-only first, designer 1 sub-section InnerSteps). 6 chunk per-commit (build + ef + test pass mỗi chunk per `feedback_per_chunk_commit.md`): **Chunk A (`13ab533`)** Domain enum `PositionLevel` (NV/PP/TP) + entity `PurchaseEvaluationWorkflowStepInnerStep` + ALTER User.PositionLevel int? + ALTER PEDeptApprovals.InnerStepId Guid? + EF config + **Migration 18** `AddPeWorkflowInnerStepsAndPositionLevel` (1 CREATE TABLE + 2 ALTER + 3 index + FK Cascade Step / Restrict Dept/InnerStep). 3-file rule. **Chunk B (`0e56bd0`)** Application CQRS DTO — `PeWorkflowStepInnerStepDto` + extend `PeWorkflowStepDto` + `CreatePeWorkflowStepInnerStepInput` (default null backward compat existing PeWorkflowAdminTests) + Validator child rules + Handler atomic batch insert + UserDto +PositionLevel field + `SetUserPositionLevelCommand` mirror SetBypassReview. **Chunk C (`0c62e24`)** Service N-stage logic — **Migration 19** `AlterPeDeptApprovalsUniqueFilteredForInnerSteps` (filtered unique: legacy `WHERE InnerStepId IS NULL` + N-stage `WHERE InnerStepId IS NOT NULL`) cho phép multi-row cùng dept khác inner step. PurchaseEvaluationWorkflowService refactor: load definition InnerSteps eager + reject branch clear N-stage rows + dept block split hasInnerSteps→N-stage logic / else→legacy 2-stage. N-stage flow: yêu cầu actor có DeptId+PositionLevel, match firstPending (Order asc IsRequired) same dept + (exact level OR canBypass + level≥), exact match upsert 1 row InnerStepId, bypass batch upsert NV+PP+TP cùng dept ≤ actor level (audit IsBypassed cho cấp dưới skip), recheck stillPending → BLOCK + log "duyệt cấp X (còn Y pending)" / all done → fall through phase transition. Backward compat: workflow no InnerSteps fallback legacy + InnerStepId=null filter unique cũ vẫn enforce. **Chunk D (`3d76c6b`)** Tests N-stage 6 test mới (NV first blocks / 3-level sequential pass / TP bypass skips / wrong dept throws 403 / reject clears rows / legacy fallback no inner) + IdentityFixture extend `+positionLevel` arg + helper SeedWorkflowDefinitionAsync 2 step adjacent (ChoPurchasing+ChoCCM) cho FromDefinition build transition policy guard pass. Total 83→**89 test pass**. **Chunk E (`83ffabd`)** API `PATCH /users/{id}/position-level` mirror SetBypassReview pattern + body `{positionLevel:int?}` Authorize Users.Update. **Chunk F (current)** FE-Admin types/users.ts +positionLevel field + PositionLevel const + Label/Short maps. PeWorkflowsPage Designer extend: InnerStepDto type + EditInnerStep type + copyFromDefinition include + departmentsList query + sub-section "Cấp duyệt nhỏ trong phòng" per step card với drag-drop list { Phòng × Cấp + required checkbox } + button "+ Thêm cấp duyệt" (xanh emerald) + payload include innerSteps Order asc. UsersPage column "Cấp" badge NV/PP/TP emerald + action button cycle null→1→2→3→null call positionLevelMut PATCH. KHÔNG đụng fe-user (admin-only feature). Docs/Skill update. PE-only first. Backward compat 100%: workflow no InnerSteps + data legacy 2-stage rows không phá. | `13ab533` (A) · `0e56bd0` (B) · `0c62e24` (C) · `3d76c6b` (D) · `83ffabd` (E) · (current F) | | 2026-05-08 00:30 | Claude | **🎯 SESSION PHASE 2 WRAP-UP — B12-B14 PE detail polish iterate (3 commit FE-only sau wrap-up `6e7a6db`)** — User UAT iteration tiếp, áp rule strict verify khi rename/remove (lesson hotfix CI). 3 batch nhỏ: **B12 (`378c993`)** "Lưu" no-close (chỉ toast + invalidate, KHÔNG đóng workspace) + nút "Xóa phiếu" red bottom CHỈ Bản nháp (soft-delete `IsDeleted=true` qua AuditableEntity, không xóa hoàn toàn DB) + bỏ header bar workspace mode "Sửa header"/"Xóa"/"Đóng" (chuyển hết xuống bottom action bar) + Section 4 column header dùng `s.supplierName` thay `displayName` (NCC master) + Section 3 row chặn xóa NCC khi đã có quotes (`hasQuotes` computed) + tooltip "xóa báo giá trước". **B13 (`e320027`)** InfoTab `useEffect` watch `[autoEdit, canEdit, ev.id, ...]` → re-trigger edit mode khi pencil click phiếu khác (fix useState mount-time only) + sync values từ ev mới (tránh stale state) + Pencil "sáng lên" active state khi `editingRowId === p.id` (bg-brand-100 + text-brand-700 + ring-brand-300 + shadow-sm + tooltip cập nhật) + wire `editingRowId={autoEditHeader ? selectedId : null}` từ Workspace → PeListPanel. **B14 (`d2306b8`)** QuoteDialog bỏ checkbox "Chọn NCC này cho hạng mục" (consolidate winner ở Section 2.a NccSelectorRow, isSelected vẫn gửi BE giữ trạng thái cũ) + winner column Section 4 matrix highlight emerald (header `bg-emerald-50` + `✓ ` prefix + cells `bg-emerald-50 font-semibold` cho ENTIRE column, không chỉ cell có quote) + loading overlay full-screen QuoteDialog (`bg-white/70 backdrop-blur-sm` + spinner ring brand-600 + text "Đang lưu báo giá…"/"Đang xóa…") + NccSelectorRow inline spinner "Đang chọn NCC + sync cột giá Section 4…" + disable Hủy/Xóa/Lưu buttons khi `isSaving`. Verify: `npm run build` × 2 app pass mỗi commit · `dotnet test` 83 pass (KHÔNG regression). | `378c993` (B12) · `e320027` (B13) · `d2306b8` (B14) | | 2026-05-07 | Claude | **🎯 SESSION WRAP-UP S10-11+++++++ — PE Workspace UX overhaul đầy đủ (23 commit / ~3500 LOC FE + Mig 17 BE)** — User UAT live mode iterate liên tục, áp rule `feedback_uat_skip_verify` (skip dotnet test sau mỗi chunk, push ngay). 7 batch chính: **B1 (S10) PE Thao tác 2-panel workspace** — leaf `Pe_*_Create` từ page Create header riêng → workspace 2-panel `[320px_1fr]` mirror HĐ Thầu phụ; PeListPanel pure picker + sticky "+ Thêm mới"; PeDetailTabs `mode='workspace'` ẩn Workflow/Approvals/History + Section 5 disabled "nhập khi duyệt" (4 commit). **B2 (S11) Migration 17** `AddManualBudgetFieldsToPeAndContract` — 4 ALTER (PE + HĐ × `BudgetManualName` nvarchar(200) + `BudgetManualAmount` decimal(18,2)) cho fallback "user nhập tay khi không link Budget entity approved". Domain + EF config + App CQRS Create/Update + DTO + Validator + carry-forward `CreateContractFromEvaluation`. FE toggle "Nhập tay" trong PeHeaderForm + ContractCreatePage NewForm/EditForm × 2 app (5 commit). **B3 (S11+) BudgetFieldRow inline editor** — Section 2 "b. Ngân sách" thay FormRow tĩnh → editable component (toggle + Select OR 2 input + Save dirty + Hủy). canEdit cho cả 3 view (Workspace/Danh sách/Duyệt mode), readOnly chỉ display (3 commit). **B4 (S11++) InfoTab inline edit + PeListPanel pencil hover** — Section 1 "✎ Sửa" button flip display↔inputs (Tên/Địa điểm/Mô tả/Payment editable, Dự án locked). PeListPanel thêm pencil icon group-hover absolute right + URL `?editHeader=1` chain → `autoEditHeader` prop trigger mount-time edit (3 commit). **B5 (S11+++) Workspace "new" sectioned create view** — `PeWorkspaceCreateView.tsx` ~230 LOC layout 5 sections giống PeDetailTabs visual. S1 + S2.b editable, S3-5 LockedHint "Lưu phiếu trước". POST trigger create. Replace `PeHeaderForm` trong workspace mode='new' (1 commit). **B6 (S11++++) Danh sách disable toàn bộ interactions** — PurchaseEvaluationsListPage `readOnly={true}` hardcoded cho PeDetailTabs + `readOnly={!pendingMe}` cho PeWorkflowPanel (List view → ẩn Chuyển tiếp + show hint "Vào menu Duyệt"; Pending vẫn approve được) (2 commit). **B7 (S11+++++) Lock Loại quy trình + payment preset** — workspace `` theo URL `?type=N`. `