# database-agent — MEMORY (L1 HOT)

- **S161 (07-29) W1 KHKK design-review Mig 69 `[hmw wf_a95c9909-14b · return CLEAN + sub-md 21KB]`:** **PASS 8/8 + DB11, 0 blocker** — Mig `20260729122015_AddContractSigningPlans` 7 CreateTable/20 CreateIndex/0 ALTER. Phép đo đáng giữ làm khuôn review sau: (1) **Down-proof KHÔNG destructive**: `dotnet ef migrations script <mig> <prev> --no-build` sinh script Down atomic (1 transaction) — thay cho apply-rollback thật trên DB đang dùng (DB2); (2) **đo `sys.foreign_keys`/`sys.indexes.filter_definition`/`sys.columns.max_length` thay đọc code** — nvarchar max_length = BYTE = 2× ký tự; (3) **multiple-cascade-path né bằng loose-Guid+HasIndex** (Attachments.DossierItemId — FK Cascade ở đó = err 1785 từ chối CREATE); (4) **snapshot-drift check rẻ**: diff Designer-vs-Snapshot bỏ blank/comment → chỉ được phép lệch header (Migration attr/BuildTargetModel); (5) **twin-parity làm chuẩn thiếu-index**: 4 cột không IX đều parity PE (cột hiển thị không vào WHERE site nào đã wire); LevelOpinions unique KHÔNG filter = ĐÚNG (đo cả 2 twin PE/Contract cùng dạng — UPSERT 1-row-per-level); (6) IX_SlaDeadline VẮNG là ĐÚNG (SlaExpiryJob chỉ quét Contracts; entity tự khai SLA=display-only) — index không job quét = index chết; (7) parity 2 DB LocalDB: TABLECOUNT 96 ×2 + MIGTOP ×2 + IXCOUNT 27 (=20 IX + 7 PK) khớp 1:1 file. Rủi ro khai: SERIALIZABLE read-modify-write có thể deadlock 1205 fail-safe (parity 4 module Office prod). Tag `[s161, mig69-review, down-script-proof-no-destructive, sys-catalog-over-code, twin-parity-index-baseline]`

> READ-advisory DB specialist SOLUTION_ERP (.NET 10 EF Core 10 + SQL Server, single `ApplicationDbContext` dbo). Adopt AI_INFRA broadcast `2026-06-08-Agent-database-codebase-agents` (floor DB1–DB11), S52 2026-06-08. Seed = em main. **Nấc hiện: executed-file — verified-runtime CHỜ anh restart CLI + spawn-test.**

## Vai trò (FORM tailored SE)
- **READ-advisory tier** — DESIGN/REVIEW/PERF/CONCURRENCY-advise, KHÔNG author file. `implementer-backend` author entity+config+migration; em main solo quyết schema-design cuối. database-agent = deep-DB lens hỗ trợ + review.
- Floor DB1–DB11 (canonical, KHÔNG hạ) — chi tiết `.claude/agents/database-agent.md`.
- Skill: `sql-database-assistant` (SQL Server raw, KHÔNG cover EF-Core) + `ef-core-migration` (EF Core 10 pin + 3-file rule). Verify present TRƯỚC wire.
- `store_memory` STRIPPED → ghi finding vào FILE này; em main + re-index → RAG.

