Files
solution-erp/docs/changelog/sessions/2026-05-15-s23-turn5-plan-o-cascade-4-lookup-sites.md
pqhuy1987 a1c8386712
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m27s
[CLAUDE] Docs: Chunk O7 — S23 t5 Plan O HOTFIX wrap: docs + session log + memory 5 sites enum
Plan O cumulative 2 commits:
- O1-O5 atomic (ae01ca5) BE fix 4 lookup sites + 3 regression test 111/111 PASS
- O7 (this) docs + memory + session log

Docs update:
- docs/STATUS.md Last updated S23 t5 + stats 111 test (+3)
- docs/HANDOFF.md TL;DR S23 t5 với 5 sites enumerate
- docs/changelog/sessions/2026-05-15-s23-turn5-plan-o-cascade-4-lookup-sites.md

Memory user-level update (1 entry CRITICAL HOTFIX S23 t5 section):
- feedback_per_nv_permission_scope.md — 5 lookup sites enum SOLUTION_ERP PE module
  documented + audit grep `FirstOrDefault.*Order ==` checklist + Reviewer
  pre-commit guideline. 4× cumulative gap analysis 3× refactor (Mig 29 + 30 +
  31) wire SAME bug toàn 5 sites → 2 sessions hotfix (Plan N + Plan O) catch
  hết. Memory entry reinforced 2 lần Plan N + Plan O.

Pattern reusable cross-project: refactor schema 1-row-per-role (OR-of-N) +
bug fix lookup site → BẮT BUỘC grep enum tất cả lookup sites cùng pattern,
KHÔNG fix theo bug report mỗi lần.

Pending Chunk O8: CICD Monitor post-deploy verify Plan O.
Pending Bug 2 F2 follow-up: verify workflow v14 DB structure (BOD Bước 3
setup đúng?). F2 logic line 483-524 Service đã đúng (lastStepIdx +
lastLevelMaxOrder).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 13:09:57 +07:00

