Files
solution-erp/.claude/agent-memory/reviewer/MEMORY.md
pqhuy1987 0c6efdaf4f [CLAUDE] Docs: Session 25 chốt cuối — Plan AB→AF cumulative 7 commits + 4 agent MEMORY flush
S25 wrap final:
- STATUS.md + HANDOFF.md prepend Plan AB→AF cumulative narrative (7 commits cdfd542..506cada + 7 CICD Runs #215-#221)
- gotchas.md +2 NEW entries:
  - #48 Multi-Changelog.Add() SQLite frozen-clock tie-break (Run #215 catch, fix Plan AB Chunk A2)
  - #49 UI dual-phase badge confusion khi state machine self-loop (Plan AD drop + extractNextTargetHint helper)
- Checklist debug bug mới +2 entries (24-25)
- Session log NEW docs/changelog/sessions/2026-05-19-s25-pe-history-visibility.md (~360 LOC)
- 4 agent MEMORY drift sync:
  - investigator/MEMORY.md (30→32KB) FIFO entry S25 wrap + count metadata
  - implementer/MEMORY.md (34→36KB) FIFO entry + patterns 16-18 saved
  - reviewer/MEMORY.md (31→32KB) FIFO entry + lesson SQLite tie-break + UAT skip risk reinforced
  - cicd-monitor/MEMORY.md (~72KB CRITICAL OVER) — 7 Run entries #215-#221 + curate flag MAX

Memory user-level +2 NEW entries (separate commit memory dir, KHÔNG trong this commit):
- feedback_fe_merge_synthetic_audit.md (Plan AC2 pattern)
- feedback_fe_usermap_fallback.md (Plan AF pattern)

Stats final S25:
- 31 mig (no schema) · 59 tables · ~146 endpoints · 35 FE pages
- 111 test unchanged (UAT defer test-after per §7)
- 49 gotcha (+2: #48 + #49)
- 23 memory user-level (+2 NEW S25 patterns)
- 6 skills · 4 sub-agents active
- 7 commits cumulative S25 · 7 CICD Runs (1 FAIL caught + 6 PASS)
- 6× bundle rotate × 2 app (Run #220 BE-only unchanged)

Critical pending S26+:
- Memory curate cicd-monitor PRIORITY MAX (~72KB strongly over hard threshold)
- Plan B Contract V2 wire HIGH priority (5-6 chunk pre-allocated S23 HANDOFF)

Per §6.5 KEEP narrative — KHÔNG cut rationale/gotcha context, chỉ phân tầng prepend latest.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 16:33:41 +07:00

167 lines
32 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Reviewer Agent — Persistent Memory
> **Persistent diary cross-session.** Auto-injected first 200 lines / 25KB at spawn.
> Update BEFORE every stop. Curate when > 25KB.
---
## 🎯 Role baseline
Adversarial pre-commit reviewer for SOLUTION_ERP. Read-only verification + live curl on prod UAT environment (`*.solutions.com.vn`). Tools: Read, Grep, Glob, Bash (curl + git diff + sqlcmd read). Output: PASS/FAIL verdict + concrete issues file:line.
---
## 🚨 Recurring SOLUTION_ERP bug patterns (catch with priority)
### Gotcha #44 — Silent 403 class-level Authorize quá strict (S18 lesson)
- Symptom: Drafter dropdown V2 workflow empty silent (no error toast)
- Root: `[Authorize(Policy = "Workflows.Read")]` class-level → non-admin 403, TanStack Query catch silent → UI empty
- Verify: grep `\[Authorize\(Policy = .*\)\]` class-level vs action-level + curl với non-admin token expect 200
- Fix pattern: class-level `[Authorize]` only (any authenticated). POST/PUT/DELETE giữ `[Authorize(Policy = "X.Create")]` admin-only
### Gotcha #43 — Step.Order ≠ index 0-based
- Symptom: EF query `Where(s => s.Order == i)` returns wrong row
- Verify: grep `step.Order` arithmetic — array index 0-based vs Order field 1-based
- Fix pattern: precompute candidates EF query → in-memory `OrderBy(s => s.Order).ToList()` → array index access
### Gotcha #42 — Dual schema workflow V1 vs V2 — Service phải branch
- Symptom: PE submit failed do Service không biết V1 hay V2 schema
- Verify: grep `evaluation.ApprovalWorkflowId is Guid awId` — phải branch theo pin field
- Fix pattern: `if (evaluation.ApprovalWorkflowId is Guid awId) ApproveV2Async(...) else ApproveV1LegacyAsync(...)`
### Wire BE claim recurring bug pattern
- Symptom: claim wire CRUD nhưng grep diff finds `// Mock` / `alert(...)` / no POST/PUT/DELETE call
- Verify: grep diff mock markers + live curl POST/PUT/DELETE expect 2XX
- Severity: CRITICAL — block commit
### Gotcha #17 — EF migration 3-file rule
- 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)
### Category 1: Wire BE / feature claim verify
- Grep mock markers in diff (`// Mock`, `alert(`, `setEditing(null) // close UI`, `TODO.*wire`)
- Grep actual API call: `await api\.(post|put|delete|patch)\(` trong FE diff
- Live curl POST/PUT/DELETE/PATCH if deploy claim (`https://api.solutions.com.vn/...`)
- Status code matrix expected vs actual
### Category 2: Schema integrity (44 active gotchas)
- Reference `docs/gotchas.md` + skill `dependency-audit-erp`
- Check 3-file rule Mig
- Check column types vs entity definition (Mig 27 lesson: `IsVisible bit NOT NULL DEFAULT 1` + `DisplayLabel nvarchar(200) NULL`)
### Category 3: Security
- `[Authorize]` class-level on ALL new controllers
- Per-action `[Authorize(Policy = "...")]` cho admin-scoped (gotcha #44 lesson)
- Permission guard wrap new admin pages (FE)
- Route permission map populate (`menuKeys.ts` mirror BE `MenuKeys.cs` + `All[]`)
- Input validation FluentValidation Validator class
- SQL parameterized (EF Core default OK) + XSS escape
### Category 4: Code quality
- `dotnet build SolutionErp.slnx` clean 0 err
- `npm run build` × fe-admin + fe-user clean (TS6 strict)
- Tests baseline 81 PASS (Phase 9 UAT exception OK)
- No `--no-verify` bypass (forbidden absolute)
- Anti-fiddle audit (scope drift > 20% LOC outside spec = FAIL)
- Mirror 2 FE app khi feature FE (rule §3.9)
### Category 5: Test coverage
- New helper static → unit test (xUnit)
- New Repository method → repo test
- New endpoint API → integration test (WebApplicationFactory)
- Bug recurring → regression test TDD-style (test BEFORE fix)
- **Phase 9 UAT exception:** test-after default OK theo memory `feedback_uat_skip_verify`
- Test count baseline 81 → tăng khi feature added theo §7
---
## ⚠️ Anti-patterns observed (DO NOT)
1. ❌ Recommend code edits — only describe issue + acceptance criteria
2. ❌ Skip live curl verify if deploy claim — recurring risk
3. ❌ Accept "wire" claim without grep proof
4. ❌ Defer to em main authority — escalate disagreement explicitly
5. ❌ Skip MEMORY.md update với anti-patterns observed
6. ❌ Lower bar to match em main quality — Smart Friend anti-pattern Cognition
---
## 🛡️ Smart Friend anti-pattern guard
Per Cognition documented research:
- NEVER lower bar to match em main's apparent quality
- If em main code fine → say PASS
- If em main code has issues → FAIL with specifics regardless social pressure
- "Quality ceiling was set by the primary, not the escalation." — Your value = raise quality through catch
---
## 🧠 SOLUTION_ERP review essentials
- **Tests baseline:** 111/111 PASS (S25 unchanged from S23 t3 +7 Plan M edge case tests; pre-Plan AB Chunk A2 SQLite tie-break regression Run #215 caught by CICD test gate → em main fix EntityType + Summary discriminator filter restored 111 PASS). Must increase nếu feature added per §7; UAT iteration exception per memory `feedback_uat_skip_verify`
- **Gotchas:** 47 active (`docs/gotchas.md` reference) — +#48 Multi-Changelog.Add() trong same SaveChangesAsync transaction → SQLite test frozen clock CreatedAt tie-break non-deterministic (S25 pending docs add). +#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:** 31 latest `AddPeLevelOpinionsForV2` (S19 — Mig 31 Plan K1 swap F2 Drafter→Approver scope per-Level slot S23 t1). Mig 30 `AddAllowApproverEditBudgetToLevels` (S22+5 per-slot F4 flag admin opt-in 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`
---
## 🔑 Critical pin verify (gotcha #1-4)
- MediatR `12.4.1` (14 fail DI)
- Swashbuckle `6.9.0` (10 conflict OpenApi 2)
- Microsoft.OpenApi `1.x` (2 breaking)
- Node engines `>= 20` + CI `20.x` (Node latest fail Windows IIS)
Flag commit nếu thấy `<PackageReference Include="MediatR" Version="14...` hoặc tương tự.
---
## 📅 Recent activity (last 10 FIFO)
- **2026-05-19 (S25 wrap — Plan AB pre-commit verify + 6 follow-up plans em main solo self-review + 1 lesson catched by CICD):** Plan AB Chunk A pre-commit verify spawn 1× ~22K with 5-category checklist + 8 adversarial deep checks PASS 0 blocker (1 minor V1 legacy fallback acceptable). Recommended PROCEED push cdfd542. **MISSED gotcha #48**: Multi-Changelog.Add() trong same SaveChangesAsync transaction → SQLite test frozen clock CreatedAt tie-break non-deterministic → Plan M existing tests `.OrderByDescending(CreatedAt).FirstAsync()` picked wrong entry → CI Run #215 FAIL. **Lesson reinforced**: UAT mode `feedback_uat_skip_verify` skip `dotnet test` per chunk risk recurring khi BE refactor > 100 LOC + signature change. Em main resumed local `dotnet test` post Plan AB Chunk A2 fix — caught by CICD test gate (no prod impact). **Em main solo self-review** Plan AC-AF (5 plans em main solo, Reviewer KHÔNG re-spawn — em main verify build+test+npm × 2 app mỗi chunk + CICD post-deploy verify thay vai pre-commit). Cumulative S25: 0 prod regression, 6 commits PASS CICD. **Pattern caught: SQLite frozen clock multi-row tie-break** — tests querying audit table cần discriminator beyond timestamp (EntityType + Summary keyword). Cross-ref future Contract V2 test setup. **Smart Friend guard still active** for future spawn — Reviewer should ADD test filter discriminator check vào Category 5 checklist post-S25.
- **2026-05-19 (S25 Plan AB Chunk A pre-push verify, spawn):** Adversarial verify commit `cdfd542` Plan AB Chunk A — fix Changelog visibility 2 bug PE ID `3248f2f9` (Budget Adjust không hiện history + Trả lại Người chỉ định không log). Diff scope: 3 files +146/-95 LOC (BE PurchaseEvaluationWorkflowService.cs Service-only refactor + 2 FE PeDetailTabs.tsx mirror §3.9). **Verdict: PASS — 0 critical / 0 major / 1 minor observation — proceed push OK.** Wire BE: line 226-236 V1 legacy fallback return early UNCHANGED (giữ S17 behavior, KHÔNG log new Changelog — V1 phiếu read-only history per Mig 22-24 spec, acceptable). V2 ApplyReturnModeAsync refactor Drafter early return → if/else common path: Drafter mode line 285-292 set Phase=TraLai + clear pointer + SLA=null SEMANTIC IDENTICAL pre-refactor (only difference: removed `return` so fallthrough log block). OneLevel/OneStep/Assignee unchanged logic line 302-378 + SLA=AddDays(7) reset line 381. New `db.PurchaseEvaluationChangelogs.Add()` line 403-412 EntityType=Workflow(5) + Action=Update(2) + PhaseAtChange=evaluation.Phase (TraLai cho Drafter, ChoDuyet cho 3 mode khác) + UserId=actorUserId + UserName resolved via userManager.FindByIdAsync (mirror LogTransitionAsync line 700-704 pattern) + Summary `"Trả lại ({modeName}): {summary}"`. KHÔNG SaveChangesAsync mới — TransitionAsync caller line 101 downstream save persists. Wire FE 2 file `HistoryTab` filter extend 3 rule: ADD `summary.includes('Trả lại')` Workflow entity + ADD `entityType === Header(1) && summary.toLowerCase().includes('ngân sách')` cover Budget Adjust + empty placeholder text update Vietnamese chuẩn "Chưa có lịch sử trả lại / điều chỉnh ngân sách / gửi duyệt lại." Schema integrity: 0 migration mới (verify `git diff --name-only | grep Migrations/` empty); reuse PurchaseEvaluationChangelog Mig 12 + S21 t4 Mig 28 schema; PurchaseEvaluationEntityType enum line 25-33 + ChangelogAction enum ContractChangelog.cs:38-44 match BE values FE constant. Security: actorUserId param đã có từ caller TransitionAsync — non-admin user log đúng UserId không escalate permission; EF Core parameterized SQL default; FE substring filter trên BE-trusted Summary string không XSS risk. Code quality: dotnet build PASS 0 err 2 warn (DocxRenderer pre-existing); npm build fe-user PASS 394ms 0 TS err; npm build fe-admin PASS 421ms 0 TS err; anti-fiddle scope drift 0%; mirror 2 FE app §3.9 verify exact identical 17 lines (admin line 2033-2049 mirror user 2027-2043, chỉ shift 6 LOC do prior component lines khác). Test coverage: Phase 9 UAT exception ACCEPT per memory `feedback_uat_skip_verify` — test-after bundle defer (Bug fix Service refactor ideally test-before nhưng UAT mode skip OK). Cumulative state unchanged: 111 test baseline preserve. Adversarial deep checks all PASS: (1) **Drafter mode refactor regression** — line 285-292 set Phase=TraLai + clear pointer + SLA=null PRESERVED (S17 backward compat). V1 legacy 226-236 fallback return early no semantic change. (2) **Drafter summary redundancy**`$"Trả lại (Người soạn thảo): Trả về Người soạn thảo"` redundancy acceptable (audit clarity > terseness; consistent format 4 mode). (3) **actorName resolve N+1** — single `userManager.FindByIdAsync` per ApplyReturnModeAsync invocation, mirror exact LogTransitionAsync line 700-704 pattern; KHÔNG N+1 (single call per transition). (4) **FE filter substring case**`.includes('Trả lại')` exact match BE format `$"Trả lại ({modeName}): ..."` — case-sensitive Vietnamese diacritics lowercase 'l' match. (5) **FE placeholder Vietnamese** — "Chưa có lịch sử trả lại / điều chỉnh ngân sách / gửi duyệt lại." chuẩn Vietnamese diacritics. (6) **Comment update 5 bullet rule** — clear narrative, KHÔNG noise, Plan AB S25 marker rõ. (7) **Gotcha #45 regression** — Plan AB Service-only refactor KHÔNG đụng PE button TraLai click logic (gotcha #45 đã fix S21 t3 BE guard `decision != Reject throw line 64-72` defense), no regression. (8) **Anti-fiddle scope** — 3 file đúng spec, KHÔNG out-of-scope (no agent-memory commit, no docs/changelog touch). **Minor observation (non-block, design choice):** V1 legacy phiếu (line 226-236 fallback) return early TRƯỚC khi reach new Changelog.Add() block → V1 phiếu KHÔNG có new detailed "Trả lại (X): ..." log. FE filter `phaseAtChange === TraLai(98)` line 2042 vẫn catch V1 generic LogTransitionAsync log (V1 fallback set Phase=TraLai) → V1 history vẫn hiện (1 generic row "Chuyển phase X → TraLai" thay vì detailed mode breakdown). Acceptable per Mig 22-24 spec V1 = read-only history, không cần backfill audit detail. Recommendation: PASS proceed push remote. Smart Friend guard active.
- **2026-05-15 (S24 Plan AA post-wrap cumulative finalize, no re-spawn):** Post-Chunk C learning append — em main iterate **4 polish chunks back-to-back** (`da218f1` px-2 hotfix + `4d60598` redesign v1 panel-per-NV color + `fbbd361` redesign v2 table layout rowSpan + `ee0902a` wrap fix sidebar label) UAT visual feedback bro, cumulative ~1.5h, KHÔNG Reviewer spawn. **Anti-pattern observed S24 polish chunks**: UI/UX iteration thuần CSS/layout KHÔNG cần Reviewer spawn mỗi chunk — cost overhead too high (~25K spawn × 4 = ~100K cumulative). Pattern reusable: chỉ self-verify build pass + bro visual confirm post-deploy là đủ. **Smart Friend guard validation S24**: Plan AA cumulative ~620 LOC qua 7 commits (`a1a910f..ee0902a`) — Reviewer spawn 1 lần (Chunk C cumulative A+B BE+FE wire) cost ~25K = ROI tốt. Multi-spawn polish chunks 4× sẽ ~100K = ROI thấp. **Pattern saved: BUNDLE cumulative verify cho heavy chunk (wire BE / migration / cross-stack), SKIP cho < 30 min polish (CSS / layout / color)**. **Discovery #3 anomaly re-tested S24**: Plan AA Run #210 sha=ac2c859 push range mixed BE+FE+docs → CICD trigger normal 4/4 wire end-to-end + bundle hash 2 app rotate. KHÔNG reinforce docs-only anomaly hypothesis (S22 #47 gap pending separate). Defer Investigator follow-up. **Low note S24**: BE filter `IsUserSelectable=false` non-admin leak workflow chưa ghim — defer follow-up (severity Low, workflow data non-sensitive, NOTE only — Smart Friend guard didn't escalate to block commit). CICD Monitor Run #210 sha=ac2c859 PASS verify 4/4 wire end-to-end + bundle hash 2 app rotate. Cumulative state unchanged S24: 31 mig, 111 test, 47 gotcha.
- **2026-05-15 (S24 Plan AA cumulative pre-commit verify, spawn):** Adversarial verify Chunk A (BE + Layout, em main solo) + Chunk B (FE WorkflowMatrixViewPage, Implementer Case 2 PASS). Diff scope 8 file modified + 2 file CREATE — ~76 LOC BE + ~270 LOC FE, scope khớp Plan AA. **Verdict: PASS with 0 critical / 0 major / 0 minor issues — proceed commit OK.** Wire BE: Controller line 21-26 `Overview(applicableType, isUserSelectable, ct) → new GetAwAdminOverviewQuery(applicableType, isUserSelectable)` end-to-end. Handler line 114-117 `request.IsUserSelectable is bool ius → query.Where(d => d.IsUserSelectable == ius)` correct conditional. FE WorkflowMatrixViewPage line 29-33 actual `api.get<AwAdminOverviewDto>('/approval-workflows-v2', { params: { applicableType: typeInt, isUserSelectable: true } })` — real call no mock/alert. Route `/purchase-evaluations/workflow-matrix?type=N` wired App.tsx:38 + Layout.tsx resolvePath:81-86. Schema integrity: 0 migration mới (column `IsUserSelectable` from Mig 25), DbInitializer INSERT-OR-UPDATE-Order idempotent verified (MenuItemConfiguration HasKey(Key) → PK unique, ToDictionary safe; existing rows UPDATE Order only nếu mismatch; new rows INSERT). 7 Allow* flag count match BE DTO ↔ FE type. ApplicableType enum {1,2,3} — FE chỉ wire 2 type PE matrix view correct. Security: class-level `[Authorize]` bare line 18 PRESERVED (gotcha #44 protect). GET any authenticated → non-admin Drafter OK access. Non-admin pass `isUserSelectable=false` BE không block → workflow chưa ghim leak risk — Low severity (workflow data non-sensitive, NOTE only). Permission seed 7 role × WfView leaf via `SeedPurchaseEvaluationPermissionDefaultsAsync:1573` added — idempotent. Admin role qua `SeedAdminPermissionsAsync` chỉ cover `MenuKeys.All[]` static root keys (Pe_*_WfView dynamic NOT in All) — but admin uses fe-admin Designer, KHÔNG cần WfView access fe-user. Code quality: BE dotnet build PASS 0 err 2 warn (DocxRenderer pre-existing); FE fe-user npm build claim PASS (Implementer Case 2); FE fe-admin npm build VERIFY tự chạy PASS 1926 modules / 740ms / 0 TS err. Anti-fiddle 0% drift, scope khớp spec. Mirror 2 FE §3.9: Layout.tsx widen `w-72 xl:w-80` cả 2 ✓; remove truncate 3 site fe-user (MenuGroup+MenuLeaf+StaticLeaf) + 2 site fe-admin (MenuGroup+MenuLeaf, no StaticLeaf component) — structural asymmetry acceptable (fe-user only có "Hộp thư" StaticLeaf); WorkflowMatrixViewPage fe-user only correct (admin có Designer riêng). Test coverage: Phase 9 UAT exception accept — test-after default OK. Adversarial deep checks: (1) DbInitializer ToDictionary duplicate key risk → PK constraint trên Key, tree.Add chỉ thêm Pe_DuyetNcc_WfView + Pe_DuyetNccPhuongAn_WfView (2 typeCode distinct) → no in-memory dup. (2) Sidebar widen 1280px responsive verified per task spec (288 sidebar + 992 main fit). (3) Plan U revert clean — 0 remaining truncate class outside Plan AA comments. (4) Mig drift schema: column existed Mig 25, no new mig needed. 2 SaveChangesAsync non-atomic giữa menu seed + label backfill (pre-existing, OUT scope Plan AA). Comment quality: Plan AA marker rõ ràng 8+ sites, no TODO/FIXME zombie. TypeScript: `FlagCell` indexed-access type clever từ Pick 7 keys union — TS happy compile clean. Recommendations defer: (a) enforce admin/non-admin policy filter (current GET cho phép user pass isUserSelectable=false — low risk, audit follow-up); (b) MenuKeys.All[] không cover dynamic Pe_*_WfView nên Admin role không auto grant — OK vì admin dùng fe-admin Designer, NOTE để future audit khi admin cần inspect user-side matrix view. Cumulative state unchanged: 31 mig, 111 test, 47 gotcha. Smart Friend guard active — KHÔNG lower bar (3 minor risk noted defer follow-up, không block commit).
- **2026-05-15 (S23 t4-t11 cumulative em main self-review — Plan N+O+P+Q+R+S+T+U, no Reviewer spawn):** 8 plan consecutive em main self-review per UAT mode (`feedback_uat_skip_verify` rule — em main verify build + test + npm build mỗi chunk). Key validations cumulative: **Plan N** GetPe handler `PurchaseEvaluationFeatures.cs:765` per-NV `ApproverUserId` discriminator + admin fallback row đầu. **Plan O** 4 lookup sites cascade fix (`EnsureCanRejectV2Async:201` + `ApplyReturnModeAsync:248` + `EnsureEditableForDetailsAsync:72` + `AdjustBudgetCommandHandler:311`) — pattern uniform Plan N, +3 regression test `PurchaseEvaluationPerNvLookupRegressionTests.cs`. **Plan P** Controller `TransitionPeBody:267` record +3 fields mirror `TransitionPurchaseEvaluationCommand` schema — root cause 2 ngày prod bug F1+F2 wire fail. **Plan Q** FE banner mx-5 inset gap fix (CSS layout polish mirror 2 app). **Plan R+S+T5** destructive sqlcmd cleanup prod ~720 rows wiped cumulative + Plan F precedent avoid được (V1 active workflow giữ nguyên → BE healthy startup). **Plan T** DbInitializer `DemoSeed:Disabled` flag config — disable 5 demo seed methods (Workflow V1 + PE V1 + Demo Contracts + Demo PE + Sample V2). **Plan U** FE sidebar truncate + tooltip 2 app mirror — handle long DisplayLabel (Mig 27 admin custom). Anti-patterns observed: (1) **Plan N point 9 chỉ catch 1/5 sites** — em main + Investigator spawn miss 4 sites khác → Plan O cascade fix. Lesson: grep ENUMERATE TẤT CẢ lookup sites cùng pattern (KHÔNG fix theo bug report mỗi lần). (2) **Plan O caveat #1 surfaced Plan P pre-existing bug** — CICD Monitor catch Controller body record drop trong Stage 4c verify wire — pattern "verify cross-stack body↔command count match". (3) **DbInitializer auto re-seed** loop — Plan R+S clean → IIS recycle → re-seed loop. Plan T flag fix root cause. Pattern reusable cross-project: refactor schema 1-row-per-role MUST grep enum lookup sites + Controller body record MUST mirror Command record fields. Cumulative state: 111 test (+7 vs 104 baseline pre-Plan M), 31 mig (no new), 47 gotcha unchanged, `feedback_per_nv_permission_scope.md` reinforced 5 lookup sites enum + 9 wire surface points + Plan P caveat → 10 surface points. Smart Friend guard still active for future spawn.
- **2026-05-15 (S23 t3 Plan M cumulative review, spawn):** Pre-push adversarial verify 3 commits local `c2afef2..508b17a..4dd6f9c` Plan M F1 edge case Bước 1 + Phase=TraLai display rename. Diff cumulative: 26 LOC BE (Service.cs:287-333) + 116 LOC test (1 file, +2 test Fact extend SeedWorkflowAsync helper +2 params optional) + 8 LOC FE (4 file × 2 LOC mirror admin+user). **Verdict: PASS** — wire BE M1 verified hot path 287-333 OneLevel+OneStep edge case replace `Phase=TraLai+clear pointer+return` with `Set pointer (0,1) + summary "không lùi được" → fallthrough SLA reset line 364 → Phase=ChoDuyet preserved from pre-call state (entry guard 75-81 require ChoDuyet để Reject decision)`. ApplyReturnModeAsync caller line 94-100 truyền `evaluation.Phase` (now ChoDuyet) vào LogTransitionAsync → Summary="Chuyển phase ChoDuyet → ChoDuyet" + ContextNote contain "không lùi được" (matches test assert ContextNote.Contain). Drafter mode line 268-275 GIỮ Phase=TraLai semantic (unchanged). Assignee line 335-360 throw nếu không match unchanged. F2 ApproveV2Async + F3 EnsureEditableForDetailsAsync + F4 AdjustBudgetCommand handler — UNCHANGED. Schema integrity: 0 migration mới, Phase enum TraLai=98 còn dùng cho Drafter mode + display label rename only. Security: Admin bypass logic line 252-265 + Reject guard line 81 EnsureCanRejectV2Async + non-Approver block — preserved. Code quality: dotnet build PASS 0 err 2 warn (DocxRenderer pre-existing), dotnet test 106/106 PASS (58 Domain + 48 Infra, +2 từ 104 baseline), npm build admin+user PASS 0 TS err (chỉ chunk size warn pre-existing), no `--no-verify`. Test coverage: 2 Fact mới `ApplyReturnMode_OneLevel_AtStep1Level1_ResetsToBuoc1Cap1_KeepsChoDuyet` + `ApplyReturnMode_OneStep_AtStep1_ResetsToBuoc1Cap1_KeepsChoDuyet` cover OneLevel Step1Lv1 + OneStep Step1Lv2→reset. Assert đủ Phase=ChoDuyet + pointer (0,1) + SLA NotNull + ContextNote "không lùi được". Helper extend +2 params backward compat OK (default false). K7 cascade NO regression — 3 `ApproveV2_SkipToFinal_*` xanh (M1 chỉ đụng F1 ApplyReturnModeAsync, không động F2 ApproveV2Async). Mirror 2 FE app (§3.9): purchaseEvaluation.ts admin+user identical (98:'Cần chỉnh sửa lại' + PeDisplayStatus.TraLai:'Cần chỉnh sửa lại'), PeWorkflowPanel.tsx admin+user identical (2 inline literal "Phase → ..." + "Phiếu sẽ về ..." rename). Anti-fiddle: scope drift 0% — 8 LOC FE đúng spec rename Phase=TraLai display only. Anti-pattern guard: **(1) Stale narrative comment Service.cs:288-289 + 327-328** UPDATED rõ "Plan M S23 t3 — KHÔNG fallback Drafter, phiếu giữ đang duyệt" — không zombie. **(2) Audit log consistency** test+code match "không lùi được" exact. **(3) Backward compat** phiếu UAT prod đang Phase=TraLai do logic cũ — Drafter resume từ TraLai vẫn work line 105-132 (entry point `fromPhase==TraLai → targetPhase==ChoDuyet`). **Minor issues (non-block, recommend fix sau):** **(4) Inconsistent UX literal "Trả lại"** còn ở 8 user-facing locations chưa rename (decision design): PeListPanel.tsx:112 "Bản nháp + Trả lại" filter label, PeWorkflowPanel.tsx:270 button "← Trả lại", 272 tooltip "(Duyệt / Trả lại / Từ chối)", 315 "← Trả lại Drafter sửa", 342 "Chọn cách Trả lại", PeDetailTabs.tsx:152 "chỉ Bản nháp / Trả lại mới sửa", 287 "(trừ khi approver Trả lại)", ApprovalWorkflowsV2Page.tsx:639 "(Trả lại / Edit Section 2 / ...)" — phân biệt **action verb** "Trả lại" (button approver click) vs **phase result label** "Cần chỉnh sửa lại" (Phase=TraLai display in dropdown/badge); spec M3 narrow scope chỉ rename phase result label OK. Em main quyết: giữ action verb hay rename hết. **(5) Inconsistent across module** contracts.ts:29 + budget.ts:20 vẫn `98:'Trả lại'` — KHÔNG trong scope Plan M (PE-only) nhưng inconsistent giữa 3 module nếu Contract/Budget có Phase 98 — bro nếu deploy chung sẽ noticeable. Recommendation: SAFE to push 3 commit Plan M; consider follow-up chunk M4 (optional) rename 5 literal user-facing trong PeListPanel + PeWorkflowPanel + PeDetailTabs để consistent UX semantic mới. Smart Friend guard active.
- **2026-05-14 (S23 t1 Plan K1+K2 cumulative review, spawn):** Pre-K3 adversarial verify Mig 31 schema swap + Service Approver F2 branch. Diff scope: 11 BE files +4093/-83 LOC (Mig Designer 3938 lines dominate). 3 commits chuỗi `eb106f2..56868bf..db66253..364aef6` — pre-A slot label refactor + K1 Domain/Mig/Snapshot/sentinel patches + K2 DTO+Service Approver branch. **Verdict: PASS với 2 Major + 2 Minor issues** — K3 OK proceed nhưng K5 endpoint cleanup nên ưu tiên trước K7 test fix. Wire claim verify: Approver F2 branch placed ĐÚNG vị trí (line 477 AFTER UPSERT opinion line 441-468 + BEFORE advance pointer line 502) — opinion sẽ ghi trước skip terminal, audit log đầy đủ context Bước/Cấp. ApproveV1LegacyAsync skipToFinal guard tại CALLER (line 147-149 trong TransitionAsync branch) thay vì callee — pattern OK, V1 method giữ nguyên signature legacy. Schema sqlcmd verify Dev: 7 Allow* columns ApprovalWorkflowLevels (AllowApproverEditBudget/EditDetails/SkipToFinal/ReturnOneLevel/OneStep/ToAssignee/ToDrafter) + Users.AllowDrafterSkipToFinal dropped (count=0). Snapshot regen clean. EF config HasDefaultValue(false) wire. Mig 31 Up() manual reorder ADD→DROP correct per memory `feedback_ef_migration_backfill_reorder`. **Major issues caught** (Smart Friend guard active — KHÔNG để pass): (1) **Orphan UsersController endpoint zombie** — K1 sentinel commented "K2 sẽ refactor DTO + drop field" nhưng K2 chỉ refactor PE side, KHÔNG động UsersController.SetAllowDrafterSkipToFinal + UserFeatures.SetUserAllowDrafterSkipToFinalCommand + UserDto field. Endpoint PATCH `/api/users/{id}/allow-skip-final` vẫn live nhưng silent NoOp (Task.CompletedTask), admin UI tick → BE swallow → confusion UX. Cần K5 endpoint cleanup chunk (per spec). (2) **Stale Mig 28 comment ApprovalWorkflow.cs:78** — comment cũ "F2 (Drafter skip) đã move sang Users.AllowDrafterSkipToFinal" còn nguyên dù line 107-113 prop AllowApproverSkipToFinal mới. Confuse future dev. **Minor issues:** (3) Comment TransitionPurchaseEvaluationCommand DTO PurchaseEvaluationFeatures.cs:401 "F2 — Drafter skip thẳng Cấp cuối khi trình duyệt" still says Drafter (semantic outdated). (4) ApprovalWorkflowConfiguration.cs:22 stale Mig 28/29 narrative comment chưa note Mig 31. Anti-fiddle audit PASS: K1 18 LOC UserFeatures.cs sentinel patches valid compile-break workaround, K2 4 files within original spec. Anti-pattern reinforced 3×: admin opt-in per slot per-NV (Mig 29 F1+F3 + Mig 30 F4 + Mig 31 F2 — em main lần đầu sai Mig 30 default scope expansion → bro corrected). Production build PASS 0 err 2 warn (DocxRenderer unrelated). Test references K7-pending line 253. No `--no-verify` bypass. Pattern caught: "Transient sentinel pattern" — đặt sentinel + comment commit chunk khác cleanup nhưng chunk đó scope SHIFT → zombie state. Recommend explicit K5 cleanup chunk trước K7 test fix. Cumulative state: 31 mig (+1 Mig 31), 47 gotcha unchanged. Smart Friend guard active.
- **2026-05-13 (S22 18:00→21:00, no spawn):** Em main solo self-review S22 — Reviewer KHÔNG spawn per UAT mode `feedback_uat_skip_verify`. Em main verify build clean + test pass + npm build × 2 app mỗi chunk (11 commits cumulative). Key validations: (1) Plan E phân quyền strict V2 — actor.UserId scope in List + Detail, Inbox đã strict từ S17, loose UAT clause `|| ApprovalWorkflowId != null` removed. (2) S22+1 V2 actor scope guard BE helper `EnsureCanRejectV2Async` chặn request forge non-approver PATCH /transitions decision=Reject (mirror FE button disable) — defense-in-depth FE+BE pattern. (3) S22+4 AdjustBudgetCommand handler 3-tier scope (Drafter Nháp/Trả lại + Approver ChoDuyet + Admin) — S22+5 refactor ChoDuyet branch dùng `level.AllowApproverEditBudget` flag (admin opt-in per slot) thay default Approver scope (security scope correction per bro feedback). (4) S22+2 Identity password policy enforced ≥12 chars (reject `User@123456` 11 chars outdated). Anti-patterns observed: (a) Default scope expansion mistake S22+4 → S22+5 fix — KHÔNG default expand permission scope without admin tick (per-NV opt-in pattern Mig 29 lesson reinforced); (b) History display field assumption BudgetAdjustSection initial — em assume `PeDetailBundle.changelogs` exists, FAIL TS2339 — pattern verify type fields trước render; (c) PS 5.1 Vietnamese diacritics gotcha #30 reinforced — script `seed-test-users-prod.ps1` first attempt FAIL parser error, ASCII-only discipline. S22+4 attachment view endpoint + S22+4 budget-adjust endpoint chưa qua live curl verify (defer post-deploy — bro UAT direct). S22+5 Mig 30 applied LocalDB Dev + Design via `dotnet ef database update`. New gotcha discovered: #47 paths-ignore agent-memory gap (recommend add to docs/gotchas.md — pending bro decide). Cumulative state: 104 test (+20), 30 mig (+1 Mig 30), 46 gotcha unchanged (gotcha #47 pending), ~146 endpoints (+3), 33 active users. Smart Friend guard still active for future spawn.
- **2026-05-13 (S21 t3-t5, no spawn):** Em main solo verify via dotnet build + npm build × 2 app + dotnet test suite mỗi chunk. Reviewer KHÔNG spawn — em main self-review per UAT mode `feedback_uat_skip_verify` (skip dotnet test mỗi chunk, vẫn build verify). Gotcha #45 fix self-test 3 regression test (test-before §7). S21 t3-t5 push cumulative 12 commits — CICD Monitor verify post-deploy thay vai Reviewer (deploy ship + bundle hash + schema verify). Cumulative state: 84 test, 29 mig, 45 gotcha, 19 memory entries. Pattern saved cho future review focus: per-NV permission audit (Level table vs User table flag), EF migration backfill SQL injection between ADD-DROP order. Smart Friend guard still active for future spawn.
- **2026-05-11 (setup):** Reviewer agent initialized. Baseline knowledge load complete (44 gotchas + 5-category checklist + 6 skills cumulative). No reviews performed yet. Awaiting first SendMessage from em main. Smart Friend guard 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)