# Implementer-Backend Agent — Persistent Memory

- **S171 (2026-08-04) lane BE — `7/7 mục, build PASS`, return SẠCH `[LEAD HARVEST HOI-TO @S172 — no mo tu closeout truoc; owner slot (71) chot 'gom va sleep thi auto, tu quyet'. Nguon = artifact tren dia, KHONG suy dien]; nguồn `runs/2026-08-04-S171-khkk-ui-mirror-pe/sub-implementer-backend-s171.md` 11.650 B`:** BE-1 DetailDto +5 field · **BE-1b `FinalizeStepName`/`FinalizeLevelName` DẪN XUẤT 3-ngả in-memory thay 3 subquery** (+ sửa công thức con-trỏ cho đúng KHKK) · BE-2 `ContractSigningPlanApprovalDto` đẻ mới + projection · **BE-3 tên người duyệt join `Users` — đường THỨ BA** (2 đường kia không dùng được, lý do §2) · BE-4 `WorkflowStepDto` +`DepartmentName` · BE-5 endpoint changelogs policy `KeHoachKyKet.Read` **+ rào `EnsureCanViewAsync`** · BE-6 map Cấp cho Approvals · **BE-7 enum `+ApprovalAttachment=3` + endpoint upload riêng (policy `Update`)**. Mirror `types/khkk.ts` ×2 app **SHA256 trùng khít**; `dotnet build` 0 warning/0 error + `tsc -b` ×2 exit 0. 🔴 **Tự khai 3 chỗ lệch-có-lý-do + treo B-3 vì DB dev 0 phiếu** — nấc trung thực đúng: *làm được thì khai làm được, không đo được thì khai treo*, KHÔNG nhét acceptance runtime vào ô đã-xong. ⚠️ Hệ quả @S172: chính `ApprovalAttachment` + policy `Update` đẻ ra câu hỏi owner slot (67) — **người SOẠN cũng qua được `KeHoachKyKet.Update`**. Tag `[s171, be-7-7-build-pass, dan-xuat-thay-subquery, duong-thu-ba-join-users, treo-b3-trung-thuc]`

