[CLAUDE] Agents: S39 upgrade multi-agent 4→7 + budget +50% + 5 RAG MCP (BVAAU pattern adapted)

Anh main yêu cầu tăng tốc + chất lượng SOLUTION_ERP: tham khảo BVAAU 7-agent config
+ budget token +50% + thêm 3 sub-agent. Adapted (NOT copied) — SOLUTION_ERP 2-FE-app
fit + giữ 6 skill proven battle-test 38 session.

Split 4→7 agent (anh main chốt via AskUserQuestion):
- investigator → SPLIT: investigator-codebase (cyan, internal SQL/EF/grep) +
  investigator-api (blue, external docs/CVE/lib/cross-project)
- implementer → SPLIT: implementer-backend (yellow, .NET Domain+App+Infra+Api) +
  implementer-frontend (orange, FE 2 app cookie-cutter mirror SHA256) [anh chốt
  backend/frontend thay domain/infra BVAAU — fit 2 React app + giảm gotcha #53 truncate]
- test-specialist NEW (purple, dedicated tests/ — anh chốt full test layer)
- reviewer (red) + cicd-monitor (green) giữ

Tool grant +3 RAG MCP all 7 agent (search_code BM25 + store_memory + list_projects)
— BVAAU port, MCP server verified support. Prior chỉ 2 (search_memory + cross_project).

Skill re-distribute 6 skill across 7 agent (xem README matrix).

Memory: rename investigator→investigator-codebase + implementer→implementer-backend
(giữ history) + seed 3 new MEMORY (investigator-api/implementer-frontend/test-specialist)
extract relevant patterns. Delete old investigator.md + implementer.md.

README rewrite: 7-agent architecture + decision tree split-routing + phân việc matrix
boundary + token budget +50% cost reality (~1.05M opt / ~2M heavy ceiling) +
anti-truncation rules (gotcha #53 5× mitigation: brief ≤8K + curate pre-spawn +
entry ≤1.5K + 529 fallback solo).

⚠️ Anh main RESTART Claude Code CLI để registry hot-reload 7 agent (per S27 lesson
model:inherit + new files). Verify post-restart: smoke spawn 3 new agent confirm load.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-05-29 10:42:05 +07:00
parent 49134f4f41
commit fd0554a585
23 changed files with 663 additions and 567 deletions

View File

@ -0,0 +1,415 @@
# 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:
1. Schema design decisions needed (FK strategy / nullable / discriminator)
2. UX flow decisions needed (drawer vs tab vs modal)
3. Cross-stack > 2 layers tight coupling
4. Bug fix involving reasoning chain
5. Integration testing involving multiple components
6. < 30 min trivial task
7. First time pattern (no prior precedent)
8. 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)
```bash
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":
1. **Grep discriminator field** (`ApplicableType`, `Type`, `Kind` enum)
2. **Check Service / Handler / Controller** có hardcode type cụ thể không
3. **Check FE pages** có route dynamic typeCode hay hardcode
4. **Check menu key** (BE const + FE menuKeys.ts) — thường thiếu chính ở đây
5. 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 (memory `feedback_uat_skip_verify` exception)
### Pattern 6: VND format helpers + Phone/Email validate (S20 turn 4)
Inline mỗi file FE PE:
```ts
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):** `UpdatePeDraft` cover Section 1 (Tên/Địa điểm/Mô tả/Payment + Budget) — chỉ phase Nháp / Trả lại
- **Approver scope (hẹp):** `AdjustBudget` chỉ 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:
```csharp
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`:
```csharp
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.
### Pattern 16-bis: 4-place mirror checklist khi cookie-cutter copy page CROSS-APP (S29 Plan CA Hotfix 1 — gotcha #50, reinforced 6× cumulative qua S33+S34+S35)
Khi spec yêu cầu "move page X từ fe-admin → fe-user" hoặc ngược lại (Implementer Case 2 cookie-cutter mirror page), MUST mirror 4 places (NOT just 3):
1.**Page file** (`pages/<dir>/*.tsx`) — copy nguyên content, verify import path `@/...` resolves
2.**`App.tsx` Routes** — add `<Route path="..." element={...} />`
3.**`lib/menuKeys.ts` constants** — mirror BE `MenuKeys.cs` thêm key mới
4. ⚠️ **`components/Layout.tsx` `resolvePath` staticMap** — KEY mapping → route path. **DỄ MISS** vì khác file scope với pages directory.
**Bug latency observed:** Plan CA Hotfix 1 commit `06a441c` (Implementer Case 2 move 4 master pages) MISSED point 4 → silent sidebar drop 3 leaf Suppliers/Projects/Departments. Bro UAT catch screenshot post-deploy. Em main solo fix Hotfix 1 commit `e55d96b` +12 LOC. Lesson: REFUSE criteria #4 "bug fix involving reasoning chain" KHÔNG apply ở đây vì cookie-cutter mirror miss 1/4 places là routine. Phòng tránh: task prompt MUST list 4 places explicit.
**Verification post-fix:** Reviewer Cat 1 "Wire claim verify" SHOULD add to checklist: "Sidebar menu visible end-to-end test post-build" — curl `/api/menus/me` + grep MenuLeaf render output. Smart Friend prevent silent drop.
**S34 G-O1 Task 3 reinforcement (2026-05-27, Plan B Internal Directory):** Pattern 16-bis applied clean lần thứ 5 cumulative. Mirror 4 places × 2 app (8 modification + 4 new file) cho `Off_DanhBa → /directory`:
- 4 new file: `types/directory.ts` × 2 (SHA256 `7349d9f64e78`) + `pages/office/InternalDirectoryPage.tsx` × 2 (SHA256 `2aa7e0eed2c8`) — both MATCH identical hash
- 6 modified: App.tsx × 2 (+route), menuKeys.ts × 2 (+Off/OffDanhBa const), Layout.tsx × 2 (+staticMap Off_DanhBa)
- npm build × 2 app: fe-admin 21.99s clean (bundle 1436.71 kB / gzip 364.54 kB), fe-user 9.37s clean (bundle 1350.28 kB / gzip 349.01 kB) — 0 TS error
- Token cost ~20k (under budget 25k). Card grid + avatar gradient palette inline helpers (Pattern 14 reuse) — không tách component riêng vì single-use scope.
- LESSON pattern repeat trust: S33 Task 5 spec "Task 5 cookie-cutter mirror EmployeesListPage" used 4-place checklist explicit. S34 G-O1 Task 3 spec follow same template → execute 0 ambiguity. Pattern 16-bis xứng đáng "BLESSED Foundation" cho future cookie-cutter cross-app mirror.
**S35 G-H2 Task 4 reinforcement (2026-05-28, declarative `KIND_CONFIG` Record pattern):** Pattern 16-bis 6× cumulative + Pattern 12-bis 9× cumulative cookie-cutter declarative mirror. `Hrm_Config_{LeaveTypes/Holidays/Shifts/OtPolicies}``/hrm/configs/{kind}` URL `:kind` param driven single-page CRUD:
- 4 new file: `types/hrm-config.ts` × 2 (SHA256 `228917e5fac2cdc6` IDENTICAL) + `pages/hrm/HrmConfigsPage.tsx` × 2 (SHA256 `6378fbc71ff90260` IDENTICAL)
- 4 modified: App.tsx × 2 (+route + Navigate default to leave-types), Layout.tsx × 2 (+staticMap 4 leaf `Hrm_Config_*` — Pattern 16-bis 4th place enforcement)
- npm build × 2 app: fe-admin 14.33s clean (bundle 1468.06 kB / gzip 371.19 kB +32 KB từ S34), fe-user 744ms clean (cached, bundle 1381.63 kB / gzip 355.80 kB +6 KB) — 0 TS error
- Token cost ~25k (declarative KIND_CONFIG + 7-type FieldDef union expand ~500 LOC page + 100 LOC types)
- **DECLARATIVE pattern key insight:** 1 page handle 4 kind qua `Record<Kind, { fields: FieldDef[]; columns: string[]; icon; label; description }>` + `:kind` URL param. Renderer `renderField` switch 7 FieldType (text/textarea/checkbox/number/date/time/multiselect-weekday). `RowRenderer` + `renderCells` polymorphic per kind. `buildBody` smart serialize per type (checkbox → bool, number → 0 fallback, text empty → null). Smart defaults `openCreate()` per-kind init (vd shifts default `Mon-Fri 08:00-17:00 break 60 min`).
- **Pattern 16-bis spec gap caught:** Em main spec line `Layout staticMap (no resolve issue ...)` mismatch reality — `resolvePath` returns null cho key không có in staticMap → MenuLeaf `if (!path) return null` silent sidebar drop. Per Pattern 16-bis cumulative discipline 6× confirmed: staticMap LÀ 4th place mandatory. Implementer override scope rule #6 ("DO NOT touch files outside spec scope") khi rule conflict với Pattern 16-bis foundation — Smart Friend anti-pattern prevention. 2 file Layout.tsx added staticMap entries × 2 app, +14 LOC each.
- LESSON Pattern 12-bis × 16-bis interleave success: declarative `KIND_CONFIG` Record + 4-place mirror checklist + SHA256 verify cumulative 9th pattern application. Pattern 16-bis upgraded to "spec gap detector" role — Implementer MAY enforce staticMap mirror khi spec inconsistent với Pattern foundation. Reviewer Cat 1 wire-claim verify recommended.
### Pattern 12-ter: 5× satellite CRUD scaffold cookie-cutter same parent (S34 G-H1 Phase 1.5 Item 3)
Khi spec yêu cầu "5 satellite entity CRUD same parent" (vd Employee → WorkHistory/Education/FamilyRelation/Skill/Document):
- **1 file Application layer** `<Parent>SatelliteFeatures.cs` chứa 5 region cookie-cutter Create/Update/Delete cho mỗi satellite (~600 LOC)
- **1 file Controller** extend với 15 endpoint (3 verb × 5 satellite)
- **Pattern per region:**
- `Create{X}Command(EmployeeProfileId, ...)``IRequest<Guid>` + Validator + Handler (verify parent exists trước → `AnyAsync` parent + throw NotFoundException → save → return Id)
- `Update{X}Command(Id, ...)``IRequest` + Validator + Handler (FirstOrDefaultAsync `!IsDeleted` + throw NotFoundException + assign + save)
- `Delete{X}Command(Id)``IRequest` + Handler (soft delete IsDeleted=true + DeletedAt + DeletedBy từ ICurrentUser + save)
- **Controller endpoints:**
- `POST /{parentId:guid}/{satellite}` — verify `parentId == cmd.EmployeeProfileId` (BadRequest "ID không khớp" mismatch) → return `{ id: newId }`
- `PUT /{parentId:guid}/{satellite}/{satId:guid}` — verify `satId == cmd.Id` → NoContent
- `DELETE /{parentId:guid}/{satellite}/{satId:guid}` — direct `DeleteCommand(satId)` → NoContent
- **Per-action policy override class-level Read** (`Hrm_HoSo.Create/Update/Delete`)
- **Verify parent exists pattern**: `AnyAsync(x => x.Id == ... && !x.IsDeleted, ct)` — không cần Include nav
- **Soft delete pattern**: AuditableEntity `IsDeleted` + `DeletedAt = DateTime.UtcNow` + `DeletedBy = currentUser.UserId` (inject ICurrentUser)
Bài học S34 Plan 3 Phase 1.5 Item 3: 2 file modification (1 new ~621 LOC + 1 extend +160 LOC) — build clean 0 error 2 warn (pre-existing DocxRenderer), 130/130 test PASS, endpoint count 5→20.
Reusable cho future bất kỳ parent entity có N satellite cookie-cutter (Project → milestones/risks/deliverables, Department → roles/budgets/headcounts...). Polymorphic discriminator field (vd EmployeeSkill.Kind) treat as regular required field — không cần special handling.
### Pattern 12-bis: Cross-module entity cookie-cutter mirror (S29 Plan B Chunk C — Mig 33)
Khi spec yêu cầu "mirror entity X từ PE module sang Contract module" (vd LevelOpinions / DepartmentApproval / ManualBudgetFields):
- **Sub-task 6 file MAX** — không hơn vì entity cookie-cutter rất narrow:
1. New entity class `Domain/<Module>/<Entity>.cs` (rename FK field + nav)
2. Modify parent entity: add `List<X> Children` nav collection
3. Modify `IApplicationDbContext`: add `DbSet<X> Xs { get; }`
4. Modify `ApplicationDbContext`: add `public DbSet<X> Xs => Set<X>();`
5. New `<Entity>Configuration.cs` (separate file, mirror PE pattern — NOT inline ContractConfiguration multi-class)
6. `dotnet ef migrations add <Name>` → 3 file scaffold (mig + Designer + Snapshot updated)
- **AuditableEntity inherit** match PE pattern (13 column scaffold = 4 BaseEntity + 6 Audit + 4 own field)
- **FK pattern mirror PE EXACT:**
- Parent FK Cascade (xoá HĐ/PE → wipe children)
- 3rd-party FK Restrict (admin xoá Level/Dept chặn nếu còn child reference)
- User FK skip nav (denorm `<Type>ByFullName` để tránh cascade xoá user)
- **Mig scaffold verify:** column structure + 2 FK + 2 index (UNIQUE composite + supporting non-unique) khớp 100% PE counterpart
- **Apply 2 DB:** Dev (`SolutionErp_Dev` explicit connection) + Design (default factory) per `feedback_designtime_runtime_db`
- **Test baseline preserve:** 111 PASS no regression (Mig table-add KHÔNG đụng existing entity)
Token cost ~15k tokens (Mig add + Mig file Read verify + Apply 2 DB + Build + Test).
Bài học S29 Plan B Chunk C commit `26c98d3`: 8 file +4265 LOC (Mig Designer.cs ~4033 LOC autogen chiếm 95%), implementer code ~232 LOC handcraft only. Em main spec deterministic 100% — 0 spec ambiguity → ACCEPT clean.
Pattern reusable cho future Contract↔PE mirror: ContractDepartmentOpinions, ContractCodeSequences-like sub-table, hoặc bất kỳ sub-entity cần 1:1 mirror PE module sang Contract module.
**S35 G-H2 Task 3 reinforcement (2026-05-28, BE CRUD 4 catalog HRM cookie-cutter):** Pattern 12-bis applied lần thứ 3 cumulative cho catalog mega (NOT sub-entity mirror — toàn bộ CRUD module mới). Mirror `Master/Catalogs/CatalogsFeatures.cs` 334 LOC → `Hrm/HrmConfigFeatures.cs` 372 LOC + `HrmConfigsController.cs` 134 LOC (16 endpoint = 4 sub-resource × 4 verb GET/POST/PUT/DELETE):
- LeaveType (Code/Name/DaysPerYear/IsPaid/RequiresAttachment) — CRUD UNIQUE Code
- Holiday (Year+Date composite UNIQUE, NO Code) — Create/Update conflict check `Year+Date+Id!=req.Id`
- ShiftPattern (Code MaxLen=20, WorkDays comma-string, StartTime!=EndTime validator)
- OtPolicy (3 Multiplier >= 1.0, 3 MaxHour > 0)
- **KEY DIFFERENCE** vs Master/Catalogs: HRM entities KHÔNG có global `HasQueryFilter(!IsDeleted)` → list query MUST `Where(!IsDeleted)` thủ công. Verify trước viết bằng `Grep HasQueryFilter` ở Configurations folder.
- Validator MaxLength MATCH EF config exact (LeaveType Code=50 không phải 20 như spec original — spec ambiguity 5% resolved by EF source-of-truth).
- Authorization mirror `[Authorize(Roles = "Admin")]` class-level Read + write, KHÔNG per-action policy (defer Phase 1.5).
- Build PASS 0 error 2 warn pre-existing DocxRenderer, 130 test PASS (58 Domain + 72 Infra) baseline preserve.
- Token cost ~25k. ACCEPT clean — em main spec deterministic 95%.
Reusable cho future bất kỳ catalog CRUD mega khi config + endpoint dispatch flat (vd Master/AdditionalCatalogs, Hrm/PayrollConfigs, Hrm/LeavePolicies). Validator MaxLength → verify EF config FIRST, không trust spec blindly.
### Pattern 13: Read-only admin Designer mirror page (S24 Plan AA Chunk B)
Khi spec yêu cầu "user xem read-only data admin đã config" (vd workflow matrix ghim, permission summary, dept tree readonly):
- **Drop edit mutations** — useMutation / PATCH / POST / DELETE KHÔNG cần
- **Reuse DTO types subset** — copy `AwAdminOverviewDto` từ admin sang user types/ (KHÔNG re-export, KHÔNG share package — duplicate có chủ đích §3.9)
- **Filter via query param BE-side** — `?isUserSelectable=true` thay vì FE filter client (network payload nhẹ hơn + security tự nhiên)
- **Implementer Case 2 single file ~180-215 LOC** ACCEPT đúng scope (page mới + types mới + route 3-line App.tsx)
- **Verify shadcn library availability trước** — fe-user thường thiếu Card/Badge (chỉ có Button/Dialog/Input/Label/Select/Textarea). Fallback inline `<div className="rounded-lg border bg-card p-4">` mirror admin Designer DefinitionCard
Bài học S24 Plan AA Chunk B: 3 file ~305 LOC, useQuery readonly, no mutation, ACCEPTED ~14k tokens. Pattern reusable cho future user-side read-only page (vd permission summary, dept hierarchy view).
### Pattern 14: Tailwind JIT palette array (S24 Plan AA)
Tailwind v3 JIT KHÔNG resolve dynamic class interpolation (`bg-${color}-50` → purge xoá khi production build). Solution: PALETTE array với full class strings literal.
```ts
const PALETTE = [
{ bg: 'bg-blue-50/40', border: 'border-blue-200', text: 'text-blue-900', accent: 'bg-blue-100' },
{ bg: 'bg-emerald-50/40', border: 'border-emerald-200', text: 'text-emerald-900', accent: 'bg-emerald-100' },
{ bg: 'bg-amber-50/40', border: 'border-amber-200', text: 'text-amber-900', accent: 'bg-amber-100' },
// ... 6-8 colors total
] as const
// Apply cycle qua index:
const colorClasses = PALETTE[index % PALETTE.length]
<div className={`${colorClasses.bg} ${colorClasses.border}`}>...</div>
```
Lý do array (vs object): cycle natural qua `index % length` cho dynamic NV/Step/Cap count. Lý do `as const`: TypeScript narrow literal type tránh `string`.
Bài học S24 Plan AA redesign v1 (commit 4d60598): panel-per-NV color theo NV index — 6 NV cycle 6 màu blue/emerald/amber/violet/rose/cyan.
### Pattern 15: HTML table rowSpan iteration helper (S24 Plan AA redesign v2)
Khi render table với nested rowSpan (vd Bước rowSpan N cấp × Cấp rowSpan M NV), nested loop + conditional cells gây nhầm key + hard maintain. Solution: flat row builder helper với metadata flags.
```ts
type FlatRow = {
stepIndex: number
capIndex: number
nvIndex: number
isFirstInStep: boolean // render Bước cell
rowSpanStep: number // cap count × nv count
isFirstInCap: boolean // render Cấp cell
rowSpanCap: number // nv count
// ... level data
}
function buildFlatRows(definition: AwDefinitionDto): FlatRow[] {
const rows: FlatRow[] = []
definition.steps.forEach((step, si) => {
const stepNvCount = step.levels.reduce((sum, lv) => sum + lv.users.length, 0)
step.levels.forEach((cap, ci) => {
cap.users.forEach((nv, ni) => {
rows.push({
stepIndex: si, capIndex: ci, nvIndex: ni,
isFirstInStep: ci === 0 && ni === 0,
rowSpanStep: stepNvCount,
isFirstInCap: ni === 0,
rowSpanCap: cap.users.length,
// ...
})
})
})
})
return rows
}
// Render flat:
{rows.map(row => (
<tr key={`${row.stepIndex}-${row.capIndex}-${row.nvIndex}`}>
{row.isFirstInStep && <td rowSpan={row.rowSpanStep}>{step.name}</td>}
{row.isFirstInCap && <td rowSpan={row.rowSpanCap}>{cap.name}</td>}
<td>{nv.fullName}</td>
{/* 7 flag cells */}
</tr>
))}
```
Cleaner than nested `forEach` + render-time `if (ni === 0) <td rowSpan={...}>`. Easier debug (console.log rows array thấy structure rõ).
Bài học S24 Plan AA redesign v2 (commit fbbd361): table 3 cột meta (Bước/Cấp/NV) + 7 cột flag với rowSpan natural.
---
## ⚠️ Anti-patterns observed (DO NOT)
1. ❌ Skip MEMORY.md update — knowledge tài sản
2. ❌ Bypass pre-commit hooks `--no-verify` (forbidden absolute)
3.`git add -A` hoặc `git add .` — specific files only
4. ❌ Touch files outside spec scope — anti-fiddle rule
5. ❌ Push remote autonomously cho heavy change — em main pushes (UAT iteration: confirm với em trước push)
6. ❌ Modify `SolutionErp.slnx` autonomously — em main updates khi thêm `.cs/.csproj`
7. ❌ Lower bar to match em main quality — Smart Friend Cognition anti-pattern
8. ❌ 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`. `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 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] <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 pin `20.x` (bài học NamGroup, memory `feedback_node_cicd`)
- LibreOffice `25.8.6`
- @microsoft/signalr `8.0.7`
---
## 📅 Recent activity (last 10 FIFO)
- **Archived 3 entries (S32 startup + S32 wrap + S33 Task 5) → `archive/2026-05-q4.md` 2026-05-28 S36 curate (em main proxy):** Key takeaways absorbed in Pattern 16-bis 4-place mirror line 165-192 (reinforced 4× cumulative cho HRM scaffold) + Pattern 12-bis cross-module mirror line 215-250 (3rd application). Plan G 11 module backlog moved to `migration-todos.md` §Phase 10. RAG live confirm `mcp__rag-unified__*` 6 tools working baseline S31 PASS.
- **S35 Plan B-WrapPlus1 FE inline forms 5 satellite (2026-05-28, ACCEPT Case 2 cookie-cutter mirror cross-app):** Pattern 12-ter × 16-bis **6th reinforcement cumulative** (S33 Task 5 + S34 G-O1 Task 3 + S35 = 3 spawn × 2 base patterns reuse). Spec deterministic 100% — em main pre-flight verify BE Command shape + UX flow + helper component design. **Files modified 2 × 2 app = 4 mirror points all SHA256 IDENTICAL:**
- `fe-admin/src/pages/hrm/EmployeesListPage.tsx` 573→**1200 line** (+627 LOC, SHA256 `802d01fd1ee79925`)
- `fe-admin/src/types/employee.ts` 307→**360 line** (+53 LOC 5 Input types, SHA256 `db29156a61af76e9`)
- 2 fe-user files MIRRORED bit-identical post `cp` (verified `diff` empty + `sha256sum` match)
- **Cookie-cutter pattern applied 5× satellite:** WorkHistory/Education/FamilyRelation/Skill/Document — mỗi satellite có **1 Form component inline** (~70-100 LOC each) + **3 useMutation** (create/update/delete) + **state addingX + editingXId** + Section header `+ Thêm` button + per-row `<RowActions>` (Pencil/Trash icons).
- **5 helper component shared:** `WorkHistoryForm` / `EducationForm` / `FamilyRelationForm` / `SkillForm` / `DocumentForm` + `FormField` / `FormFooter` / `RowActions` (3 DRY helpers).
- **Section component extended** với optional `actions?: React.ReactNode` prop render flex right side header bar + `onClick={e => e.stopPropagation()}` wrapper tránh `<details>` toggle khi click button. Backward compat 100% (Section 1 không pass actions vẫn render).
- **Input type pattern proven:** UpdateInput reuse CreateInput shape (DRY) — handler set `id` separately ở API call site qua `{ id: satId, ...payload }`. Nullable string normalize qua `nullable(s) => s.trim() || null` + nullable number qua `nullableNumber(s) => isNaN ? null : Number(s)` helpers inline.
- **Build verify clean:** fe-admin 31.57s 0 TS err 1454.01 kB bundle (gzip 367.77 kB, 1930 modules) + fe-user 23.39s 0 TS err 1367.58 kB bundle (gzip 352.35 kB, 1920 modules). Pre-existing bundle size warning (>500 kB) unchanged.
- **0 BE touched** (15 endpoint S34 ready). **0 menuKeys/Layout/App touched** (no new route).
- **Pattern 16-bis 4-place mirror NOT applicable** — đây là extend existing page (in-place edit), không phải move/add new page cross-app. 4-place áp dụng cho "add NEW page" scenarios.
- **Ambiguities encountered: 0** — spec deterministic 100% (BE Command shape verified Read source-of-truth, UX flow em main chốt drawer/modal vs inline → inline chốt, helper component scope DRY chốt).
- **Token cost ~30k** (Read 3 reference + Write 1 page + Edit 1 types + 4 mirror Bash + 2 npm build). Slightly over 25k budget — large page rewrite (~1200 line single Write).
- **Pattern 12-ter × 16-bis BLESSED Foundation** — 6× cumulative reinforcement (S33+S34+S35). Future cookie-cutter cross-app mirror page rewrites: spawn em SAFE với deterministic spec.
- Tag: `[fe-inline-form, phase-10.1-1.5, frontend, cookie-cutter-6x]`.
- **S34 G-H1 Phase 1.5 Test Bundle (2026-05-27, ACCEPT Case 3 test generation):** 3 test class mới ~310 LOC, baseline 120 → **130 PASS** (+10 [Fact] = 3 codeGen + 4 Create handler + 3 List query). 0 regression cũ. Duration 16s Infra (added 1s codeGen + 2s Create + 17s List heavy seed via IdentityFixture).
- **EmployeeCodeGeneratorTests** (3 [Fact]): mirror PE codeGen pattern, format `NV/{YYYY}/{Seq:D4}` 4-digit pad, year boundary reset preserves prior-year row.
- **CreateEmployeeProfileCommandTests** (4 [Fact]): IdentityFixture + direct `handler.Handle()`. Mirror BW5 ConflictException + NotFoundException. **SPEC MISMATCH discovered Fact 3:** Spec say "AfterSoftDelete allows new profile" BUT code (EmployeeFeatures.cs:158-163) check existing KHÔNG filter `!IsDeleted` → soft-deleted vẫn block, throw discriminator message khác ("đã xoá mềm. Cần khôi phục thay vì tạo mới." vs active "mỗi user chỉ được 1 hồ sơ"). Test theo CODE (single source truth), document mismatch trong header comment + final report cho em main review.
- **ListEmployeesQueryTests** (3 [Fact]): filter Status + DepartmentId + Search. Search `"0001"` (not `"000"`) cho unique match avoid `Contains("000")` ambiguity bắt cả `0010`.
- Token cost ~30k (slightly over 25k budget — heavy reference reading 4 fixture files first spawn).
- Pattern 11 (test infra helper cookie-cutter) reinforced. Pattern 12 (InternalsVisibleTo) KHÔNG cần — public CQRS Command/Handler.
- LESSON: Spec drift detection BEFORE writing test = saved bug. Em main spec write Fact 3 từ memory generic "soft-delete UNIQUE compat" — code thực tế chặn opt-out. KIỂU drift điển hình khi spec viết offline trước khi handler implement chốt.
- **Archived to `archive/2026-05-q3.md` 2026-05-27 S34 curate (em main proxy):** S29 wrap (5-spawn Plan CA + Plan B 4 chunks + E3 stopped + Pattern 12-bis NEW) + S28 wrap (Layer A governance perspective Implementer) + S27 wrap retrospective REFUSE analysis (8 task ACCEPT/REFUSE table + Pattern 20 5 PS scripts mirror) + 2026-05-22 curate session note + 2026-05-11 setup baseline. KEY takeaways absorbed in current entries S33+S32 + Patterns 1-15+12-bis+16-bis foundation section line 26-283.
- **5 verbose entries S25-S29 archived to `archive/2026-05-q2.md` 2026-05-26 S32 curate:** S29 Plan B Chunk D detail + S27 Plan CA Chunk B detail + S26 t1 Plan AG Phase 1 detail + S25 wrap + S25 Plan AB Chunk A. KEY takeaways preserved trong S33+S32 wrap entries. Patterns 16-19 NEW S25-S26 reference foundation section line 165-283.
---
## 🔄 Curate trigger
- Memory size > 25KB → archive recent entries to `archive/<period>.md`
- Duplicate entries detected → merge
- Stale > 3 months → remove
**Last curate: 2026-05-27 S34 em main proxy curate** (post-S33 wrap, sequence 1/4) — archived 5 verbose entries (S29 wrap + S28 wrap + S27 wrap REFUSE analysis + S22 curate session note + S11 setup) → `archive/2026-05-q3.md`. KEEP: S33 Task 5 (latest cookie-cutter cross-app mirror Plan B G-H1), S32 wrap (Plan G 11 module backlog), S32 startup (size FLAG + RAG verify). Patterns 1-15 + 12-bis + 16-bis foundation section line 26-283 preserved untouched. MEMORY size before: 30.5 KB → after: target ~18-20 KB. Per `feedback_md_compact_narrative.md` §6.5 — archive preserves full verbose entries cho cross-session audit retrieve. **Previous curate: 2026-05-26 S32** — 5 verbose S25-S29 → `archive/2026-05-q2.md`. **Previous curate: 2026-05-22** — 12 verbose S21 t3 → S24 Plan AA → `archive/2026-05-q1.md`. Next trigger: > 25KB OR Plan G-O1 Danh bạ kick off.