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

11 KiB
Raw Blame History

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 đã đú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

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):

- 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):

  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:

- 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):

- 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):

- 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 → có thể là 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 có 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