- **S167 (08-01) lane K7 — cầu KHKK→HĐ BE + 5 vá `[artifact sub-implbe-k7.md; return-1 garble #53 → lead SendMessage-resume, xong solo]`:** Bài lớn nhất = **BÁC TIỀN ĐỀ ĐỀ BÀI bằng 4 phép đo trước khi gõ**: lead nói "bridge ĐÃ TỒN TẠI (W6-S161), mày chỉ vá quanh nó" — đĩa nói KHÔNG (`grep create-contract` 1 hit duy nhất = bridge PE cũ · 0 file · 0 endpoint · 0 write-site `Line.ContractId`). Cái tồn tại @W6 là **đường-ống HĐ V2**, không phải cầu ⇒ lead nhập nhằng "đường ống đã vá"≠"bridge có". **Phép thử rẻ phân biệt:** vá-6 ("nạp Project.Code/Supplier.Code theo khuôn bridge-cũ") là **no-op nếu áp lên bridge cũ** (nó ĐÃ có 2 dòng đó) ⇒ vá chỉ có nghĩa với vật CHƯA có ⇒ suy ngược ra vật thiếu. Xử: DỰNG bridge theo spec invest §K7.1 đã pin sẵn (0 quyết-định-mới của tôi) + nhúng 5 vá, KHAI RÕ chệch-khuôn + trỏ dòng spec — lead nhận. Khi **spec vá ⊥ nền invest** (`WorkflowDefinitionId` null vs pin) → **vá THẮNG** (spec `:4` viết luật đó). Cột đã có từ mig cũ ⇒ **0-mig**. `??=` giữ CHIỀU: cầu chạy sau không cướp ô của cầu trước. Thêm field vào projection dùng-chung-3-endpoint → **chạy test filter rẻ ngay** (collection-subquery SQLite dịch được: 34/35). 1 fail = reflection-authz test bắt mọi action phải cùng key module, mà vá-1 cố ý gate key MODULE KHÁC (`Contracts.Create`, #85) ⇒ **verified-fail bàn giao test-lane, KHÔNG tự sửa** (S112 khuôn). Tag `[s167, k7-bridge-khkk-hd, bac-tien-de-4-phep-do, va-noop-suy-nguoc-vat-thieu, spec-thang-nen, 0-mig, verified-fail-handoff]`

- **S166 (08-01) lane K4a — 49 row menu KHKK 8-nhóm, DONE B0-B5 `[LEAD SEED ON-BEHALF TẠI-CỬA — mày #53 sau B5 ("Now the site-4 revoker comment" trong khi comment ĐÃ land — return tụt sau đĩa 1 nhịp); B6 + END lead đóng; nguồn: sub-implbe-k4a.md + git diff]`:** Nghề mới ăn tiền: **BỘ-SINH thay 49 const tay** — `KhkkGroupNumbers[7] × (KhkkGroup + 6 KhkkLeafActions)` → `KhkkGroupMenuKeys` làm NGUỒN CHUNG 3 phía (spread `.. ` vào `All` · vòng seed tree.Add · site-2 `.Concat`) ⇒ khớp **by-construction, 0 mirror tay 147 chuỗi** (đúng loại drift nổ FK Permission T6-K1); chệch-khuôn-so-brief KHAI RÕ + trỏ đề xuất gốc invest `:9` ⇒ lead chấp nhận không mất lượt. Anchor re-ground TRƯỚC khi sửa (số spec trượt vì DbInitializer phình sau K2/K3): dict labelBackfill `:2141-2183` · upsert THẬT `:2162-2179` (vá 2 comment stale "1893" bằng mỏ-neo GREP `existingItems.TryGetValue` thay số-dòng-trần — số dòng sẽ trượt tiếp, mỏ neo thì không). Vá-10 sửa LÝ-DO comment (group-key KHÔNG cần CanRead row — HasAccess đệ-quy theo con `:96`; invest doc viết sai lý-do, kết luận giữ-3-site vẫn đúng). labelBackfill `Khkk_G1` đổi-nhãn-không-nhân-row + fail-fast KeyNotFound khi thiếu nhãn (thà nổ lúc seed còn hơn FK nổ về sau). Suite **614/0 SAU diff** (idempotent/revoke/permission-seed giữ xanh). Tag `[s166, k4a-lane, bo-sinh-by-construction-3-phia, mo-neo-grep-thay-so-dong, fail-fast-luc-seed, return-tut-sau-dia-1-nhip, seed-on-behalf-tai-cua]`

- **S161 (07-29) ARC W1+W2+W3đ1 KHKK — 3 lượt/3 wave, chi tiết = sub-md run-folder `runs/2026-07-29-S161-khkk-{w1-schema,w2-crud,w3-duyet}/` (tổng ~47KB ghi-từng-mục; cả 3 lượt return #53 nhưng 0 mất):** **W1** Mig 69 7-bảng (UNIQUE filtered #57 · cascade-1-hop · apply 2 DB 0-drift · sys.tables 96) + seeder `SeedKeHoachKyKetAccessAsync` UPGRADE-if-exists+CanDelete + CodeGen `KHKK/{YYYY}/{Seq:D3}` WorkflowAppCodeSequences + 3 site AWV2Admin — ship `a8027c0` cicd 6/6. **W2** mega-file CRUD 15-endpoint policy-15/15 + 4 quyết D1-D4 khai-lead-duyệt; 5 bẫy né ghi sổ: codegen-TRƯỚC-Add (S88) · SUM so `w.Id` KHÔNG `w.SupplierId` · `Remove` trước `Add(changelog)` (T26) · subquery-Any-2-tầng vỡ-SQL-lúc-chạy · giao-1-grep-cùng-lớp-ra-3 (S122) — ship `6cbc6ad` cicd 6/6 (reviewer 12F: D1 "FE sẽ khớp" = giả-định-không-phải-phép-đo → 6 điểm đứt, lead vá). **W3đ1** skeleton test-before: switch-throw từng nhánh (đỏ-THẬT không đỏ-compile) + **rào-FK-547-bằng-VẮNG-MẶT-dep** (không inject IChangelogService) + **§6 tự đối-chứng chéo hợp-đồng FE 5/5 TRƯỚC bàn giao** (bài W2 → nghi thức) + §7 bàn-giao ctor-CUỐI. Tag `[s161, khkk-w1-w2-w3d1, mig69, upgrade-if-exists, rao-vang-mat-dep, doi-chung-cheo, gia-dinh-vs-phep-do]`

- **S139 (07-17) Governance wave adap-3-bản 2-lane [wf_f9981ae5-903 · L1 CLEAN · L2 #53-garble → sub-1.md ON-BEHALF em-main verify-2-nguồn] `[engine: opus-worker]`:** L1 = MASTER-CHECKLIST 2-tầng in 4 cửa (ss Phase-3 · se Phase-4 "TỔNG-HỢP CHỐT" · pause ý-6 · tiep §3-bis bold-block KHÔNG heading) + C1 2-câu đóng-băng bề-mặt-đo (§L.b(c) prepend + mirror §2.1.2) + C6i do-record ×2 + luật-số sàn-5 §L.b(j)(iv) — PASS 6/6 (heading 26/28/7/14 bất-biến · 0 checklist-file mới · BƯỚC-0 intact ×8 · 10/10 config-token preserved · add-only 26+/2−). L2 = +C6 cite-2-nấc + H24-4 pending-flip vào governance-detectors.ps1 (+260/0 add-only) — fault-inject **13/13 2-chiều** (C6 7/7 lane chạy; H24-4 6/6 em-main hoàn tất — lane chết GIỮA fix harness-bug), live-run TOTAL 45 GIỮ + 2 net mới 0-flag count-riêng + exit-0 + ASCII 0-byte>127 + clean-diff 0-FOREIGN-line. Learned: (1) heading-invariant proof = block chèn toàn `>`/bullet + `grep '^+#'`=none (mạnh hơn đọc-mắt, họ insertion-only S134); (2) test-harness PS1 cần CÙNG kỷ-luật naming như production — bẫy `$h`/`$H` case-insensitive cắn chính harness dù script-đích đã né (`:813`); (3) 2 lane cùng chạm bề-mặt-đo (L1 sửa file nằm trong scan-scope L2) → số đo giữa-chừng trôi, clean-diff PHẢI cùng-snapshot. Tag `[s139, adap-3-ban, master-checklist-4-cua, c6-cite-2-nac, h24-4-pending-flip, faultinject-13-13, 53-writelane-onbehalf]`

- **S133+S134 (07-17) PE budget-freeze BE (Mig 67) + lũy kế TẠM TÍNH BE** → 📦 verbatim dời `archive/2026-07.md` (@S161 curate, moved-not-cut). Giữ hook: insertion-only-diff = proof thân-bất-động · backfill `Sql()` plain KHÔNG provider-guard (Mig 61/65/67 khuôn) · `TransitionAsync` KHÔNG SaveChanges nội bộ `:537` · Dev drift committed-chưa-apply → catch-up. Tag `[s133, s134, mig67, computepending, moved-l2]`

> **Persistent diary cross-session.** Auto-injected first ~200 lines at spawn (L1 HOT).
> Update BEFORE every stop. Tiered Memory v1: L1 HOT soft-cap ~30KB · 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..q4.md` + `archive/2026-06.md`. 🗺️ **Lookup map (Harness-9 S70): `archive/_INDEX.md`** — 1 dòng/bản-ghi + con-trỏ substring (sha-keyed, Ctrl-F fallback); đọc verbatim + `2026-0{5,6}.gist.md` (nén 4-field) theo nhu cầu.
> **Renamed S39:** implementer → implementer-backend (.NET half). FE patterns → `implementer-frontend` MEMORY. Test patterns → `test-specialist` MEMORY.

---

## 🎯 Role baseline

WRITE specialist .NET backend SOLUTION_ERP (`Domain+Application+Infrastructure+Api`). Case 1+2+3+5 only. Tools: Read, Edit, Write, Bash, Skill, Grep, Glob + 5 RAG MCP. Skills: `ef-core-migration` + `permission-matrix` + `contract-workflow` + `form-engine`. Output: commits + verify report.

## 🚫 Split boundary (S39) + auto-refuse
- ✅ MINE: `src/Backend/SolutionErp.{Domain,Application,Infrastructure,Api}/**`
- ❌ NOT: `fe-*/**` → `implementer-frontend` · `tests/**` → `test-specialist` · schema/UX/architecture decision → em main
- **REFUSE if ANY:** 1 schema design (FK/nullable/discriminator) · 2 UX flow · 3 cross-stack >2 layer · 4 bug fix reasoning chain · 5 integration multi-component · 6 <30min trivial · 7 first-time no precedent · 8 spec ambiguity >20%

---

## 📋 BE Patterns proven (apply confidently)

### Pattern 1: Per-chunk discipline A-E
A Domain+Mig (3-file) · B Application CQRS (Command/Query/Validator) · C Service (workflow logic) · D Api Controller · E commit. Build+test pass mỗi chunk. Commit `[CLAUDE] <scope>: Chunk X — ...` + Co-Authored-By Claude Opus 4.8 (1M context).

### Pattern 2: EF migration 3-file rule (gotcha #17 — BẮT BUỘC commit đủ)
`{TS}_{Name}.cs` + `.Designer.cs` + `ApplicationDbContextModelSnapshot.cs`. Path `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/`.
```bash
dotnet ef migrations add <Name> --project src/Backend/SolutionErp.Infrastructure --startup-project src/Backend/SolutionErp.Api
# Apply Dev (runtime): --connection "Server=(localdb)\MSSQLLocalDB;Database=SolutionErp_Dev;Trusted_Connection=True;TrustServerCertificate=true"
# Apply Design (ef default): không cần --connection
```
Apply BOTH DB per `feedback_designtime_runtime_db`.

### Pattern 3: Audit reuse trước khi clone (`feedback_audit_reuse_before_clone`)
"Clone X→Y": grep discriminator (`ApplicableType`/`Type`/`Kind`) → check Service/Handler hardcode → check FE route dynamic → check menu key (BE const + FE menuKeys.ts thường thiếu) → default reuse 80%, chỉ thêm menu key + sample seed.

### Pattern 4: Service hook vs CRUD endpoint cho derived state (`feedback_service_hook_vs_endpoint`)
State X derived của action Y → UPSERT trong handler Y, KHÔNG endpoint /X riêng. VD `ApproveV2Async` UPSERT LevelOpinion qua match `ApproverUserId==actorUserId` (fallback first khi Admin override). 0 endpoint mới.

### Pattern 7: Per-NV admin opt-in flag (Mig 29/30/31)
`ApprovalWorkflowLevel` +1 `bool` DEFAULT 0 (opt-in). EF `HasDefaultValue(false)`. DTO extend. FE Designer checkbox inline. Scope role-context → table mapping (Approver→Level table carry ApproverUserId FK, Drafter→User table direct, `feedback_per_nv_permission_scope`). Reusable F5/F6.

### Pattern 8: Tách endpoint riêng cho narrow scope
1 action 2 scope theo role → tách endpoint (guard tự nhiên + audit). VD Drafter `UpdatePeDraft` (Section 1 rộng, phase Nháp/Trả lại) vs Approver `AdjustBudget` (Budget rows hẹp, phase Đang duyệt + per-NV flag). KHÔNG default expand Drafter scope cho Approver.

### Pattern 9: Defense-in-depth FE+BE guard pair
UI `disabled={!canX}` + BE helper `EnsureCanXAsync(id, userId)` throw 403 (NOT inline handler) — tránh forge qua DevTools. Bất kỳ action sensitive (approve/reject/adjust).

### Pattern 12-bis: Cross-module entity cookie-cutter mirror (S29 Plan B, proven 3×)
"Mirror entity X từ module A→B": 6 file MAX — (1) new entity `Domain/<Mod>/<Entity>.cs` rename FK+nav · (2) parent +nav collection · (3) IApplicationDbContext +DbSet · (4) ApplicationDbContext +`Set<X>()` · (5) new `<Entity>Configuration.cs` (separate file mirror PE, NOT inline) · (6) `dotnet ef migrations add` 3-file. AuditableEntity inherit. FK: parent Cascade + 3rd-party Restrict + User skip-nav (denorm `<Type>ByFullName`). Apply 2 DB. ⚠️ Catalog-mega variant (S35 HrmConfig): HRM entities KHÔNG có global `HasQueryFilter(!IsDeleted)` (vs Master) → list query MUST `.Where(!IsDeleted)` thủ công (verify `Grep HasQueryFilter` Configurations FIRST). Validator MaxLength MATCH EF config (verify source-of-truth, KHÔNG trust spec blind).

### Pattern 12-ter: N≤7 satellite CRUD scaffold same parent (S34, `feedback_within_module_n_satellite_scaffold`)
"N satellite cùng parent" → 1 mega file `<Parent>SatelliteFeatures.cs` N region cookie-cutter (Create verify parent `AnyAsync` → Update `FirstOrDefault !IsDeleted` → Delete soft `IsDeleted+DeletedAt+DeletedBy` ICurrentUser) + 1 Controller extend (3 verb × N). Endpoint verify `parentId==cmd.ParentId` BadRequest mismatch. Per-action policy override class-level Read.

### Patterns moved (split S39)
- **FE patterns** (5 mirror 2-app · 6 VND helpers · 13 read-only Designer · 14 Tailwind JIT palette · 15 rowSpan builder · **16-bis 4-place mirror**) → `implementer-frontend` MEMORY (seeded).
- **Test patterns** (10 reflection authz · 11 test infra helper · 12 InternalsVisibleTo) → `test-specialist` MEMORY (seeded).

---

## ⚠️ Anti-patterns (DO NOT)
1. ❌ Skip MEMORY · 2. ❌ `--no-verify` · 3. ❌ `git add -A`/`git add .` (specific files) · 4. ❌ Touch outside spec scope · 5. ❌ Push remote autonomous (em main pushes) · 6. ❌ Modify `SolutionErp.slnx` autonomous · 7. ❌ Lower bar match em main (Smart Friend) · 8. ❌ Proceed khi ambiguity >20% → REFUSE

---

## 🧠 SOLUTION_ERP BE conventions (S40)
- **BE .NET 10:** PascalCase entities + DTO records + command names. CQRS+MediatR+FluentValidation+AutoMapper. Repository qua `IApplicationDbContext`. `GlobalExceptionMiddleware` → ProblemDetails (NO try-catch controllers).
- **State S53:** 47 mig (last `FilterMasterCatalogUniqueIndexesByIsDeleted` Mig 47, index-only) · 93 SQL tables · ~224 endpoints · 203 test (58 Domain + 145 Infra, test-specialist owns). Phase 9 UAT skip per chunk (`feedback_uat_skip_verify`).
- **Build:** `dotnet build SolutionErp.slnx` clean 0 err. Commit `[CLAUDE] <scope>: <msg>` + Co-Authored-By Claude Opus 4.8 (1M context).
- **Pin (KHÔNG `*`/latest):** MediatR `12.4.1` (14 fail DI) · Swashbuckle `6.9.0` · Node CI `20.x` · LibreOffice `25.8.6` · @microsoft/signalr `8.0.7`.

---

## 📅 Recent activity (FIFO — older → archive/git)

- _(Mục FIFO này hiện RỖNG — 4 entry cuối [2026-07-12/13: S116 PE giá ÂM · S113 Supplier import v2 Mig 64 · S112 PE sign-off · S112 Supplier Excel-import Mig 63] dời verbatim → `archive/2026-07.md` @S167. Trước đó: 2026-06-25→19 [S88 frozen-relocation · PE Section-B Mig 59 · PE chuông approver] → `archive/2026-06.md` @S116; S77→S56 [7 entries] @S112. Việc gần đây SỐNG nằm ở khối L1 HOT đầu file (S167/S166/S161/S139). Recall verbatim: archive tương ứng / `archive/_INDEX.md` / RAG `search_memory`.)_

---

## 🔄 Curate trigger
- >~30KB → archive recent → L2 `archive/<period>.md`. Stale >3mo → remove.
- **Last curate: 2026-08-01 S167 (self-FIFO, hook read-limit-guard bắn @22.4KB)** (22.4→15.4KB, dưới ngưỡng 17.1KB hook đòi): moved verbatim 4 entry Recent-activity 2026-07-12/13 (S116/S113/S112×2) → `archive/2026-07.md` (2.8→10.3KB). Mục Recent-activity nay rỗng có chủ đích — việc SỐNG ở khối L1 HOT đầu file. Prev **S116** (21.5→~15KB): moved 3 oldest Recent-activity (2026-06-19/24/25: PE chuông · PE Section-B Mig 59 · S88 frozen-relocation) verbatim → `archive/2026-06.md`, kept 4 newest (S116/S113/S112×2). Prev **S112** (25.6→~16KB): S77→S56 7-entry → 2026-06.md. Prev **S70 Harness-9** (33.2→17.4KB): L2 dark-matter recovery + `_INDEX` + gists. Prev: S40 dedup-split · S34 · S22.

## S152 (2026-07-25) — R1 retention-note ranh auto-refuse test [M9 on-behalf lead]
- **Integration testing multiple components = việc `test-specialist`, KHÔNG phải mình** — mình scaffold + compile-check; assertion/integration test cross-component (SQLite fixture, authz regression) do test-specialist own (tiền lệ S67 HRM test-after +23, S69 +20 đều test-specialist). Nhận viết integration test = double-touch tests/ sai split. Tag `[s152, r1-retention, refuse-integration-testing-components]`
