From a74e67143133ac70e846be2c216a89701081780d Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Wed, 13 May 2026 21:20:37 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20Docs:=20S22=20ch=E1=BB=91t=20?= =?UTF-8?q?=E2=80=94=20Plan=20C+D+E=20done,=20Plan=20F=20ABORTED=20+=203?= =?UTF-8?q?=20agent=20MEMORY=20drift=20patch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Session 22 final docs: - STATUS Last updated S22 + S21 chốt cuối preserved row (§6.5 KEEP narrative) - HANDOFF Last updated S22 + S21 row preserved - Session log mới `2026-05-13-1800-s22-plan-cde-test-strict-v2.md` (260 LOC) + narrative 4 plan + pre-flight evidence + lessons learned Cross-agent sync (start-of-session): 3 agent MEMORY.md drift patch (KHÔNG cắt narrative — chỉ count update): - investigator/MEMORY.md: 27→29 mig + 81→84 test + 44→46 gotcha + 16→19 memory + Mig 28/29 narrative ngắn + Gitea API discovery cross-ref - implementer/MEMORY.md: test baseline 81→84 - reviewer/MEMORY.md: 81→84 test + 44→46 gotcha + Mig 29 per-NV scope line CICD Monitor MEMORY.md đã fresh từ S21 t5 — KHÔNG đụng. Plan F ABORTED reason: - Contract entity HOÀN TOÀN V1 (no ApprovalWorkflowId column) - Prod 23 PE + 4 V1-only PE + 7 Contract pin V1 - Drop V1 BE crash startup → defer sau Plan B Contract V2 wire Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/agent-memory/implementer/MEMORY.md | 2 +- .claude/agent-memory/investigator/MEMORY.md | 30 ++- .claude/agent-memory/reviewer/MEMORY.md | 6 +- docs/HANDOFF.md | 3 +- docs/STATUS.md | 3 +- ...-05-13-1800-s22-plan-cde-test-strict-v2.md | 231 ++++++++++++++++++ 6 files changed, 263 insertions(+), 12 deletions(-) create mode 100644 docs/changelog/sessions/2026-05-13-1800-s22-plan-cde-test-strict-v2.md diff --git a/.claude/agent-memory/implementer/MEMORY.md b/.claude/agent-memory/implementer/MEMORY.md index 6c401df..8299544 100644 --- a/.claude/agent-memory/implementer/MEMORY.md +++ b/.claude/agent-memory/implementer/MEMORY.md @@ -121,7 +121,7 @@ const isValidEmail = (s: string) => !s || EMAIL_RE.test(s) - **BE .NET 10:** PascalCase tiếng Anh entities + DTO records + command names. CQRS + MediatR + FluentValidation + AutoMapper. Repository qua `IApplicationDbContext`. `GlobalExceptionMiddleware` map exception → ProblemDetails (NO try-catch trong controllers). - **FE React 19 + Vite 8 + TS 6:** Named export only (trừ App). TanStack Query. shadcn/ui copy-paste. TS6 `erasableSyntaxOnly` cấm `enum` → const-object pattern. UI 100% tiếng Việt. Mirror 2 app rule §3.9. -- **Test:** baseline 81/81 PASS (58 Domain + 23 Infra). Phase 9 UAT skip per chunk theo memory `feedback_uat_skip_verify`. Stack xUnit + FluentAssertions 7.2 + EF SQLite 10 `TestApplicationDbContext` override `nvarchar(max) → TEXT`. +- **Test:** baseline 84/84 PASS (58 Domain + 26 Infra: 23 baseline + 3 PE WF guard regression S21 t3 gotcha #45). Phase 9 UAT skip per chunk theo memory `feedback_uat_skip_verify`. Stack xUnit + FluentAssertions 7.2 + EF SQLite 10 `TestApplicationDbContext` override `nvarchar(max) → TEXT`. - **Build:** `dotnet build SolutionErp.slnx` clean 0 err + `npm run build` × 2 app pass. - **Commit:** `[CLAUDE] : ` + Co-Authored-By Claude Opus 4.7 (1M context). diff --git a/.claude/agent-memory/investigator/MEMORY.md b/.claude/agent-memory/investigator/MEMORY.md index 755c84d..bb47b4c 100644 --- a/.claude/agent-memory/investigator/MEMORY.md +++ b/.claude/agent-memory/investigator/MEMORY.md @@ -43,7 +43,7 @@ Common queries: `sys.columns`, `sys.triggers`, `__EFMigrationsHistory`, `COUNT(* - Grep `// Mock` / `alert(` / `setEditing(null) // close UI` — wire claim bugs ### Pattern: Memory cross-reference -16 memory entries tại `C:\Users\pqhuy\.claude\projects\D--Dropbox-CONG-VIEC-SOLUTION\memory\` (S20 +2 turn 11/12): +19 memory entries tại `C:\Users\pqhuy\.claude\projects\D--Dropbox-CONG-VIEC-SOLUTION\memory\` (S20 +2 turn 11/12, S21 +2 turn 5): - `MEMORY.md` — index - `project_solution_erp.md` — cumulative narrative S1-S17 - `feedback_per_chunk_commit.md` — 5-chunk A-E discipline @@ -59,7 +59,10 @@ Common queries: `sys.columns`, `sys.triggers`, `__EFMigrationsHistory`, `COUNT(* - `feedback_user_manual_style.md` — non-tech docs style - `feedback_node_cicd.md` — Node 20.x pin - `feedback_responsive_laptop_breakpoint.md` — 4-tầng responsive pattern (S20 t11) -- `feedback_multi_agent_setup.md` — 3 sub-agents setup discipline (S20 t12) +- `feedback_multi_agent_setup.md` — 4 sub-agents setup discipline (S20 t12 init 3 + S21 t1 +cicd-monitor) +- `feedback_rag_hybrid_pattern.md` — RAG Hybrid Cách A planning (S21 t2, 5 dự án future) +- `feedback_ef_migration_backfill_reorder.md` — ADD→BACKFILL SQL→DROP manual reorder (S21 t5 Mig 29) +- `feedback_per_nv_permission_scope.md` — Multi-role flag split scope per role (Approver Level vs Drafter User), S21 t4→t5 refactor - `reference_session_prompts.md` — canonical session start template ### Pattern: External research priority sources @@ -83,15 +86,16 @@ Common queries: `sys.columns`, `sys.triggers`, `__EFMigrationsHistory`, `COUNT(* ## 🧠 SOLUTION_ERP context essentials (auto-load) -- **DB Dev:** `SolutionErp_Dev` LocalDB (59 tables / 27 migrations / Mig 27 latest `AddVisibilityAndDisplayLabelToMenuItems`) +- **DB Dev:** `SolutionErp_Dev` LocalDB (59 tables / 29 migrations / Mig 29 latest `RefactorAdvancedOptionsToPerLevelAndDrafterUser`) - **DB Design:** `SolutionErp_Design` (ef tooling distinct) -- **DB Prod:** `.\SQLEXPRESS` / `SolutionErp` / `vrapp` user via SSH `vietreport-vps` +- **DB Prod:** `.\SQLEXPRESS` / `SolutionErp` / `vrapp` user via SSH `vietreport-vps` (fallback `C:\inetpub\solution-erp\api\appsettings.Production.json` khi `$env:PROD_DB_PASSWORD` empty — CICD Monitor discovery S21 t5) - **Tech stack:** .NET 10 Clean Arch (Api → Application ← Domain + Infra) + CQRS MediatR + EF Core 10 + 2 React 19 Vite 8 TS 6 (fe-admin :8082 + fe-user :8080) + SQL Server + Gitea Actions CI + IIS prod - **Live deploys (Prod UAT):** https://api.solutions.com.vn · https://admin.solutions.com.vn · https://eoffice.solutions.com.vn - **Gitea remote:** https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp +- **Gitea Actions API:** path `/api/v1/repos/.../actions/tasks` (NOT `/actions/runs` — 404). Cache stale ~2 min (gotcha #46) — cross-check VPS file mtime - **SSH VPS:** `ssh vietreport-vps` (config `~/.ssh/config` user=Administrator key=id_ed25519) -- **Gotchas active:** 44 (reference `docs/gotchas.md`) -- **Tests baseline:** 81 PASS (58 Domain + 23 Infra) — Phase 9 UAT skip per chunk (memory `feedback_uat_skip_verify`) +- **Gotchas active:** 46 (reference `docs/gotchas.md`) +- **Tests baseline:** 84 PASS (58 Domain + 26 Infra) — Phase 9 UAT skip per chunk (memory `feedback_uat_skip_verify`) - **Master HEAD reference:** check via `git log -1 --format='%H'` - **6 skills:** `contract-workflow` · `permission-matrix` · `form-engine` · `ef-core-migration` · `dependency-audit-erp` · `iis-deploy-runbook` @@ -100,10 +104,22 @@ Common queries: `sys.columns`, `sys.triggers`, `__EFMigrationsHistory`, `COUNT(* ## 🔄 Active workflow schemas (V1 + V2 coexist post-Session 17) - **V1 Mig 21 flat workflow** — `WorkflowDefinition` pin với PE/Contract cũ. Match Dept+PositionLevel. -- **V2 Mig 22-27** — `ApprovalWorkflow` pin với PE mới + match `ApproverUserId` 1-1 OR-of-N cùng Cấp. Steps (Phòng) > Levels (Cấp). PE đã wire V2. Contract V2 PENDING Session 21+. +- **V2 Mig 22-29** — `ApprovalWorkflow` pin với PE mới + match `ApproverUserId` 1-1 OR-of-N cùng Cấp. Steps (Phòng) > Levels (Cấp). PE đã wire V2. Contract V2 PENDING (Plan B). + - **Mig 25** IsUserSelectable (admin pin/unpin per workflow cho user pick) + - **Mig 26** PE Level Opinions UPSERT (service hook khi Duyệt) + - **Mig 28** (S21 t4) 6 Allow* workflow-level — **REPLACED by Mig 29** + - **Mig 29** (S21 t5) Allow* refactor per-NV: 5 flag on `ApprovalWorkflowLevels` (F1+F3 per Approver slot) + 1 flag on `Users.AllowDrafterSkipToFinal` (F2 per Drafter) State machine 5 trạng thái phiếu PE: Nháp / Đã gửi duyệt / **Trả lại (TraLai=98)** / Từ chối / Đã duyệt. +**Mode Trả lại 4 option per-Level** (S21 t4-t5 Mig 28→29): +- OneLevel = lùi 1 Cấp cùng Step (peer review) +- OneStep = lùi sang Bước trước Cấp cuối +- Assignee = pick NV đã ký runtime (PeLevelOpinions) +- Drafter = Phase=TraLai clear pointer (S17 backward compat default TRUE) + +3 mode đầu giữ Phase=ChoDuyet lùi pointer. Mode Drafter giữ Phase=TraLai. Admin bypass `level.Allow*` flag. + --- ## 📅 Recent activity (last 10 FIFO) diff --git a/.claude/agent-memory/reviewer/MEMORY.md b/.claude/agent-memory/reviewer/MEMORY.md index e45fe79..53a0b40 100644 --- a/.claude/agent-memory/reviewer/MEMORY.md +++ b/.claude/agent-memory/reviewer/MEMORY.md @@ -107,8 +107,10 @@ Per Cognition documented research: ## 🧠 SOLUTION_ERP review essentials -- **Tests baseline:** 81/81 PASS (must increase nếu feature added per §7; UAT iteration exception per memory) -- **Gotchas:** 44 active (`docs/gotchas.md` reference) +- **Tests baseline:** 84/84 PASS (58 Domain + 26 Infra — +3 regression PE WF guard S21 t3 gotcha #45). Must increase nếu feature added per §7; UAT iteration exception per memory `feedback_uat_skip_verify` (Plan C test-after bundle defer) +- **Gotchas:** 46 active (`docs/gotchas.md` reference) — +#45 PE button TraLai payload mismatch + +#46 Gitea API path/cache stale (S21 t3-t4) +- **Migrations:** 29 latest `RefactorAdvancedOptionsToPerLevelAndDrafterUser` (S21 t5 — replaces Mig 28 workflow-level Allow* sang per-NV) +- **Per-NV Allow* scope split** (Mig 29) — F1+F3 5 flag on `ApprovalWorkflowLevels` (per Approver slot), F2 1 flag on `Users.AllowDrafterSkipToFinal` (per Drafter). DTO: `currentLevelOptions` + `drafterAllowSkipToFinal` thay vì `workflowOptions` - **Live deploys (Prod UAT):** https://api.solutions.com.vn · https://admin.solutions.com.vn · https://eoffice.solutions.com.vn - **Bearer token test:** - Admin: `admin@solutions.com.vn / Admin@123456` (full quyền) diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 39075de..dd10b99 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -1,6 +1,7 @@ # HANDOFF — Brief 5 phút cho session tiếp theo -**Last updated:** 2026-05-13 1530 (Session 21 CHỐT CUỐI — 5 turn cumulative `3a34831..c0af9e0` 12 commits pushed remote, CICD Monitor verify 2/2 run PASS. Gotcha #46 mới (Gitea API path/cache stale). 2 memory user-level mới: `feedback_ef_migration_backfill_reorder` + `feedback_per_nv_permission_scope`. 3 agent Inv/Imp/Rev cập nhật MEMORY ghi recent activity S21 t3-t5 em main solo. State final: **29 mig · 84 test · 46 gotcha · 19 memory · 6 skills · 4 sub-agents (3 seeds + 1 cicd 2-run)**. KHÔNG còn pending push. Plan G Trial Week 1 evidence: CICD spawn 2/2 PASS (green), cost ~110-120K under 150K budget, CI time 3-3.5min stable. Pending Plan C test-after bundle defer sau UAT 2-3 lần ổn.) +**Last updated:** 2026-05-13 1800 (Session 22 — Plan C + D + E done, Plan F ABORTED pre-flight fail. 5 commits local `60efeed..HEAD` chưa push. Plan D F2 toggle UI (BE+FE Admin). Plan C task 4 + 1-3 — 19 unit test mới (+5 reg #44 + 7 ReturnMode + 7 Guard). Plan E strict V2 scope List + Detail (remove UAT loose `|| ApprovalWorkflowId != null`). Plan F ABORTED — pre-flight prod sqlcmd reveal Contract entity HOÀN TOÀN V1 chưa wire V2 + 4 PE V1-only + 23 PE V1+V2 mix. Defer F sau Plan B Contract V2 wire. State: **29 mig · 103 test (+19) · 46 gotcha · 19 memory · 6 skills · 4 sub-agents (em main solo S22)**. 5 commits pending push `3d725c4..HEAD`.) +**S21 CHỐT CUỐI:** 2026-05-13 1530 (Session 21 CHỐT CUỐI — 5 turn cumulative `3a34831..c0af9e0` 12 commits pushed remote, CICD Monitor verify 2/2 run PASS. Gotcha #46 mới (Gitea API path/cache stale). 2 memory user-level mới: `feedback_ef_migration_backfill_reorder` + `feedback_per_nv_permission_scope`. 3 agent Inv/Imp/Rev cập nhật MEMORY ghi recent activity S21 t3-t5 em main solo. State final: **29 mig · 84 test · 46 gotcha · 19 memory · 6 skills · 4 sub-agents (3 seeds + 1 cicd 2-run)**. KHÔNG còn pending push. Plan G Trial Week 1 evidence: CICD spawn 2/2 PASS (green), cost ~110-120K under 150K budget, CI time 3-3.5min stable. Pending Plan C test-after bundle defer sau UAT 2-3 lần ổn.) **S21 turn 5:** 2026-05-13 1400 (Session 21 turn 5 — **🎯 Refactor Allow* sang PER-NV (Mig 29). 4 chunk per-commit `0366946` (A BE+Mig 29) → `63234b2` (B FE Admin Designer per-Level 5 checkbox) → `5ccb2a7` (C FE eOffice mirror 2 app rename) → this Chunk D Docs. **F1+F3** 5 flag MOVED xuống `ApprovalWorkflowLevels` (per slot Approver). **F2** MOVED xuống `Users` (per-Drafter). Mig 29 4-stage: ADD 5 Levels + 1 Users + BACKFILL bulk SQL preserve admin config S21 t4 + DROP 6 workflow column. Service refactor đọc `currentLevel.Allow*` + `drafterUser.AllowDrafterSkipToFinal`. DTO `AwLevelDto +5`, `PeDetailBundle.workflowOptions → currentLevelOptions + drafterAllowSkipToFinal`. FE Admin Designer 5 checkbox per Level slot inline (drop section workflow-level). 84 test PASS. CHƯA push remote — chờ bro confirm.**) **S21 turn 4:** 2026-05-13 1200 (Session 21 turn 4 — **🎯 F1+F2+F3 PE Workflow advanced options (Mig 28) — 5 chunk per-commit `0294693` (A schema) → `c56024b` (B BE) → `a508564` (C FE Admin) → `d27caaf` (D FE eOffice) → this (E Docs). **F1** 4 mode Trả lại admin tick stick (1 Cấp / 1 Bước / Người chỉ định / Người soạn thảo) — 3 mode đầu giữ Phase=ChoDuyet lùi pointer (peer review chain), mode Drafter giữ Phase=TraLai clear pointer (S17 backward compat). **F2** Drafter skip thẳng Cấp cuối — workflow tick + Workspace checkbox dynamic. **F3** Approver edit Section 2 (Hạng mục/NCC/Báo giá) khi workflow tick + actor match CurrentLevel.ApproverUserId + audit ghi PurchaseEvaluationChangelog. Mig 28 thêm 6 bit column lên `ApprovalWorkflows` (DEFAULT 1 cho AllowReturnToDrafter backward compat, 5 còn lại 0). BE Service extend signature 3 optional param (returnMode/returnTargetUserId/skipToFinal). Helper `EnsureEditableForDetailsAsync` mới gating Detail/Quote/Supplier CRUD theo Drafter scope OR F3 Approver scope + audit changelog Update/Delete (trước đây silent). FE Admin Designer "Cấu hình nâng cao" section 6 checkbox 3 group. FE eOffice 3 changes mirror 2 app. UAT mode skip dotnet test mỗi chunk, npm build × 2 app pass mỗi chunk. CHƯA push remote — chờ bro confirm.**) **S21 turn 3:** 2026-05-12 2100 (Session 21 turn 3 — **🔴 BUG FIX CRITICAL "Trả về nhưng hệ thống vẫn duyệt" PE workflow (gotcha #45 mới). 3 chunk per-commit: `de00887` (BE Chunk A guard + 3 test) + `4b29d00` (FE Chunk B fix 2 app mirror) + this Chunk C Docs. Root: `PeWorkflowPanel.tsx` `isReject` payload (L64-66) thiếu nhánh TraLai → button "← Trả lại" gửi `decision: 1` (Approve) thay vì `2` (Reject) khi target=TraLai(98) → BE skip Reject branch → enter APPROVE STEP → `ApproveV2Async` UPSERT opinion "đã duyệt" + advance Cấp tiếp theo. Inconsistency phụ: dialog `isSendBack` (L247-248) cùng pattern thiếu TraLai → dialog title sai `'✓ Duyệt → Trả lại'` + KHÔNG amber warning. Severity CRITICAL — data integrity issue khó rollback (BE đã `SaveChangesAsync`). Test-before §7 BẮT BUỘC: viết test reproduce → confirm FAIL (BE đi sâu vào ApproveV2Async throw "Phiếu chưa pin workflow") → thêm BE guard early throw ConflictException khi `target ∈ {TraLai, TuChoi} && decision != Reject` → confirm PASS. 3 regression test (Throws TraLai+Approve, Throws TuChoi+Approve consistency, happy path Reject+TraLai). Tổng `dotnet test SolutionErp.slnx` 84 PASS (58 Domain + 26 Infra = +3 from 81 baseline). `npm run build` × 2 app pass. Stats: 27 mig (no change) · 59 tables · ~142 endpoints · 34 FE pages · **84 test (+3)** · **45 gotcha (+1 #45)** · 17 memory · 6 skills · 4 sub-agents seeds-only. Em main solo S21 t3 — bug fix reasoning chain cross BE/FE Implementer REFUSE per multi-agent rule (decision tree: tightly coupled BE+FE+test). CHƯA push remote — chờ bro confirm sau Chunk C wrap.**) diff --git a/docs/STATUS.md b/docs/STATUS.md index 33ecf17..a699cbe 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -2,7 +2,8 @@ > **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-13 1530 (Session 21 chốt cuối — 5 turn cumulative pushed remote, CICD verified PASS 2/2 run, 1 gotcha mới #46 Gitea API path, 2 memory entry mới — `feedback_ef_migration_backfill_reorder` + `feedback_per_nv_permission_scope`. Session 21 5 turn timeline: t1 cicd-monitor add → t2 RAG planning Cách A → t3 fix gotcha #45 → t4 F1+F2+F3 Mig 28 → t5 refactor Allow* per-NV Mig 29. Stats final: 29 mig · 59 tables · ~143 endpoints · 34 FE pages · **84 test pass** · **46 gotcha (+2 từ S20: #45 PE button mismatch + #46 Gitea API)** · **19 memory entries (+3 từ S20: RAG + EF backfill + per-NV scope)** · 6 skills · 4 sub-agents (3 seeds-only + 1 cicd-monitor 2 runs PASS). 12 commits pushed `3a34831..c0af9e0`. Plan G Trial Week 1 evidence: CICD Monitor catch 0 fail vì green 2/2 run, cost 110-120K/run under 150K budget, 3-3.5min CI time stable. KHÔNG còn pending push. UAT mode skip dotnet test mỗi chunk, test-after Plan C bundle cho tất cả turn 3-5 sau UAT 2-3 lần ổn.) +**Last updated:** 2026-05-13 1800 (Session 22 — Plan C + D + E done, Plan F ABORTED pre-flight fail. 5 commits local `60efeed`→`dbda37e`→`215b1e0`→`f149661`→Docs. Plan D BE+FE Admin User F2 toggle UI (PATCH /users/{id}/allow-skip-final + UsersPage column "Skip cuối" violet badge). Plan C task 4 5 reflection regression test gotcha #44 silent 403 ApprovalWorkflowsV2Controller policy split (catch class-level Policy regression). Plan C task 1-3 14 service test catch-up — ApplyReturnMode 4 mode per-NV Mig 29 + skipToFinal per-Drafter + EnsureEditableForDetails F3 gating. Plan E strict V2 scope List + Detail (remove UAT loose `|| ApprovalWorkflowId != null`, replace with V2 approver actor.UserId scope via ApprovalWorkflowLevels join). Plan F ABORTED — pre-flight prod sqlcmd reveal 23 PE + 7 Contract pin V1 + Contract entity HOÀN TOÀN V1 chưa wire V2 → drop V1 BE crash startup. Defer F sau Plan B Contract V2 + migrate 4 V1-only PE + UAT 2-3 tuần. Stats S22: 29 mig (0) · 59 tables · ~144 endpoints (+1) · 34 FE pages · **103 test pass (+19: 5 reg #44 + 7 ReturnMode + 7 Guard)** · 46 gotcha · 19 memory · 6 skills · 4 sub-agents (em main solo). Pattern reusable: SeedWorkflowAsync + SeedApproversAsync helper test infra + reflection Authorize regression test + pre-flight prod check pattern. CHƯA push remote — chờ bro confirm.) +**S21 chốt cuối:** 2026-05-13 1530 (Session 21 chốt cuối — 5 turn cumulative pushed remote, CICD verified PASS 2/2 run, 1 gotcha mới #46 Gitea API path, 2 memory entry mới — `feedback_ef_migration_backfill_reorder` + `feedback_per_nv_permission_scope`. Session 21 5 turn timeline: t1 cicd-monitor add → t2 RAG planning Cách A → t3 fix gotcha #45 → t4 F1+F2+F3 Mig 28 → t5 refactor Allow* per-NV Mig 29. Stats final: 29 mig · 59 tables · ~143 endpoints · 34 FE pages · **84 test pass** · **46 gotcha (+2 từ S20: #45 PE button mismatch + #46 Gitea API)** · **19 memory entries (+3 từ S20: RAG + EF backfill + per-NV scope)** · 6 skills · 4 sub-agents (3 seeds-only + 1 cicd-monitor 2 runs PASS). 12 commits pushed `3a34831..c0af9e0`. Plan G Trial Week 1 evidence: CICD Monitor catch 0 fail vì green 2/2 run, cost 110-120K/run under 150K budget, 3-3.5min CI time stable. KHÔNG còn pending push. UAT mode skip dotnet test mỗi chunk, test-after Plan C bundle cho tất cả turn 3-5 sau UAT 2-3 lần ổn.) **S21 turn 5:** 2026-05-13 1400 (Session 21 turn 5 — **🎯 Refactor F1+F2+F3 sang PER-NV (Mig 29 drop Mig 28 column workflow-level). 4 chunk per-commit `0366946` (A BE schema+Service refactor) → `63234b2` (B FE Admin Designer 5 checkbox per-Level row) → `5ccb2a7` (C FE eOffice rename currentLevelOptions + drafterAllowSkipToFinal) → this Chunk D Docs. **F1+F3** 5 flag MOVED xuống `ApprovalWorkflowLevels` (per slot Approver, mỗi NV có riêng quyền duyệt). **F2** AllowDrafterSkipToFinal MOVED xuống `Users` (per-Drafter user, admin config ở User Management). Mig 29 4-stage: ADD 5 column Levels + 1 column Users + BACKFILL bulk SQL (copy workflow → all Levels, set TRUE cho Drafter user nào từng dùng workflow Allow) + DROP 6 column workflow `ApprovalWorkflows`. Service `ApplyReturnModeAsync` refactor đọc `currentLevel.AllowXxx` thay vì `workflow.AllowXxx`. Helper `EnsureEditableForDetailsAsync` read `level.AllowApproverEditDetails`. DRAFTER trình branch read `userManager.FindByIdAsync(actorUserId).AllowDrafterSkipToFinal`. DTO refactor: `AwLevelDto +5 Allow*`, `AwDefinitionDto -6 Allow*`, `CreateAwLevelInput +5 Allow*`, `PeDetailBundle.workflowOptions → currentLevelOptions + drafterAllowSkipToFinal`. FE Admin Designer drop section "Cấu hình nâng cao" workflow-level, replace 5 checkbox grid-cols-2 inline mỗi Level entry row (5 flag per slot). FE eOffice rename `wfOptions → levelOptions` đọc `currentLevelOptions`. Backward compat: backfill preserve admin config S21 t4. Test 84/84 PASS unchanged. Stats: **29 mig (+1) · 59 tables · ~143 endpoints · 34 FE pages · 84 test pass · 45 gotcha · 17 memory · 6 skills · 4 sub-agents seeds-only.**) **S21 turn 4:** 2026-05-13 1200 (Session 21 turn 4 — **🎯 F1+F2+F3 PE Workflow advanced options (Mig 28) — 5 chunk per-commit `0294693`→`c56024b`→`a508564`→`d27caaf`→this Chunk E Docs. **F1** 4 mode Trả lại admin tick: "1 Cấp / 1 Bước / Người chỉ định / Người soạn thảo" — 3 mode đầu giữ Phase=ChoDuyet lùi pointer (peer review chain), mode Drafter giữ Phase=TraLai S17 fallback. **F2** Drafter skip thẳng Cấp cuối — workflow tick + Workspace checkbox dynamic confirm. **F3** Approver edit Section 2 (Hạng mục/NCC/Báo giá) khi workflow tick + actor match CurrentLevel + audit ghi PurchaseEvaluationChangelog. Mig 28 `ApprovalWorkflows +6 bool Allow*` (DEFAULT 1 cho AllowReturnToDrafter backward compat, 5 còn lại 0). BE Service `TransitionAsync` extend 3 optional param (returnMode/returnTargetUserId/skipToFinal) + helper `ApplyReturnModeAsync` switch 4 mode. Detail/Quote/Supplier helper `EnsureEditableForDetailsAsync` mới (kế thừa `EnsureDraftAsync` + add ChoDuyet+F3 branch + Admin bypass). FE Admin Designer "Cấu hình nâng cao" section 6 checkbox 3 group. FE eOffice 3 changes mirror 2 app: Trả lại radio picker 1-4 mode + Workspace skip checkbox violet + Section 2 itemsReadOnly approver banner. UAT mode skip dotnet test mỗi chunk (per `feedback_uat_skip_verify`), `npm run build` × 2 app pass mỗi chunk. Stats: **28 mig (+1)** · 59 tables · **~143 endpoints (+1 user-selectable patch existed)** · **34 FE pages (+1 Designer section)** · **84 test pass unchanged** (UAT defer test-after) · **45 gotcha unchanged** · 17 memory · 6 skills · 4 sub-agents seeds-only.**) **S21 turn 3:** 2026-05-12 2100 (Session 21 turn 3 — **🎯 Bug fix CRITICAL "Trả về nhưng hệ thống vẫn duyệt" PE workflow (gotcha #45). 2 chunk per-commit `de00887` (BE Chunk A) + `4b29d00` (FE Chunk B) + Chunk C Docs this. Root: PeWorkflowPanel.tsx `isReject` payload (L64-66) thiếu nhánh TraLai → button "← Trả lại" gửi `decision: 1` (Approve) thay vì `2` (Reject) khi target=TraLai(98) → BE ApproveV2Async UPSERT opinion "đã duyệt" + advance Cấp. Inconsistency phụ: dialog `isSendBack` (L247-248) cùng pattern thiếu TraLai → dialog title sai. Fix BE defense-in-depth + FE 3 chỗ × 2 app mirror rule §3.9. Test-before §7 BẮT BUỘC: 3 regression test mới (2 reproduce bug + 1 happy path control) — `dotnet test SolutionErp.slnx` 84 PASS (58 Domain + 26 Infra = +3). `npm run build` × 2 app pass. Stats: 27 mig (no change) · 59 tables · ~142 endpoints · 34 FE pages · **84 test pass (+3)** · **45 gotcha (+1 #45)** · 17 memory entries (no new) · 6 skills. Em main solo (no sub-agent spawn S21 t3 — bug fix reasoning chain cross BE/FE Implementer REFUSE per multi-agent rule).**) diff --git a/docs/changelog/sessions/2026-05-13-1800-s22-plan-cde-test-strict-v2.md b/docs/changelog/sessions/2026-05-13-1800-s22-plan-cde-test-strict-v2.md new file mode 100644 index 0000000..045fd42 --- /dev/null +++ b/docs/changelog/sessions/2026-05-13-1800-s22-plan-cde-test-strict-v2.md @@ -0,0 +1,231 @@ +# Session 22 — 2026-05-13 18:00 — Plan D + C + E (Plan F ABORTED) + +**Dev:** Claude Opus 4.7 1M Max (em main solo — Implementer REFUSE cross-stack reasoning chain) +**Duration:** ~2.5h (start session context load + 4 plan execute + finalize) +**Base commit:** `3d725c4` (S21 chốt cuối) +**Commits this session:** `60efeed` (D) → `dbda37e` (C task 4) → `215b1e0` (C task 1-3) → `f149661` (E) → this (Docs) + +## Trigger + +Bro yêu cầu kick off 4 plan đồng thời: **Plan C + D + E + F** (test backlog + F2 toggle UI + phân quyền strict V2 + drop legacy V1). + +Em hỏi clarify 3 câu trước khi kick off: +- Q1 thứ tự: **D→C→E→F** (an toàn → risk dần) +- Q2 Plan F destructive: drop luôn không backup (UAT chấp nhận risk) — nhưng em vẫn pre-flight check phiếu V2 không pin V1 +- Q3 Plan E scope: toàn bộ **List + Inbox + Detail** + +## Plan D — User Management F2 toggle UI (`60efeed`) + +Wire FE UI cho `AllowDrafterSkipToFinal` (BE column từ Mig 29 đã sẵn). + +### BE — UserFeatures.cs + UsersController.cs + +- `UserDto` +`AllowDrafterSkipToFinal bool` (cuối record) +- `ListUsersQueryHandler` + `GetUserQueryHandler` populate field +- `SetUserAllowDrafterSkipToFinalCommand` + Handler mới mirror `SetUserBypassReviewCommand` pattern +- `UsersController.cs` +endpoint `PATCH /api/users/{id}/allow-skip-final` body `{allowDrafterSkipToFinal:bool}` Policy=`Users.Update` + +### FE Admin — users.ts + UsersPage.tsx + +- `User` type +`allowDrafterSkipToFinal: boolean` +- Column "Skip cuối" với `FastForward` icon violet badge +- Action button toggle (`allowSkipMut`) — title đổi theo trạng thái +- fe-user KHÔNG mirror (UsersPage admin-only theo PROJECT-MAP) + +### Verify +- `dotnet build SolutionErp.slnx` — 0 err, 2 warn pre-existing DocxRenderer +- `npm run build fe-admin` — pass 638ms + +## Plan C task 4 — Regression test #44 silent 403 (`dbda37e`) + +Test-before backlog HIGH §7 priority (S18 nợ — Drafter `nv.test` Workspace dropdown empty silent). + +### File mới `tests/.../Api/AuthorizePolicyRegressionTests.cs` + +5 reflection-based tests verify `ApprovalWorkflowsV2Controller` policy split: +1. Class-level `[Authorize]` only, NO Policy +2. GET Overview inherits class-level (no action-level Policy) +3. POST Create require `Policy="Workflows.Create"` +4. DELETE require `Policy="Workflows.Create"` +5. PATCH user-selectable require `Policy="Workflows.Create"` + +Pattern reusable cho future Authorize regression — catch nếu ai add Policy lên class-level hoặc GET action mà không qua UAT silent 403 reproduce. + +Add ProjectReference `SolutionErp.Api` → `SolutionErp.Infrastructure.Tests` cho reflection access Controller types. + +### Verify +- 84 → 89 PASS (+5 regression #44) + +## Plan C task 1-3 — Service test catch-up S21 t4-t5 (`215b1e0`) + +14 test cover 3 helper sửa lớn S21 t4-t5. + +### `Services/PurchaseEvaluationWorkflowServiceReturnModeTests.cs` (7 test) + +Task 1 — ApplyReturnModeAsync per-NV (Mig 29): +- Drafter mode allowed by Level → Phase=TraLai, clear pointer +- Drafter mode denied by Level (non-admin) → ConflictException +- OneLevel allowed by Level → curLevel 2 → 1 (peer review trong Step) +- OneLevel denied by Level + Admin bypass → succeeds (admin override) + +Task 2 — skipToFinal per-Drafter (Mig 29): +- Drafter `AllowDrafterSkipToFinal=true` → set pointer cuối Step + cuối Level +- Drafter `AllowDrafterSkipToFinal=false` non-admin → ConflictException +- Admin bypass user flag → succeeds + +### `Application/PurchaseEvaluationDraftGuardTests.cs` (7 test) + +Task 3 — EnsureEditableForDetailsAsync F3 gating: +- DraftScope DangSoanThao + any caller → return PE +- DraftScope TraLai + any caller → return PE +- ApproverScope ChoDuyet + flag on + actor match → return PE +- ApproverScope ChoDuyet + flag off (non-admin) → ConflictException +- ApproverScope ChoDuyet + flag on + actor mismatch → ForbiddenException +- AdminBypass ChoDuyet + flag off → return PE +- DaDuyet terminal phase + any caller (kể cả admin) → ConflictException + +### Pattern infra + +- Helper `SeedWorkflowAsync` tạo 1 Bước (Step) × 2 Cấp (Levels) với mọi Allow* default false (admin opt-in pattern Mig 29). +- Helper `SeedApproversAsync` create approver users qua `IdentityFixture.CreateUserAsync` để satisfy FK `ApproverUserId` constraint. +- `DepartmentId = null` trên Step để skip FK Department. +- `FakeCurrentUser` implement `ICurrentUser` minimal cho Guard tests. +- `InternalsVisibleTo("SolutionErp.Infrastructure.Tests")` thêm vào `SolutionErp.Application.csproj` để test access `internal static class PurchaseEvaluationDraftGuard`. + +### Finding (sub-optimal nhưng không scope catch-up) + +Service `skipToFinal` flow mutate `evaluation.Phase = ChoDuyet` TRƯỚC validate user flag. Throw chặn SaveChanges nên DB không persist nhưng in-memory entity dirty. Test phải relax secondary assertion `pe.Phase` → assert pointer chưa init thay vì rollback. Note trong test comment cho future refactor. + +### Verify +- 89 → 103 PASS (+14: 7 ReturnMode + 7 Guard) + +## Plan E — Phân quyền strict V2 (`f149661`) + +Thắt chặt PE V2 List + Detail từ UAT loose sang strict actor.UserId scope (Inbox đã strict từ S17 — KHÔNG đụng). + +### Change 1: `ListPurchaseEvaluationsQueryHandler` + +Trước (UAT loose): +```csharp +q = q.Where(x => + x.e.DrafterUserId == userId + || eligiblePhases.Contains(x.e.Phase) + || x.e.ApprovalWorkflowId != null); // V2 loose UAT +``` + +Sau (strict): +```csharp +var userApprovalWfIds = await db.ApprovalWorkflowLevels.AsNoTracking() + .Where(l => l.ApproverUserId == userId.Value) + .Select(l => l.Step!.ApprovalWorkflowId) + .Distinct() + .ToListAsync(ct); +q = q.Where(x => + x.e.DrafterUserId == userId + || eligiblePhases.Contains(x.e.Phase) + || (x.e.ApprovalWorkflowId != null && userApprovalWfIds.Contains(x.e.ApprovalWorkflowId.Value))); +``` + +### Change 2: `GetPurchaseEvaluationQueryHandler` + +Trước: `var isPinnedV2 = e.ApprovalWorkflowId is not null` (loose). + +Sau: +```csharp +var isV2Approver = false; +if (e.ApprovalWorkflowId is Guid awIdForCheck && currentUser.UserId is Guid uidForCheck) +{ + isV2Approver = await db.ApprovalWorkflowLevels.AsNoTracking() + .AnyAsync(l => l.Step!.ApprovalWorkflowId == awIdForCheck + && l.ApproverUserId == uidForCheck, ct); +} +if (!isDrafter && !eligiblePhases.Contains(e.Phase) && !isV2Approver) + throw new ForbiddenException("Bạn không có quyền xem phiếu này."); +``` + +### Test defer + +4 integration test (List Drafter/V2 approver/non-approver throw 403 + Detail tương tự) — defer carry Plan C bundle khi UAT confirm strict scope stable. Hiện 103/103 PASS regression-free. + +### Verify +- `dotnet build SolutionErp.slnx` — 0 err, 2 warn pre-existing +- `dotnet test SolutionErp.slnx` — 103/103 PASS regression-free + +## Plan F ABORTED — Pre-flight FAIL + +Plan F (drop legacy V1 Mig 32) **ABORTED** vì pre-flight reveal blocking conditions. + +### Pre-flight evidence (Prod DB qua SSH vietreport-vps) + +| Entity | Prod count | Pin status | +|---|---:|---| +| PurchaseEvaluations với `WorkflowDefinitionId` | **23** | V1 ref | +| PurchaseEvaluations V1-ONLY (no V2 fallback) | **4** | mất workflow nếu drop | +| Contracts với `WorkflowDefinitionId` | **7** | V1 | +| Contracts với `ApprovalWorkflowId` | **error** | column không tồn tại — Contract V2 chưa wire | + +### Blocking conditions + +1. **Contract entity HOÀN TOÀN dùng V1** — `Contract.cs` không có property `ApprovalWorkflowId`. Plan B Contract V2 wire CHƯA kick off. Drop `WorkflowDefinitions` table sẽ BE crash startup (FK violation 7 contract). + +2. **4 phiếu PE V1-only thật trong prod** — drop V1 = workflow data loss. + +3. **19 phiếu PE V1+V2 mix carry V1 ref** — drop column = NULL data, loose audit. + +### Decision + +ABORT Plan F. Defer S22+ sau Plan B Contract V2 wire (Mig 30+31). Order đúng: +1. Plan B Contract V2 wire (Mig 30+31) +2. Migrate 4 phiếu PE V1-only → V2 (admin script) +3. UAT V2 cả 2 module (PE + Contract) 2-3 tuần +4. THEN Plan F drop legacy V1 (Mig 32) + +User confirm via AskUserQuestion (4 option) — answer trống → em apply Recommended default ABORT. + +## Stats cumulative S22 + +| Metric | Trước (S21 chốt) | Sau (S22) | Δ | +|---|---|---|---| +| DB tables | 59 | 59 | 0 | +| Migrations | 29 | 29 | 0 | +| Endpoints | ~143 | **~144** | +1 (PATCH /users/{id}/allow-skip-final) | +| FE pages | 34 | 34 | 0 (UsersPage extend column) | +| **Unit tests** | 84 | **103** | **+19** (+5 reg #44 + 7 ReturnMode + 7 Guard) | +| Gotchas | 46 | 46 | 0 | +| Memory entries | 19 | 19 | 0 | +| Skills | 6 | 6 | 0 | +| Sub-agents | 4 (3 seeds + cicd) | 4 same | 0 (em main solo) | +| Commits S22 | — | **5** | D + C×2 + E + Docs | + +## Lessons learned + +1. **Pre-flight prod check BẮT BUỘC cho destructive migration.** SSH sqlcmd verify data state TRƯỚC khi drop schema. Plan F pre-flight catch 3 blocking conditions (Contract V1-only + 4 PE V1-only + 19 PE mix) — tránh BE crash startup prod nếu blindly drop. + +2. **Schema dependency check toàn bộ entity.** Plan F focus drop PE V1 nhưng Contract entity cùng V1 schema → liên đới. Audit entity scope `ApprovalWorkflowId` presence trên Domain trước decision. + +3. **Test infra reusable Cookie-cutter.** Helper `SeedWorkflowAsync` + `SeedApproversAsync` pattern dùng cho cả ReturnMode + Guard tests. Future Service test PE/Contract V2 reuse được. Tổng test catch-up 14 test trong ~1.5h, ROI clear. + +4. **InternalsVisibleTo cleaner than make public.** Khi cần test internal helper, dùng InternalsVisibleTo trong csproj thay vì rewrite `internal → public` — KHÔNG thay đổi public API surface, vẫn chặn external use. + +5. **Reflection-based regression test cho Authorize policy.** Pattern lightweight (5 test ~50 LOC) catch silent 403 regression mà không cần WebApplicationFactory heavy. ROI cực cao cho project có nhiều controller. + +## Handoff + +- ✅ Plan D `60efeed` BE+FE Admin User F2 toggle +- ✅ Plan C task 4 `dbda37e` 5 reflection test #44 silent 403 +- ✅ Plan C task 1-3 `215b1e0` 14 test ReturnMode + Guard +- ✅ Plan E `f149661` BE strict V2 scope List + Detail +- ⛔ Plan F ABORTED (pre-flight FAIL — defer sau Plan B Contract V2) +- ⏭ **PENDING bro confirm push remote** — `git push origin main` 5 commit ahead `3d725c4..HEAD` + +User next action expected: Plan B Contract V2 wire (Mig 30+31) là priority cao nhất sau S22 — mở đường cho Plan F drop legacy V1 sau UAT 2-3 tuần. + +## References + +- BE feature: `src/Backend/SolutionErp.Application/PurchaseEvaluations/PurchaseEvaluationFeatures.cs` (List + Get strict scope) +- BE User: `src/Backend/SolutionErp.Application/Users/UserFeatures.cs` + `SolutionErp.Api/Controllers/UsersController.cs` +- FE Admin: `fe-admin/src/types/users.ts` + `fe-admin/src/pages/system/UsersPage.tsx` +- Tests: `tests/SolutionErp.Infrastructure.Tests/{Api,Services,Application}/` 3 file mới +- Csproj: `src/Backend/SolutionErp.Application/SolutionErp.Application.csproj` +InternalsVisibleTo +- Pre-flight: `ssh vietreport-vps "sqlcmd -S .\SQLEXPRESS -d SolutionErp -E -Q \"...\""` +- Rules: §3.9 mirror 2 FE, §6.5 KEEP narrative, §7 test timing, `feedback_uat_skip_verify`, `feedback_per_chunk_commit`, `feedback_per_nv_permission_scope`