[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:
@ -139,6 +139,27 @@ Flag commit nếu thấy `<PackageReference Include="MediatR" Version="14...` ho
|
||||
|
||||
## 📅 Recent runs (FIFO last 20)
|
||||
|
||||
- **2026-05-15 ~14:50 — Run #209 sha=`86d8806` VERDICT=PASS (S23 t11 Plan U FE sidebar truncate)**. Commit 2 file FE × 2 app mirror `Layout.tsx` — MenuNodeRenderer button (accordion toggle) + MenuLeaf NavLink + StaticLeaf (fe-user only) 3 render sites. Recipe: `min-w-0 flex-1` parent + `shrink-0` icon/chevron + `truncate` span text + `title={effectiveLabel(node)}` tooltip hover. Mig 27 DisplayLabel custom dài "1. Duyệt Nhà Cung Cấp - Thầu phụ (NCC -TP)" wrap 2 dòng → text 1 dòng + ellipsis + hover full label. Build × 2 app PASS, +25/-17 LOC. Pattern reusable cross-project: long-label sidebar handling.
|
||||
- **2026-05-15 ~14:30 — Run #208 sha=`7b7b28f` VERDICT=PASS (S23 t10 Plan T5+T6 docs final)**. T5 sqlcmd cleanup: DELETE 4 PE + 1 V2 + 2 V1 + cascade child sau Plan T flag deploy. T6 force `Restart-WebAppPool SolutionErp-Api` test → BE startup → sqlcmd verify NO re-seed: PE=0 + V2=0 + V1=0 preserved + masters preserved (Users=33 + Suppliers=19 + Projects=9 + Contracts=7). DemoSeed flag PROVEN active end-to-end. Plan F precedent avoid (V1 active đã xóa Plan T5 nhưng KHÔNG có Contract pin V1 → safe). Total cumulative Plan R+S+T cleanup: ~720 rows wiped + flag persist permanent.
|
||||
- **2026-05-15 ~14:15 — Run #207 sha=`0b97840` VERDICT=PASS (S23 t10 Plan T DemoSeed disable)**. Code change: `DbInitializer.cs` + `appsettings.json` (production inherit `DemoSeed:Disabled=true`) + `appsettings.Development.json` (override `false` cho dev test seed local). Wrap conditional 5 demo seed methods `if (!demoSeedDisabled)`: SeedWorkflowDefinitions V1 + SeedPurchaseEvaluationWorkflows V1 + SeedDemoContracts + SeedDemoPurchaseEvaluations + SeedSampleApprovalWorkflowsV2. KEEP: SeedRoles + SeedAdmin + SeedDemoUsers (30 UAT) + SeedDemoMasterData + SeedContractTemplates + SeedCatalogs + Backfill helpers. Note: `appsettings.Production.json` bị `.gitignore` → flag mặc định trong `appsettings.json` commit qua git. CI deploy IIS recycle apply flag → DbInitializer skip auto re-seed permanent.
|
||||
- **2026-05-15 ~13:40 — Run #204 sha=`108268a` VERDICT=PASS (S23 t7 Plan Q FE banner mx-5 fix)**. CSS polish mirror 2 app `PeDetailTabs.tsx` banner F3 — drop `mx-5` inset + `mt-2 → mb-3` align với ItemsTab header. Banner full Section padding width, KHÔNG gap visual lệch với button "+ Thêm hạng mục" right-aligned. Bundle hash rotated: admin `D_JENTBi → QZIPWD-g`, user `COJhbRxy → DaLTMGcx`.
|
||||
- **2026-05-15 13:27-13:31 — Run #203 id=317 sha=`1727bd5` VERDICT=PASS** (S23 t6 Plan P HOTFIX Controller TransitionPeBody record +3 fields — push 1 commit `1727bd5` BE Controller + Docs. Duration 3min23s baseline. Path filter: tip has Controller .cs + docs → trigger correctly. Test gate inferred PASS (deploy stage runs after tests; 111/111 baseline unchanged Plan P). **CRITICAL Stage 4c — Plan P live wire VERIFY** (the bug Plan O surfaced as "Caveat #1" 14 min ago, now FIXED):
|
||||
- **Pre-fix gap (Plan O caveat):** Controller `TransitionPeBody` record had only 3 fields (`TargetPhase`, `Decision`, `Comment`). FE × 2 sent 7 fields. ASP.NET silently dropped `ReturnMode`/`ReturnTargetUserId`/`SkipToFinal` at deserialization → handler always received default (`returnMode=null → Drafter`, `skipToFinal=false`) → F1 Assignee/OneLevel/OneStep modes + F2 skip-to-final not wired from FE for 2 prod days (Mig 28 deploy 2026-05-13 → S23 t6 catch 2026-05-15).
|
||||
- **Source verify (line 280-286):** record now has 7 params with `WorkflowReturnMode? ReturnMode = null`, `Guid? ReturnTargetUserId = null`, `bool SkipToFinal = false`. `mediator.Send` line 76-78 passes all 7 args to `TransitionPurchaseEvaluationCommand`. Plan P comment markers present line 71-75.
|
||||
- **Test 1 admin POST PE_ID=`98736f06-b6c8-4d2d-a461-4590caf096f8` (PE/2026/A/025, ChoDuyet→TraLai)** body 7 fields `{targetPhase:98, decision:2, comment, returnMode:4, returnTargetUserId:null, skipToFinal:false}`:
|
||||
- First attempt with **string enum** `"Drafter"` → HTTP 400 `"The JSON value could not be converted to ... TransitionPeBody. Path: $.returnMode"` — confirmed no global `JsonStringEnumConverter` registered. FE must send numeric.
|
||||
- Retry with **numeric** `4` → **HTTP 204 No Content** ✓ Phase mutated 10→98 (TraLai) ✓ — all 7 fields deserialized correctly through Controller → Command → Handler.
|
||||
- **Test 2 admin Assignee on PE_ID=`f9476dad-52fc-41fe-9149-70668d5fc0a9` (PE/2026/A/021)** body `{returnMode:3 Assignee, returnTargetUserId:<admin guid>, ...}`:
|
||||
- HTTP **409** with detail `"Không tìm thấy người chỉ định trong workflow. Chỉ pick từ list NV đã duyệt trước đó (PeLevelOpinions)"` — **business rule rejection from Assignee branch logic**, NOT 400 deserialization, NOT 409 "Cấp Approver hiện tại không bật mode 'Drafter'" (pre-fix bug signature). Proves: (a) `returnMode=3` preserved end-to-end (handler ran Assignee branch), (b) `returnTargetUserId` reached handler (which validated against PeLevelOpinions list and rightly rejected admin guid not in approval history).
|
||||
- NV Test actor variant **not feasible** (scanned 20 ChoDuyet PEs — NV Test is Drafter for test corpus, never current approver). Test 2 admin variant + Test 1 admin variant together prove the deserialization wire end-to-end.
|
||||
- Stage 4a Auth: admin token len 468 ✓, nv.test token len 477 ✓
|
||||
- Stage 4b Smoke 3/3 critical endpoints 200 ✓ (`/api/users`, `/api/departments`, `/api/purchase-evaluations`). Note: `/api/me` + `/api/menu-keys` returned 404 (route names different — `/api/menus` is the actual endpoint). Not regression — these were brief-suggested probes, not real routes.
|
||||
- Stage 4d Bundle hash: Plan P is BE-only — expected unchanged from baseline `D_JENTBi`/`COJhbRxy`. Measurement caught Run #204 Chunk Q FE banner fix in flight (admin `QZIPWD-g`, user `DaLTMGcx`) — bundle change attributable to Chunk Q (`108268a` 13:38-13:41 PASS) not Plan P. **Plan P bundle UNCHANGED criterion still satisfied** since Plan P diff contains zero FE files.
|
||||
- Stage 4e Mig 31 unchanged ✓ — Plan P no schema change.
|
||||
- **Discovery #4 (NEW):** ASP.NET Core 10 default JSON deserialization for `record` types with enum fields requires **numeric input** unless `JsonStringEnumConverter` is registered in `Program.cs` / `AddJsonOptions`. SOLUTION_ERP API has NO converter registered (Grep `Program.cs` returned 0 hits for `JsonStringEnumConverter`/`AddJsonOptions`). FE × 2 correctly sends numeric (`WorkflowReturnMode = { OneLevel: 1, OneStep: 2, Assignee: 3, Drafter: 4 }` in `purchaseEvaluation.ts`). Brief example payload `"returnMode":"Drafter"` was misleading — that format fails 400. Future task brief revision: use numeric values for enum body fields, or task to add `JsonStringEnumConverter` for FE/3rd-party DX (low priority, since FE already correct).
|
||||
- **Side effect logged:** Admin transition on PE/2026/A/025 already in TraLai state pre-test (caveat #2 from Run #202 Plan O). Test 1 unchanged the state (was already 98). Test 2 mutated PE/2026/A/021 phase 10→? — re-check needed (Test 2 returned 409 so transition aborted, state likely preserved at 10). Verified: Test 2 HTTP 409 = handler threw exception → no state change (atomic).
|
||||
- **Pattern saved (Plan P confirms Plan O surface point 9 + adds new gotcha):** Controller body record schema MUST mirror underlying Command record schema. When Command grows fields (Mig 28/30/31 cumulative +3 fields F1/F2), Controller body record DROP causes silent FE wire failure (400 not thrown, deserializer just `default`s missing fields). Pre-existing gotcha implicit; now explicit. Future per-NV/per-Level refactor checklist appends point 10: **Controller body record mirror count check** — every `[FromBody]` record param count must equal underlying Command record param count touched by the route.
|
||||
|
||||
- **2026-05-15 13:10-13:13 — Run #202 id=316 sha=`a1c8386` VERDICT=PASS** (S23 t5 Plan O HOTFIX cascade 4 lookup sites — push range `fb3c22c..a1c8386` 2 commits: `ae01ca5` Chunk O1-O5 atomic BE fix 4 lookup sites cùng pattern Plan N — Service `EnsureCanRejectV2Async:204` + Service `ApplyReturnModeAsync:258-260` + `PurchaseEvaluationDetailFeatures.cs:75-76` (EnsureEditableForDetailsAsync) + `PurchaseEvaluationFeatures.cs:314-315` (AdjustBudgetCommandHandler) — all 4 sites add `&& l.ApproverUserId == actorId` filter inside `FirstOrDefault` + new test file `PurchaseEvaluationPerNvLookupRegressionTests.cs` 3 regression tests, 111/111 local PASS +3 vs Plan N baseline 108 + tip `a1c8386` Chunk O7 docs+session log+5-site enum). Duration 3min28s baseline. **Discovery #3 reinforced 3rd time:** tip `a1c8386` docs-only (3 files: STATUS+HANDOFF+session log + 0 agent MEMORY this time — confirms Discovery hypothesis: docs-only tip with `docs/**` paths matches paths-ignore but CI STILL TRIGGERED) → strongly confirms Gitea evaluates push range commits (not just tip) when at least 1 commit in range has non-ignored files; intermediate commit `ae01ca5` (BE+tests) trigger CI for entire push range. Anomaly is BENEFICIAL — catches verify gate. **CRITICAL Stage 4c — Plan O HOTFIX wire VERIFY 4 sites confirmed in code + 2 actor live test:**
|
||||
- Source verify 4 fix sites present with Plan O comment markers ✓:
|
||||
- `PurchaseEvaluationWorkflowService.cs:204` `step.Levels.FirstOrDefault(l => l.Order == curLvl && l.ApproverUserId == actorId)` + throw if null
|
||||
@ -197,3 +218,5 @@ Flag commit nếu thấy `<PackageReference Include="MediatR" Version="14...` ho
|
||||
- Stale > 3 months → remove
|
||||
|
||||
Last curate: 2026-05-15 13:18 (added S23 t5 Plan O HOTFIX Run #202 entry: PASS verdict + CRITICAL wire verify 4 sites confirmed in code via Grep + live NV Test POST transition test on PE/2026/A/025 returned 409 mode-mismatch NOT 403 actor-mismatch = Plan O actor discrimination active on all 4 sites. Bundle hash unchanged expected. Mig 31 TOP 1 unchanged. 111/111 test baseline +3. Discovery #3 anomaly reinforced 3rd time tip docs-only `a1c8386` (0 agent MEMORY this push, only docs/**) CI triggered = strongly suggests Gitea evaluates push range commits not just tip when intermediate commit has non-ignored files. **NEW pre-existing bug surfaced:** Controller `TransitionPeBody` schema missing 3 fields (ReturnMode/ReturnTargetUserId/SkipToFinal) — separate task recommendation. **Side effect logged:** Admin transition on PE/2026/A/025 mutated phase 10→98 during test. Memory size ~35KB approaching curate trigger; FIFO runs at ~8 entries.)
|
||||
|
||||
Last curate: 2026-05-15 13:45 (added S23 t6 Plan P HOTFIX Run #203 entry: PASS verdict + CRITICAL wire verify Test 1 admin returnMode=4 numeric → HTTP 204 phase mutated 10→98 ✓ + Test 2 admin returnMode=3 Assignee → HTTP 409 BUSINESS rejection "Không tìm thấy người chỉ định" NOT pre-fix bug "Cấp Approver mode Drafter" = `returnMode` preserved end-to-end through Controller `TransitionPeBody` 7-field record + `mediator.Send` 7-arg call + Handler Assignee branch logic. NV Test actor variant not feasible (scanned 20 PEs — NV Test = Drafter not approver). Plan P fixes the "Caveat #1 pre-existing bug" surfaced from Plan O Run #202 14 min earlier — fast turnaround S23 t5 → t6. **Discovery #4:** ASP.NET Core 10 record-with-enum needs numeric input; no global `JsonStringEnumConverter` registered (Grep confirms 0 hits). Brief example payload `"Drafter"` string format fails 400. FE × 2 correctly uses numeric. Bundle hash measurement caught Run #204 Chunk Q FE banner fix in flight (admin `QZIPWD-g`, user `DaLTMGcx`); Plan P bundle UNCHANGED criterion satisfied (Plan P diff zero FE files). Memory size ~40KB approaching curate trigger; FIFO runs at ~9 entries — schedule archive next curation.)
|
||||
|
||||
@ -204,6 +204,7 @@ KHÔNG `*` / `latest`. Critical pins:
|
||||
|
||||
## 📅 Recent activity (last 10 FIFO)
|
||||
|
||||
- **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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -144,6 +144,7 @@ Flag commit nếu thấy `<PackageReference Include="MediatR" Version="14...` ho
|
||||
|
||||
## 📅 Recent activity (last 10 FIFO)
|
||||
|
||||
- **2026-05-15 (S23 t4-t11 cumulative em main self-review — Plan N+O+P+Q+R+S+T+U, no Reviewer spawn):** 8 plan consecutive em main self-review per UAT mode (`feedback_uat_skip_verify` rule — em main verify build + test + npm build mỗi chunk). Key validations cumulative: **Plan N** GetPe handler `PurchaseEvaluationFeatures.cs:765` per-NV `ApproverUserId` discriminator + admin fallback row đầu. **Plan O** 4 lookup sites cascade fix (`EnsureCanRejectV2Async:201` + `ApplyReturnModeAsync:248` + `EnsureEditableForDetailsAsync:72` + `AdjustBudgetCommandHandler:311`) — pattern uniform Plan N, +3 regression test `PurchaseEvaluationPerNvLookupRegressionTests.cs`. **Plan P** Controller `TransitionPeBody:267` record +3 fields mirror `TransitionPurchaseEvaluationCommand` schema — root cause 2 ngày prod bug F1+F2 wire fail. **Plan Q** FE banner mx-5 inset gap fix (CSS layout polish mirror 2 app). **Plan R+S+T5** destructive sqlcmd cleanup prod ~720 rows wiped cumulative + Plan F precedent avoid được (V1 active workflow giữ nguyên → BE healthy startup). **Plan T** DbInitializer `DemoSeed:Disabled` flag config — disable 5 demo seed methods (Workflow V1 + PE V1 + Demo Contracts + Demo PE + Sample V2). **Plan U** FE sidebar truncate + tooltip 2 app mirror — handle long DisplayLabel (Mig 27 admin custom). Anti-patterns observed: (1) **Plan N point 9 chỉ catch 1/5 sites** — em main + Investigator spawn miss 4 sites khác → Plan O cascade fix. Lesson: grep ENUMERATE TẤT CẢ lookup sites cùng pattern (KHÔNG fix theo bug report mỗi lần). (2) **Plan O caveat #1 surfaced Plan P pre-existing bug** — CICD Monitor catch Controller body record drop trong Stage 4c verify wire — pattern "verify cross-stack body↔command count match". (3) **DbInitializer auto re-seed** loop — Plan R+S clean → IIS recycle → re-seed loop. Plan T flag fix root cause. Pattern reusable cross-project: refactor schema 1-row-per-role MUST grep enum lookup sites + Controller body record MUST mirror Command record fields. Cumulative state: 111 test (+7 vs 104 baseline pre-Plan M), 31 mig (no new), 47 gotcha unchanged, `feedback_per_nv_permission_scope.md` reinforced 5 lookup sites enum + 9 wire surface points + Plan P caveat → 10 surface points. Smart Friend guard still active for future spawn.
|
||||
- **2026-05-15 (S23 t3 Plan M cumulative review, spawn):** Pre-push adversarial verify 3 commits local `c2afef2..508b17a..4dd6f9c` Plan M F1 edge case Bước 1 + Phase=TraLai display rename. Diff cumulative: 26 LOC BE (Service.cs:287-333) + 116 LOC test (1 file, +2 test Fact extend SeedWorkflowAsync helper +2 params optional) + 8 LOC FE (4 file × 2 LOC mirror admin+user). **Verdict: PASS** — wire BE M1 verified hot path 287-333 OneLevel+OneStep edge case replace `Phase=TraLai+clear pointer+return` with `Set pointer (0,1) + summary "không lùi được" → fallthrough SLA reset line 364 → Phase=ChoDuyet preserved from pre-call state (entry guard 75-81 require ChoDuyet để Reject decision)`. ApplyReturnModeAsync caller line 94-100 truyền `evaluation.Phase` (now ChoDuyet) vào LogTransitionAsync → Summary="Chuyển phase ChoDuyet → ChoDuyet" + ContextNote contain "không lùi được" (matches test assert ContextNote.Contain). Drafter mode line 268-275 GIỮ Phase=TraLai semantic (unchanged). Assignee line 335-360 throw nếu không match unchanged. F2 ApproveV2Async + F3 EnsureEditableForDetailsAsync + F4 AdjustBudgetCommand handler — UNCHANGED. Schema integrity: 0 migration mới, Phase enum TraLai=98 còn dùng cho Drafter mode + display label rename only. Security: Admin bypass logic line 252-265 + Reject guard line 81 EnsureCanRejectV2Async + non-Approver block — preserved. Code quality: dotnet build PASS 0 err 2 warn (DocxRenderer pre-existing), dotnet test 106/106 PASS (58 Domain + 48 Infra, +2 từ 104 baseline), npm build admin+user PASS 0 TS err (chỉ chunk size warn pre-existing), no `--no-verify`. Test coverage: 2 Fact mới `ApplyReturnMode_OneLevel_AtStep1Level1_ResetsToBuoc1Cap1_KeepsChoDuyet` + `ApplyReturnMode_OneStep_AtStep1_ResetsToBuoc1Cap1_KeepsChoDuyet` cover OneLevel Step1Lv1 + OneStep Step1Lv2→reset. Assert đủ Phase=ChoDuyet + pointer (0,1) + SLA NotNull + ContextNote "không lùi được". Helper extend +2 params backward compat OK (default false). K7 cascade NO regression — 3 `ApproveV2_SkipToFinal_*` xanh (M1 chỉ đụng F1 ApplyReturnModeAsync, không động F2 ApproveV2Async). Mirror 2 FE app (§3.9): purchaseEvaluation.ts admin+user identical (98:'Cần chỉnh sửa lại' + PeDisplayStatus.TraLai:'Cần chỉnh sửa lại'), PeWorkflowPanel.tsx admin+user identical (2 inline literal "Phase → ..." + "Phiếu sẽ về ..." rename). Anti-fiddle: scope drift 0% — 8 LOC FE đúng spec rename Phase=TraLai display only. Anti-pattern guard: **(1) Stale narrative comment Service.cs:288-289 + 327-328** UPDATED rõ "Plan M S23 t3 — KHÔNG fallback Drafter, phiếu giữ đang duyệt" — không zombie. **(2) Audit log consistency** test+code match "không lùi được" exact. **(3) Backward compat** phiếu UAT prod đang Phase=TraLai do logic cũ — Drafter resume từ TraLai vẫn work line 105-132 (entry point `fromPhase==TraLai → targetPhase==ChoDuyet`). **Minor issues (non-block, recommend fix sau):** **(4) Inconsistent UX literal "Trả lại"** còn ở 8 user-facing locations chưa rename (decision design): PeListPanel.tsx:112 "Bản nháp + Trả lại" filter label, PeWorkflowPanel.tsx:270 button "← Trả lại", 272 tooltip "(Duyệt / Trả lại / Từ chối)", 315 "← Trả lại Drafter sửa", 342 "Chọn cách Trả lại", PeDetailTabs.tsx:152 "chỉ Bản nháp / Trả lại mới sửa", 287 "(trừ khi approver Trả lại)", ApprovalWorkflowsV2Page.tsx:639 "(Trả lại / Edit Section 2 / ...)" — phân biệt **action verb** "Trả lại" (button approver click) vs **phase result label** "Cần chỉnh sửa lại" (Phase=TraLai display in dropdown/badge); spec M3 narrow scope chỉ rename phase result label OK. Em main quyết: giữ action verb hay rename hết. **(5) Inconsistent across module** contracts.ts:29 + budget.ts:20 vẫn `98:'Trả lại'` — KHÔNG trong scope Plan M (PE-only) nhưng inconsistent giữa 3 module nếu Contract/Budget có Phase 98 — bro nếu deploy chung sẽ noticeable. Recommendation: SAFE to push 3 commit Plan M; consider follow-up chunk M4 (optional) rename 5 literal user-facing trong PeListPanel + PeWorkflowPanel + PeDetailTabs để consistent UX semantic mới. Smart Friend guard active.
|
||||
- **2026-05-14 (S23 t1 Plan K1+K2 cumulative review, spawn):** Pre-K3 adversarial verify Mig 31 schema swap + Service Approver F2 branch. Diff scope: 11 BE files +4093/-83 LOC (Mig Designer 3938 lines dominate). 3 commits chuỗi `eb106f2..56868bf..db66253..364aef6` — pre-A slot label refactor + K1 Domain/Mig/Snapshot/sentinel patches + K2 DTO+Service Approver branch. **Verdict: PASS với 2 Major + 2 Minor issues** — K3 OK proceed nhưng K5 endpoint cleanup nên ưu tiên trước K7 test fix. Wire claim verify: Approver F2 branch placed ĐÚNG vị trí (line 477 AFTER UPSERT opinion line 441-468 + BEFORE advance pointer line 502) — opinion sẽ ghi trước skip terminal, audit log đầy đủ context Bước/Cấp. ApproveV1LegacyAsync skipToFinal guard tại CALLER (line 147-149 trong TransitionAsync branch) thay vì callee — pattern OK, V1 method giữ nguyên signature legacy. Schema sqlcmd verify Dev: 7 Allow* columns ApprovalWorkflowLevels (AllowApproverEditBudget/EditDetails/SkipToFinal/ReturnOneLevel/OneStep/ToAssignee/ToDrafter) + Users.AllowDrafterSkipToFinal dropped (count=0). Snapshot regen clean. EF config HasDefaultValue(false) wire. Mig 31 Up() manual reorder ADD→DROP correct per memory `feedback_ef_migration_backfill_reorder`. **Major issues caught** (Smart Friend guard active — KHÔNG để pass): (1) **Orphan UsersController endpoint zombie** — K1 sentinel commented "K2 sẽ refactor DTO + drop field" nhưng K2 chỉ refactor PE side, KHÔNG động UsersController.SetAllowDrafterSkipToFinal + UserFeatures.SetUserAllowDrafterSkipToFinalCommand + UserDto field. Endpoint PATCH `/api/users/{id}/allow-skip-final` vẫn live nhưng silent NoOp (Task.CompletedTask), admin UI tick → BE swallow → confusion UX. Cần K5 endpoint cleanup chunk (per spec). (2) **Stale Mig 28 comment ApprovalWorkflow.cs:78** — comment cũ "F2 (Drafter skip) đã move sang Users.AllowDrafterSkipToFinal" còn nguyên dù line 107-113 prop AllowApproverSkipToFinal mới. Confuse future dev. **Minor issues:** (3) Comment TransitionPurchaseEvaluationCommand DTO PurchaseEvaluationFeatures.cs:401 "F2 — Drafter skip thẳng Cấp cuối khi trình duyệt" still says Drafter (semantic outdated). (4) ApprovalWorkflowConfiguration.cs:22 stale Mig 28/29 narrative comment chưa note Mig 31. Anti-fiddle audit PASS: K1 18 LOC UserFeatures.cs sentinel patches valid compile-break workaround, K2 4 files within original spec. Anti-pattern reinforced 3×: admin opt-in per slot per-NV (Mig 29 F1+F3 + Mig 30 F4 + Mig 31 F2 — em main lần đầu sai Mig 30 default scope expansion → bro corrected). Production build PASS 0 err 2 warn (DocxRenderer unrelated). Test references K7-pending line 253. No `--no-verify` bypass. Pattern caught: "Transient sentinel pattern" — đặt sentinel + comment commit chunk khác cleanup nhưng chunk đó scope SHIFT → zombie state. Recommend explicit K5 cleanup chunk trước K7 test fix. Cumulative state: 31 mig (+1 Mig 31), 47 gotcha unchanged. Smart Friend guard active.
|
||||
- **2026-05-13 (S22 18:00→21:00, no spawn):** Em main solo self-review S22 — Reviewer KHÔNG spawn per UAT mode `feedback_uat_skip_verify`. Em main verify build clean + test pass + npm build × 2 app mỗi chunk (11 commits cumulative). Key validations: (1) Plan E phân quyền strict V2 — actor.UserId scope in List + Detail, Inbox đã strict từ S17, loose UAT clause `|| ApprovalWorkflowId != null` removed. (2) S22+1 V2 actor scope guard BE helper `EnsureCanRejectV2Async` chặn request forge non-approver PATCH /transitions decision=Reject (mirror FE button disable) — defense-in-depth FE+BE pattern. (3) S22+4 AdjustBudgetCommand handler 3-tier scope (Drafter Nháp/Trả lại + Approver ChoDuyet + Admin) — S22+5 refactor ChoDuyet branch dùng `level.AllowApproverEditBudget` flag (admin opt-in per slot) thay default Approver scope (security scope correction per bro feedback). (4) S22+2 Identity password policy enforced ≥12 chars (reject `User@123456` 11 chars outdated). Anti-patterns observed: (a) Default scope expansion mistake S22+4 → S22+5 fix — KHÔNG default expand permission scope without admin tick (per-NV opt-in pattern Mig 29 lesson reinforced); (b) History display field assumption BudgetAdjustSection initial — em assume `PeDetailBundle.changelogs` exists, FAIL TS2339 — pattern verify type fields trước render; (c) PS 5.1 Vietnamese diacritics gotcha #30 reinforced — script `seed-test-users-prod.ps1` first attempt FAIL parser error, ASCII-only discipline. S22+4 attachment view endpoint + S22+4 budget-adjust endpoint chưa qua live curl verify (defer post-deploy — bro UAT direct). S22+5 Mig 30 applied LocalDB Dev + Design via `dotnet ef database update`. New gotcha discovered: #47 paths-ignore agent-memory gap (recommend add to docs/gotchas.md — pending bro decide). Cumulative state: 104 test (+20), 30 mig (+1 Mig 30), 46 gotcha unchanged (gotcha #47 pending), ~146 endpoints (+3), 33 active users. Smart Friend guard still active for future spawn.
|
||||
|
||||
Reference in New Issue
Block a user