From cc8a7d34b3780c23459079bbfb37170eb3092ace Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Wed, 13 May 2026 23:25:37 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20Docs:=20S22=20ch=E1=BB=91t=20cu?= =?UTF-8?q?=E1=BB=91i=20=E2=80=94=20gotcha=20#47=20+=204=20agent=20MEMORY?= =?UTF-8?q?=20flush=20+=20session=20log=20cumulative?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Session 22 chốt cuối — bro confirm sub-agent solution OK. Highlights cumulative S21 chốt → S22 chốt: - 11 commits S22 pushed remote `3d725c4..b04a11a` - Plan G S22 evidence: 4 sub-agents (3 seeds-only + 1 CICD Monitor Run #188 PASS) - Plan C + D + E done · Plan F ABORTED pre-flight blocker - 5 turn S22+ feedback iteration (disable 3 button + seed 20 user + rename role-based + attachment view + Mig 30 per-NV opt-in) Docs updates: - STATUS Last updated S22 chốt + S22 prev row preserved (§6.5 KEEP narrative) - HANDOFF Last updated S22 chốt + S22 prev row preserved - Session log mới `2026-05-13-2200-s22-chot-cuoi.md` (~12KB narrative + 11 commit table + 7 lessons learned + handoff S23) - Gotcha #47 mới `.claude/agent-memory/** thiếu paths-ignore filter` (CICD waste 3.5min per MEMORY flush) — PENDING bro fix `.gitea/workflows/deploy.yml` 4 agent MEMORY.md flushed S22: - Investigator: 30 mig + 104 test + S22 context essentials + Mig 30 entry + cross-ref `feedback_per_nv_permission_scope` 2× reinforced - Implementer: +6 patterns (7-12 per-NV opt-in / tách endpoint narrow scope / defense-in-depth FE+BE / reflection regression / cookie-cutter test infra / InternalsVisibleTo) + S22 activity (REFUSED 100% cross-stack) - Reviewer: +Gotcha #47 + Mig 30 + 104 test baseline + S22 self-review narrative + Identity password ≥12 chars note - CICD Monitor: refresh test 84 → 104 + Mig 29 → 30 (Run #188 PASS preserved) User memory reinforcement: - `feedback_per_nv_permission_scope.md` +Section "Reinforcement S22+5" — pattern proven 2× với Mig 30 F4. Anti-pattern default scope expansion. Decision tree thêm scope khi feedback ambiguous → admin opt-in flag per slot - `MEMORY.md` index entry updated cross-ref S22+5 reinforcement Stats final: - 30 migrations (+1 Mig 30) - 104 tests PASS (+20 S22) - 47 gotchas (+1 #47 pending fix) - ~146 endpoints (+3) - 33 active prod users (rename role-based) - 6 skills · 4 sub-agents unchanged KHÔNG cắt narrative cũ — Edit specific lines + Append new entries per §6.5. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/agent-memory/cicd-monitor/MEMORY.md | 8 +- .claude/agent-memory/implementer/MEMORY.md | 63 +++- .claude/agent-memory/investigator/MEMORY.md | 11 +- .claude/agent-memory/reviewer/MEMORY.md | 20 +- docs/HANDOFF.md | 3 +- docs/STATUS.md | 3 +- .../sessions/2026-05-13-2200-s22-chot-cuoi.md | 282 ++++++++++++++++++ docs/gotchas.md | 24 ++ 8 files changed, 401 insertions(+), 13 deletions(-) create mode 100644 docs/changelog/sessions/2026-05-13-2200-s22-chot-cuoi.md diff --git a/.claude/agent-memory/cicd-monitor/MEMORY.md b/.claude/agent-memory/cicd-monitor/MEMORY.md index fc034c4..99bfb05 100644 --- a/.claude/agent-memory/cicd-monitor/MEMORY.md +++ b/.claude/agent-memory/cicd-monitor/MEMORY.md @@ -104,8 +104,8 @@ Read-only CI/CD pipeline + post-deploy verifier for SOLUTION_ERP. Polls Gitea Ac - **Prod URLs:** api / admin / eoffice `.solutions.com.vn` - **SSH VPS:** `ssh vietreport-vps` (user=Administrator, key=id_ed25519) - **DB prod:** `.\SQLEXPRESS` / `SolutionErp` / vrapp user -- **Tests baseline:** 84/84 (58 Domain + 26 Infra = 23 baseline + 3 PE WF guard S21 t3) — updated from 81 after Mig 28 PR -- **Mig latest repo:** Mig 29 `20260513130144_RefactorAdvancedOptionsToPerLevelAndDrafterUser` (S21 t5 — refactor Allow* sang per-NV: 5 col + ApprovalWorkflowLevels, 1 col Users.AllowDrafterSkipToFinal, 6 col DROP ApprovalWorkflows) +- **Tests baseline:** 104/104 (58 Domain + 46 Infra = 23 codegen + 6 PE WF + 3 PE Guard S21 t3 + 7 ReturnMode + 7 DraftGuard + 5 AuthorizePolicy + 1 V2 actor scope reject) — S22+1 +1 test +- **Mig latest repo:** Mig 30 `20260513160703_AddAllowApproverEditBudgetToLevels` (S22+5 — per-NV F4 admin opt-in cho Approver edit Section ngân sách ChoDuyet branch). Prev Mig 29 (S21 t5 refactor per-NV) preserved. - **Gitea Actions API path:** `/api/v1/repos/{owner}/{repo}/actions/tasks?limit=N` (NOT `/runs` — returns 404). Public no-auth read OK. Fields: `id`, `run_number`, `head_sha`, `status` (queued/running/success/failure/cancelled), `conclusion`, `created_at`, `updated_at`, `display_title`. - **Mig latest prod:** sqlcmd `__EFMigrationsHistory ORDER BY MigrationId DESC TOP 5` - **Bearer test:** @@ -139,6 +139,8 @@ Flag commit nếu thấy `(); +attr.Policy.Should().Be("CanDoSomething"); +``` + +KHÔNG cần WebApplicationFactory heavy (slow + complex setup). Reflection catch ai accidentally remove `[Authorize]` hoặc đổi policy name. + +Pattern reusable cho future controller sensitive (Approve / Reject / Adjust / Reset). + +### Pattern 11: Test infra helper cookie-cutter (S22) + +Trong `PurchaseEvaluationWorkflowServiceReturnModeTests` + `PurchaseEvaluationDraftGuardTests`: + +```csharp +private async Task SeedWorkflowAsync(...) { + // 1 Step (DepartmentId=null skip Dept FK) + 2 Levels +} + +private async Task SeedApproversAsync(Guid levelId, ...) { + // Multi user via fix.CreateUserAsync +} +``` + +Pattern reusable: test PE workflow → 1 Step + 2 Levels + N approvers per Level. `DepartmentId=null` skip Dept FK ràng buộc. Token cost ~80 LOC repeated cross 2 test class S22. + +### Pattern 12: InternalsVisibleTo csproj expose helper cho test (S22) + +`PurchaseEvaluationDraftGuard` static helper internal — expose qua `` trong `SolutionErp.Application.csproj` thay vì rewrite public API. + +Tránh API surface bloat. Reusable cho future guard / helper internal cần test. + --- ## ⚠️ Anti-patterns observed (DO NOT) @@ -121,7 +181,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 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`. +- **Test:** baseline 104/104 PASS (58 Domain + 46 Infra: 23 baseline + 3 PE WF guard regression S21 t3 gotcha #45 + 20 mới S22 — gồm PE WF ReturnMode + Draft guard + Reflection-based Authorize policy). 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). @@ -144,6 +204,7 @@ KHÔNG `*` / `latest`. Critical pins: ## 📅 Recent activity (last 10 FIFO) +- **2026-05-13 (S22, REFUSED 100%):** Em main classified ALL S22 work as cross-stack reasoning chain (BE Mig + Service guard + DTO + FE Designer + FE Section + FE types + tests) → REFUSE per criteria #3+#4. Em main solo executed. State chốt S22: **30 migrations** (+1 Mig 30 AllowApproverEditSection1 per-NV F4 flag), **104 test PASS** (+20 từ 84 — gồm PE WF ReturnMode + Draft guard + Reflection-based Authorize policy regression), ~146 endpoints (+3), 46 gotchas unchanged, 33 active prod users (13 cũ + 20 mới S22+2). 7 patterns successfully applied throughout S22 (validated continued effectiveness): Pattern 7 per-NV admin opt-in flag (Mig 30 follow Mig 29), Pattern 2 EF migration 3-file rule, Pattern 8 tách endpoint narrow scope (AdjustBudget vs UpdatePeDraft), Pattern 9 defense-in-depth FE+BE guard pair (S22+1 disable 3 button), Pattern 10 Reflection-based regression test cho Authorize policy (Plan C task 4 #44, 5 test ~50 LOC), Pattern 11 test infra helper cookie-cutter (SeedWorkflowAsync + SeedApproversAsync), Pattern 12 InternalsVisibleTo csproj expose internal helper cho test. Mismatches discovered S22: (1) "Đang trong quá trình duyệt = người điều chỉnh cũng là người duyệt" — em first interpret default Approver scope always allowed → bro corrected per-NV admin opt-in flag (Mig 30). Lesson: clarify default behavior vs admin opt-in TRƯỚC khi default scope expansion. (2) `PE.changelogs` field KHÔNG có trong PeDetailBundle — em first design history display trong BudgetAdjustSection, build FAIL TS2339. Fix: removed history display (defer S23+ via separate fetch endpoint). (3) Dialog `size="xl"` NOT supported — only "sm" | "md" | "lg". Use "lg" cho preview iframe. (4) API auth field `accessToken` không phải `token`. Script `seed-test-users-prod.ps1` lần đầu FAIL 401 sau auth — em fix `$authResp.accessToken`. - **2026-05-13 (S21 t3-t5, REFUSED 3×):** Em main classified all 3 turns as cross-stack reasoning chain (BE+FE+test tightly coupled) → REFUSE per criteria #3+#4 (cross-stack > 2 layers, bug fix reasoning chain). Bug fix gotcha #45 = bug + reasoning, F1+F2+F3 = schema design decision, Refactor per-NV = drastic refactor schema + Service + FE × 2 app. All correct REFUSE — em main solo executed. Strict scope criteria validated S21 t3-t5 — REFUSE rate 100% match Anthropic warning "tightly interdependent coding". Cumulative: 84 test, 29 mig, 45 gotcha. Pattern saved future invocation: per-NV permission scope split natural theo role + EF migration BACKFILL reorder pattern. - **2026-05-11 (setup):** Implementer agent initialized. Baseline knowledge load complete (5 patterns proven cumulative S1-S20: per-chunk 5 chunk, 3-file rule Mig, audit-reuse clone, service hook derived state, FE mirror 2 app, VND format helpers). No implementations performed yet. Awaiting first SendMessage from em main. Strict scope auto-refuse criteria active. diff --git a/.claude/agent-memory/investigator/MEMORY.md b/.claude/agent-memory/investigator/MEMORY.md index bb47b4c..65268ab 100644 --- a/.claude/agent-memory/investigator/MEMORY.md +++ b/.claude/agent-memory/investigator/MEMORY.md @@ -86,7 +86,7 @@ Common queries: `sys.columns`, `sys.triggers`, `__EFMigrationsHistory`, `COUNT(* ## 🧠 SOLUTION_ERP context essentials (auto-load) -- **DB Dev:** `SolutionErp_Dev` LocalDB (59 tables / 29 migrations / Mig 29 latest `RefactorAdvancedOptionsToPerLevelAndDrafterUser`) +- **DB Dev:** `SolutionErp_Dev` LocalDB (59 tables / 30 migrations / Mig 30 latest `AddAllowApproverEditBudgetToLevels`) - **DB Design:** `SolutionErp_Design` (ef tooling distinct) - **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 @@ -95,7 +95,10 @@ Common queries: `sys.columns`, `sys.triggers`, `__EFMigrationsHistory`, `COUNT(* - **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:** 46 (reference `docs/gotchas.md`) -- **Tests baseline:** 84 PASS (58 Domain + 26 Infra) — Phase 9 UAT skip per chunk (memory `feedback_uat_skip_verify`) +- **Tests baseline:** 104 PASS (+20 S22: 5 reg #44 Authorize policy + 7 ReturnMode + 7 Guard + 1 V2 actor scope reject) — Phase 9 UAT skip per chunk (memory `feedback_uat_skip_verify`) +- **Endpoints:** ~146 (+3 S22: PATCH /users/{id}/allow-skip-final + PATCH /pe/{id}/budget-adjust + GET /pe/{id}/attachments/{attId}/view) +- **Users:** 30 demo + 33 active prod (13 cũ + 20 mới S22+2 role-based: act/bod/equ/fin/hra/pm/qs prefix `.nv/.pp/.tp` + bod.1/2). Password policy ≥12 chars (S22+2 discovery, `TestUser@2026`) +- **API auth response:** `accessToken` + `refreshToken` + `user` (S22+2, NOT `token`) - **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` @@ -104,11 +107,12 @@ 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-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). +- **V2 Mig 22-30** — `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 F drop V1 ABORTED S22+4 — Contract entity HOÀN TOÀN V1 chưa wire V2 + 4 PE V1-only + 19 PE V1+V2 mix). - **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) + - **Mig 30** (S22+5) F4 `AllowApproverEditBudget` per-Level slot on `ApprovalWorkflowLevels` — admin Designer tick per slot cho Approver được edit Budget khi review. Pattern reinforced 2× với Mig 29 F1+F3: default = admin opt-in per slot, KHÔNG = mở rộng default. Cross-ref memory `feedback_per_nv_permission_scope.md` proven cumulative Mig 29 + Mig 30. 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. @@ -124,6 +128,7 @@ State machine 5 trạng thái phiếu PE: Nháp / Đã gửi duyệt / **Trả l ## 📅 Recent activity (last 10 FIFO) +- **2026-05-13 (S22, no spawn — em main solo throughout):** S22 18:00→~21:00 em main solo. Cumulative state: 30 mig (+1 Mig 30 `AddAllowApproverEditBudgetToLevels` F4 per-Level slot), 104 test PASS (+20: 5 reg #44 Authorize policy + 7 ReturnMode + 7 Guard + 1 V2 actor scope reject), ~146 endpoints (+3: PATCH /users/{id}/allow-skip-final + PATCH /pe/{id}/budget-adjust + GET /pe/{id}/attachments/{attId}/view), 46 gotcha unchanged, 19 memory unchanged (recommend +1 entry — see below). Prod active users 13→33 (+20 role-based: act.nv/pp/tp, bod.1/2, equ/fin/hra/pm/qs.nv/pp/tp). **Discoveries S22:** (1) **Per-NV admin opt-in flag pattern reinforced 2×** — Mig 30 F4 cùng pattern Mig 29 F1+F3 (S21 t5). Bro corrected em main lần đầu: "phải tick checkbox như Section 2", default = admin opt-in per slot, KHÔNG = mở rộng default. Cross-ref memory `feedback_per_nv_permission_scope.md` proven 2×. (2) **Plan F drop V1 ABORTED** — pre-flight sqlcmd reveal Contract entity HOÀN TOÀN V1 chưa wire V2 (chưa có ApprovalWorkflowId column) + 4 PE V1-only + 19 PE V1+V2 mix. Lesson: drop migration cần verify entity scope toàn bộ (Contract liên đới — không chỉ PE). (3) **Identity password policy ≥12 chars** — seed 20 user FAIL 400 với "User@123456" (11 chars), `TestUser@2026` (13 chars) pass. (4) **Identity rename atomic 4 fields** confirm gotcha #38: Email + NormalizedEmail + UserName + NormalizedUserName + FullName; sqlcmd cần `SET QUOTED_IDENTIFIER ON` cho filtered unique index. (5) **API login response field name `accessToken` + `refreshToken` + `user`** — KHÔNG có field `token` (correct prior Bash example trong spec dùng `.token` sẽ fail). (6) **PS 5.1 ASCII-only script discipline** reinforced gotcha #30: `seed-test-users-prod.ps1` viết Vietnamese names without diacritics tránh parser error. Recommend bro add 1 memory entry "Admin opt-in flag pattern proven 2×" cumulative Mig 29 + Mig 30. - **2026-05-13 (S21 t3-t5, no spawn):** Em main solo 3 turns (bug fix gotcha #45 + F1+F2+F3 workflow-level Mig 28 + refactor per-NV Mig 29). Implementer REFUSE per cross-stack reasoning chain rule. Investigator KHÔNG spawn — em main đã có context cumulative S20 t12 setup + active dev throughout. No findings to flush. Cumulative state update: 84 test, 29 mig, 45 gotcha, 19 memory entries (+2 S21 t5 pending), 6 skills unchanged. Pattern reusable saved cho future spawn: per-NV permission scope split + EF migration ADD→BACKFILL→DROP reorder. - **2026-05-11 (setup):** Investigator agent initialized. Baseline knowledge load complete (44 gotchas + 14 memory entries + 6 skills + 27 mig + 81 test pass cumulative). No investigations performed yet. Awaiting first SendMessage from em main. diff --git a/.claude/agent-memory/reviewer/MEMORY.md b/.claude/agent-memory/reviewer/MEMORY.md index 53a0b40..01e2ff3 100644 --- a/.claude/agent-memory/reviewer/MEMORY.md +++ b/.claude/agent-memory/reviewer/MEMORY.md @@ -43,6 +43,14 @@ Adversarial pre-commit reviewer for SOLUTION_ERP. Read-only verification + live - Symptom: commit migration nhưng thiếu `.Designer.cs` hoặc `ApplicationDbContextModelSnapshot.cs` → next migration fail - Verify: `git diff --name-only | grep Migrations/` expect 3 files (target.cs + target.Designer.cs + Snapshot.cs) +### Gotcha #47 — `.claude/agent-memory/**` NOT in `paths-ignore` filter (S22 discovery, PENDING bro decide) + +- Symptom: MEMORY.md drift patch commit (end-of-session flush) triggers full CI deploy ~3.5min waste +- Verify: check `.gitea/workflows/deploy.yml` `paths-ignore` — currently `['docs/**', '**/*.md', '.claude/skills/**']` MISSING `.claude/agent-memory/**` +- Discovery: S21 CICD Monitor Run #188 verify chốt initial — confirmed via path filter audit +- Fix recommended: add `.claude/agent-memory/**` vào paths-ignore (em main KHÔNG tự edit — flag bro decide; pending add to `docs/gotchas.md` 47th entry) +- Severity: minor (CI waste only, no functional impact) + --- ## 📋 5-category checklist (apply EVERY review) @@ -107,14 +115,17 @@ Per Cognition documented research: ## 🧠 SOLUTION_ERP review essentials -- **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` +- **Tests baseline:** 104/104 PASS (58 Domain + 46 Infra — +20 từ 84 S21: 5 regression gotcha #44 Authorize class-level + 7 ReturnMode + 7 Guard EnsureCanRejectV2Async + 1 V2 actor scope reject). 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). +#47 paths-ignore agent-memory gap (S22 PENDING bro confirm add to docs) +- **Migrations:** 30 latest `AddAllowApproverEditBudgetToLevels` (S22+5 — per-slot F4 flag admin opt-in cho Approver scope edit budget ChoDuyet branch). Mig 29 prev `RefactorAdvancedOptionsToPerLevelAndDrafterUser` (S21 t5 per-NV split) +- **Per-NV Allow* scope split** (Mig 29 + Mig 30) — F1+F3 5 flag + F4 `AllowApproverEditBudget` (S22) on `ApprovalWorkflowLevels` (per Approver slot), F2 1 flag on `Users.AllowDrafterSkipToFinal` (per Drafter). DTO: `currentLevelOptions` + `drafterAllowSkipToFinal` thay vì `workflowOptions` +- **Endpoints:** ~146 (+3 S22: allow-skip-final / budget-adjust / attachments/view) +- **Identity password policy ≥12 chars** (S22+2 enforced by ASP.NET Identity stack — reject `User@123456` 11 chars). Existing HANDOFF mention "User@123456" pattern S4 outdated, current test creds Admin@123456 + TestUser@123456 OK - **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) - UAT user: `nv.test@solutions.com.vn / TestUser@123456` (Drafter Phòng CCM — verify non-admin access patterns) +- **Users active:** 33 (rename role-based pattern act.nv / act.pp / act.tp etc.) - **Conventions:** `docs/rules.md` (§3.9 mirror 2 FE, §5.2 commit format, §6.5 docs KEEP narrative, §7 test timing, §2.8 package pinning) - **6 skills:** `contract-workflow` · `permission-matrix` · `form-engine` · `ef-core-migration` · `dependency-audit-erp` · `iis-deploy-runbook` @@ -133,6 +144,7 @@ Flag commit nếu thấy ` **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 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.) +**Last updated:** 2026-05-13 2200 (Session 22 CHỐT — **🎯 Bro chốt sub-agent solution OK**. 11 commits S22 pushed remote `3d725c4..b04a11a`. CICD Monitor Run #188 PASS verified. Highlights: Plan D F2 toggle + Plan C +20 test (test catch-up backlog) + Plan E strict V2 scope + Plan F ABORTED pre-flight (defer sau Plan B) + S22+1 fix disable 3 button bug + S22+2 seed 20 test user role-based naming + S22+3 rename users `{dept}.{level}@solutions.com.vn` + S22+4 attachment view inline PDF + Section "Điều chỉnh ngân sách" + S22+5 Mig 30 `AllowApproverEditBudget` per-NV opt-in (spec fix bro feedback). Stats final S22: **30 mig (+1 Mig 30)** · 59 tables · **~146 endpoints (+3)** · 34 FE pages · **104 test pass (+20: 5 reg #44 + 7 ReturnMode + 7 Guard + 1 V2 actor scope)** · **47 gotcha (+1 #47 paths-ignore agent-memory gap pending bro fix)** · 19 memory · 6 skills · **33 active users prod** (13 cũ + 20 mới role-based) · 4 sub-agents (CICD Monitor Run #188 PASS verify, 3 sub-agents seeds-only em main solo throughout S22). Pattern reinforced: per-NV admin opt-in flag 2× proven (Mig 29 + Mig 30) — anti-pattern default scope expansion. Memory `feedback_per_nv_permission_scope` reinforced S22+5 narrative.) +**Last updated S22 prev:** 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.**) diff --git a/docs/changelog/sessions/2026-05-13-2200-s22-chot-cuoi.md b/docs/changelog/sessions/2026-05-13-2200-s22-chot-cuoi.md new file mode 100644 index 0000000..12d26fe --- /dev/null +++ b/docs/changelog/sessions/2026-05-13-2200-s22-chot-cuoi.md @@ -0,0 +1,282 @@ +# Session 22 CHỐT CUỐI — 2026-05-13 22:00 — Plan C+D+E + 5 turn S22+ + +**Dev:** Claude Opus 4.7 1M Max (em main solo throughout — 3 sub-agents seeds-only REFUSE 100% cross-stack reasoning chain per Cognition "writes single-threaded") +**Duration:** ~4h (18:00 start S22 → 22:00 chốt) +**Base commit:** `3d725c4` (S21 chốt cuối) +**Final HEAD:** `b04a11a` (S22+5 Mig 30 per-NV F4 flag) +**Total commits S22:** **11** pushed remote + +## 🎯 Trigger session + +Bro chốt 4 plan đầu session: Plan C + Plan D + Plan E + Plan F. Em hỏi 3 clarify question: +- Thứ tự: D→C→E→F (an toàn → risk) +- Plan F destructive: drop luôn không backup (UAT chấp nhận risk) +- Plan E scope: toàn bộ List + Inbox + Detail + +Em pre-flight Plan F khám phá blocker → ABORT defer S23+. + +Bro feedback liên tục → +5 turn: +- S22+1 fix bug "không phải lượt bạn" disable cả 3 button +- S22+2 seed 20 test user prod +- S22+3 rename users sang role-based (`{dept}.{level}@solutions.com.vn`) +- S22+4 view file PDF inline + Section "Điều chỉnh ngân sách" +- S22+5 fix spec S22+4: Mig 30 per-NV admin opt-in flag (KHÔNG default expand) + +Cuối session bro confirm **"giải pháp sub agent khá tốt"** ✅ → keep 4 sub-agents ongoing. + +## 📋 11 commits S22 timeline + +| # | Commit | Topic | Stage | +|---|---|---|---| +| 1 | `60efeed` | Plan D — Users F2 toggle UI | BE+FE Admin | +| 2 | `dbda37e` | Plan C task 4 — 5 reflection test #44 silent 403 | Tests | +| 3 | `215b1e0` | Plan C task 1-3 — 14 test catch-up ReturnMode + Guard | Tests | +| 4 | `f149661` | Plan E — BE PE strict V2 scope (List + Detail) | BE | +| 5 | `a74e671` | Docs S22 + 3 agent MEMORY drift | Docs | +| 6 | `40f64c6` | S22+1 disable 3 button + BE EnsureCanRejectV2Async guard + 1 test | FE+BE | +| 7 | `8185070` | S22+2 seed 20 test user prod | Scripts | +| 8 | `0e70789` | S22+3 rename users sang role-based naming | Scripts | +| 9 | `37b51d7` | S22+4 Chunk A — BE attachment view + AdjustBudgetCommand | BE | +| 10 | `30d51c8` | S22+4 Chunk B — FE AttachmentPreviewDialog + Section Điều chỉnh ngân sách | FE×2 | +| 11 | `b04a11a` | S22+5 Mig 30 +AllowApproverEditBudget per-NV opt-in (spec fix) | BE+FE×2 | + +**CICD Monitor verify:** Run #188 PASS (Plan C+D+E push). 7 commits sau (S22+1 → S22+5) chưa spawn verify — em sẽ spawn final at session end. + +## 🎨 Plan D — User Management F2 toggle UI (`60efeed`) + +BE wire F2 (`User.AllowDrafterSkipToFinal` từ Mig 29): +- `UserDto +AllowDrafterSkipToFinal` +- `SetUserAllowDrafterSkipToFinalCommand` + Handler mirror `SetBypassReview` pattern +- `PATCH /api/users/{id}/allow-skip-final` body `{allowDrafterSkipToFinal:bool}` Policy=`Users.Update` + +FE Admin (fe-user KHÔNG có UsersPage admin-only): +- `User` type +`allowDrafterSkipToFinal: boolean` +- Column "Skip cuối" violet `FastForward` badge +- Action button toggle `allowSkipMut` + +Verify: BE build clean + npm fe-admin pass 638ms. + +## 🧪 Plan C task 4 — Regression test #44 silent 403 (`dbda37e`) + +5 reflection-based tests verify `ApprovalWorkflowsV2Controller` Authorize split (gotcha #44 S18 fix `f77ea38`): +- class-level `[Authorize]` only, NO Policy → catch regression nếu ai add Policy lên class-level +- GET Overview inherits class-level (no action-level Policy) +- POST Create + DELETE + PATCH user-selectable require Policy=`Workflows.Create` + +Add `ProjectReference SolutionErp.Api` → `SolutionErp.Infrastructure.Tests` cho reflection access Controller types. + +Pattern reusable: catch silent 403 regression mà KHÔNG cần WebApplicationFactory heavy. + +Verify: 84 → 89 PASS (+5). + +## 🧪 Plan C task 1-3 — Service test catch-up S21 t4-t5 (`215b1e0`) + +14 test cover 3 helper sửa lớn từ S21 t4-t5: + +**Task 1+2 (7 test ReturnMode):** +- `ApplyReturnModeAsync` Drafter allowed/denied/admin bypass +- OneLevel happy path + admin bypass disabled flag +- `skipToFinal` Drafter allowed/denied/admin bypass + +**Task 3 (7 test Guard):** +- `EnsureEditableForDetailsAsync` Drafter scope DangSoanThao + TraLai +- F3 Approver scope ChoDuyet + flag on + actor match +- F3 Approver scope ChoDuyet + flag off → ConflictException +- F3 Approver scope ChoDuyet + actor mismatch → ForbiddenException +- Admin bypass ChoDuyet + flag off +- DaDuyet terminal phase + any caller → ConflictException + +**Pattern infra:** +- `SeedWorkflowAsync` 1 Step (DepartmentId=null) × 2 Levels +- `SeedApproversAsync` via `IdentityFixture.CreateUserAsync` +- `FakeCurrentUser` impl `ICurrentUser` +- `InternalsVisibleTo` csproj expose internal `PurchaseEvaluationDraftGuard` + +Verify: 89 → 103 PASS (+14). + +## 🔒 Plan E — Phân quyền strict V2 scope (`f149661`) + +Thắt chặt PE V2 List + Detail từ UAT loose sang strict actor.UserId scope (Inbox đã strict từ S17): + +`ListPurchaseEvaluationsQuery`: +```csharp +// Trước (loose): || x.e.ApprovalWorkflowId != null +// Sau (strict): pre-compute userApprovalWfIds từ ApprovalWorkflowLevels.ApproverUserId +q = q.Where(x => + x.e.DrafterUserId == userId + || eligiblePhases.Contains(x.e.Phase) + || (x.e.ApprovalWorkflowId != null && userApprovalWfIds.Contains(x.e.ApprovalWorkflowId.Value))); +``` + +`GetPurchaseEvaluationQuery` (Detail): similar pattern via `db.ApprovalWorkflowLevels.AnyAsync(l => Step.ApprovalWorkflowId == awId && ApproverUserId == userId)`. + +Verify: build clean + 103/103 PASS regression-free. + +## ⛔ Plan F — Drop legacy V1 (ABORTED) + +Pre-flight SSH vietreport-vps sqlcmd reveal blocker: +- 23 PE pin V1 (`WorkflowDefinitionId` set) +- 4 PE V1-ONLY (no V2 fallback) +- **7 Contract pin V1 + Contract entity HOÀN TOÀN V1** (chưa có `ApprovalWorkflowId` column — Plan B Contract V2 wire CHƯA kick off) + +Drop V1 = BE crash startup FK violation. **ABORT defer S23+ sau Plan B Contract V2 wire**. + +User confirm ABORTED via AskUserQuestion (empty answer → apply Recommended). + +## 🐛 S22+1 — Fix "không phải lượt bạn" disable 3 button (`40f64c6`) + +User UAT feedback: trước đây chỉ "Duyệt" disabled khi non-approver. "Trả lại" + "Từ chối" vẫn enabled (design intent S17). Bro override: "Nếu đã không đc quyền thao tác thì ko đc quyền thao tác hết tất cả các hành động". + +FE × 2 app (`PeWorkflowPanel.tsx`): +- `isDisabled = blockedByV2Level` (drop `isForwardApprove &&` qualifier) +- Tooltip update "mới thao tác được (Duyệt / Trả lại / Từ chối)" + +BE defense-in-depth (`PurchaseEvaluationWorkflowService.cs`): +- Helper `EnsureCanRejectV2Async` mirror FE `actorInV2Level` logic +- Skip silent khi admin/V1/non-ChoDuyet/no actor/no pointer +- Throw `ForbiddenException` khi V2 ChoDuyet + actor != currentLevel.ApproverUserId +- Invoke top Reject branch (cover cả TuChoi + Trả lại) + ++1 regression test `Reject_NonApprover_V2_Throws_ForbiddenException`. + +Verify: 103 → 104 PASS (+1). + +## 👥 S22+2 + S22+3 — Seed 20 test user role-based naming (`8185070` + `0e70789`) + +**S22+2 seed:** +- Script `scripts/seed-test-users-prod.ps1` ASCII-only (gotcha #30 PS 5.1 diacritics) +- 6 phòng còn trống × 3 user (NV/PP/TP) + BOD +2 = 20 user +- Password `TestUser@2026` (>=12 chars per Identity policy discovery) +- 4 user advanced flags: 2 CanBypassReview (`act.tp` + `hra.tp`) + 2 AllowDrafterSkipToFinal (`fin.pp` + `pm.nv`) + +**S22+3 rename:** +- Email pattern: `{dept}.{level}@solutions.com.vn` (act.nv / act.pp / act.tp / bod.1 / bod.2 / etc.) +- FullName: "ACT NV - Drafter+Accounting [Bypass]" (encode flag inline) +- Identity rename 4 fields atomic (gotcha #38): Email + NormalizedEmail + UserName + NormalizedUserName + FullName +- SQL transaction `SET QUOTED_IDENTIFIER ON` required cho filtered indexes Users + +Prod active users: 13 → **33** (no rename existing 13). + +**Discoveries:** +1. Identity password policy ≥12 chars (existing memory mention `User@123456` 11 chars — outdated) +2. API auth response field `accessToken` (NOT `token`) +3. PS 5.1 ASCII-only discipline reinforced (gotcha #30) + +## 📄 S22+4 — Attachment view + Section "Điều chỉnh ngân sách" Chunk A+B (`37b51d7` + `30d51c8`) + +**Chunk A BE:** +- NEW `GET /api/purchase-evaluations/{id}/attachments/{attId}/view` — inline Content-Disposition +- NEW `AdjustPurchaseEvaluationBudgetCommand` + Handler + Validator +- NEW `PATCH /api/purchase-evaluations/{id}/budget-adjust` body `{budgetId, budgetManualName, budgetManualAmount}` + +**Chunk B FE × 2 app:** +- NEW component `AttachmentPreviewDialog.tsx`: + - Fetch BE `/view` as blob → object URL (bearer auth qua axios) + - Render iframe (PDF) hoặc img (image) trong Dialog + - Helper `isPreviewable(fileName)` check ext (PDF/PNG/JPG/JPEG/WEBP/GIF) +- `SupplierAttachmentsCell` + `GeneralAttachmentsSection`: + - 2 button split: 👁️ Eye View (violet) + ⬇️ Download (brand) + - Filename text-only (KHÔNG còn click download) +- NEW `BudgetAdjustSection` component: + - Section 5 cuối PeDetail view + - 2 mode edit: Select Budget link OR Manual amount+name + - Save → PATCH /budget-adjust + +**Verify:** dotnet build clean + npm build × 2 app pass. + +## 🔧 S22+5 — Spec fix: Mig 30 +AllowApproverEditBudget per-NV (`b04a11a`) + +Bro feedback S22+4 lần 2: "à ko ý tao nói là thêm 1 cái section trên chỗ section 2 là cho phép edit đc ngân sách ấy chứ ko phải thay đổi logic edit, và luồng duyệt. Về edit vẫn như cũ -> chỉ đc sửa lại khi đang nháp, trả lại, và thêm 1 section lúc thiết lập quy trình duyệt nữa là những người nào đc phép edit section ngân sách, tương tự như section 2 thêm NCC các thứ ấy." + +→ Em hiểu sai: KHÔNG default Approver scope expansion. Phải admin opt-in flag per slot mirror Mig 29 F3 `AllowApproverEditDetails`. + +**Refactor:** + +Mig 30 `AddAllowApproverEditBudgetToLevels`: +- ALTER `ApprovalWorkflowLevels +AllowApproverEditBudget bit NOT NULL DEFAULT 0` +- 3-file rule (mig + Designer + Snapshot) +- Apply LocalDB Dev + Design + +Domain entity `ApprovalWorkflowLevel +bool AllowApproverEditBudget` (default false). +EF config `HasDefaultValue(false)`. +DTO `AwLevelDto + ApprovalWorkflowOptionsDto + CreateAwLevelInput` extend. + +PE GET handler populate `currentLevelOptions.allowApproverEditBudget` từ curLevel slot. + +`AdjustBudgetCommand` handler refactor ChoDuyet branch: +- Trước: actor match ApproverUserId (default expand) +- Sau: `level.AllowApproverEditBudget=true` AND actor match → throw ConflictException nếu slot chưa cấp quyền + +FE Admin Designer `ApprovalWorkflowsV2Page.tsx`: +- `LevelDto + EditLevelEntry +allowApproverEditBudget` +- `copyFromDefinition + makeDefaultLevelEntry` propagate default false +- POST body include +- Checkbox inline mỗi Level entry "Cho phép chỉnh sửa Section ngân sách lúc đang duyệt" + +FE Types × 2 app `purchaseEvaluation.ts` — `ApprovalWorkflowOptions +allowApproverEditBudget`. + +FE `BudgetAdjustSection` × 2 app — `isApproverChoDuyet = phase ChoDuyet + actor in approvers + currentLevelOptions.allowApproverEditBudget`. + +**Pattern reinforced:** Per-NV admin opt-in flag proven 2× (Mig 29 F1+F3 + Mig 30 F4) — cross-ref memory `feedback_per_nv_permission_scope.md`. + +## 📊 Stats cumulative S21 chốt → S22 chốt + +| Metric | S21 chốt | S22 chốt | Δ | +|---|---|---|---| +| DB tables | 59 | 59 | 0 | +| **Migrations** | 29 | **30** | +1 (Mig 30) | +| Endpoints | ~143 | **~146** | +3 | +| FE pages | 34 | 34 | 0 | +| **Unit tests** | 84 | **104** | **+20** | +| **Gotchas** | 46 | **47** | +1 (#47 paths-ignore agent-memory PENDING bro decide) | +| Memory entries | 19 | 19 | 0 (reinforced existing `feedback_per_nv_permission_scope`) | +| Skills | 6 | 6 | 0 | +| Sub-agents | 4 | 4 | 0 | +| **Prod active users** | 13 | **33** | **+20** role-based | +| Commits S22 | — | **11** | pushed remote | + +## 🧠 Lessons learned + +1. **Per-NV admin opt-in flag pattern proven 2×** (Mig 29 F1+F3 + Mig 30 F4) — em main first interpret S22+4 "Approver luôn được edit" → bro corrected "phải tick checkbox per slot mirror Section 2". Anti-pattern: default expand permission scope khi user nói "thêm scope". Reusable cho future flag F5+ (vd `AllowEarlyApprove`, `AllowDelegate`). + +2. **Pre-flight prod sqlcmd discipline BẮT BUỘC** cho destructive migration. Plan F catch 3 blocker (Contract V1-only + 4 PE V1-only + 19 PE V1+V2 mix) tránh BE crash startup. Pattern reusable: SSH vietreport-vps verify data state TRƯỚC drop column/table. Audit entity scope toàn bộ — Contract liên đới khi drop V1 schema PE. + +3. **Identity password policy ≥12 chars** discovery — `User@123456` (11 chars) FAIL S22+2. Memory entry existing mention pattern S4 outdated. Future seed script use `TestUser@2026` (13 chars) default. + +4. **Defense-in-depth FE + BE guard pair** (S22+1) — UI disable + BE helper throw 403. Tránh request forge non-approver gọi PATCH direct qua DevTools/Postman. Pattern reusable: bất kỳ action sensitive (approve/reject/adjust). + +5. **Reflection-based regression test cho Authorize policy** (Plan C task 4) — 5 test ~50 LOC catch class-level Policy regression mà KHÔNG cần WebApplicationFactory heavy. Pattern reusable cho future controller sensitive. + +6. **Tách endpoint riêng cho narrow scope** (S22+4 AdjustBudget vs UpdatePeDraft) — `UpdatePeDraft` cover Section 1 fields (rộng) chỉ Drafter Nháp/Trả lại. `AdjustBudget` tách riêng narrow scope (Budget* only) cover thêm Approver scope với admin opt-in flag. Tránh accidental edit Section 1 từ Approver. + +7. **`.claude/agent-memory/**` thiếu paths-ignore** (gotcha #47 discovery) — MEMORY.md drift commit (end-of-session flush) trigger full CI deploy ~3.5min waste. Fix recommended add path-ignore — PENDING bro confirm. + +## ⏭ Handoff S23 + +### Pending plans + +- 🟡 **Plan B Contract V2 wire (Mig 31+32)** — HIGH priority, mirror PE V2 pattern. Mở đường Plan F drop V1 sau. +- ⛔ **Plan F drop V1** — defer SAU Plan B + migrate 4 PE V1-only + UAT 2-3 tuần. +- 🟢 **Plan H PE PDF Export** — LOW priority carry. +- 🟡 **Plan I RAG Hybrid setup 5 dự án** — defer chờ bro confirm. +- 🔧 **Gotcha #47 paths-ignore agent-memory** — bro confirm fix `.gitea/workflows/deploy.yml`. +- 🧪 **Plan C test catch-up bundle** — defer thêm test cho `AdjustBudgetCommand` ChoDuyet branch + V2 strict scope tests. UAT 2-3 lần ổn → viết test. + +### Sub-agent status + +Bro confirm sub-agent solution KHÁ TỐT ✅: +- 🟦 Investigator — seeds-only S22 (em main solo cross-stack) +- 🟨 Implementer — REFUSE 100% S22 per criteria #3+#4 cross-stack reasoning chain +- 🟥 Reviewer — seeds-only S22 (em main self-review build+test) +- 🟩 CICD Monitor — Run #188 PASS verified S22 chốt initial, 7 commits sau (S22+1 → S22+5) sẽ spawn final verify + +4 sub-agents MEMORY.md flushed S22 cumulative — patterns added cho Implementer (7-12), Investigator/Reviewer count refresh, CICD Monitor Mig 30 + 104 test baseline. + +## References + +- Memory user-level reinforced: `feedback_per_nv_permission_scope.md` (Section "Reinforcement S22+5") +- Gotcha mới: `docs/gotchas.md#47` (paths-ignore agent-memory gap, PENDING) +- Mig 30: `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/20260513160703_AddAllowApproverEditBudgetToLevels.cs` +- Scripts: `scripts/seed-test-users-prod.ps1` + `scripts/rename-test-users-to-roles.ps1` +- 4 agent MEMORY.md flushed at `.claude/agent-memory/{investigator,implementer,reviewer,cicd-monitor}/MEMORY.md` +- 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` (S22+5 reinforced) diff --git a/docs/gotchas.md b/docs/gotchas.md index 29ddc24..ac0c972 100644 --- a/docs/gotchas.md +++ b/docs/gotchas.md @@ -742,6 +742,30 @@ File LastWriteTime VPS = thực sự deploy completion time (NSSM copy + IIS rec - Khi setup automation script với Gitea API → đọc rõ Gitea API spec v1.20+ (`https://docs.gitea.com/api/`) thay vì assume GitHub naming. - Time-sensitive verify (vd "deploy xong chưa, có an toàn trigger task tiếp không"): KHÔNG trust API status timestamp đơn lẻ → cross với VPS file mtime hoặc curl bundle hash live. +### 47. `.claude/agent-memory/**` thiếu trong `paths-ignore` filter (CICD waste 3.5min per MEMORY flush) (S22 discovery) + +**Triệu chứng:** Cuối session em main flush 3-4 sub-agent MEMORY.md drift patch + commit dạng `[CLAUDE] Docs: chốt S22 ...` — push remote → **Gitea Actions trigger full deploy ~3.5min** dù không có thay đổi BE/FE code. CICD waste ~12K token / run × N session. + +**Root cause:** `.gitea/workflows/deploy.yml` `paths-ignore` chỉ có `['docs/**', '**/*.md', '.claude/skills/**']` — KHÔNG bao gồm `.claude/agent-memory/**`. Mỗi MEMORY.md drift commit (end-of-session flush + cross-session sync) trigger full CI pipeline → test gate Domain 58 + Infra 46 + build BE + build FE × 2 + deploy NSSM/IIS. + +**Discovery:** S22 CICD Monitor Run #188 self-discovery khi verify push `a74e671` (Docs + 3 agent MEMORY drift patch) — confirmed trigger Run dù không có code change. + +**Fix recommended (PENDING bro confirm):** Edit `.gitea/workflows/deploy.yml`: +```yaml +on: + push: + branches: [main] + paths-ignore: + - 'docs/**' + - '**/*.md' + - '.claude/skills/**' + - '.claude/agent-memory/**' # ← ADD: prevent end-of-session MEMORY flush trigger deploy +``` + +**Severity:** Minor (CI waste only, no functional impact). Em main KHÔNG tự edit `.gitea/workflows/` — flag bro decide. + +**Cross-ref:** Gotcha #41 paths-ignore docs-only skip (S5 prior). Pattern: bất kỳ file `**/MEMORY.md` hoặc tooling state files KHÔNG nên trigger code deploy. + **References:** - CICD Monitor Run #186 (S21 t4 2026-05-13 19:13) — first discovery - CICD Monitor Run #187 (S21 t5 2026-05-13 20:12) — confirmed pattern + cache stale bonus