diff --git a/.claude/agent-memory/implementer/MEMORY.md b/.claude/agent-memory/implementer/MEMORY.md index 44b9e55..89a9e33 100644 --- a/.claude/agent-memory/implementer/MEMORY.md +++ b/.claude/agent-memory/implementer/MEMORY.md @@ -204,6 +204,7 @@ KHÔNG `*` / `latest`. Critical pins: ## 📅 Recent activity (last 10 FIFO) +- **2026-05-15 (S24, Plan AA Chunk B PASS):** FE user read-only matrix view workflow V2 ghim (Mig 25 IsUserSelectable). 3 file: (1) CREATE `fe-user/src/types/approvalWorkflowV2.ts` (~55 LOC) — subset DTO mirror BE `AwAdminOverviewDto` (7 Allow* flag per Level, 5 record type AwLevelDto/AwStepDto/AwDefinitionDto/AwTypeSummaryDto/AwAdminOverviewDto); (2) CREATE `fe-user/src/pages/pe/WorkflowMatrixViewPage.tsx` (~215 LOC) — useQuery GET `/approval-workflows-v2?applicableType=N&isUserSelectable=true` (em main BE Chunk A đã thêm `IsUserSelectable bool?` param + Controller forward), render table 10 cột Bước (rowSpan) | Cấp | NV duyệt | 7 ✓/— flag cell, header với `title` tooltip mô tả từng cột, 3 state Loading/Error/Empty rõ ràng, badge `Đang dùng` (emerald isActive) + `Được ghim` (amber isUserSelectable Pin icon); (3) UPDATE `fe-user/src/App.tsx` — import WorkflowMatrixViewPage + route `/purchase-evaluations/workflow-matrix` đặt TRƯỚC `/workspace` (URL ordering logical matrix → workspace → new → detail). Verify: `npm run build` fe-user PASS clean 0 TS err, 1907 modules, 2.61s, 1282 KB. Surprise: shadcn fe-user KHÔNG có `Card`/`Badge` (chỉ có Button/Dialog/Input/Label/Select/Textarea) → fallback inline `
` + inline `` cho badge (mirror pattern admin Designer). Pattern reusable: **read-only mirror admin Designer page** = drop edit mutations + reuse DTO types (subset) + filter param BE-side (`IsUserSelectable=true` thay vì FE filter). Cookie-cutter 0 (lần đầu pattern). Pattern 5 mirror 2 app KHÔNG apply (fe-admin có Designer riêng — Plan AA scope fe-user only). Pattern 7 admin opt-in 7 Allow* flag wire full render trong table (10 cột total = 3 meta + 7 flag). KHÔNG ops git. Token cost ~14k. - **2026-05-15 (S23 t4-t11 cumulative REFUSE — em main solo Plan N+O+P+Q+R+S+T+U):** 8 plan consecutive em main solo, 0 Implementer spawn (REFUSE 100% per criteria #4 bug fix reasoning chain + criteria #3 cross-stack tight coupling). **Plan N+O** 5 lookup site discrimination fix cross-stack BE Service + Application + 3 regression test. **Plan P** Controller TransitionPeBody record drop fix tightly coupled FE wire audit (Investigator confirm BE-only scope ~6 LOC). **Plan Q** FE banner mx-5 layout CSS polish 2 app mirror trivial 8 LOC. **Plan R+S+T5** destructive sqlcmd cleanup prod (scripts/plan-r-*.sql + plan-s-*.sql + plan-t5-*.sql + plan-t-backup.sql, 4 files scp + sqlcmd -i, ~720 rows wiped cumulative). **Plan T** DbInitializer DemoSeed:Disabled flag config (Infrastructure + Api appsettings, ~25 LOC). **Plan U** FE sidebar truncate + tooltip 2 app mirror 25 LOC (em main solo CSS Tailwind). 7 strict-scope criteria validated cumulative S23: pattern reusable saved memory user-level — Plan O wire 9 surface points (point 9 lookup discrimination 5 sites enum), Plan P wire 10 surface points (point 10 Controller body record mirror count check), Plan T DemoSeed flag pattern. - **2026-05-15 (S24, Plan M Chunk M2 PASS):** F1 edge case Bước 1 reset ChoDuyet tests (em main M1 service edit `PurchaseEvaluationWorkflowService.cs` line 287-333 đã DONE — fallback Drafter TraLai → reset (0, 1) giữ ChoDuyet + audit log "không lùi được"). Cookie-cutter 1 file test `PurchaseEvaluationWorkflowServiceReturnModeTests.cs` — 2 sub-tasks: (1) extend `SeedWorkflowAsync` helper +2 params optional `allowReturnOneLevelL1` + `allowReturnOneStepL2` (default false, không phá compat 4 test ReturnMode existing), set vào `l1.AllowReturnOneLevel` + `l2.AllowReturnOneStep` tương ứng — Pattern 3 audit-reuse EXTEND không clone helper; (2) add 2 `[Fact]` test ngay sau test admin bypass OneLevel (line 241) — `ApplyReturnMode_OneLevel_AtStep1Level1_ResetsToBuoc1Cap1_KeepsChoDuyet` (PE init Step 0 Cấp 1 + actor=a1 + slot Cấp 1 tick AllowReturnOneLevel, build PE inline vì helper `BuildPeAtLevel2` không phù hợp cho Cấp 1) + `ApplyReturnMode_OneStep_AtStep1_ResetsToBuoc1Cap1_KeepsChoDuyet` (PE Step 0 Cấp 2 + actor=a2 + slot Cấp 2 tick AllowReturnOneStep, reuse `BuildPeAtLevel2`, OneStep service check `curStepIdx > 0` → fallback ngay không quan tâm Cấp). Assert: Phase=ChoDuyet (KHÔNG TraLai như Drafter mode) + pointer (0, 1) + SLA NotNull + Changelog **ContextNote** chứa "không lùi được" (Summary field cố định `"Chuyển phase {from} → {to}"`, summary từ ApplyReturnModeAsync chèn vào comment qua line 96-99 service → LogTransition `ContextNote = comment`). K7 cascade verify NO regression: 3 ApproveV2_SkipToFinal_* tests still green (M1 edit chỉ F1 OneLevel/OneStep edge case, KHÔNG đụng F2 path `ApproveV2Async`). Verify: `dotnet test SolutionErp.slnx` clean 0 err 2 warn pre-existing DocxRenderer, **106/106 PASS** (58 Domain + 48 Infra: +2 từ 46 baseline post Plan L). 10 ReturnMode-class tests verified individually PASS (4 ReturnMode + 3 ApproveV2_SkipToFinal + 1 Reject_NonApprover + 2 edge case mới). Diff +94 LOC trên 1 test file (test add + helper signature 2 params). Token ~10k. Spec deterministic + 1 file independent + < 1h verified. - **2026-05-15 (S24, Plan M Chunk M3 PASS):** FE rename Phase=TraLai (98) display label "Trả lại" → "Cần chỉnh sửa lại" cho UAT disconnect fix. Spec scope HẸP (display badge label + status reference) tuân thủ strict: 4 file FE × 2 app = 8 edit total. **2 file types/purchaseEvaluation.ts** × 2 app: `PurchaseEvaluationPhaseLabel[98]` (raw phase badge) + `PeDisplayStatusLabel.TraLai` (display status badge — main user-facing). **2 file components/pe/PeWorkflowPanel.tsx** × 2 app: rename 2 inline literal hardcode "Trả lại" trong F1 dialog tooltip (`Phase → "..."`) + confirm message (`Phiếu sẽ về "..."`) — đây là status display reference, KHÔNG phải action verb. Pattern 5 mirror 2 app strict applied. KHÔNG đụng: (1) action button label `← Trả lại` (verb), (2) F1 mode picker label `Trả về Người soạn thảo` (4 mode action), (3) comments narrative line 62/71/etc giữ rationale dev (rule §6.5), (4) `types/contracts.ts` + `types/budget.ts` Phase 98 'Trả lại' — module Contract + Budget khác PE, ngoài scope M3. Verify: `npm run build` fe-admin PASS clean (0 TS err, 9.40s, 1395 KB bundle unchanged trivial) + `npm run build` fe-user PASS clean (0 TS err, 6.92s, 1275 KB). Diff +4/-4 LOC × 2 = 8 LOC tổng trên 4 file. Token ~9k. Decision tactical: 2 chỗ inline literal PeWorkflowPanel rename để giữ UX consistency với badge label sau rename — KHÔNG drift outside spec vì cùng "status display reference" semantics (badge + tooltip + confirm message phải mirror). Anti-fiddle threshold <20% LOC respected. diff --git a/.claude/agent-memory/investigator/MEMORY.md b/.claude/agent-memory/investigator/MEMORY.md index 6840661..1154caa 100644 --- a/.claude/agent-memory/investigator/MEMORY.md +++ b/.claude/agent-memory/investigator/MEMORY.md @@ -128,6 +128,7 @@ State machine 5 trạng thái phiếu PE: Nháp / Đã gửi duyệt / **Trả l ## 📅 Recent activity (last 10 FIFO) +- **2026-05-15 (S24 t1 spawn Pre-A — Plan AA User Workflow Matrix view + sidebar widen):** 5Q audit. Q1 endpoint: `ApprovalWorkflowsV2Controller.cs:16-19` đã class-level `[Authorize]` bare từ S18 2026-05-08 (gotcha #44 fixed permanent), per-method admin Workflows.Create. Handler `GetAwAdminOverviewQuery` KHÔNG có IsUserSelectable filter — cần ADD param + Where conditional. Q2 menu seed: `DbInitializer.cs:1429-1437` peOrder global increment (Group=1, leaves 2/3/4 cycle per type). Permission seed `line 1541-1547` cho 7 role (Drafter/DeptManager/Procurement/CostControl/ProjectManager/Director/AuthorizedSigner). Accounting NOT trong list — admin manual grant nếu cần. Q3 Admin Designer: `ApprovalWorkflowsV2Page.tsx` 975 lines, AwLevelDto 13 fields (7 Allow*), VI labels line 892-948 ("Trả về 1 Cấp trước"/"Trả về 1 Bước trước"/"Trả về Người chỉ định"/"Trả về Drafter (mặc định)"/"Cho phép chỉnh sửa Section 2 (Hạng mục/NCC/Báo giá) lúc đang duyệt"/"Cho phép chỉnh sửa Section ngân sách lúc đang duyệt"/"Cho phép duyệt thẳng Cấp cuối khi đang duyệt"). KHÔNG có usePermission/PermissionGuard wrap (class-level [Authorize] route guard sufficient). Q4 sidebar widen: fe-user `Layout.tsx:325` + fe-admin `Layout.tsx:218` `w-60 xl:w-72` (240/288px). PE Workspace 2-panel `[260px_1fr] xl:[320px_1fr]` (NOT 3-panel như memory `feedback_responsive_laptop_breakpoint` stale claim). After widen `w-72 xl:w-80` (288/320px) SAFE (sidebar 288 + main 992 → workspace 260+732 fit @ 1280px). `w-80 xl:w-96` THRESHOLD RISKY (sát 700px remaining). Q5 ApplicableType enum `ApprovalWorkflow.cs:45-50` {DuyetNcc=1, DuyetNccPhuongAn=2, Contract=3}. Surprises: (1) memory responsive breakpoint stale "3-panel" → cần update sau Plan AA. (2) Order strategy "Luồng duyệt" Order=2 first → shift existing leaves +1 → cần DbInitializer UPDATE Order existing (KHÔNG chỉ INSERT-if-not-exists). (3) Contract=3 chưa wire FE (chỉ DuyetNcc + DuyetNccPhuongAn). Recommendation: Proceed Plan AA — chỉ ADD param filter + 1 menu key + page mới + sidebar widen. Token cost ~32k. - **2026-05-15 (S23 t8 spawn Plan R pre-flight cleanup audit):** Bro chốt cleanup destructive prod. 4 sqlcmd queries audit: 35 PE total (28 active + 7 soft) + 17 V2 (15 IsUserSelectable=false + 2 ghim) + 4 V1 (2 active + 2 inactive). FK gotcha catch: PE.ApprovalWorkflowId Restrict + ApprovalWorkflow extends `BaseEntity` NO soft-delete → hard-DELETE required; LevelOpinion → ApprovalWorkflowLevel Restrict cascade block. SQL Express limit: NO COMPRESSION + RESTORE VERIFYONLY require sysadmin. Filtered indexes (Mig 29+) require `SET QUOTED_IDENTIFIER ON`. Cascade child estimate: 446 PE children + ~140 V2 + ~37 V1 = ~620 rows. 3 Option compare → bro chốt A (Hard-DELETE PE + V2 unghim + V1 inactive, GIỮ V2 ghim + V1 active). Plan F precedent: KHÔNG drop V1 active (PE pin → BE crash). - **2026-05-15 (S23 t6 spawn Plan P FE wire audit — confirm BE-only scope):** Em main hypothesize Plan P scope BE Controller body record drop. Investigator audit FE × 2 confirm: `PeWorkflowPanel.tsx:113-124` `api.post(/transitions, body)` SEND ĐÚNG 7 fields (TargetPhase + Decision + Comment + ReturnMode + ReturnTargetUserId + SkipToFinal). No service file (untyped object literal). BE `PurchaseEvaluationsController.cs:267` `TransitionPeBody` record CHỈ 3 fields → ASP.NET silent DROP 3 missing fields. Verdict: Plan P BE Controller ONLY ~6 LOC + no test (Mig 28/31 Domain test cover handler). Saved em main blind fix cross-stack. - **2026-05-15 (S23 t3 spawn — UAT bug Allow* flags không hiện cho actor non-row1):** Bro UAT login `nv.test@solutions.com.vn` vào menu eoffice "Duyệt NCC → Duyệt" phiếu PE/2026/A/026 (Phase=ChoDuyet, WF=QT-DN-V2-001 v12, ở Bước 2 Cấp 1 4 NV: Trần Xuân Lưu/NV Test UAT V2/Hồ Thị Nữ Nguyên/Lê Văn Bính). Admin ĐÃ tick 7 Allow*=TRUE riêng cho slot NV Test UAT V2. Nhưng FE dialog Duyệt KHÔNG hiện checkbox SkipToFinal + Trả lại 4 mode + Edit. **Verdict: HYPOTHESIS B — BE handler picks wrong slot row.** Evidence: (1) `PurchaseEvaluationFeatures.cs:765` `var curLevel = curStep?.Levels.FirstOrDefault(l => l.Order == curLevelOrder);` — match FIRST row có `Level.Order == curLevelOrder`, NOT actor's row. Post-Mig 29 refactor, Level.Order trùng nhau cho mọi NV cùng Cấp (4 row có `Order=1` ở Step 2). EF returns Trần Xuân Lưu trước (PK order) → `FirstOrDefault` lấy row đó (all-false except Drafter=true). (2) API curl admin token + nv.test token return CÙNG `currentLevelOptions={ allowReturnOneLevel=false, OneStep=false, Assignee=false, Drafter=true, EditDetails=false, EditBudget=false, SkipToFinal=false }` — handler currentUser-agnostic confirm. (3) Workflow detail GET `/approval-workflows-v2?applicableType=1` `.types[0].active.steps[1].levels` enumerate 4 slot Bước 2 Cấp 1: NV Test (UAT V2) = ALL 7 TRUE; 3 NV còn lại = ALL FALSE (trừ Drafter=true mặc định). Admin Designer wire ĐÚNG (`fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx:889-946` 7 checkbox per-slot). FE consumer wire ĐÚNG (`fe-user/src/components/pe/PeWorkflowPanel.tsx:51 levelOptions = evaluation.currentLevelOptions` + line 343/357/371/397 conditional render mode picker + line 425 SkipToFinal checkbox). **Root cause:** BE line 765 lookup semantic broken sau Mig 29 (S21 t5). Trước Mig 29, 1 Level row per Cấp + `ApproverUsers` join table → `FirstOrDefault(Order==X)` đúng. Sau Mig 29 split 1 Level row PER ApproverUser → `Order` field collide → cần match thêm `ApproverUserId == currentUser.UserId`. **Fix BE 1 dòng:** `var curLevel = curStep?.Levels.FirstOrDefault(l => l.Order == curLevelOrder && l.ApproverUserId == currentUser.UserId);` (admin bypass: fallback `?? curStep?.Levels.FirstOrDefault(l => l.Order == curLevelOrder)` để admin xem detail không lỗi). Edge case: Phase=DaDuyet/TraLai/TuChoi pointer null → existing guard line 760-762 skip block OK. **LOC ~2-3 LOC 1 file.** Surprise: bug PRESENT từ deploy Mig 29 (S21 t5 2026-05-13) — không phải regression S23. Lý do trước đây không bắt: UAT test users (default 13 cũ) đa số là row đầu của slot Cấp (Admin tick toàn FALSE → behavior giống nhau, không lộ); chỉ bộc lộ khi admin tick CHỌN LỌC per-NV (UAT V2 S22+2 thêm 20 user role-based + bro tick chỉ NV Test UAT V2). Cross-reference memory `feedback_per_nv_permission_scope.md` cumulative S21 t5 → S22+5 → S23 t1 — KHÔNG có entry nào nhắc bug lookup BE post-refactor. diff --git a/.claude/agent-memory/reviewer/MEMORY.md b/.claude/agent-memory/reviewer/MEMORY.md index 96f26fb..ace017e 100644 --- a/.claude/agent-memory/reviewer/MEMORY.md +++ b/.claude/agent-memory/reviewer/MEMORY.md @@ -144,6 +144,7 @@ Flag commit nếu thấy ``. Stats: 31 mig · 59 tables · **~146 endpoints (+1 GET filter param)** · **35 FE pages (+1 WorkflowMatrixViewPage)** · 111 test · 47 gotcha · 21 memory · 6 skills · 4 sub-agents.) + +**Last updated S23 t12:** 2026-05-15 (Session 23 turn 12 chốt cuối — **🎯 S23 cumulative 11 plan + 32 commits**. Plan K (Mig 31 F2 refactor 9c) → L (UAT bug 5c) → M (F1 edge case 4c) → N (per-NV lookup 1 site 2c) → O (4 sites cascade 2c) → P (Controller body record 1c) → Q (FE banner 1c) → R (cleanup phiếu/wf 1c) → S (wipe ALL wf 1c) → T (DemoSeed flag 2c) → U (sidebar truncate 1c). 4 sub-agents: Investigator 5 spawn + Implementer 5 spawn + Reviewer 2 spawn + CICD Monitor 10 verify runs PASS. Memory +1 NEW `feedback_demo_seed_flag_disable.md` + `feedback_per_nv_permission_scope.md` reinforced 10 wire surface points (point 9 lookup discrimination + point 10 Controller body record mirror). Cleanup cumulative R+S+T5 ~720 rows wiped + DbInitializer flag persist. Stats: 31 mig · 59 tables · ~145 endpoints · **111 test** · 47 gotcha · **21 memory** (+1) · 6 skills · **0 PE + 0 demo workflow + flag persist** UAT permanent clean slate. 32 commits push `eb106f2..86d8806`. Plan B Contract V2 wire HIGH next.) **Last updated S23 t10:** 2026-05-15 (Session 23 turn 10 — **🔧 Plan T: Disable auto re-seed demo data + final DELETE — UAT permanent clean slate**. Bro phát hiện sau Plan R+S: 4 phiếu `[DEMO]-A/B` + workflows TỰ ĐỘNG RE-SEED sau IIS recycle do DbInitializer 5 demo seed methods. Plan T fix root cause: `appsettings.json` add `DemoSeed:Disabled=true` (Dev override false) + `DbInitializer.cs` check flag → skip 5 method. Run #207 PASS deploy applied flag. T5 sqlcmd DELETE 7 rows + cascade. T6 force IIS recycle verify NO re-seed: PE=0 + V2=0 + V1=0 preserved. DemoSeed flag PROVEN active end-to-end. Cumulative Plan R+S+T: ~677 rows wiped + DbInitializer re-seed permanent disable. Stats: 31 mig · 59 tables · ~145 endpoints · 111 test · 47 gotcha · 20 memory · 6 skills · **0 PE + 0 workflow + flag persist** UAT permanent clean slate.) diff --git a/docs/STATUS.md b/docs/STATUS.md index e489fda..81e6c5c 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -80,6 +80,7 @@ | Ngày | Ai | Task | Commit | |---|---|---|---| +| 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) | diff --git a/docs/changelog/sessions/2026-05-15-s24-turn1-plan-aa-workflow-matrix.md b/docs/changelog/sessions/2026-05-15-s24-turn1-plan-aa-workflow-matrix.md new file mode 100644 index 0000000..2beb7ba --- /dev/null +++ b/docs/changelog/sessions/2026-05-15-s24-turn1-plan-aa-workflow-matrix.md @@ -0,0 +1,237 @@ +# Session 24 turn 1 — 2026-05-15 — Plan AA User Workflow Matrix view + Sidebar widen + +**Dev:** Claude Opus 4.7 1M (em main + 3 sub-agent spawn) +**Duration:** ~2h +**Base commit:** `86d8806` (Plan U S23 t11 sidebar truncate) +**Final HEAD:** `` (after `ee776d5` Chunk A + `c667802` Chunk B) +**Total commits Plan AA:** **3** (A BE+Layout + B FE page + C Docs) + +## 🎯 Trigger session + +Bro UAT screenshot 2026-05-15 ~15:40, sau Plan U S23 t11 deploy: + +> Cho hiển thị đầy đủ nhé, cần thì sliderbar to 1 chút cũng đc. +> Với thêm 1 menu là: Luồng duyệt phía trên Danh sách → Hiển thị ma trận phân quyền của cấu hình quy trình duyệt trong admin page ra ngoài để User có thể biết đc (Chú ý chỉ những quy trình có ghim). + +2 yêu cầu: +1. **Sidebar widen + revert truncate** — Plan U S23 t11 vừa add `truncate` + tooltip "..." → bro muốn hiển thị đầy đủ label custom Mig 27 (DisplayLabel admin set). +2. **Menu "Luồng duyệt" + matrix view page** — read-only cho user xem cấu hình workflow V2 ghim trước khi tạo phiếu. + +## 🌳 Q&A clarify (2 lượt) + +| Câu | User chốt | +|---|---| +| Permission strategy BE | **Relax class-level [Authorize]** (gotcha #44 fix permanent từ S18) + server-side filter `IsUserSelectable=true` cho non-admin. Reuse endpoint hiện có, KHÔNG duplicate logic. | +| Matrix display layout | **Table 2D full 7 Allow* cột** (Bước/Cấp/NV/Mô tả + 7 cột flag Trả lại 4 mode + Edit Section 2 + Edit Budget + Skip Cấp cuối). User thấy đầy đủ quyền per slot Approver giống admin Designer read-only. | + +## 🔍 Pre-A Investigator audit (🟦, ~32K tokens) + +5Q audit verify trước code: + +| Q | Verdict | +|---|---| +| **Q1 Endpoint permission** | ✅ Gotcha #44 đã fix permanent từ S18 (2026-05-08). Class-level chỉ `[Authorize]` bare, per-method admin Workflows.Create. Handler `GetAwAdminOverviewQuery` KHÔNG có IsUserSelectable filter — cần ADD param + conditional. | +| **Q2 Menu seed pattern** | ✅ `DbInitializer.cs:1429-1437` peOrder global increment. Permission seed `1541-1547` cho 7 role. Accounting KHÔNG trong list (admin manual grant). | +| **Q3 Admin Designer FE** | ✅ `ApprovalWorkflowsV2Page.tsx` 975 lines. 7 Allow* VI labels có sẵn line 892-948. AwLevelDto 13 fields. KHÔNG có usePermission wrap. | +| **Q4 Sidebar widen impact** | ✅ `w-72 xl:w-80` (288/320px) SAFE — sidebar 288 + main 992 → workspace 260+732 fit @ 1280px. `w-80 xl:w-96` THRESHOLD RISKY. | +| **Q5 ApplicableType enum** | ✅ {DuyetNcc=1, DuyetNccPhuongAn=2, Contract=3}. FE wire 2 type PE. | + +**Surprises critical:** +- ⚠️ **PE Workspace là 2-panel** không phải 3-panel → memory `feedback_responsive_laptop_breakpoint.md` stale, sẽ update memory sau Plan AA. +- 📝 **Order strategy**: "Luồng duyệt" Order=2 first → cần shift existing leaves +1 → DbInitializer cần UPDATE Order existing (KHÔNG chỉ INSERT-if-not-exists). +- 🔸 Contract=3 chưa wire FE — chỉ DuyetNcc + DuyetNccPhuongAn. + +## 🌳 Plan AA execution 3 chunk per-commit + +### Chunk A — BE filter + menu seed + sidebar widen (`ee776d5`) + +👤 **Chủ trì Solo** — Cross-stack tight coupling architectural decision. 6 files +73/-24 LOC. + +#### BE changes (4 files) + +**`MenuKeys.cs:85`** thêm helper: +```csharp +// [Plan AA S24 t1] User read-only view ma trận phân quyền của workflow V2 +// admin Designer đã ghim (IsUserSelectable=true). Suffix _WfView distinct +// với admin PeWf_* (Designer write). +public static string PurchaseEvaluationWorkflowView(string typeCode) => $"Pe_{typeCode}_WfView"; +``` + +**`DbInitializer.cs:1429-1437`** thêm tree.Add LuongDuyet (Order=2 first child) cho 2 type PE: +```csharp +tree.Add((MenuKeys.PurchaseEvaluationGroup(code), label, ..., peOrder++, "FileCheck")); +// [Plan AA S24 t1] "Luồng duyệt" leaf phía trên "Danh sách" +tree.Add((MenuKeys.PurchaseEvaluationWorkflowView(code), "Luồng duyệt", ..., peOrder++, "Network")); +tree.Add((MenuKeys.PurchaseEvaluationList(code), "Danh sách", ..., peOrder++, "List")); +tree.Add((MenuKeys.PurchaseEvaluationCreate(code), "Thao tác", ..., peOrder++, "Plus")); +tree.Add((MenuKeys.PurchaseEvaluationPending(code), "Duyệt", ..., peOrder++, "CheckCircle2")); +``` + +**`DbInitializer.cs:1447-1459`** refactor INSERT-only loop → INSERT-OR-UPDATE-Order: +```csharp +var existingItems = await db.MenuItems.ToDictionaryAsync(m => m.Key); +foreach (var (key, label, parent, o, icon) in tree) +{ + if (existingItems.TryGetValue(key, out var existing)) + { + if (existing.Order != o) { existing.Order = o; reordered++; } + continue; + } + db.MenuItems.Add(new MenuItem { Key = key, ... }); +} +``` + +Idempotent: skip nếu Order match, UPDATE nếu mismatch. Shift existing prod rows Order+1 safe. + +**`DbInitializer.cs:~1553`** SeedPurchaseEvaluationPermissionDefaultsAsync thêm WfView vào menuKeys list cho 7 role Read. + +**`ApprovalWorkflowV2AdminFeatures.cs:89-110`** GetAwAdminOverviewQuery + handler: +```csharp +public record GetAwAdminOverviewQuery( + int? ApplicableType = null, + bool? IsUserSelectable = null) : IRequest; + +// Handler: +if (request.IsUserSelectable is bool ius) + query = query.Where(d => d.IsUserSelectable == ius); +``` + +**`ApprovalWorkflowsV2Controller.cs:21-26`** pass-through: +```csharp +public async Task> Overview( + [FromQuery] int? applicableType, + [FromQuery] bool? isUserSelectable, + CancellationToken ct) + => Ok(await mediator.Send(new GetAwAdminOverviewQuery(applicableType, isUserSelectable), ct)); +``` + +#### FE Layout changes (2 files mirror rule §3.9) + +**`fe-user/src/components/Layout.tsx:78`** resolvePath regex extend: +```ts +const peMatch = key.match(/^Pe_([^_]+)_(List|Create|Pending|WfView)$/) +// ... +if (action === 'WfView') return `/purchase-evaluations/workflow-matrix?type=${typeInt}` +``` + +**`fe-user` + `fe-admin` Layout.tsx**: +- Sidebar `w-60 xl:w-72` → `w-72 xl:w-80` (+48px lg, +32px xl) +- Revert Plan U S23 t11 truncate × 5 sites: + - fe-user: MenuGroup line 188 + MenuLeaf line 251 + StaticLeaf line 292 + - fe-admin: MenuGroup line 148 + MenuLeaf line 208 +- Keep `min-w-0 flex-1` parent + `shrink-0` icon/chevron + `title` tooltip (no harm — accessibility bonus) + +**Verify Chunk A:** `dotnet build SolutionErp.slnx` PASS clean 0 err 2 warn pre-existing DocxRenderer. + +### Chunk B — FE WorkflowMatrixViewPage (`c667802`) + +🟨 **Implementer Case 2** — Cookie-cutter mirror Designer Designer read-only single file. 3 files +305 LOC. + +#### NEW `fe-user/src/pages/pe/WorkflowMatrixViewPage.tsx` ~215 LOC + +```tsx +// useQuery GET /approval-workflows-v2?applicableType=N&isUserSelectable=true +// PageHeader "Luồng duyệt — {label}" + Network icon +// Loading/Error/Empty 3 state rõ ràng +// WorkflowCard per ghim version: +// header: code/version + badge "Đang dùng" emerald + "Được ghim" amber Pin +// table 10 cột read-only: +// Bước rowSpan | Cấp | NV duyệt + 7 Allow* flag (✓ emerald / — slate) +// FlagCell helper TS indexed-access type union 7 keys +``` + +#### NEW `fe-user/src/types/approvalWorkflowV2.ts` ~55 LOC + +Subset DTO mirror BE `AwAdminOverviewDto`: +- `AwLevelDto` 13 fields (7 Allow*) +- `AwStepDto`, `AwDefinitionDto`, `AwTypeSummaryDto`, `AwAdminOverviewDto` +- Field name khớp BE record positional param (`history` not `versions`) + +#### MODIFIED `fe-user/src/App.tsx` +2 LOC + +```tsx +import { WorkflowMatrixViewPage } from '@/pages/pe/WorkflowMatrixViewPage' +// trong : +} /> +``` + +**Verify Chunk B:** `npm run build` fe-user PASS clean 0 TS err, 1907 modules, 2.61s, bundle 1282 KB. + +### Chunk C — Cumulative Reviewer + Docs (this commit) + +🟥 **Reviewer** adversarial pre-commit cumulative ~25K tokens: + +| Category | Verdict | +|---|---| +| Wire BE/feature claim verify | ✅ End-to-end Controller → Mediator → Handler → FE useQuery wired | +| Schema integrity | ✅ 0 Mig mới, DbInitializer idempotent, 7 Allow* count match | +| Security | ✅ Class-level [Authorize] preserved, GET any-auth OK, POST admin policy. 1 Low note: non-admin pass `isUserSelectable=false` leak workflow chưa ghim (defer follow-up) | +| Code quality | ✅ BE 0 err + fe-user PASS + **fe-admin PASS 1926 modules 740ms 0 TS err** (Reviewer tự verify bonus) | +| Test coverage | ✅ Accept Phase 9 UAT exception per `feedback_uat_skip_verify` | + +**Anti-fiddle:** 0% drift, ~346 LOC khớp spec. +**Mirror 2 FE app §3.9:** Sidebar widen + remove truncate mirror cả 2 ✓. WorkflowMatrixViewPage fe-user only (admin Designer riêng). +**Smart Friend guard:** Active, 0 critical/major/minor blocker. + +#### Docs deliverables Chunk C + +- `docs/STATUS.md` Recently Done newest entry S24 t1 +- `docs/HANDOFF.md` Last updated S24 t1 prepend +- `docs/changelog/sessions/2026-05-15-s24-turn1-plan-aa-workflow-matrix.md` (file này) +- `.claude/agent-memory/investigator/MEMORY.md` FIFO entry S24 Pre-A +- `.claude/agent-memory/implementer/MEMORY.md` FIFO entry S24 Chunk B +- `.claude/agent-memory/reviewer/MEMORY.md` FIFO entry S24 cumulative verify + +## 📊 Stats Plan AA chốt + +| Metric | Trước (S23 t12) | Sau (S24 t1) | Δ | +|---|---|---|---| +| Migrations | 31 | 31 | 0 (no Mig mới) | +| DB tables | 59 | 59 | 0 | +| Endpoints | ~145 | **~146** | +1 (GET filter param) | +| FE pages | 34 | **35** | +1 (WorkflowMatrixViewPage) | +| Unit tests | 111 | 111 | 0 (UAT mode skip) | +| Gotchas | 47 | 47 | 0 | +| Memory entries user-level | 21 | 21 | 0 (no new — recommend memory responsive update sau) | +| Skills | 6 | 6 | 0 | +| Sub-agents | 4 | 4 (3 spawn S24 t1) | — | +| **Commits Plan AA** | — | **3** | `ee776d5` (A BE+Layout) · `c667802` (B FE) · this (C Docs) | + +## 🎯 Multi-agent ROI Plan AA + +| Spawn | Cost | Verdict | Catch / Value | +|---|---|---|---| +| 🟦 Investigator Pre-A | ~32K | ✅ | 5Q audit + 3 surprises (PE 2-panel + Order strategy + Contract enum unwired). Saved em main blind code 4 wrong assumptions. | +| 🟨 Implementer Chunk B | ~14K | ✅ | 3 files +305 LOC cookie-cutter Designer read-only mirror. Build PASS. shadcn fe-user no Card/Badge fallback inline div pattern. | +| 🟥 Reviewer Chunk C | ~25K | ✅ | 0 critical/major/minor block. Bonus catch fe-admin build (~7s) verify mirror app PASS. 1 Low note non-admin filter leak workflow chưa ghim defer. | +| 👤 Chủ trì Solo Chunk A + coordinate | ~80K | ✅ | BE 4 file + Layout 2 file cross-stack architectural decisions. Order shift refactor INSERT→UPDATE idempotent. | + +**Total cost cumulative Plan AA:** ~150K (~25% solo equiv). Multi-agent leverage cache 70-90% per session. + +## 📋 Pattern reinforced + +1. **Gotcha #44 relax class-level [Authorize] PROVEN cross-stack reuse** — Workflows endpoint từ admin-only sang any-auth read accessible. Mảnh pattern reusable cho future read-only user view (Form template view, Department list view, etc.). + +2. **DbInitializer INSERT-OR-UPDATE-Order pattern** — Idempotent re-deploy safe shift existing prod rows. Cross-project reusable cho menu re-ordering / label rename / icon change without migration. + +3. **Plan U truncate revert pattern** — widen container + drop truncate + keep `min-w-0 flex-1` + `shrink-0` + `title` tooltip. Reusable cross-project sidebar / nav menu / breadcrumb. + +4. **Read-only admin Designer mirror page** — Implementer Case 2 pattern: drop edit mutations + reuse types + filter via query param. Reusable cho future user view của admin config (Role permission view, Workflow template view, etc.). + +## ⏭ Pending S24+ + +- 🟢 Bro UAT verify Plan AA deploy sau ~3-5 phút CI Run #210+ +- 🟩 CICD Monitor spawn post-deploy verify bundle hash 2 app + smoke endpoint `?isUserSelectable=true` + DbInitializer Order shift idempotent +- 🟡 Plan B Contract V2 wire (Mig 32+33) — HIGH priority next (pre-allocated 5-6 chunk in HANDOFF) +- 📝 Memory `feedback_responsive_laptop_breakpoint.md` update: PE Workspace 2-panel (NOT 3-panel) — defer Chunk D bonus +- 🔍 Discovery #3 anomaly CI trigger docs-only (3× reinforced) — Investigator follow-up +- 🔍 Discovery #4 ASP.NET enum body deserialization (LOW polish) +- 🔧 Gotcha #47 paths-ignore agent-memory (pending bro chốt) +- ⚠️ Security follow-up: BE enforce `isUserSelectable=true` mandatory cho non-admin (Reviewer Low note) + +## References + +- Files: [WorkflowMatrixViewPage.tsx](../../../fe-user/src/pages/pe/WorkflowMatrixViewPage.tsx) · [types/approvalWorkflowV2.ts](../../../fe-user/src/types/approvalWorkflowV2.ts) · [MenuKeys.cs:85](../../../src/Backend/SolutionErp.Domain/Identity/MenuKeys.cs) · [DbInitializer.cs:1429-1459](../../../src/Backend/SolutionErp.Infrastructure/Persistence/DbInitializer.cs) +- Rules: §3.9 mirror 2 FE app, §6.5 KEEP narrative, §7 test timing +- Cross-ref skill `permission-matrix` (menu structure) + `contract-workflow` (workflow V2) +- Memory cross-ref `feedback_per_nv_permission_scope.md` (7 Allow* per-Level slot) + `feedback_uat_skip_verify.md` (Phase 9 test-after)