[CLAUDE] Docs: Chunk S23-Wrap — Session 23 chốt cuối cumulative (11 plan + 32 commits)

Bro chốt session S23 — 9 yêu cầu wrap:

1. **Flush 4 agent MEMORY.md cumulative S23 t4-t11:**
   - Investigator (+2 entries Plan R audit + Plan P FE wire audit)
   - Implementer (+1 entry cumulative REFUSE Plan N-U em main solo)
   - Reviewer (+1 entry cumulative em main self-review Plan N-U + Smart
     Friend pattern reusable)
   - CICD Monitor (+4 entries Run #204 Q + #207 T + #208 T5+T6 + #209 U
     PASS verify)

2. **Update MD docs:**
   - STATUS.md: Last updated S23 t12 chốt cuối + stats final cumulative
   - HANDOFF.md: TL;DR S23 t12 đầy đủ + Plan tree summary
   - Session log new: 2026-05-15-s23-turn11-plan-u-sidebar-truncate.md

3. **Skill list:** 6 project-specific skills unchanged
   (contract-workflow + permission-matrix + form-engine + ef-core-migration
   + dependency-audit-erp + iis-deploy-runbook) — agent allocation
   preserved (no new skill, no audit due — next 2026-06-01)

4. **Unit test verify:** 111/111 PASS unchanged
   (58 Domain + 53 Infra = +7 cumulative S23 từ 104 baseline pre-Plan M)

5. **Memory user-level update:**
   - feedback_per_nv_permission_scope.md +1 section S23 t6 CRITICAL HOTFIX
     Plan P (wire 10 surface points — point 10 Controller body record
     mirror count check)
   - NEW feedback_demo_seed_flag_disable.md (Plan T pattern reusable
     cross-project)
   - MEMORY.md index +1 entry NEW + update existing entry narrative
   - 21 memory entries total (+1 từ 20)

6. **§6.5 KEEP narrative discipline:** preserved — không cắt narrative
   any MD, chỉ phân tầng cumulative qua sections riêng (Reinforcement
   S22+5 → S23 t1 → t3 → t4 → t5 → t6 each separate section trong
   per_nv_permission_scope).

7. **Plan tree chốt cuối S23 11 plan:**
   K (Mig 31 F2 refactor 9c) → L (UAT bug 5c) → M (F1 edge case 4c) →
   N (per-NV lookup 2c) → O (4 sites cascade 2c) → P (Controller body
   record 1c) → Q (FE banner CSS 1c) → R (cleanup test data 1c) →
   S (wipe ALL workflows 1c) → T (DemoSeed flag 2c) → U (sidebar
   truncate 1c) = 32 commits total cumulative S23

8. **Release verify ALL PASS:**
   - CI runs: #195 K10 + #199 L + #200 M + #201 N + #202 O + #203 P +
     #204 Q + #207 T + #208 T5+T6 + #209 U — 10 verify runs ALL PASS
   - HEAD synced với origin/main: 86d8806

9. **Multi-agent ROI cumulative S23:**
   - 🟦 Investigator 5 spawn (K0 + N hypothesis + P FE wire + R audit + L2)
   - 🟨 Implementer 5 spawn (K1/K3/K5/K7 Case 2+3 + M2 + M3)
   - 🟥 Reviewer 2 spawn (K2 pre-commit + M3 cumulative review)
   - 🟩 CICD Monitor 10 verify runs PASS
   - Em main solo 6/11 plan (cross-stack reasoning chain Plan N+O+P+Q+R+S+T+U)

Stats final S23 chốt cuối:
- 31 mig · 59 tables · ~145 endpoints · 34 FE pages
- **111 test PASS unchanged** (+7 cumulative)
- 47 gotcha · **21 memory entries** (+1 NEW Plan T)
- 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

Pending S24+:
- Plan B Contract V2 wire (Mig 32+33) — HIGH priority next
- Discovery #4 ASP.NET enum body deserialization (low priority polish)
- Discovery #3 anomaly CI trigger docs-only — 3× reinforced
- Gotcha #47 paths-ignore agent-memory pending bro confirm

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-05-15 15:49:03 +07:00
parent 86d8806afc
commit a1a910f576
7 changed files with 137 additions and 2 deletions

