[CLAUDE] Docs: S22 chốt cuối — gotcha #47 + 4 agent MEMORY flush + session log cumulative

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) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-05-13 23:25:37 +07:00
parent b04a11a62f
commit cc8a7d34b3
8 changed files with 401 additions and 13 deletions

View File

@ -1,6 +1,7 @@
# HANDOFF — Brief 5 phút cho session tiếp theo
**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`.)
**Last updated:** 2026-05-13 2200 (Session 22 CHỐT — **bro confirm sub-agent solution OK**. 11 commits pushed remote `3d725c4..b04a11a`. CICD Monitor Run #188 PASS verified. State final: **30 mig · 104 test · 47 gotcha (+1 #47) · 19 memory · 6 skills · 4 sub-agents · 33 active users prod**. KHÔNG còn pending push. Plan G S21-S22 evidence: Trial Week 1 → Week 2 sub-agent ROI confirmed. Bro test UAT 4 flag pattern + view file PDF + Section Điều chỉnh ngân sách.)
**S22 prev:** 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.**)

View File

@ -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 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.**)

View File

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

View File

@ -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