## SE facts cốt lõi (DB10 evidence-based — re-ground khi cần)
- **45 migration → 92 tables** (S51). `sys.tables` = ground-truth (narrative count drift "incremented-per-session" → re-ground).
- 2 DB instance: LocalDB `SolutionErp_Dev` (runtime) / `SolutionErp_Design` (design-time) — gotcha designtime-vs-runtime DB (apply migration cả 2 qua `--connection` override). Prod = `.\SQLEXPRESS\SolutionErp`.
- Soft-delete UNIQUE index PHẢI `.HasFilter("[IsDeleted]=0")` (gotcha #57 — 13× pattern; S45 Holiday + S51 LeaveType/Shift/OtPolicy/Vehicle/Driver). EXT backlog: Department/Supplier/Project (Mig 46 worktree).
- Codegen atomic = `WorkflowAppCodeGen.GenerateMaDonTuAsync` dùng `IsolationLevel.Serializable` tx (Prefix-keyed sequence) — pattern ĐÚNG tham chiếu cho concurrency.

## 🎯 DB11 gap đã biết (concurrency — vai trò chính)
- **S43/S56 LeaveBalance lost-update — FIX DESIGNED S56 (approach A, NO migration).** `ApproveLeaveRequestHandler` terminal DaDuyet branch (LeaveOtApprovalFeatures.cs:355-386) đọc `bal.UsedDays` in-memory + `+= p.NumDays` + bare SaveChanges → 2 concurrent terminal-approve cùng (User,Type,Year) lost-update. **Fix:** wrap terminal-branch trong explicit `BeginTransactionAsync` → (1) SaveChanges persist opinion-upsert + status=DaDuyet + ensure balance-row exists (insert UsedDays=0 nếu absent), (2) `ExecuteUpdateAsync(SET UsedDays = UsedDays + n)` atomic DB-side race-free, (3) Commit. Atomic-with-approval preserved (1 tx all-or-nothing). Exactly-once untouched (early `Status != DaGuiDuyet` guard :296). NO ambient TransactionBehavior (chỉ ValidationBehavior) → handler own tx boundary. **Cast `(DbContext)db` để reach Database** (IApplicationDbContext chỉ expose DbSet+SaveChangesAsync). Existing terminal test (Case 4 :226) assert Status/Level/opinion-count only — KHÔNG assert UsedDays-on-tracked → ExecuteUpdate (bypass tracker) WON'T break suite. Spec authoritative → implementer-backend author.
- OtRequest terminal KHÔNG trừ phép (chỉ status) → no lost-update bên OT.
- P11-D SLA flags (`SlaWarnedSent`/`SlaBreached`) + P11-F codegen = concurrency-sensitive → DB11 lens áp được.

## Boundary (⟂)
- vs implementer-backend: DESIGN/REVIEW vs AUTHOR (KHÔNG double-touch migration file).
- vs investigator-codebase: deep DB-layer (introspection/query-plan/concurrency) vs broad grep/audit.
- vs reviewer: DB-layer design-review (DB6/DB11/DB5) TRƯỚC author vs adversarial pre-commit cross-stack.
- KHÔNG: FE · business-logic · deploy · session-lifecycle audit.

## Accuracy (G-015)
- DB7 scope-DB-only = PHÂN-VAI, KHÔNG "read-only enforced" (giữ `Bash` → write-channel shell mở; containment = em main single-writer + git-diff post-session).
- Schema/perf-claim từ introspection THẬT (`sqlcmd`/`dotnet ef`), KHÔNG narrative.

## Log
- **S52 (2026-06-08):** Seeded (em main, adap-apply database-agent). Roster 10→11. Nấc executed-file. CHỜ restart + spawn-test → verified-runtime.
- **S56 (2026-06-09) — pre-golive verify (2nd real spawn, verified-runtime ✓):** Schema/Mig integrity P11 + S55 master-data = SOLID. Mig 42-48 applied IDENTICALLY Dev+Design+prod `.\SQLEXPRESS` (48 mig / 92 tables, **NO S53-style unapplied-local drift**); 0 pending model-snapshot diff; **9 gotcha-#57 filtered-unique** confirmed BOTH EF config (HasFilter×13) AND DB (`filter_definition=([IsDeleted]=(0))`) incl Vehicle/Driver day-1; FK đúng (LeaveBalance→LeaveType Restrict + UNIQUE(User,Type,Year); 4× LevelOpinions Cascade(parent)/Restrict(Level)+UNIQUE composite; ItTicket SLA nullable). **DB11 FAIL (1 major, fast-follow KHÔNG blocker):** `LeaveBalance.UsedDays += NumDays` @ `LeaveOtApprovalFeatures.cs:361-386` chạy bare SaveChanges (no tx / no RowVersion / READ COMMITTED) → concurrent double-approve lost-update. **S43 gap STILL OPEN.** Fix-pattern sẵn repo: Serializable tx (codegen `:34`) HOẶC RowVersion+retry (1 mig). Low-prob ~30 user, recoverable Admin Adjustment. Tag [s56, pre-golive-verify, schema-pass, db11-lostupdate-open].
- **S57bis (2026-06-11) — PE gắn Hạng mục design (3rd real spawn, DELIVERED — on-behalf em main ghi hộ, H2-proposed):** Introspect LocalDB → design `PurchaseEvaluations.WorkItemId Guid?` scalar **loose-Guid + IX, KHÔNG FK vật lý** — nhất quán convention PE (ProjectId/SelectedSupplierId đều loose; duy nhất ApprovalWorkflowId có FK). Guard = validator + handler mirror S43 FK-invariant. KHÔNG đụng CodeSequences (mã phiếu giữ `PE/{YYYY}/{A|B}/{Seq}`). LEARNED: PE FK-convention map (loose-Guid là norm module này — FK vật lý là exception). SURPRISE: WorkItems = catalog GLOBAL (KHÔNG ProjectId) → 2 dropdown độc lập; "Dự án (năm) – Hạng mục" chỉ là chuỗi ghép hiển thị, không ràng buộc quan hệ. Ship: Mig 49 deploy Run #381 prod-applied. Tag `[s57bis, mig49-design, on-behalf]`.
- **S97 (2026-07-01) — PE prod-điều-tra A/005 + deep-review lane (4th+5th real spawn, on-behalf em main ghi hộ, H2-proposed @S98):** (4th) Điều tra prod (API+sqlcmd): A/005/A/008 Phase=7 `endsBeforeCeo=False`, lịch-sử 4 record dừng "[Bước 2 — Cấp 2]" KHÔNG có record CEO; đối chứng A/010 9-record có CEO. **Chẩn đoán:** A/005/A/008 finalize-CCM Cấp 2 (KHÔNG qua CEO) — lịch-sử ĐÚNG, sai là badge do `EndedByLevelFinalize=0` → phát hiện **Mig 60 backfill WRONG-TABLE** (query `Approvals.Comment` nhưng marker do `LogTransitionAsync:1073` ghi `Changelogs.ContextNote`) → dẫn tới Mig 61 + gotcha #79. (5th — `wf_4951c347` lane prod-data-integrity) **CLEAN:** flag-vs-marker 3 phiếu DaDuyet khớp (A/005/A/008=1+marker · A/010=0+no-marker), 0 miss/0 over-reach, Mig 60/61 applied, NCC=21, 0 orphan changelog, DB11 pointer-NULL đúng. LEARNED: **PE 2 bảng lịch-sử TÁCH VAI** — `PurchaseEvaluationApprovals` = per-approver record (:745) vs `PurchaseEvaluationChangelogs.ContextNote` = transition+marker (`LogTransitionAsync:1073`); backfill PHẢI xác định đúng bảng-writer + verify marker-count prod TRƯỚC. SURPRISE: backfill=0 rows @ cicd #482 lẽ ra là CỜ ĐỎ nhưng bị bỏ qua ("tưởng no-pre-Mig60 data"). Evidence: `docs/changelog/sessions/2026-07-01-S97-pe-finalize-wording-default-flip.md:97-133`. Tag `[s97, mig60-wrongtable, gotcha79, prod-integrity-clean, on-behalf]`.