View File

@ -128,6 +128,8 @@ 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 (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.
- **2026-05-15 (S23 t2 spawn M0 — Plan M F1+F2+F3+F4 ChoDuyet semantic audit):** Bro UAT post-Plan L deploy 2026-05-15 ~02:00: "Hiện logic cũ là khi trả lại 1 cấp hoặc chỉ định hoặc edit là trạng thái draft -> cái này thay đổi lại nhé, các tính năng duyệt thẳng, trả lại 1 cấp hoặc người chỉ định hoặc cho edit thì cho xử lý đc ở trạng thái đang gửi duyệt luôn." Audit 4 BE file + 4 FE file × 2 app cho F1+F2+F3+F4 phase semantic ChoDuyet preservation. **Verdict: CODE ĐÃ ĐÚNG semantic mới — đây là DISCONNECT bro mental model vs code reality post-Mig 28/29/30/31.** Evidence per feature: **F1.OneLevel** `PurchaseEvaluationWorkflowService.cs:285-312` SWITCH branch lùi 1 Cấp cùng Step (`curLevel-1`) ELSE lùi Step trước Cấp cuối; **Phase KHÔNG đổi** (giữ ChoDuyet, line 364 reset SLA only); fallback Drafter `:303-310` CHỈ KHI đang Bước 1 Cấp 1 no further back (clear pointer + Phase=TraLai). **F1.Assignee** `:335-360` foreach Steps find ApproverUserId match → set pointer; Phase giữ ChoDuyet; ConflictException nếu không tìm thấy NV trong workflow. **F1.OneStep** `:314-333` lùi prev Step Cấp max; Phase giữ ChoDuyet; fallback Drafter nếu đang Bước 1. **F1.Drafter** `:268-275` SET `Phase=TraLai=98` + clear cả 2 pointer + SLA null — đây là CASE DUY NHẤT về "draft". **F2 skipToFinal** `:483-524` Plan K L1 ĐÃ FIX advance pointer tới Bước cuối Cấp cuối (max), **Phase giữ ChoDuyet** (NV cuối duyệt thật để DaDuyet); guard line 485 ConflictException non-admin + flag off; opinion UPSERT trước line 441-468 ensure actor's signature lưu trước. **F3 EnsureEditableForDetailsAsync** `PurchaseEvaluationDetailFeatures.cs:42-99` 2 trường hợp accepted: Drafter scope (DangSoanThao/TraLai return pe sớm line 49-51) **OR Approver scope ChoDuyet line 54-94** (V2 schema + actor match level.ApproverUserId + level.AllowApproverEditDetails flag). **F4 AdjustPurchaseEvaluationBudgetCommandHandler** `PurchaseEvaluationFeatures.cs:272-329` Drafter scope `:283-290` (DangSoanThao/TraLai + isDrafter) ELSE Approver scope ChoDuyet `:291-323` (V2 + pointer init + actor match + level.AllowApproverEditBudget flag) — Phase ChoDuyet đã handle full. **Validation Allow* flag location** `PurchaseEvaluationWorkflowService.cs:252-265` ApplyReturnModeAsync gate per slot per mode: throw ConflictException nếu disabled (Admin bypass line 252). **FE PeWorkflowPanel.tsx**: TraLai dialog `:331-422` 4 radio button (OneLevel/OneStep/Assignee/Drafter) gated bằng `levelOptions?.allowReturnXxx` flag per current Approver Cấp (line 343-396); useEffect `:60-68` S23 t2 fix default first available mode KHÔNG Drafter khi admin tick F1 modes. Skip Final checkbox `:425-442` chỉ visible khi `levelOptions?.allowApproverSkipToFinal` + Approve forward direction (line 425). **FE PeDetailTabs.tsx F3+F4 wire**: `itemsReadOnly = readOnly && !approverEditMode` line 118 bypass readOnly khi F3 enabled (Mig 28 pattern); `canAdjust = isAdmin || (!readOnly && isDrafter && isDrafterPhase) || isApproverChoDuyet` line 977 bypass readOnly khi F4 enabled (L2 fix). Mirror fe-admin/fe-user line 109-115 + 967-979 ĐỒNG BỘ rule §3.9. **Surprise**: "Trả lại" trong UI memory docs đôi khi gọi "draft" colloquial — bro confuse 2 khái niệm `Phase=TraLai=98` (Drafter sửa rồi gửi LẠI từ Step 0) vs `Phase=DangSoanThao=1` (chưa từng gửi duyệt). KHÔNG code path nào trong F1 OneLevel/OneStep/Assignee/F2/F3/F4 set targetPhase=DangSoanThao mà không nên. **Recommendation: LOW effort** (0-20 LOC): chỉ cần communication clarification em main confirm với bro 4 mode F1 + F2 + F3 + F4 đã giữ Phase=ChoDuyet trừ F1.Drafter; mở DB SELECT phiếu UAT confirm `Phase` column number sau click test; OPTIONAL touch up FE label nếu user thấy "Đã gửi duyệt" nhầm "Bản nháp". KHÔNG cần Service refactor hay handler change. Nếu bro thấy phiếu cụ thể về Phase=1 SAU click F1.OneLevel hoặc F2 → spawn lại Investigator audit DB state + log Changelog cho phiếu đó cụ thể (data debug, not code bug).
- **2026-05-15 (S23 t2 spawn L2 — F3+F4 edit menu Duyệt audit):** Bug UAT prod 409a967: admin tick F3+F4 cho slot Approver, login actor user, vào menu "Duyệt" (`?pendingMe=1` cùng `PurchaseEvaluationsListPage` 3-panel), click PE Phase=ChoDuyet → Section 2 Hạng mục/NCC/Báo giá + Section 5 Điều chỉnh ngân sách vẫn read-only. **Root cause F3 = OK / F4 = BROKEN at readOnly short-circuit:** F3 wire ĐÚNG (`PeDetailTabs.tsx:118 itemsReadOnly = readOnly && !approverEditMode` override readOnly per Mig 28 S21 t4 → ItemsTab read prop OK). F4 wire SAI (`PeDetailTabs.tsx:245` passes `readOnly={readOnly}` (=true từ menu Duyệt) xuống BudgetAdjustSection, line 973 compute `canAdjust = !readOnly && (isAdmin || (isDrafter && isDrafterPhase) || isApproverChoDuyet)``!readOnly` short-circuits to false BEFORE F4 isApproverChoDuyet evaluate. Edit pencil button hidden line 1030 `{canAdjust && <Button>Điều chỉnh</Button>}`. Asymmetric vs F3 pattern Section 2 (PeDetailTabs.tsx:113-118). **Evidence:** FE F3 OK `fe-user/src/components/pe/PeDetailTabs.tsx:113-118` + `:224 ItemsTab ev={evaluation} readOnly={itemsReadOnly}`; FE F4 BUG `:957-973 BudgetAdjustSection !readOnly gate` + `:245 readOnly={readOnly}`; menu Duyệt route `fe-user/src/pages/pe/PurchaseEvaluationsListPage.tsx:256-261 PeDetailTabs readOnly={true}` (cùng route Danh sách); BE GetPe `PurchaseEvaluationFeatures.cs:735-770 currentLevelOptions populate 7 fields` OK; BE budget-adjust handler `:281-329` Phase ChoDuyet F4 branch + actor match + flag check ĐẦY ĐỦ + return ConflictException nếu Allow=false; BE PurchaseEvaluationDraftGuard.EnsureEditableForDetailsAsync ở `PurchaseEvaluationDetailFeatures.cs:42` (8 callsites Detail+Supplier handlers). **Recommendation:** Fix `BudgetAdjustSection` line 973 mirror approverEditMode pattern Section 2: thay `canAdjust = !readOnly && (isAdmin || ...)` thành `canAdjust = isAdmin || (!readOnly && isDrafter && isDrafterPhase) || isApproverChoDuyet` — Approver bypass readOnly khi F4 conditions met. **LOC ~3-5 LOC 1 file.** Surprise: Inbox `/inbox` route (InboxPage.tsx) navigate sang `/purchase-evaluations/:id` (mobile DetailPage route, default readOnly=false) — chỉ Danh sách `?pendingMe=1` desktop 3-panel mới hard readOnly=true.