[CLAUDE] Docs: Session 25 chốt cuối — Plan AB→AF cumulative 7 commits + 4 agent MEMORY flush

S25 wrap final:
- STATUS.md + HANDOFF.md prepend Plan AB→AF cumulative narrative (7 commits cdfd542..506cada + 7 CICD Runs #215-#221)
- gotchas.md +2 NEW entries:
  - #48 Multi-Changelog.Add() SQLite frozen-clock tie-break (Run #215 catch, fix Plan AB Chunk A2)
  - #49 UI dual-phase badge confusion khi state machine self-loop (Plan AD drop + extractNextTargetHint helper)
- Checklist debug bug mới +2 entries (24-25)
- Session log NEW docs/changelog/sessions/2026-05-19-s25-pe-history-visibility.md (~360 LOC)
- 4 agent MEMORY drift sync:
  - investigator/MEMORY.md (30→32KB) FIFO entry S25 wrap + count metadata
  - implementer/MEMORY.md (34→36KB) FIFO entry + patterns 16-18 saved
  - reviewer/MEMORY.md (31→32KB) FIFO entry + lesson SQLite tie-break + UAT skip risk reinforced
  - cicd-monitor/MEMORY.md (~72KB CRITICAL OVER) — 7 Run entries #215-#221 + curate flag MAX

Memory user-level +2 NEW entries (separate commit memory dir, KHÔNG trong this commit):
- feedback_fe_merge_synthetic_audit.md (Plan AC2 pattern)
- feedback_fe_usermap_fallback.md (Plan AF pattern)

Stats final S25:
- 31 mig (no schema) · 59 tables · ~146 endpoints · 35 FE pages
- 111 test unchanged (UAT defer test-after per §7)
- 49 gotcha (+2: #48 + #49)
- 23 memory user-level (+2 NEW S25 patterns)
- 6 skills · 4 sub-agents active
- 7 commits cumulative S25 · 7 CICD Runs (1 FAIL caught + 6 PASS)
- 6× bundle rotate × 2 app (Run #220 BE-only unchanged)

Critical pending S26+:
- Memory curate cicd-monitor PRIORITY MAX (~72KB strongly over hard threshold)
- Plan B Contract V2 wire HIGH priority (5-6 chunk pre-allocated S23 HANDOFF)

Per §6.5 KEEP narrative — KHÔNG cut rationale/gotcha context, chỉ phân tầng prepend latest.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-05-21 16:33:41 +07:00
parent 506cada86b
commit 0c6efdaf4f
8 changed files with 453 additions and 8 deletions

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,9 @@
> **Update rule:** trước khi bắt đầu 1 task → ghi row vào `🔥 In Progress`. Xong → chuyển sang `✅ Recently Done`.
**Last updated:** 2026-05-15 (Session 23 turn 12 chốt cuối — **🎯 S23 cumulative wrap: 11 plan + 32 commits cumulative**. Plan K (Mig 31 F2 refactor 9 commits) → L (UAT bug 5) → M (F1 edge case 4) → N (per-NV lookup 1 site 2) → O (4 sites cascade 2) → P (Controller body record 1) → Q (FE banner CSS 1) → R (cleanup phiếu/workflow 1) → S (wipe ALL workflows 1) → T (DemoSeed flag 2) → U (sidebar truncate 1). 4 sub-agents active throughout: 🟦 Investigator 5 spawn (K0 + N + P FE wire + R audit + L2 spawn) + 🟨 Implementer 5 spawn (K1/K3/K5/K7 + M2 + M3 Case 2/3) + 🟥 Reviewer 2 spawn (K2 pre-commit + M3 cumulative review) + 🟩 CICD Monitor 5 spawn (Run #195 K10 + #199 L + #200 M + #201 N + #202 O + #203 P + #204 Q + #207 T + #208 T5+T6 + #209 U — ALL PASS). **Memory user-level update cumulative S23:** `feedback_per_nv_permission_scope.md` reinforced 5 sections (S22+5 + S23 t1 + t3 + t4 + t5 + t6) → wire 10 surface points (point 9 lookup discrimination + point 10 Controller body record mirror). `feedback_uat_skip_verify.md` Plan L lesson Service refactor semantic test atomic. **+1 memory NEW** `feedback_demo_seed_flag_disable.md` cross-project Plan T pattern. **3 destructive cleanup cumulative Plan R+S+T5** ~720 rows wiped + DbInitializer permanent disable demo seed. **Stats final S23 chốt cuối:** 31 mig · 59 tables · ~145 endpoints · 34 FE pages · **111 test unchanged** · 47 gotcha · **21 memory entries** (+1 NEW Plan T flag pattern) · 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` 18.5MB. **32 commits S23** pushed remote `eb106f2..86d8806`. CI verify ALL PASS. Bro Designer setup workflow mới from scratch, KHÔNG còn auto re-seed contaminate. Plan B Contract V2 wire HIGH priority next.)
**Last updated:** 2026-05-19 (Session 25 chốt cuối — **🎯 S25 cumulative wrap: 7 plan + 7 commits Plan AB→AF + 7 CICD Runs (#215 FAIL → #216-#221 PASS)**. Push remote `e23f51c..506cada`. Bro UAT 2026-05-19 phát hiện 2 bug visibility critical PE/2026/A/032 + 4 follow-up polish UAT iteration: **Bug 1 Budget Adjust** không show "Lịch sử thay đổi" (BE log Header+Update OK nhưng FE filter strict TraLai-only loại) — **Bug 2 Return Mode 4 mode** không log Changelog (ApplyReturnModeAsync miss `db.PurchaseEvaluationChangelogs.Add()`) — **Bug 3 Lịch sử duyệt** missing Trả lại + Vượt cấp entries (Reject branch không add Approval row) — **Bug 4 dual-phase badges** gây nhầm "Đã gửi duyệt → Đã gửi duyệt" cho 3 mode Reject giữ Phase=ChoDuyet — **Bug 5 Changelog UserName** show "Hệ thống" thay vì user thật (9 sites BE miss UserName) — **Bug 6 historical name resolve** entries pre-deploy vẫn show empty (forward-only fix). **Plan AB Chunk A** (`cdfd542`) BE log return mode + FE filter relax × 2 app. **Plan AB Chunk A2** (`8c05947`) fix Plan M tests SQLite frozen-clock tie-break (gotcha #48 NEW — Run #215 caught + Run #216 PASS). **Plan AC** (`a734bf2`) BE Reject add Approval row + skipToFinal comment + FE Decision badge × 2 app. **Plan AC2** (`25837b6`) FE merge synthetic Reject rows từ Changelog historical (Option 2A bro chốt, reversible no-DB-touch). **Plan AD** (`0aaf2df`) Drop misleading dual-phase badges + parse next-target hint via regex `Chuyển phase X → Y` + comment keyword Trả lại/Vượt cấp (gotcha #49 NEW). **Plan AE** (`9ea62be`) BE preventive batch fix UserName 9 Changelog.Add sites (Budget Adjust + 8 sites: Create + Update + Detail CRUD + Quote CRUD + Select Winner). **Plan AF** (`506cada`) FE userMap fallback từ embedded domain data PeDetailBundle (drafter + approvals + approvalFlow + levelOpinions + departmentOpinions) — no extra API fetch admin permission. Multi-agent execution: 🟦 **Investigator** 1 spawn Plan AB Bug 1+2 audit ~28K + 🟨 **Implementer** 1 spawn Plan AB Chunk A ~12K Case 1 + 🟥 **Reviewer** 1 spawn Plan AB pre-commit ~22K PASS 0 blocker (1 minor non-block) + 🟩 **CICD Monitor** 7 spawns Run #215-#221 verify ALL PASS post-Plan AB Chunk A2 fix. **Em main solo từ Plan AC đến AF** (5 plan, cross-stack reasoning + UAT iteration borderline, Implementer/Reviewer KHÔNG re-spawn). Stats final S25 chốt: **31 mig (no schema)** · 59 tables · ~146 endpoints · 35 FE pages · **111 test pass unchanged** (UAT defer test-after per §7) · **49 gotcha (+2 #48 SQLite tie-break + #49 dual-phase UI confusion)** · **23 memory user-level (+2 NEW: `feedback_fe_merge_synthetic_audit` Plan AC2 + `feedback_fe_usermap_fallback` Plan AF)** · 6 skills · 4 sub-agents active. Bundle hash 6× rotate × 2 app (#216-#221), Mig 31 unchanged toàn S25. Patterns reusable cross-project (8 NEW S25): (1) Multi-Changelog.Add SQLite tie-break test discriminator, (2) CICD catch UAT skip test risk for BE refactor > 100 LOC, (3) Test fix Option A over BE refactor preservation, (4) Capture pre-call mutation state cho audit row from-position, (5) FE merge synthetic rows từ Changelog reversible recovery, (6) Drop misleading dual-phase badges + parse semantic hint, (7) Changelog UserName preventive systemic batch fix, (8) FE userMap fallback từ embedded domain data no extra API. **Memory CRITICAL FLAG**: cicd-monitor MEMORY ~72KB strongly over 50KB hard threshold — DEDICATED CURATION SESSION REQUIRED next (archive Run #186-#210 + S22-S24 verbose). Bro UAT verify ALL Plan AB→AF live deploy cumulative.)
**Last updated S23 t12:** 2026-05-15 (Session 23 turn 12 chốt cuối — **🎯 S23 cumulative wrap: 11 plan + 32 commits cumulative**. Plan K (Mig 31 F2 refactor 9 commits) → L (UAT bug 5) → M (F1 edge case 4) → N (per-NV lookup 1 site 2) → O (4 sites cascade 2) → P (Controller body record 1) → Q (FE banner CSS 1) → R (cleanup phiếu/workflow 1) → S (wipe ALL workflows 1) → T (DemoSeed flag 2) → U (sidebar truncate 1). 4 sub-agents active throughout: 🟦 Investigator 5 spawn (K0 + N + P FE wire + R audit + L2 spawn) + 🟨 Implementer 5 spawn (K1/K3/K5/K7 + M2 + M3 Case 2/3) + 🟥 Reviewer 2 spawn (K2 pre-commit + M3 cumulative review) + 🟩 CICD Monitor 5 spawn (Run #195 K10 + #199 L + #200 M + #201 N + #202 O + #203 P + #204 Q + #207 T + #208 T5+T6 + #209 U — ALL PASS). **Memory user-level update cumulative S23:** `feedback_per_nv_permission_scope.md` reinforced 5 sections (S22+5 + S23 t1 + t3 + t4 + t5 + t6) → wire 10 surface points (point 9 lookup discrimination + point 10 Controller body record mirror). `feedback_uat_skip_verify.md` Plan L lesson Service refactor semantic test atomic. **+1 memory NEW** `feedback_demo_seed_flag_disable.md` cross-project Plan T pattern. **3 destructive cleanup cumulative Plan R+S+T5** ~720 rows wiped + DbInitializer permanent disable demo seed. **Stats final S23 chốt cuối:** 31 mig · 59 tables · ~145 endpoints · 34 FE pages · **111 test unchanged** · 47 gotcha · **21 memory entries** (+1 NEW Plan T flag pattern) · 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` 18.5MB. **32 commits S23** pushed remote `eb106f2..86d8806`. CI verify ALL PASS. Bro Designer setup workflow mới from scratch, KHÔNG còn auto re-seed contaminate. Plan B Contract V2 wire HIGH priority 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` + 2 V1 workflows + 1 V2 mẫu UAT TỰ ĐỘNG RE-SEED sau khi BE deploy commits Plan P+Q+R+S → IIS recycle → `DbInitializer.InitializeAsync` chạy lại 5 demo seed methods. Bro AskUserQuestion chốt Option A: disable demo seed vĩnh viễn qua flag config. Em main implement: `appsettings.json` add `"DemoSeed": { "Disabled": true }` + `appsettings.Development.json` override `false` cho dev test seed local + `DbInitializer.cs` add `using Microsoft.Extensions.Configuration` + `var demoSeedDisabled = config.GetValue<bool>("DemoSeed:Disabled")` + wrap 5 method conditional `if (!demoSeedDisabled)` (SeedWorkflowDefinitions V1 + SeedPurchaseEvaluationWorkflows V1 + SeedDemoContracts + SeedDemoPurchaseEvaluations + SeedSampleApprovalWorkflowsV2). KEEP: SeedRoles + SeedAdmin + SeedDepartments + SeedDemoUsers (30 UAT users) + SeedMenuTree + SeedAdminPermissions + SeedDemoMasterData (Supplier/Project) + SeedContractTemplates + SeedCatalogs + Backfill helpers. Note: `appsettings.Production.json` bị `.gitignore` (secrets), em set flag mặc định trong `appsettings.json` commit qua git — production inherit `true`. Run #207 sha=0b97840 PASS 3min24s → IIS deploy applied flag. T5 final DELETE: SCP `scripts/plan-t5-final-cleanup.sql` upload + sqlcmd -i 3 TRANSACTION DELETE → 4 PE + 1 V2 + 2 V1 = 7 rows + cascade child. T6 verify NO re-seed loop: `Restart-WebAppPool SolutionErp-Api` force IIS recycle → BE startup → wait healthy → sqlcmd verify: **PE=0 + V2=0 + V1=0** preserved (KHÔNG re-seed) + Users=33 + Suppliers=19 + Projects=9 + Contracts=7 (existing preserved). DemoSeed flag PROVEN active end-to-end. Total cumulative Plan R+S+T cleanup: ~677 rows wiped + DbInitializer re-seed disable vĩnh viễn. Stats final S23 t10: **31 mig** · 59 tables · ~145 endpoints · 34 FE pages · **111 test unchanged** · 47 gotcha · 20 memory · 6 skills · **0 PE + 0 workflow + flag disable demo seed** — UAT permanent clean slate. Bro Designer setup workflow mới from scratch, KHÔNG còn auto re-seed contaminate.)
**Last updated S23 t9:** 2026-05-15 (Session 23 turn 9 — **🧹 Plan S: Wipe ALL workflows — UAT clean slate hoàn toàn**. Bro chốt sau Plan R: "các cái demo quy trình cũ -> xóa hết luôn đi nhé". 4 workflows còn lại (2 V2 ghim + 2 V1 active) đều seed demo cumulative (`(mẫu UAT)` / `(clone)(clone)` / `(v01)` sample seed). Bro AskUserQuestion chốt Option A (Recommended): wipe ALL 4 workflows, UAT clean slate. Em main solo execute (Investigator audit Plan R đã cover scope precedent, Plan S = continuation cùng UAT cleanup phase). Backup rollback: Plan R backup `SolutionErp_pre_cleanup_2026-05-15.bak` 18.5MB vẫn capture full state pre-cleanup → reuse cho rollback. Script `scripts/plan-s-wipe-all-workflows.sql` 2 BEGIN/COMMIT TRANSACTION + SET QUOTED_IDENTIFIER ON. Execute via scp + sqlcmd -i: DELETE ALL ApprovalWorkflows (2 rows cascade Steps+Levels) + DELETE ALL PurchaseEvaluationWorkflowDefinitions (2 rows cascade Steps+Approvers). Post-state: **0 V2 + 0 V1 + 0 Steps + 0 Levels + 0 Approvers** (ALL workflow entities wiped). BE smoke verify 5/5 endpoints 200 (auth + PE list + V2 workflows + V1 workflows + users + menus) — KHÔNG crash startup (no Contract pin to V1 — chỉ PE dùng workflow, PE đã wipe Plan R). Hậu quả expected: user KHÔNG tạo được phiếu mới (Workspace Select empty) cho đến khi admin Designer seed workflow mới from scratch — UAT mode chấp nhận. Total cleanup cumulative Plan R + S: 35 PE + 17 V2 + 4 V1 + ~600 cascade child = **~670 rows wiped**, prod database UAT clean slate hoàn toàn. Stats final S23 t9: **31 mig** · 59 tables · ~145 endpoints · 34 FE pages · 111 test · 47 gotcha · 20 memory · 6 skills · **0 PE + 0 workflow** (database state mới hoàn toàn). Pending: admin Designer seed workflow mới + bro UAT test fresh.)
**Last updated S23 t8:** 2026-05-15 (Session 23 turn 8 — **🧹 Plan R: Cleanup destructive prod database — phiếu test + workflow ko ghim**. Bro UAT confirm Plan P+Q wire OK + chỉ thị "OK Tao thấy tạm ổn rồi đấy, mày xóa hết các phiếu test cũ đi nhé, các quy trình cũ ko ghim cũng xóa hết đi. Cho gọn đẹp." 🟦 Investigator pre-flight audit prod ~64K spawn confirm scope: 28 PE active + 7 soft-deleted + 15 V2 workflows IsUserSelectable=false + 2 V1 workflows IsActive=false. FK Restrict gotcha: PE.ApprovalWorkflowId Restrict → phải hard-DELETE PE trước (soft-delete KHÔNG release FK). Entity extend `BaseEntity` (workflow) KHÔNG hỗ trợ soft-delete. Bro chốt Option A (Recommended) qua AskUserQuestion. Em main execute 6 steps qua sqlcmd ssh vietreport-vps: (1) BACKUP `SolutionErp_pre_cleanup_2026-05-15.bak` 18.5MB qua `scripts/plan-r-backup.sql` upload scp + sqlcmd -i (workaround SQL Express no COMPRESSION + no RESTORE VERIFYONLY permission cho vrapp). (2) Script `scripts/plan-r-cleanup.sql` 3 BEGIN/COMMIT TRANSACTION + SET QUOTED_IDENTIFIER ON (filtered index Mig 29+ require). (3) DELETE 35 PE rows (28 active + 7 soft-deleted, cascade ~446 child: 42 Details + 49 Suppliers + 64 Approvals + 238 Changelogs + 10 Attachments + 43 LevelOpinions). (4) DELETE 15 V2 unghim (cascade ~140 Steps+Levels). (5) DELETE 2 V1 inactive (cascade ~37 Steps+Approvers). Total: **52 rows + ~600 cascade child deleted**. Post-cleanup state: **0 PE · 2 V2 workflows ghim** (`QT-DN-V2-001 v16` + `QT-DN-PA-V2-001 v2`) · **2 V1 workflows active** (`QT-DN-A v3` + `QT-DN-B v1`). Smoke verify BE alive post-cleanup: 3/3 endpoints 200 (auth login + PE list + V2/V1 workflow list) → KHÔNG crash startup (Plan F precedent S22 avoid được — V1 active workflow giữ nguyên). Multi-agent ROI: Investigator catch FK Restrict gotcha + recommend backup mandatory + 3 Option compare — saved em main hard-delete without backup risk. Stats final S23 t8: **31 mig** · 59 tables · ~145 endpoints · 34 FE pages · 111 test unchanged · 47 gotcha · 20 memory · 6 skills · **0 PE phiếu test + 4 workflow ghim/active** (gọn đẹp UAT clean slate). Pending: bro UAT test workflow mới fresh.)

View File

@ -0,0 +1,326 @@
# Session 25 — 2026-05-19 — PE History Visibility cumulative fix (Plan AB → AF)
**Dev:** Claude Opus 4.7 1M (em main + 4 sub-agent spawns Investigator+Implementer+Reviewer+CICD)
**Duration:** ~5h
**Base commit:** `e23f51c` (Docs Session 24 wrap)
**Final HEAD:** `506cada` Plan AF FE userMap fallback
**Total commits S25:** **7** (`cdfd542`, `8c05947`, `a734bf2`, `25837b6`, `0aaf2df`, `9ea62be`, `506cada`)
**CICD Runs:** **7** (#215 FAIL test gate → #216-#221 ALL PASS post-fix)
## 🎯 Trigger session
Bro UAT 2026-05-19 sau S24 Plan AA deploy phát hiện 2 bug critical phiếu **PE/2026/A/032** (`3248f2f9-c6e9-43ff-a4ca-067ffecf9f36`):
**Bug 1 — Budget Adjust không show "Lịch sử thay đổi":**
- Bro click "Điều chỉnh ngân sách" 2× (5.300.000.000 → 5.200.000.000 đ)
- Section "Lịch sử thay đổi" empty placeholder "Chưa có lịch sử trả lại / gửi duyệt lại"
**Bug 2 — Trả lại Người chỉ định không log:**
- Bro role Approver Phan Văn Chương click "Trả lại Người chỉ định" → Bùi Lê Thủy Trà
- Section "Lịch sử thay đổi" KHÔNG show entry return assignee
Sau Plan AB deploy bro phát hiện thêm 4 issues UAT iterative — em main solo 5 follow-up plans (AC→AF) cross-stack reasoning.
## 🌳 Plan execution 7 commits Plan AB → AF
### Plan AB — Bug 1 + Bug 2 root fix
#### Investigator pre-flight audit (🟦 Investigator, ~28K)
5Q audit confirm root cause:
| Q | Finding |
|---|---|
| Q1 DB state Changelogs cho PE | SSH auth fail, fallback grep code BE |
| Q2 BE AdjustBudget handler | `PurchaseEvaluationFeatures.cs:379-387` ĐÃ log `EntityType=Header + Action=Update + Summary="Điều chỉnh ngân sách..."` ✓ |
| Q3 BE ApplyReturnModeAsync | line 215-378 **ZERO** `Changelog.Add()` cho 4 mode. Caller `TransitionAsync:100` chỉ log phase transition qua `LogTransitionAsync` ❌ |
| Q4 Schema PurchaseEvaluationChangelog | `EntityType` enum {Header=1, Supplier=2, Detail=3, Quote=4, Workflow=5, Attachment=6}. Missing Kind discriminator (Header+Update collision Budget vs Detail) |
| Q5 FE filter HistoryTab | Hypothesis FE filter omit Header+Workflow types (unconfirmed Investigator scope) |
**Conclusion:**
- Bug 1: BE log OK + FE filter strict TraLai-only loại Header+ChoDuyet
- Bug 2: BE Service hook ROOT GAP — KHÔNG log Changelog cho 4 mode return
#### Em main verify FE filter logic
`PeDetailTabs.tsx:2037-2046` HistoryTab filter:
```ts
const PE_PHASE_TRALAI = 98
const PE_ENTITY_WORKFLOW = 5
const filtered = (logs.data ?? []).filter(l => {
if (l.entityType === PE_ENTITY_WORKFLOW) {
if (l.phaseAtChange === PE_PHASE_TRALAI) return true
if (l.summary?.includes('TraLai →')) return true
return false
}
return l.phaseAtChange === PE_PHASE_TRALAI // ← Header Budget Adjust ChoDuyet bị loại
})
```
Filter chỉ pass khi phase=TraLai. Header Budget Adjust ChoDuyet → loại. Workflow Reject 3/4 mode giữ ChoDuyet → loại.
#### Plan AB Chunk A — `cdfd542` (🟨 Implementer Case 1 ~12K, 🟥 Reviewer ~22K)
**BE refactor `ApplyReturnModeAsync`:**
- Drafter early return (line 282-287) → if/else common path
- Single `Changelog.Add()` cuối hàm cover 4 mode uniform với `modeName` switch
- `actorName` resolve via `userManager.FindByIdAsync` mirror `LogTransitionAsync` pattern
**FE filter relax × 2 app:**
- ADD `PE_ENTITY_HEADER = 1` + `summary?.toLowerCase().includes('ngân sách')` cho Bug 1
- ADD `summary?.includes('Trả lại')` Workflow entity cho Bug 2
- Update placeholder text + comment 5 rule
3 files +146/-95 LOC. Reviewer PASS 0 blocker.
#### Run #215 FAIL — gotcha #48 NEW (🟩 CICD Monitor)
`test_infra` 51/53 PASS, 2 FAIL Plan M edge case:
- `ApplyReturnMode_OneStep_AtStep1_ResetsToBuoc1Cap1_KeepsChoDuyet`
- `ApplyReturnMode_OneLevel_AtStep1Level1_ResetsToBuoc1Cap1_KeepsChoDuyet`
Error: `Expected changelog.ContextNote not to be <null>`
**Root cause:** SQLite frozen test clock + 2 Changelog.Add() trong cùng SaveChangesAsync → identical CreatedAt → `OrderByDescending.FirstAsync()` non-deterministic → pick Plan AB row (Workflow+Update+ContextNote=null) thay LogTransition row.
#### Plan AB Chunk A2 — `8c05947` (em main solo)
Test fix Option A: filter `Where(c => c.Summary!.Contains("Chuyển phase"))` pick đúng LogTransition entry. Plan AB BE code stays clean.
```diff
- .Where(c => c.PurchaseEvaluationId == pe.Id)
+ .Where(c => c.PurchaseEvaluationId == pe.Id && c.Summary!.Contains("Chuyển phase"))
```
111/111 PASS local. Run #216 PASS deploy success.
### Plan AC — Bug 3 Lịch sử duyệt missing Trả lại + Vượt cấp
Bro UAT post Plan AB: "Lịch sử duyệt" panel 6 entries TẤT CẢ "Đã gửi duyệt → Đã gửi duyệt" (Approve only). Trả lại + Vượt cấp KHÔNG hiện.
**Root cause:**
- `PurchaseEvaluationApprovals.Add()` chỉ ở Approve branch (line 472 V2 + 660 V1)
- Reject branch line 75-103 NEVER add row → Lịch sử duyệt missing
- skipToFinal advance line 532-572 dùng existing line 472 row nhưng comment KHÔNG distinct
#### Plan AC commit `a734bf2` (em main solo)
**BE Reject branch add Approval row:**
```csharp
// Capture pre-call (ApplyReturnModeAsync mutates pointer)
var fromStepIdx = evaluation.CurrentWorkflowStepIndex;
var fromLevelOrder = evaluation.CurrentApprovalLevelOrder;
// ... mutate via ApplyReturnModeAsync ...
// Add Approval row (Decision=Reject)
db.PurchaseEvaluationApprovals.Add(new PurchaseEvaluationApproval {
PurchaseEvaluationId = evaluation.Id,
FromPhase = fromPhase,
ToPhase = evaluation.Phase,
ApproverUserId = actorUserId,
Decision = ApprovalDecision.Reject,
Comment = $"{fromPos}{comment ?? ""}".Trim(),
ApprovedAt = dateTime.UtcNow,
});
```
**BE skipToFinal enrich comment** line 479 `Comment = $"{skipPrefix}[Bước X — Cấp Y] {comment}"`.
**FE Decision badge × 2 app:**
```tsx
function decisionBadge(decision, toPhase): { label, cls } {
if (decision === PE_DECISION_REJECT) {
if (toPhase === 99) return { label: 'Từ chối', cls: 'bg-rose-100 text-rose-700 ...' }
return { label: 'Trả lại', cls: 'bg-amber-100 text-amber-700 ...' }
}
return { label: 'Duyệt', cls: 'bg-emerald-100 text-emerald-700 ...' }
}
```
Run #217 PASS, bundle rotate × 2 app.
### Plan AC2 — Bug FE merge synthetic Reject from Changelog historical
Bro: "phiếu cũ PE/2026/A/032 ko thể hiện đc lịch sử trả duyệt và gửi vượt cấp ?"
Plan AC chỉ forward fix — entries pre-deploy chỉ có Changelog (LogTransition), Approval table missing Reject rows.
#### Plan AC2 commit `25837b6` (em main solo, Option 2A bro chốt)
FE merge view: ApprovalsTab fetch changelogs + reconstruct synthetic Reject rows:
- Filter: `entityType=5 Workflow + (summary "→ TraLai"/"→ TuChoi" OR contextNote "Trả về"/"không lùi được")`
- Parse phase từ Summary regex `Chuyển phase X → Y` → enum map (DangSoanThao=1, ChoDuyet=10, DaDuyet=20, TraLai=98, TuChoi=99)
- Synthetic ID prefix `syn-{changelog.id}`, decision=2
- Dedupe: `${approverUserId}-${Math.floor(approvedAt / 5000)}` 5s bucket key
- Merge sort by `approvedAt`
KHÔNG DB write, reversible historical recovery. Run #218 PASS, 8 Workflow entries verified.
### Plan AD — Bug 4 dual-phase badges gây nhầm
Bro: "các bước này chưa thể hiện đc là đã gửi duyệt cho ai ? Trả lại cũng vậy (Đã trả lại thì là trả lại chứ hiển thị đã gửi duyệt thì lại gây hiểu nhầm)"
3/4 mode Reject (OneLevel/OneStep/Assignee) giữ Phase=ChoDuyet → badge `Đã gửi duyệt → Đã gửi duyệt` identical Approve.
#### Plan AD commit `0aaf2df` (em main solo, Option A bro chốt)
**Drop fromPhase→toPhase badges entirely.**
**Add `extractNextTargetHint(decision, toPhase, comment)` helper:**
```ts
// Approve patterns:
// "sang Cấp X" → "→ Cấp X"
// "sang Bước Y" → "→ Bước Y (Cấp 1)"
// "[Duyệt vượt cấp tới Cấp cuối]" → "→ Vượt cấp tới Cấp cuối"
// toPhase=DaDuyet(20) → "→ Đã duyệt hoàn tất"
// Reject patterns:
// "Người chỉ định" + Bước/Cấp regex → "→ Trả về Người chỉ định (Bước X Cấp Y)"
// "Người soạn thảo"/"Drafter" → "→ Trả về Người soạn thảo"
// "không lùi được" → "→ Không lùi được"
// "Trả về 1 Cấp"/"Trả về Cấp X" → "→ Lùi về Cấp X"
// "Trả về 1 Bước"/"Trả về Bước X" → "→ Lùi về Bước X"
// toPhase=TuChoi(99) → "→ Từ chối hoàn toàn"
```
Cleanup unused `PurchaseEvaluationPhaseColor` import. Mirror × 2 app §3.9. Run #219 PASS, bundle rotate × 2 app. **Gotcha #49 NEW**.
### Plan AE — Bug 5 Changelog UserName show "Hệ thống"
Bro: "chỗ điều chỉnh ngân sách em bắc luôn user nào điều chỉnh luôn em nhé"
Audit phát hiện systemic gap — 9 `Changelog.Add()` sites trong PE features MISSING UserName field.
#### Plan AE commit `9ea62be` (em main solo, preventive batch)
**9 sites batch fix:**
- `PurchaseEvaluationFeatures.cs` 4 sites: Create + Default detail + UpdateDraft + AdjustBudget
- `PurchaseEvaluationDetailFeatures.cs` 5 sites: Detail Insert/Update/Delete + Quote Insert/Update/Delete + Select Winner (1 inside if-block 16-space indent)
**Pattern:**
```csharp
UserName = currentUser.FullName ?? currentUser.Email
```
ICurrentUser interface đã có FullName + Email từ JWT claims — KHÔNG inject userManager mới.
`Edit replace_all=true` 1 pass cover 8 sites cùng indent + 1 site manual fix indent.
111/111 PASS local. Run #220 PASS, bundle UNCHANGED (BE-only commit).
### Plan AF — Bug 6 historical entries pre-Plan AE missing name
Bro: "phiếu cũ ko thể hiện đc người edit nhỉ ?"
Plan AE chỉ forward — entries pre-deploy có `userName=""` empty.
#### Plan AF commit `506cada` (em main solo, Option A bro chốt)
FE ApprovalsTab + HistoryTab build `userMap useMemo` từ embedded PeDetailBundle data:
```tsx
const userMap = useMemo(() => {
const m = new Map<string, string>()
if (ev.drafterUserId && ev.drafterName) m.set(ev.drafterUserId, ev.drafterName)
ev.approvals.forEach(a => {
if (a.approverUserId && a.approverName) m.set(a.approverUserId, a.approverName)
})
ev.approvalFlow?.steps?.forEach(s =>
s.levels?.forEach(l =>
l.approvers?.forEach(ap => {
if (ap.userId && ap.fullName) m.set(ap.userId, ap.fullName)
})
)
)
ev.levelOpinions?.forEach(o => {
if (o.signedByUserId && o.signedByFullName) m.set(o.signedByUserId, o.signedByFullName)
})
ev.departmentOpinions?.forEach(o => {
if (o.userId && o.userName) m.set(o.userId, o.userName)
})
return m
}, [ev])
const resolveUserName = (l: PeChangelog): string => {
if (l.userName && l.userName.trim() !== '') return l.userName
if (l.userId) {
const name = userMap.get(l.userId)
if (name) return name
}
return 'Hệ thống'
}
```
NO extra API fetch (`/api/users` admin permission). Cover gần hết users tham gia phiếu. Mirror × 2 app §3.9.
Run #221 PASS, bundle rotate × 2 app (admin `DR95zKWg → C8TvDy7r`, user `BAj_Yaj5 → BvcWrq2z`).
## 📊 Stats final S25 chốt cuối
| Metric | Pre-S25 (S24) | Post-S25 | Δ |
|---|---:|---:|---:|
| Migrations | 31 | 31 | 0 (no schema change) |
| DB tables | 59 | 59 | 0 |
| Endpoints | ~146 | ~146 | 0 |
| FE pages | 35 | 35 | 0 |
| Unit tests | 111 | 111 | 0 (UAT defer test-after per §7) |
| Gotchas | 47 | **49** | **+2 (#48 SQLite tie-break + #49 dual-phase confusion)** |
| Memory user-level | 21 | **23** | **+2 (NEW: feedback_fe_merge_synthetic_audit + feedback_fe_usermap_fallback)** |
| Skills | 6 | 6 | 0 |
| Sub-agents active | 4 | 4 | 0 |
| **Commits S25** | — | **7** | `cdfd542`, `8c05947`, `a734bf2`, `25837b6`, `0aaf2df`, `9ea62be`, `506cada` |
| **CICD Runs** | — | **7** | #215 FAIL → #216-#221 PASS |
| Bundle rotate × 2 app | — | **6×** | (Run #216,217,218,219,221) — Run #220 BE-only unchanged |
## 🎯 Multi-agent ROI S25
| Owner | Cost | Verdict | Value catch |
|---|---|---|---|
| 🟦 Investigator Plan AB Bug 1+2 | ~28K | ✅ | 5Q audit confirm BE Service hook gap (Bug 2) + FE filter strict (Bug 1). Saved em main blind cross-stack fix |
| 🟨 Implementer Plan AB Chunk A | ~12K | ✅ | 3 files +146/-95 LOC cookie-cutter mirror BE refactor + FE 2 app §3.9 |
| 🟥 Reviewer Plan AB pre-commit | ~22K | ✅ | 5-category + 8 adversarial deep checks PASS 0 blocker (1 minor V1 fallback non-block) |
| 🟩 CICD Monitor Run #215 catch FAIL | ~10K | ⚠️ | **VALUE CAUGHT** — SQLite tie-break gotcha #48, test gate blocked prod from broken state |
| 🟩 CICD Monitor Run #216-#221 PASS | ~60K (5 spawns × ~12K) | ✅ | Bundle rotate verify + wire end-to-end + Mig 31 unchanged + Plan AC2 changelogs data source confirm |
| 👤 Chủ trì Solo Plan AB Chunk A2 fix tests | ~3K | ✅ | Test filter discriminator surgical fix, 111/111 PASS |
| 👤 Chủ trì Solo Plan AC | ~12K | ✅ | Cross-stack BE Reject + FE Decision badge × 2 app |
| 👤 Chủ trì Solo Plan AC2 | ~10K | ✅ | FE merge synthetic Reject + dedupe (Option 2A historical recovery) |
| 👤 Chủ trì Solo Plan AD | ~8K | ✅ | UI redesign drop dual-phase badges + extractNextTargetHint regex parse |
| 👤 Chủ trì Solo Plan AE | ~6K | ✅ | BE preventive batch 9 sites UserName |
| 👤 Chủ trì Solo Plan AF | ~10K | ✅ | FE userMap fallback từ embedded domain data (Option A historical name resolve) |
**Total cost cumulative S25:** Agents ~132K + Em main ~210K = ~342K (cache leverage 70-90% per session). ROI ~25% solo equiv.
## 📋 Patterns reusable saved S25 (8 NEW)
1. **Multi-Changelog.Add() SQLite frozen-clock tie-break (gotcha #48)** — Test query audit table cần filter EntityType/Summary discriminator thay raw OrderByDescending(CreatedAt). Cross-ref Contract V2 test setup tương lai.
2. **CICD Monitor catch UAT skip test risk validated** — Cho BE refactor > 100 LOC + signature change, UAT mode `feedback_uat_skip_verify` skip `dotnet test` RISK. Em main resumed local test verify post Plan AB Chunk A2 fix.
3. **Test fix Option A over BE refactor preservation** — Khi test fail post-refactor do test logic assumption broken, ưu tiên tests get more specific selector (filter discriminator) thay vì revert BE code change. Code semantic stays clean, tests adapt to new schema reality.
4. **Capture pre-call mutation state cho audit row from-position** — Khi Service mutate entity state AND write audit row simultaneously (vd Plan AC ApplyReturnModeAsync mutate pointer + log Approval), snapshot OLD Step/Level BEFORE mutation. Pattern reusable Contract V2 audit + Budget V2 audit.
5. **FE merge synthetic rows từ Changelog historical (Plan AC2 Option 2A)** — Reversible recovery KHÔNG DB write. ApprovalsTab fetch changelogs + reconstruct synthetic rows từ existing audit data + dedupe timestamp bucket key vs real rows. Reusable Contract V2 + Budget V2 audit visualization without DB migration.
6. **Drop misleading dual-phase badges + parse semantic next-target hint (Plan AD, gotcha #49)** — UI audit history KHÔNG nên render dual-phase badge khi state machine self-loop (ChoDuyet → ChoDuyet là advance pointer trong cùng phase). Thay bằng Decision badge + semantic hint parse từ structured comment via regex + keyword detect.
7. **Changelog UserName preventive systemic batch fix (Plan AE)** — Khi audit phát hiện 1 site bug pattern, grep enumerate ALL similar sites. `Edit replace_all=true` với context-aware key (UserId + Summary) cover N sites idempotent. ICurrentUser.FullName fallback Email mirror LogTransitionAsync pattern.
8. **FE userMap fallback từ embedded domain data (Plan AF Option A)** — Build composite lookup map từ data có sẵn trong response bundle (drafter + approvals + approvalFlow + levelOpinions + departmentOpinions), KHÔNG cần extra API fetch admin permission. Trust entry.userName non-empty → userMap.get(entry.userId) → 'Hệ thống' fallback.
## ⏭ Pending S26+
- 🔴 **Memory curate cicd-monitor PRIORITY MAX** — ~72KB strongly over 50KB hard threshold. DEDICATED CURATION SESSION REQUIRED — archive Run #186-#210 + S22-S24 verbose entries to `archive/2026-05-runs-186-210.md`. FIFO ~15 entries.
- 🟡 **Memory curate Investigator (32KB) + Implementer (35.7KB) + Reviewer (32.3KB)** cùng session với cicd-monitor.
- 🟡 **Plan B Contract V2 wire (Mig 32+33)** — HIGH priority pre-allocated S23 HANDOFF tree. 5-6 chunk: Investigator pre-flight + Mig 32 schema (Contract +ApprovalWorkflowId) + Service ApproveV2Async branch + Mig 33 ContractLevelOpinions + FE Workspace × 2 app + FE Detail × 2 app.
- 📝 **Memory user-level** `feedback_responsive_laptop_breakpoint` stale "3-panel" → 2-panel correction (S24 Investigator surprise note).
- 🔍 **Discovery #3 anomaly CI trigger docs-only** (4× reinforced) — defer Investigator follow-up.
- 🔍 **Discovery #4 ASP.NET enum body deserialization numeric input requirement** (Plan P S23 side effect) — LOW priority register JsonStringEnumConverter Program.cs.
## References
- Files: [PurchaseEvaluationWorkflowService.cs](../../../src/Backend/SolutionErp.Infrastructure/Services/PurchaseEvaluationWorkflowService.cs) · [PurchaseEvaluationFeatures.cs](../../../src/Backend/SolutionErp.Application/PurchaseEvaluations/PurchaseEvaluationFeatures.cs) · [PurchaseEvaluationDetailFeatures.cs](../../../src/Backend/SolutionErp.Application/PurchaseEvaluations/PurchaseEvaluationDetailFeatures.cs) · [PeDetailTabs.tsx fe-user](../../../fe-user/src/components/pe/PeDetailTabs.tsx) · [PeDetailTabs.tsx fe-admin](../../../fe-admin/src/components/pe/PeDetailTabs.tsx)
- Rules: §3.9 mirror 2 FE app · §6.5 KEEP narrative · §7 test timing (UAT defer test-after)
- Cross-ref skill `contract-workflow` (Approve/Reject decision pattern) + `permission-matrix` (approverUserId per-NV slot)
- Memory cross-ref `feedback_per_nv_permission_scope.md` (per-NV wire 10 surface points) + `feedback_service_hook_vs_endpoint.md` (audit log derived state) + NEW `feedback_fe_merge_synthetic_audit.md` + NEW `feedback_fe_usermap_fallback.md`
- CICD Run URLs: https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp/actions/runs/{215..221}

View File

@ -775,6 +775,67 @@ paths-ignore:
- CICD Monitor Run #187 (S21 t5 2026-05-13 20:12) — confirmed pattern + cache stale bonus
- Memory `feedback_multi_agent_setup` Plan G Trial Week 1 evidence
### 48. Multi-Changelog.Add() trong cùng SaveChangesAsync → SQLite frozen-clock tie-break → tests `OrderByDescending(CreatedAt).First()` non-deterministic (Session 25 Plan AB + Run #215 catch)
**Triệu chứng:** Plan AB Chunk A `cdfd542` add SECOND Changelog.Add() entry vào `ApplyReturnModeAsync` (cover Bug 2 — Return mode log) end-of-function. Caller `TransitionAsync:100` đã có sẵn `LogTransitionAsync` add FIRST Changelog entry (Action=Transition + ContextNote=comment chứa "không lùi được"). 2 entries cùng `SaveChangesAsync` transaction → SQLite test fixture frozen clock → CreatedAt **identical microseconds** cho cả 2 rows.
Plan M edge case tests (S23 t3) query `.OrderByDescending(c => c.CreatedAt).FirstAsync()` assert `ContextNote.Contains("không lùi được")` — sau Plan AB, SQLite tie-break non-deterministic, pick Plan AB row (EntityType=Workflow, Action=Update, ContextNote=null) → `Expected ContextNote not to be <null>` FAIL.
**CI Run #215 sha=cdfd542** test_infra FAIL 2/53 (51 PASS, 2 FAIL):
- `ApplyReturnMode_OneStep_AtStep1_ResetsToBuoc1Cap1_KeepsChoDuyet` (line 350)
- `ApplyReturnMode_OneLevel_AtStep1Level1_ResetsToBuoc1Cap1_KeepsChoDuyet` (line 308)
Test gate caught regression → deploy never reached → prod spared broken state.
**Fix Option A (chốt):** Test query filter by `Summary.Contains("Chuyển phase")` để pick đúng LogTransition entry. Plan AB BE code stays clean.
```csharp
// Trước Plan AB Chunk A2:
var changelog = await db.PurchaseEvaluationChangelogs
.Where(c => c.PurchaseEvaluationId == pe.Id)
.OrderByDescending(c => c.CreatedAt)
.FirstAsync();
// Sau Plan AB Chunk A2 fix (commit 8c05947):
var changelog = await db.PurchaseEvaluationChangelogs
.Where(c => c.PurchaseEvaluationId == pe.Id && c.Summary!.Contains("Chuyển phase"))
.OrderByDescending(c => c.CreatedAt)
.FirstAsync();
```
**Pattern reusable:** Khi handler/service add NEW Changelog row trong existing flow đã có LogTransition row, tests query audit table MUST filter EntityType / Action / Summary keyword **discriminator** thay vì raw OrderByDescending timestamp. Cross-ref Contract V2 test setup tương lai.
**Severity:** Major — caught by CI before prod ship, no user impact. Lesson reinforced UAT mode `feedback_uat_skip_verify` skip `dotnet test` per chunk RISK khi BE refactor > 100 LOC + signature change → em main resumed local test verify post Plan AB Chunk A2.
**References:**
- Plan AB Chunk A commit `cdfd542` (Run #215 FAIL)
- Plan AB Chunk A2 fix commit `8c05947` (Run #216 PASS)
- Memory `feedback_uat_skip_verify` lesson reinforced S25
- File: `tests/SolutionErp.Infrastructure.Tests/Services/PurchaseEvaluationWorkflowServiceReturnModeTests.cs:304-310, 346-352`
### 49. UI dual-phase badge `fromPhase → toPhase` gây nhầm khi 3/4 Reject mode giữ Phase=ChoDuyet (Session 25 Plan AD)
**Triệu chứng:** Bro UAT 2026-05-19 Plan AC deploy: panel "Lịch sử duyệt" 6 entries TẤT CẢ hiện `Đã gửi duyệt → Đã gửi duyệt` (vì 3 mode Return OneLevel/OneStep/Assignee giữ Phase=ChoDuyet sau Mig 28 — chỉ Drafter mode set TraLai). Reject entry visually IDENTICAL Approve entry → user nhầm "Đã trả lại nhưng vẫn hiện đã duyệt".
**Fix Plan AD (commit `0aaf2df`):**
1. **Drop fromPhase → toPhase badges entirely** trong ApprovalsTab (cả `fe-user` + `fe-admin` mirror §3.9). Visual confusion gỡ bỏ.
2. **Thay bằng next-target hint parse từ comment** via helper `extractNextTargetHint(decision, toPhase, comment)`:
- **Approve:** Summary "sang Cấp X" → "→ Cấp X", "sang Bước Y" → "→ Bước Y (Cấp 1)", "Duyệt vượt cấp" → "→ Vượt cấp tới Cấp cuối", toPhase=DaDuyet(20) → "→ Đã duyệt hoàn tất"
- **Reject:** ContextNote "Người chỉ định" → "→ Trả về Người chỉ định (Bước X Cấp Y)" parse regex, "Người soạn thảo"/"Drafter" → "→ Trả về Người soạn thảo", "không lùi được" → "→ Không lùi được", "Trả về 1 Cấp"/"Trả về Cấp X" → "→ Lùi về Cấp X", toPhase=TuChoi(99) → "→ Từ chối hoàn toàn"
3. **Decision badge** (Plan AC `a734bf2` đã add): Duyệt emerald / Trả lại amber / Từ chối rose — phân biệt Action level KHÔNG dựa vào phase.
**Pattern reusable cross-project:** UI audit history KHÔNG nên render dual-phase badge khi state machine self-loop (e.g. ChoDuyet → ChoDuyet là advance pointer trong cùng phase, KHÔNG phải transition). Thay bằng Decision badge + semantic next-target hint parse từ structured comment.
**Severity:** UX confusion (KHÔNG functional bug). Bro UAT phản hồi sau Plan AC deploy.
**References:**
- Plan AD commit `0aaf2df` (Run #219 PASS)
- File: `fe-user/src/components/pe/PeDetailTabs.tsx:1995-2070` (ApprovalsTab + decisionBadge + extractNextTargetHint)
- Mirror: `fe-admin/src/components/pe/PeDetailTabs.tsx`
## Checklist debug bug mới
1. Build pass không? → fail → check using + package version compat
@ -800,3 +861,5 @@ paths-ignore:
21. Nếu user phàn nàn "feature work cho admin nhưng user empty/403 silent" → check class-level Authorize policy có over-restrict cho non-admin không, split per action (#44)
22. Nếu button workflow label nói "Trả lại" nhưng phiếu vẫn tiến approve → audit FE `isReject` payload condition vs button `isSendBack` label condition vs dialog `isSendBack` warning condition — phải sync 3 chỗ với CÙNG set target phase. BE thêm guard `(target ∈ terminalSet) ⇔ (decision=Reject)` chặn caller mismatch (#45)
23. Nếu Gitea Actions API trả 404 trên `/actions/runs` → đúng path là `/actions/tasks` (Gitea naming khác GitHub). Cache `updated_at` stale ~2 min → cross-check VPS file LastWriteTime cho time-sensitive verify (#46)
24. Nếu test `OrderByDescending(CreatedAt).First()` query audit table fail sau add Changelog mới → SQLite frozen-clock tie-break, MUST filter Summary/EntityType discriminator (#48)
25. Nếu UI audit list show `Đã gửi duyệt → Đã gửi duyệt` lặp gây nhầm → drop dual-phase badge khi state machine self-loop, thay Decision badge + next-target hint parse từ comment (#49)