UX polish trong ApprovalWorkflowsV2Page Designer slot label hiển thị tên user pin
(lookup từ usersList query) thay vì số thứ tự "NV #{ei + 1}". Fallback "Chưa chọn NV"
khi slot chưa pick user.
Plan K pre-Mig 31 first chunk. Mig 31 sẽ thêm AllowApproverSkipToFinal 7th checkbox
inline panel (K3 chunk sau).
Per bro decision S23 t1: "Chỗ quy trình duyệt #NV 1 - Họ tên luôn".
Verify:
- npm run build fe-admin pass
- 0 TS error
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
14 KiB
Implementer Agent — Persistent Memory
Persistent diary cross-session. Auto-injected first 200 lines / 25KB at spawn. Update BEFORE every stop. Curate when > 25KB.
🎯 Role baseline
Code execution specialist for SOLUTION_ERP. Conditional WRITE (Case 1+2+3+5 ONLY). Tools: Read, Edit, Write, Bash, Skill, Grep, Glob. Output: commits + verification report.
🚨 STRICT scope auto-refuse criteria
REFUSE if ANY:
- Schema design decisions needed (FK strategy / nullable / discriminator)
- UX flow decisions needed (drawer vs tab vs modal)
- Cross-stack > 2 layers tight coupling
- Bug fix involving reasoning chain
- Integration testing involving multiple components
- < 30 min trivial task
- First time pattern (no prior precedent)
- Spec ambiguity > 20%
📋 Patterns proven (cross-session) — apply confidently
Pattern 1: Per-chunk discipline 5-chunk A-E (Anthropic Case 2 orchestrator-workers)
Memory feedback_per_chunk_commit chốt:
- Chunk A: Domain entities + Migration (3-file rule)
- Chunk B: Application handlers (CQRS Commands + Queries + Validators)
- Chunk C: Service layer (workflow logic, business rules)
- Chunk D: API controllers + endpoints
- Chunk E: FE update (cả 2 app mirror) + Tests + Docs + commit final
Build + test pass mỗi chunk. Commit message format:
[CLAUDE] <scope>: Chunk <X> — <one-line summary>
<body>
Verify:
- Build pass (X warning, 0 error)
- N test pass (...)
Pending Chunk <Y+1>: <next>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pattern 2: 3-file rule EF migration (BẮT BUỘC commit đủ)
Memory + gotcha #17:
Migrations/{TS}_{Name}.cs(Up + Down)Migrations/{TS}_{Name}.Designer.cs(snapshot at migration time)Migrations/ApplicationDbContextModelSnapshot.cs(current snapshot)
dotnet ef migrations add <Name> \
--project src/Backend/SolutionErp.Infrastructure \
--startup-project src/Backend/SolutionErp.Api
# Apply lên DB Dev:
dotnet ef database update --project src/Backend/SolutionErp.Infrastructure \
--startup-project src/Backend/SolutionErp.Api \
--connection "Server=(localdb)\MSSQLLocalDB;Database=SolutionErp_Dev;Trusted_Connection=True;TrustServerCertificate=true"
# Apply lên DB Design (catchup nếu thiếu):
dotnet ef database update --project src/Backend/SolutionErp.Infrastructure \
--startup-project src/Backend/SolutionErp.Api
Pattern 3: Audit reuse trước khi clone (memory feedback_audit_reuse_before_clone)
Khi user nói "clone X sang Y":
- Grep discriminator field (
ApplicableType,Type,Kindenum) - Check Service / Handler / Controller có hardcode type cụ thể không
- Check FE pages có route dynamic typeCode hay hardcode
- Check menu key (BE const + FE menuKeys.ts) — thường thiếu chính ở đây
- Default reuse 80%, chỉ thêm menu key + sample seed (3 file ~60 LOC)
Bài học S17+ Clone B: 1 commit 937eb24, deploy 1 phát chạy.
Pattern 4: Service hook vs CRUD endpoint cho derived state (memory feedback_service_hook_vs_endpoint)
State X = derived của action Y → UPSERT trong handler Y, KHÔNG endpoint /X riêng.
Bài học S19 Mig 26 PE LevelOpinions: Service ApproveV2Async UPSERT row qua match ApproverUserId == actorUserId (fallback first khi Admin override). 0 endpoint mới.
Pattern 5: FE mirror 2 app rule §3.9
Duplicate fe-admin/ + fe-user/ CÓ CHỦ ĐÍCH:
- Sửa fe-admin xong → mirror fe-user (tay)
- Khi breaking change rename prop → BẮT BUỘC
npm run build× 2 app (memoryfeedback_uat_skip_verifyexception)
Pattern 6: VND format helpers + Phone/Email validate (S20 turn 4)
Inline mỗi file FE PE:
const parseVnd = (s: string): number => Number(s.replace(/[^\d]/g, '')) || 0
const formatVndInput = (n: number): string => (n > 0 ? n.toLocaleString('vi-VN') : '')
const PHONE_RE = /^0\d{9,10}$/
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
const isValidPhone = (s: string) => !s || PHONE_RE.test(s.replace(/[\s\-.]/g, ''))
const isValidEmail = (s: string) => !s || EMAIL_RE.test(s)
Pattern 7: Per-NV admin opt-in flag (S21 t5 Mig 29 + S22 Mig 30)
ApprovalWorkflowLevel +1 column bool DEFAULT 0 (opt-in admin set explicit). EF config HasDefaultValue(false). DTO extend field. FE Designer checkbox inline mỗi Level row.
Reusable cho future flag F5/F6 (vd AllowEarlyApprove, AllowDelegate): admin per-NV opt-in qua Level table thay vì global flag. Decision tree: flag scope role-context → table mapping natural (Approver → Level table carry ApproverUserId FK, Drafter → User table direct — memory feedback_per_nv_permission_scope).
Bài học S22: AllowApproverEditSection1 (Mig 30) follow same pattern Mig 29. 0 schema redesign cần.
Pattern 8: Tách endpoint riêng cho narrow scope (S22 AdjustBudget vs UpdatePeDraft)
Khi 1 action có 2 scope khác nhau theo role:
- Drafter scope (rộng):
UpdatePeDraftcover Section 1 (Tên/Địa điểm/Mô tả/Payment + Budget) — chỉ phase Nháp / Trả lại - Approver scope (hẹp):
AdjustBudgetchỉ Budget rows — phase Đang duyệt với per-NV flag
KHÔNG default expand Drafter scope cho Approver — tránh accidental edit Section 1. Endpoint tách riêng = guard tự nhiên + audit trail rõ.
Bài học S22: AllowApproverEditSection1 flag opt-in cụ thể PATCH /budget rows, KHÔNG /full-update.
Pattern 9: Defense-in-depth FE + BE guard pair (S22+1)
UI button disabled={!canReject} + BE helper EnsureCanRejectV2Async(peId, userId) throw 403 nếu non-approver. Tránh request forge non-approver gọi PATCH direct qua DevTools.
Pattern reusable: bất kỳ action sensitive (approve/reject/adjust) → FE disable + BE guard helper riêng (NOT inline trong handler).
Bài học S22+1: 3 button (Duyệt / Trả lại / Từ chối) — UI disable + BE helper. Tránh leak action qua API direct.
Pattern 10: Reflection-based regression test cho Authorize policy (S22 Plan C task 4 #44)
5 test lightweight ~50 LOC catch class-level [Authorize(Policy = "...")] regression:
var attr = typeof(ControllerXxx).GetCustomAttribute<AuthorizeAttribute>();
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:
private async Task<Guid> 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 <InternalsVisibleTo Include="SolutionErp.Infrastructure.Tests" /> 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)
- ❌ Skip MEMORY.md update — knowledge tài sản
- ❌ Bypass pre-commit hooks
--no-verify(forbidden absolute) - ❌
git add -Ahoặcgit add .— specific files only - ❌ Touch files outside spec scope — anti-fiddle rule
- ❌ Push remote autonomously cho heavy change — em main pushes (UAT iteration: confirm với em trước push)
- ❌ Modify
SolutionErp.slnxautonomously — em main updates khi thêm.cs/.csproj - ❌ Lower bar to match em main quality — Smart Friend Cognition anti-pattern
- ❌ Proceed when spec ambiguous > 20% — return REFUSE với reason
🧠 SOLUTION_ERP conventions (auto-load via skills)
- BE .NET 10: PascalCase tiếng Anh entities + DTO records + command names. CQRS + MediatR + FluentValidation + AutoMapper. Repository qua
IApplicationDbContext.GlobalExceptionMiddlewaremap 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
erasableSyntaxOnlycấmenum→ const-object pattern. UI 100% tiếng Việt. Mirror 2 app rule §3.9. - 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 10TestApplicationDbContextoverridenvarchar(max) → TEXT. - Build:
dotnet build SolutionErp.slnxclean 0 err +npm run build× 2 app pass. - Commit:
[CLAUDE] <scope>: <message>+ Co-Authored-By Claude Opus 4.7 (1M context).
Scopes (pick 1)
Contract · PurchaseEvaluation · Budget · Form · Workflow · Supplier · Auth · Admin · Api · App · Domain · Infra · FE-Admin · FE-User · Tests · Docs · CICD · Scripts · Skill
🔑 Pin versions (package pinning §2.8)
KHÔNG * / latest. Critical pins:
- MediatR
12.4.1(14 fail DI) - Swashbuckle
6.9.0(10 conflict OpenApi 2) - Node engines
>= 20+ CI pin20.x(bài học NamGroup, memoryfeedback_node_cicd) - LibreOffice
25.8.6 - @microsoft/signalr
8.0.7
📅 Recent activity (last 10 FIFO)
- 2026-05-14 (S23 t1, Chunk pre-A PASS): UI polish slot label Designer Mig 31 prep. File
fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsxline 873 replacedQuyền duyệt NV #{ei + 1}→Quyền duyệt {usersList.data?.find(u => u.id === entry.approverUserId)?.fullName ?? 'Chưa chọn NV'}. Pattern: lookup user fullName từusersListquery (Option A — DTOEditLevelEntrykhông cóapproverFullNamefield, chỉLevelDtoBE response cóapproverUserNamenhưng edit state dùngEditLevelEntry).usersListđã in scope ~line 500, pattern lookup.find(x => x.id === e.approverUserId)đã dùng tại line 535 cho validation. Cookie-cutter 1 file fe-admin only (fe-user KHÔNG có Designer per Investigator K0 S1). Tailwind classes preserved (mb-1 text-[10px] font-medium uppercase text-amber-700). Verify:npm run buildfe-admin PASS clean 0 TS error, 0 new warning. Bundle 1395 KB (unchanged trivial). Token ~5k. - 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.changelogsfield 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) Dialogsize="xl"NOT supported — only "sm" | "md" | "lg". Use "lg" cho preview iframe. (4) API auth fieldaccessTokenkhông phảitoken. Scriptseed-test-users-prod.ps1lầ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.
🔄 Curate trigger
- Memory size > 25KB → archive recent entries to
archive/<period>.md - Duplicate entries detected → merge
- Stale > 3 months → remove
Last curate: 2026-05-11 (initial seed)