Files
solution-erp/.claude/agent-memory/reviewer/MEMORY.md

104 lines
20 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 at spawn (L1 HOT).
> Update BEFORE every stop. Tiered Memory v1: L1 HOT cap ~17KB (hook 24.4KB read-limit) · L2 `archive/` on-demand · L3 RAG `search_memory` just-in-time. Keep entry ≤ 1.5K chars (gotcha #53).
> Full verbatim history pre-S40 → git `d2f52ba` + `archive/2026-05-q1..q2.md`; S51→S76 detail → `archive/2026-06.md` (S69/S70/S71/**S80** curate). Archive map: `archive/_INDEX.md` + per-period `.gist.md`.
## 📁 Area memory (L2 on-demand — Read khi review vùng tương ứng)
- [S62 PE budget soft-warning](project_s62_pe_budget_soft_warning.md) — PASS: hard-block→soft-warning; submit-guard intact + validator giữ `BudgetPeriodAmount>0`, row8 negative-safe (additive-only). Validator class `PurchaseEvaluationFeatures.cs:317`.
- [Wire/mirror claim verification anchors](feedback_wire_claim_verification_anchors.md) — sha256 twin-file · `git diff -U0` isolate true-adds · `allowNegative` bleed check · guard-still-intact grep.
- [S89 PE budget-sectionB review](project_s89_pe_sectionb_review.md) — investigator 3/3 upheld (do-not-touch FROZEN) + CAUGHT MISSED #70 race: Block B PRO row3/row8 share adjustMut + cross-echo `ev` stale + NO `||peFetching` (every other cell gated). authz-lens ≠ concurrency-lens.
- [S89 PE ends-before-CEO PLAN review](project_s89_pe_endsbeforeceo_plan_review.md) — CONCERN: BE 3-field design sound (Mig 58 col committed, no mig) but FE coverage gap — 4 PeUrgentChips render-sites, plan covered only 2 (missed InboxPage:290 despite BE Inbox A2 updated + wrong ListPage dir path).
- [S89 finalize-note PLAN review](project_s89_finalize_note_plan_review.md) — CONCERN: plan ground-truth 100% accurate (all file:line verified incl self-flagged); gaps = note doesn't say intermediate approvers still sign + skipToFinal-last-level corner + Dev DB 9 migs behind.
- **S101 H18 WF2 governance re-run** — PWC; detector fault-inject 4/4; lesson: exact-token sweep misses paraphrased labels → widen concept-phrase khi retire named artifact; #53 garble recovered first-hand. → `archive/2026-07.md`.
- [S103 H20+crystallized-backfill review](project_s103_h20_crystallized_backfill_review.md) — WF2 PASS (fable-clone ensemble); 6/6 hash recompute MATCH + 4 already-met verified file:line. **Lesson: multi-axis warning phải nằm trong OUTPUT của script, không chỉ doc** — reader chạy script inline.
- **S101 H19 WF2 review LANE-C (adap-reports+honesty)** — PASS; 4 hash recompute MATCH; hex→SHA = recompute CẢ strip/no-strip chứng content-hash ≠ commit-SHA. → `archive/2026-07.md`.
---
## 🎯 Role baseline
Adversarial pre-commit reviewer SOLUTION_ERP. Read-only verify + live curl prod UAT (`*.solutions.com.vn`). Tools: Read, Grep, Glob, Bash (curl + git diff + sqlcmd read) + 5 RAG MCP. Skills: `dependency-audit-erp` + `contract-workflow` + `permission-matrix`. Output: PASS/FAIL + concrete issues file:line. NEVER write code.
---
## 🚨 Recurring bug patterns (catch priority)
- **#44 Silent 403 class-level Authorize quá strict** — Drafter dropdown empty silent (TanStack catch silent → UI empty). Grep `\[Authorize\(Policy=.*\)\]` class-level + curl non-admin expect 200. Fix: class-level `[Authorize]` only (any authenticated); POST/PUT/DELETE giữ `[Authorize(Policy="X.Create")]`.
- **#43 Step.Order ≠ index 0-based** — `Where(s=>s.Order==i)` wrong row. Fix: EF query → in-memory `OrderBy(Order).ToList()` → index.
- **#42 Dual schema V1/V2 — Service phải branch** — `if (entity.ApprovalWorkflowId is Guid awId) ApproveV2Async else V1Legacy`.
- **Wire BE claim** — grep diff `// Mock`/`alert(`/no POST-PUT-DELETE call + live curl expect 2XX. Severity CRITICAL block.
- **#70 FE absolute-set stale-echo race** — N fields cùng-cột share 1 mutation + echo sibling từ server-snapshot (`bs`) + `invalidate()` fire-and-forget (không await) → lưu 2 ô liên-tiếp đè mất. Window mở SAU `isPending=false` (btn re-enable), KHÔNG lúc in-flight. Catch: đếm field-cùng-cột share mutation + check invalidate awaited; fix = `useIsFetching` gate nút Lưu tới khi refetch land.
- **#71 enum proxy-predicate pollution** — thêm enum value vào entity dùng-chung → UI/guard phân-loại theo PROXY-predicate (`supplierId===null`) thay vì enum tường minh → value mới lẫn nhầm phân-loại + false-pass guard. Catch: grep mọi field-proxy predicate, loại value mới tường minh; build-verify TỪNG app.
- **Cross-module security mirror (S29 Smart Friend)** — khi mirror entity/Command cross-module (PE→Contract→Budget V2), focus data-shape MISS security guard. Pattern: `aw.ApplicableType == ExpectedType` validate ON Create BEFORE instantiation (mirror `PurchaseEvaluationFeatures.cs:62-77`). Attack: Drafter forge POST với `approvalWorkflowId` của module khác → FK Restrict chỉ check Id-existence NOT ApplicableType → wrong-scope pin. Re-verify `IsActive`+`IsUserSelectable` server-side. Password ≥12 chars. Severity MAJOR.
- **#17 EF migration 3-file** — `git diff --name-only | grep Migrations/` expect 3 (target + Designer + Snapshot).
---
## 📋 6-category checklist (EVERY review)
- **Cat 1 Wire BE/feature claim:** grep mock markers diff + `await api\.(post|put|delete|patch)\(` + live curl POST/PUT/DELETE if deploy claim + status matrix.
- **Cat 2 Schema integrity:** 3-file rule Mig + column types vs entity def. Reference `docs/gotchas.md` (71 active).
- **Cat 3 Security:** `[Authorize]` class-level ALL new controllers + per-action policy admin-scoped (gotcha #44) + FE PermissionGuard + menuKeys.ts mirror BE MenuKeys.cs + FluentValidation + EF parameterized.
- **Cat 4 Code quality:** `dotnet build SolutionErp.slnx` 0 err + `npm run build` × 2 app (TS6 strict) + tests baseline **354 PASS** (Phase 9 UAT exception OK) + no `--no-verify` + anti-fiddle (scope drift >20% LOC = FAIL) + mirror 2 FE app §3.9.
- **Cat 5 Test coverage:** new helper → xUnit · new endpoint → integration · bug → regression test-before-fix. Phase 9 UAT test-after default OK (`feedback_uat_skip_verify`). Baseline 354.
- **Cat 6 Writing-quality (Harness-7, S64):** outward text (verdict gửi anh / report) = tiếng Việt câu hoàn-chỉnh đủ dấu đúng ngữ-pháp; describe issue + acceptance criteria, NOT code edits. Escalate disagreement explicit.
---
## ⚠️ Anti-patterns + 🛡️ Smart Friend guard
1. ❌ Recommend code edits (only describe issue+criteria) · 2. ❌ Skip live curl if deploy claim · 3. ❌ Accept "wire" without grep proof · 4. ❌ Defer to em main authority (escalate explicit) · 5. ❌ Skip MEMORY · 6. ❌ **Lower bar match em main** (Smart Friend Cognition anti-pattern).
**Smart Friend (Cognition):** NEVER lower bar. Em main code fine → PASS. Em main issues → FAIL with specifics regardless social pressure. "Quality ceiling set by primary, not escalation." Value = raise quality through catch.
---
## 🧠 SOLUTION_ERP review essentials (S80 verified — re-ground từ docs/STATUS.md canonical)
- **Tests baseline:** **354 PASS** (45 Domain + 309 Infra · 0 fail/skip). Must increase khi feature added (§7); Phase 9 UAT exception (`feedback_uat_skip_verify`).
- **Gotchas:** **71 active** (`docs/gotchas.md`, format `### N.`). Latest: #69 bundle-hash non-determ (deploy rebuild-FE-uncond rotate) · #70 FE stale-echo race (useIsFetching gate) · #71 enum proxy-predicate pollution · #66 Tailwind v4 unlayered `h1-h4` color thắng utility · #58 EF read-modify-write lost-update→ExecuteUpdate atomic.
- **Migrations:** **57 latest `AddPeSuggestedPriceNotes`** (path `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/`). PE-budget: Mig 50 `PeWorkItemBudgets` per-gói-thầu (DROP module Budget cũ) · Mig 54 giá-đề-xuất PRO/CCM + giá-chốt · Mig 55 CcmNote · Mig 56 ProInitial/ProAdjust split · Mig 57 ghi-chú-giá PRO/CCM.
- **Endpoints:** ~253 · **88 SQL tables**.
- **Identity password ≥12 chars** (reject 11-char). Test creds: admin `admin@solutions.com.vn/Admin@123456` (full) · UAT `nv.test@solutions.com.vn/TestUser@123456` (Drafter CCM).
- **Prod:** api/admin/eoffice.solutions.com.vn. Bundle live admin `CsJetgZH` / user `BVS0ApIm` (Run #330). **Pin:** MediatR `12.4.1` (flag `Version="14`) · Swashbuckle `6.9.0` · Node CI `20.x`.
- **Conventions:** `docs/rules.md` (§1.1 outward writing, §3.9 mirror 2 FE, §5.2 commit, §6.5 docs narrative, §7 test timing, §2.8 pin).
---
## 📅 Recent activity (compressed — full verbatim → `archive/2026-06.md` + `archive/2026-07.md` via `archive/_INDEX.md`)
- **S101 H19 fable-clone WF2 Lane-A (toggle) — PWC, CONCERN closed @S102:** marker WRITE-ONLY dead artifact — persist-claim cần CẢ reader-side (verify consumer exists, not just producer); reader BƯỚC 0.5b wired S101-cuối. → `archive/2026-07.md`.
- **S100 H18 WF2 synthesis + Lane-B — PWC 0-blocking:** ledger revert-clean + ratio-band + 6/6 🧊; anti-pattern HELD: stale WF2 run-id NOT stamped. → `archive/2026-07.md`.
- **S98 (2026-07-02) 3 verify-lane (fidelity-gist impl-fe + cicd L1-curate + mega-line re-tier) — all PASS (minor only):** Lessons: fidelity-gate 3-lớp = verbatim + pointer-arithmetic + ground-truth-code cho MỌI diễn-giải vượt-verbatim (honest-empty > bịa filler) · Windows byte-verify GROUND-TRUTH = `.NET ReadAllBytes`/`wc -c`/`od -c` (MSYS grep/sed strip `\r` báo sai) · conservation-proof = arithmetic full-accounting > spot-check; Read-tool truncate >2K → PS ReadAllLines+ordinal IndexOf. Detail → `archive/2026-07.md` (recovered S102 — cut-not-moved f229b07).
- **S97/S97-bis (2026-07-01) PE finalize UAT ×2 app + EndsBeforeCeo CONFIG→RUNTIME Mig 60/61 (anh Kiệt FDC) — PASS (0 issue):** default-flip opt-out→opt-in byte-mirror SHA256 ×2; RUNTIME-flag set DUY-NHẤT 1/5 DaDuyet-nhánh (grep-all) + 4-projection nhất-quán + Mig 3-file OK + backfill-LIKE VN-chuỗi = false-pos MINOR-only; EF-ternary List/Inbox CONFIRMED-runtime via test-in-395. Detail → `archive/2026-07.md` (recovered S102 — cut-not-moved f229b07).
- **2026-06-29 S93 Harness-16 MFE adoption review (WF2 `wf_13e3d35a` 3-lane PASS 0-blocking):** ⭐ code-gate re-derived denom-29 + leading-verb-trap DEFEATED (`NEVER commit push`→0 content-word) + READ-ONLY-budget proof (1 write-op `.mfe-state.json`); WF1-reviewer caught BLOCKING vocab-fork (memory-fidelity H6.7≠H16 → MFE+alias-map §H). Detail → adap-report harness-16-mfe. → _INDEX.
- **S92 (2026-06-29) Adversarial PROD-security hide 5 menu-groups admin-only on eoffice (uncommitted) — PASS (0 blocking, 1 awareness note):** A-E all upheld; CatalogManager Danh-mục access stripped (intended-by-spec, role assigned to 0 users post-S89, flag em-main only). Detail + per-attack file:line → [project_s92_admin_only_modules_revoke.md](project_s92_admin_only_modules_revoke.md).
- **S91 (2026-06-25) PE D2 create-contract 1→N multi-winner + winner-names (FROZEN, NOT-deployed) — PASS:** fix for S89-bis dead-end; codegen mid-loop SaveChanges flushes ONLY seq-row (contract built LOCAL); GiaTri per-winner Quote-sum join PES.Id; positional DTO arg-order verified 3 sites; FE mirror byte-identical; 419 PASS. Detail → `archive/2026-06.md`.
- **S90 (2026-06-25) PE stability-fix batch D1 (5 chg+11 test, FROZEN) — PASS:** Block B re-key SelectedSupplierId→IsWinner (single unchanged proven); CEO-notify SaveChanges (was Add-but-never-flush); HoSoLink null-safe+clear-via-empty option(b); #70 ||peFetching 2 PRO cells; 413 PASS. Detail → `archive/2026-06.md`.
- **S89-bis (2026-06-25) AREA-6 FE-consumers (PE FROZEN, investigator verify) — 4/4 confirm + 1 NEW miss:** all 4 hold; NEW catch = create-contract joint-winner DEAD-END (FE shows button on `some(isWinner)` but BE hard-blocks `SelectedSupplierId is null`). Anti-pattern: single→multi conversion stops at detail-display layer. Detail → `archive/2026-06.md`.
- **S89 (2026-06-25) AREA-4 workflow-edit (PE FROZEN, investigator verify) — 3 confirm/1 do-not-touch:** HoSoLink #73-class clear-on-partial-edit CONFIRMED + NEW 2nd destructive call-site (PeDetailTabs:817 InfoTab.save omits hoSoLink). Anti-pattern: echo-all-siblings convention rots when NEW absolute-set field added. Detail → `archive/2026-06.md`.
- **S86 (2026-06-24) PE Section B 3-cột Dự-án|PRO|CCM (Mig 59) — PASS:** authz byte-mirror UpdatePeSuggestedPriceCcm (NotFound→Forbidden, fail-closed); FE `canEditCcm` KHỚP BIT-EXACT BE gate; submit-guard untouched (grep CcmBudgetPeriod in Services=ZERO); div-by-0 safe; 402 PASS. Detail → `archive/2026-06.md`.
- **S82 (2026-06-21) Harness-15-v2 adopt review (0 code) — 3/3 lane PASS:** caught 3 MINOR. Memory-note: `broadcasts/_index.md` sha = notify self-declared `content_sha256` frontmatter NOT recompute → don't flag index-vs-file mismatch before reading frontmatter. Stamped-mark mid-session edit OK if only refresh confirmed-decision What-cell.
- **S76 (2026-06-19) PE budget 3-cột Mig 56 + badge — PASS:** MAJOR race fixed gotcha #70 (useIsFetching gate). Badge role-set MUST mirror gate bit-for-bit. SURPRISE: spec "KHÔNG migration" FALSE — đọc changed-set thật, đừng tin scope-framing em-main. → _INDEX S76.
- **S72* (2026-06-18) Mig 54 PE giá-đề-xuất + CCM-finalize OPT-IN — financial go-live PASS:** fail-closed guard throw BEFORE set Phase=DaDuyet; finalize-bypass = 3 orthogonal gate + server-recompute amount no-trust-client. → _INDEX S72*.
- **S71 (2026-06-18) Harness-10 run-trace — PASS/GAPS:** "TRACKED" 2-level = check-ignore(eligible) vs git-ls-files(committed), model only post `git add`. → _INDEX S71*.
- **S69 (2026-06-17) Office re-skin + golive authz — PASS:** re-skin proof = grep api-call+queryKey sorted -u byte-equal; public-grant = granted root NOT inherit-root (no sibling cascade); accent missing -800 stop = silent no-class Tailwind v4. → _INDEX S69*.
- **S65 (2026-06-16) public HRM Hồ sơ + PE mục E — PASS:** upgrade-path MUST MUTATE row (`if(!row.CanRead){...}`) NOT skip-existing when prior revoke pre-set false (S58-class); menu-hide ≠ API-lock. → _INDEX S65*.
- **S88 (2026-06-25) Fidelity-gate L2 gist distill (test-specialist) — FAIL (1 fabrication-by-merge):** gist gán "Mig 45" cho cluster span 2 episode khi chỉ 1 mang số đó (Master = Mig 47). Anti-pattern: merge-distill fabricates false specificity; token-PRESENCE pass BLIND to cross-episode mis-attribution → QUALITY gate after presence gate. Detail → `archive/2026-06.md`.
- **2026-07-10 (S108 first-real-run H19) `[fable-real-single]`** — **FAIL** (1 CRITICAL + 4 MAJOR + 6 minor; em-main 37-fix cùng phiên): email claim “đã sửa comment” khi hmw.js chưa sửa. Lesson: doc assemble-từ-N-worker → grep stale-claim theo CLASS toàn file sau fix đại diện; mọi outward-claim “đã sửa X” phải cat X trước gate. → `archive/2026-07.md`.
- **2026-07-10 (S109 sleep-recovery P3b fidelity-gate 2 gist L2 tooling-auditor + harvest-curator) `[fable-real-single]`:** **PASS_WITH_FIXES** (3 fix, 0 re-distill): (1) gist-2 meta-count "15 entry" ≠ disk 14 — count COPY từ brief, không đếm disk; (2) delta tự-tính "+8.8KB" cạnh cặp 32.2→41.1 (=8.9) — **self-computed derivative = lớp false-specificity MỚI** ngoài S88 merge-mis-attribution; (3) N/A-justification "(resolved monthly S96)" over-claim — ERD-debt §16+ CÒN pending (root CLAUDE.md), chỉ "(55)" resolved → **fact-claim trong N/A-note verify như content**. Bịa=0 ("S49" vượt-verbatim nhưng TRUE — chéo reviewer `_INDEX.md:28`); đảo-nghĩa=0 ("suýt" giữ ×2); drop-oan=0; 3 self-flag honesty NGUYÊN; pointer 10/10 unique+đúng-entry. Lesson: gist-metadata + số-tự-tính + drop-justification = 3 bề mặt lỗi máy token-coverage mù. Return Verdict-header dòng-1, 0 garble.
- **2026-07-12 (S111 H22 WAL session-continuity adopt cổng-cuối, working-tree + 2 commit `wal:`) `[engine: fable-real-single · Fable-S111]`:** **PASS_WITH_FIXES** (0C/1M/~7m). Fault-inject cây tạm 2 ca PASS (1-path-vắng → commit 2-path-còn exit-0, không nuốt ngoài-whitelist; 0-delta im lặng — bịt đúng nhánh lead chỉ "quan sát tự nhiên"). CATCH M-duy-nhất: engine CAVEAT còn "STAGED-until-verified" trong khi N.3 CÙNG FILE đã nâng VERIFIED-RUNTIME — lớp lỗi **nâng-nấc-quên-sync-câu-cũ cùng-diff**. Lesson mới: (1) **live-witness > forensics > lời-khai** — commit `67be443` sinh GIỮA lúc mình đang review (status→diff lệch 3 path) = tự chứng kiến hook fire, không cần tin claim; (2) **NTFS CreationTime × commit-time phân định script-RUN-tay vs hook-FIRE** — commit 11:05:39 rơi GIỮA cửa sổ worker-write (ps1 :32 → pause.md :48) → không thể là tay; (3) WAL `updated:` lead tay-ghi đi TRƯỚC commit-time 47-61' → field máy-đọc phải sinh từ `Get-Date` thật, không ước — false-positive cảnh-báo phiên-song-song /tiep; (4) PATH 3-context data-point: PS-của-mình trả Git-Bash (spawn từ bash, env-inherit) ≠ lead đo WSL-system32 — KHÔNG mâu thuẫn, càng chứng exec-env bất-định → PS1 path-independent đứng. Return verdict-header dòng-1.
- **2026-07-11 (S110 H21+MTv3 adopt cổng-cuối, uncommitted 13-file) `[fable-real-single]`:** **PASS_WITH_FIXES** (0C/5M/~9m). Sàn-5-điểm + 4-note a-d + MTv3-4-vế ĐỦ trên bề mặt chính (2 command + 0.5b/0.6 + engine K.E); X1 hmw nguyên; marker xóa; X8 sạch (mọi "code-enforced" negated). CATCH: (1) worker đổi-fact khi reword — fable-clone:33 "VALID_ROLES nhận cả 12 vai" nhưng hmw.js:22-28 = **10** (engine K.C nói đúng 10) → **claim-về-code trong doc phải grep code lại kể cả khi chỉ reword**; (2) roster-12 promise × VALID_ROLES-10 → lệnh-B 2 vai monitor degrade DEFAULT-subagent silent (hmw.js:104 fail-soft, mất persona+memory-pack) — floor mới EXPOSE edge code cũ mà H19 2-vai-cố-định che khuất; (3) point-edit sót cụm GIỮA-section: runbook §5.1 P5 "EXCLUDED all-but-2" đối đầu P0-mới roster-12 CÙNG checklist; §4.2 fixed nhưng mirror §3.2/§3.3 MISS (asymmetric-mirror-edit); §2.5 q1/q3+bảng marker nguyên un-🧊; (4) 3 supersede-note trỏ 2 adap-report CHƯA tạo = dangling-pointer + outward chưa qua gate. Lesson: sweep hậu-supersede phải đi theo CONCEPT-cluster từng section — acceptance token-hẹp ("Test-Path ngoài 🧊 = 0") PASS trong khi semantics cũ còn nguyên cụm. #53 garble lượt return (status-line thay verdict) → coordinator re-emit request trong-session = recover OK, diary ghi trước re-emit.
---
## 🔄 Curate trigger
- Hook-cap **>17.1KB** (24.4KB read-limit — đổi từ ~30KB cũ, S109) → archive recent → L2 `archive/<period>.md` (append additive) + `_INDEX.md` substring pointer. Stale >3mo → remove.
- **S109 curate (self, 2026-07-10, hook 22.4KB):** moved 4 verbatim S100/S101 (2 Area + 2 Recent, added 07-03) → `archive/2026-07.md` + `_INDEX.md` +4 pointer (count=1 verified); digest 1-dòng giữ L1.
- **S102 recovery (em-main, 2026-07-06):** cut-not-moved ×5 tại `f229b07` → recovered 5 entry từ git vào `archive/2026-07.md` + `_INDEX` +5 pointer. **Lesson: L1-shrink kèm claim "Detail→archive" = BẮT BUỘC grep moved-not-cut TRƯỚC khi trust digest.**
- **Last curate: 2026-06-20 S80 (em-main, archive-gate keep-floor-hit → manual)** (45.2→~14KB): moved 13 recent entries S65→S76 (lines 64-82 byte-exact via `sed`) → `archive/2026-06.md` (32.7→70KB, +13 entries) + `_INDEX.md` +13 substring pointers (all verified count=1). KEPT foundation + 5 compressed recent-summaries; UPDATED stale essentials (130→354 test, 55→71 gotcha, Mig 40→57, 84→88 tables, ~211→~253 endpoints) + 5-cat→6-cat (Cat-6 writing-quality). gist NOT updated (em-main distill later).
- **Prev curate: 2026-06-18 S71** (36.7→24.2KB): moved 10 entries; KEPT foundation + newest cluster. **Prev S70** (42.5→24.8KB): built `_INDEX.md` + `.gist.md` gen:1. **Prev S40** (28.4→18KB).