193 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Session 23 turn 5 — 2026-05-15 — Plan O HOTFIX 4 lookup sites cascade
**Dev:** Claude Opus 4.7 1M (em main solo — bug fix reasoning chain cross 4 sites)
**Duration:** ~1.5h
**Base commit:** `fb3c22c` (S23 t4 Plan N wrap)
**Final HEAD:** `<pending>` (Plan O atomic + docs)
**Total commits Plan O:** **2** (atomic O1-O5 + docs)
## 🎯 Trigger session
Bro UAT 2026-05-15 sau Plan N deploy phát hiện 2 bug mới:
**Bug 1 — Trả lại Người chỉ định fail:**
- Phiếu PE/2026/A/029 workflow QT-DN-V2-001 v14
- Actor NV Test (UAT V2) trong OR-of-N slot Bước 2 Cấp 1 (4 NV)
- Click ← Trả lại → Dialog mở (Plan N hotfix wire OK)
- Chọn "Trả lại Người chỉ định" + Bùi Lê Thủy Trà (Bước 1 Cấp 2)
- Click Xác nhận → **Toast: "Không phải lượt bạn — chỉ NV Cấp duyệt hiện tại mới được Trả lại / Từ chối phiếu"**
**Bug 2 — F2 Duyệt thẳng KHÔNG đến Cấp cuối:**
- Click ✓ Duyệt + checkbox skipToFinal → pointer trỏ đến **Phan Văn Chương Bước 2 Cấp 2** thay vì **Nguyễn Văn Trường Bước 3 Cấp 1 (BOD)**
- F2 logic [Service.cs:483-524](src/Backend/SolutionErp.Infrastructure/Services/PurchaseEvaluationWorkflowService.cs:483) đã đúng (`lastStepIdx = steps.Count - 1` + `lastLevelMaxOrder`)
- **Defer follow-up** — cần verify workflow v14 DB structure (có thể workflow chưa setup Bước 3 đầy đủ)
## 🔍 Em main audit grep — Phát hiện 5 lookup sites cùng bug Plan N
```bash
grep -n "FirstOrDefault.*Order ==" src/Backend/**/*.cs
```
| # | File | Line | Purpose | Pre-Plan O |
|---|---|---|---|---|
| 1 | `PurchaseEvaluationWorkflowService.cs` | 201 | `EnsureCanRejectV2Async` actor match guard | ❌ Bug bro UAT |
| 2 | `PurchaseEvaluationWorkflowService.cs` | 248 | `ApplyReturnModeAsync` read Allow flag | ❌ Bug |
| 3 | `PurchaseEvaluationDetailFeatures.cs` | 72 | F3 `EnsureEditableForDetailsAsync` | ❌ Bug |
| 4 | `PurchaseEvaluationFeatures.cs` | 311 | F4 `AdjustBudgetCommand` ChoDuyet branch | ❌ Bug |
| 5 | `PurchaseEvaluationFeatures.cs` | 765 | GetPe handler currentLevelOptions | ✅ Plan N S23 t4 fixed |
**Plan N S23 t4 chỉ catch 1/5 sites**. Plan O cần fix 4 sites còn lại cùng pattern.
## 🌳 Plan O 2 chunk execution
### Chunk O1-O5 — Atomic BE fix + regression tests (commit `ae01ca5`)
👤 Chủ trì Solo (bug fix reasoning chain cross 4 sites — Implementer auto-refuse criteria #4).
**O1 fix `EnsureCanRejectV2Async:201` (5 LOC):**
```diff
- var currentLevel = step.Levels.FirstOrDefault(l => l.Order == curLvl);
- if (currentLevel?.ApproverUserId != actorId)
+ var currentLevel = step.Levels.FirstOrDefault(l =>
+ l.Order == curLvl && l.ApproverUserId == actorId);
+ if (currentLevel is null)
throw new ForbiddenException("Không phải lượt bạn — ...");
```
**O2 fix `ApplyReturnModeAsync:248` (signature change + caller cascade):**
```diff
private async Task<string> ApplyReturnModeAsync(
PurchaseEvaluation evaluation,
WorkflowReturnMode mode,
Guid? returnTargetUserId,
+ Guid? actorUserId,
bool isAdmin,
CancellationToken ct)
- currentLevel = step.Levels.FirstOrDefault(l => l.Order == evaluation.CurrentApprovalLevelOrder);
+ currentLevel = step.Levels.FirstOrDefault(l =>
+ l.Order == evaluation.CurrentApprovalLevelOrder
+ && l.ApproverUserId == actorUserId);
```
Caller `TransitionAsync:94` cũng update: `actorUserId` thêm vào call:
```diff
- ApplyReturnModeAsync(evaluation, effectiveMode, returnTargetUserId, isAdmin, ct);
+ ApplyReturnModeAsync(evaluation, effectiveMode, returnTargetUserId, actorUserId, isAdmin, ct);
```
Non-admin actor không match slot → currentLevel=null → validation block line 252 skip → mode logic switch proceed (KHÔNG dùng currentLevel object, chỉ dùng curStepIdx + curLevel int values). Admin bypass existing line 252.
**O3 fix `EnsureEditableForDetailsAsync:72` (F3 guard, merge filter + collapse 3 throw → 2 throw):**
```diff
- var level = step?.Levels.FirstOrDefault(lv => lv.Order == levelOrder);
- if (level is null) throw ConflictException("schema lỗi");
- if (!level.AllowApproverEditDetails) throw ConflictException(...);
- if (level.ApproverUserId != actorUserId) throw ForbiddenException(...);
+ var level = step?.Levels.FirstOrDefault(lv =>
+ lv.Order == levelOrder && lv.ApproverUserId == actorUserId);
+ if (level is null) throw ForbiddenException("Chỉ NV phụ trách...");
+ if (!level.AllowApproverEditDetails) throw ConflictException(...);
```
**O4 fix `AdjustPurchaseEvaluationBudgetCommandHandler:311` (F4 ChoDuyet branch, cùng pattern):**
```diff
- var level = step.Levels.FirstOrDefault(l => l.Order == curLvl);
- if (level is null) throw ConflictException("schema lỗi");
- if (!level.AllowApproverEditBudget) throw ConflictException(...);
- if (level.ApproverUserId != actorId) throw ForbiddenException(...);
+ var level = step.Levels.FirstOrDefault(l =>
+ l.Order == curLvl && l.ApproverUserId == actorId);
+ if (level is null) throw ForbiddenException("Chỉ NV phụ trách...");
+ if (!level.AllowApproverEditBudget) throw ConflictException(...);
```
**O5 Regression test** (`PurchaseEvaluationPerNvLookupRegressionTests.cs` new file, 3 test):
1. `TransitionReject_ActorD_LastInSlot_AllowsRejectViaDrafterMode` — Actor D (non-first-row in OR-of-N) trả lại Drafter mode → no throw. Pre-fix: throw "Không phải lượt bạn" vì handler chỉ check row đầu DB (Actor A).
2. `TransitionReject_Outsider_NotInSlot_ThrowsForbidden` — Outsider không trong slot → throw đúng intent (verify fix KHÔNG over-permissive).
3. `TransitionRejectOneLevel_ActorC_HasFlagWhileOthersDont_AllowsMode` — Actor C only tick AllowReturnOneLevel, 3 NV khác KHÔNG. Actor C click "Trả lại 1 Cấp" → mode allowed. Pre-fix: read flag từ row A (false) → throw ConflictException "không bật mode OneLevel".
**Verify:**
- `dotnet build SolutionErp.slnx` clean (0 err, 2 warn pre-existing DocxRenderer)
- `dotnet test SolutionErp.slnx` **111/111 PASS** (+3 từ 108: 58 Domain + 53 Infra)
- 3 Plan O test individual PASS
### Chunk O7 — Docs + memory + push (this commit)
- `docs/STATUS.md` Last updated S23 t5 + stats 111 test (+3)
- `docs/HANDOFF.md` TL;DR S23 t5 với 5 sites enumerate
- Session log file này
- Memory user-level `feedback_per_nv_permission_scope.md` CRITICAL HOTFIX S23 t5 section:
- 5 sites checklist documented
- 4× cumulative gap analysis updated (3× refactor wire SAME bug toàn 5 sites — 2 sessions hotfix Plan N + Plan O)
- Audit checklist grep `FirstOrDefault.*Order ==`
- Reviewer pre-commit guideline grep enum lookup sites
## 📊 Stats Plan O chốt
| Metric | Trước (S23 t4) | Sau (S23 t5) | Δ |
|---|---|---|---|
| Migrations | 31 | 31 | 0 |
| Endpoints | ~145 | ~145 | 0 |
| FE pages | 34 | 34 | 0 |
| **Unit tests** | 108 | **111** | **+3** (3 site regression: OR-of-N actor + outsider + flag-only-1) |
| Gotchas | 47 | 47 | 0 |
| Memory entries | 20 | 20 | 0 (1 entry CRITICAL HOTFIX section reinforced 2 lần: S23 t4 + S23 t5) |
| Skills | 6 | 6 | 0 |
| Sub-agents | 4 | 4 (em main solo Plan O) | active |
| Commits Plan O | — | **2** (atomic O1-O5 + docs O7) | pending push |
## 🎯 Multi-agent ROI evidence Plan O
| Spawn | Agent | Cost | Output | Catch |
|---|---|---|---|---|
| Audit grep | 👤 Chủ trì | <1K | 5 lookup sites enum file:line | em main solo grep nhanh hơn spawn Investigator |
| O1-O4 fix | 👤 Chủ trì | ~self | 4 site fix surgical ~30 LOC BE | Pattern uniform sau Plan N |
| O5 tests | 👤 Chủ trì | ~self | 3 regression test new file ~210 LOC | First iter compile error (using missing) fix add `SolutionErp.Application.PurchaseEvaluations.Services` namespace |
| O7 docs | 👤 Chủ trì | ~self | docs + memory update | |
| CICD verify | 🟩 (pending spawn) | ~150K | post-deploy verify Plan O | TBD |
**Total Plan O spawn cost (excl CICD):** ~0 sub-agent em main solo cross 4 sites bug fix reasoning chain. Per directive Thứ 9: bug fix tightly coupled em solo. Investigator catch root cause Plan N đã enough Plan O = follow-up surgical fix.
**Anti-pattern caught Plan N → Plan O:**
Plan N S23 t4 fix 1 site (Plan N Investigator brief mention 9 surface points). Em main + Investigator + Reviewer ALL MISS enum 5 lookup sites chỉ catch 1 GetPe handler. Bug regression 4 sites lại prod 1 ngày sau Plan N deploy 4 sites bug present 2 ngày prod cumulative.
**Pattern reinforced cross-project:**
Khi refactor schema 1-row-per-role (OR-of-N) + bug fix lookup site BẮT BUỘC grep enum tất cả lookup sites cùng pattern, KHÔNG fix theo bug report mỗi lần. Investigator pre-flight cho bug fix tightly coupled phải `grep -rn "FirstOrDefault.*Order =="` enumerate trước.
## ⚠️ Pending S23 t6+
- 🟩 **CICD Monitor post-deploy verify Plan O** spawn sau push
- 🔍 **Bug 2 F2 follow-up** verify workflow v14 DB structure thực tế (BOD Bước 3 setup đúng?). Nếu DB OK + F2 logic line 483-524 đúng thể FE timeline render issue
- 🟦 **Investigator follow-up gotcha #41/#48 candidate** controlled test paths-ignore (carry từ Plan M)
- 🟡 **Plan B Contract V2 wire (Mig 32+33)** HIGH priority next
- **Plan F drop V1** defer
- 🟢 **Plan H PE PDF Export** LOW priority
- 🟡 **Plan I RAG Hybrid setup** defer
- 🔧 **Gotcha #47 paths-ignore agent-memory** pending
## 📋 Pattern reinforced Plan O
1. **5 lookup sites enum SOLUTION_ERP PE module** sau Mig 29 OR-of-N documented trong memory user-level `feedback_per_nv_permission_scope.md` CRITICAL HOTFIX S23 t5 section
2. **Grep `FirstOrDefault.*Order ==` audit checklist** cho future flag F5+ HOẶC migration mới OR-of-N pattern
3. **Atomic commit BE fix + regression test** (per `feedback_uat_skip_verify` rule Plan L lesson) Service refactor semantic BẮT BUỘC test cùng commit
4. **Em main solo cross-stack bug fix reasoning chain** Implementer auto-refuse criteria #4 (bug fix involving reasoning chain). Investigator pre-flight Plan N đã enough Plan O = follow-up surgical fix
5. **Method signature cascade** Khi thêm param `Guid? actorUserId` vào helper internal, phải update ALL callers (1 caller trong này TransitionAsync:94)
## References
- Memory user-level updated: `feedback_per_nv_permission_scope.md` CRITICAL HOTFIX S23 t5 section (5 sites enum + audit grep checklist)
- Files:
- [PurchaseEvaluationWorkflowService.cs:94,201,212-248](src/Backend/SolutionErp.Infrastructure/Services/PurchaseEvaluationWorkflowService.cs:201) (O1+O2)
- [PurchaseEvaluationDetailFeatures.cs:72](src/Backend/SolutionErp.Application/PurchaseEvaluations/PurchaseEvaluationDetailFeatures.cs:72) (O3)
- [PurchaseEvaluationFeatures.cs:311](src/Backend/SolutionErp.Application/PurchaseEvaluations/PurchaseEvaluationFeatures.cs:311) (O4)
- [PurchaseEvaluationPerNvLookupRegressionTests.cs](tests/SolutionErp.Infrastructure.Tests/Services/PurchaseEvaluationPerNvLookupRegressionTests.cs) (O5)
- Rules: §3.9 mirror 2 FE, §7 test timing test-before (regression bug), `feedback_per_nv_permission_scope` (S23 t5 5 sites enum), `feedback_uat_skip_verify` (Service refactor + test atomic)