distill-gen: 1
# Gist — activity-s51-s52 (test-specialist · S51-S52) · 4-field distillation

> **distill-gen: 1** — already-distilled; do NOT re-compress (Phase 0.3 double-distill guard). Each block = VIỆC · KẾT-LUẬN(+file:line) · BÀI-HỌC · BẤT-NGỜ, ending `→ substring:"…"` that grep-resolves UNIQUE in the verbatim file. Pointer-style = substring (Ctrl-F), file/phrase keyed. value tag: cao/vừa/thấp.
> source-verbatim: `activity-s51-s52.md` (4 records → 3 clusters; C-A MERGES the two gotcha#57 episodes S51-HRM + S52-Master). Distilled S88 (2026-06-25) via /sleep-recovery-memory-l2, harvest-curator Phase-1 propose + reviewer Fidelity-gate.
> ⚠️ Counts (185 / 191 / 200 PASS) are EPISODE-LOCAL FIFO-snapshots (S51-S52-era), NOT current state — canonical current → `docs/STATUS.md`.

---

## C-A · gotcha #57 filtered-unique soft-delete (MERGED S51-HRM + S52-Master) [cao]

**VIỆC:** Test-before reproduce **gotcha #57** across 2 episodes — S51 HRM (`HrmConfigFilteredUniqueTests.cs`, +4: 2 GREEN Vehicle/Driver Mig 44-already-filtered + 2 RED LeaveType/Shift) → S52 Master (`MasterCatalogFilteredUniqueTests.cs`, +3 ALL RED Dept/Project/Supplier). test-before.

**KẾT-LUẬN:** Bare `.IsUnique()` (Master configs `Dept cfg:18` / `Proj:19` / `Supp:24`) counts soft-deleted rows → `SQLite Error 19 UNIQUE constraint failed` on `{Departments|Projects|Suppliers|LeaveTypes|ShiftPatterns}.Code`. ROOT-CAUSE: app-check `AnyAsync(Code==X)` passes THROUGH `HasQueryFilter` (excludes soft-deleted → PASS) → `Add+SaveChanges` → DB UNIQUE counts the dead row → throw. **Fix = `.HasFilter("[IsDeleted]=0")`** (disk-true form `[IsDeleted] = 0`, with spaces) → flip GREEN — PER-EPISODE (do NOT cross-attribute): HRM `LeaveTypes`/`ShiftPatterns` in **Mig 45** (S51); Master `Departments`/`Projects`/`Suppliers` in **Mig 47** (S53, gotcha #57 EXT — per canonical `CLAUDE.md`; the verbatim Master record left the migration number bare). Handlers 1-dep `Create{Dept|Project|Supplier}CommandHandler(db)` / `new CreateXxxHandler(db)`. Verdict: S52 `3 RED` (Failed 3/Passed 0); S51 `2 RED INTENTIONAL + 2 GREEN`. RED ≠ test bug → REPORTED em main fix migration.

**BÀI-HỌC:** all-count assertion MUST use `IgnoreQueryFilters()` (verified 3× on disk in Master test) — **CORRECTED** the HRM-ref's wrong `Count(Code==X)`-on-filtered-DbSet (returns 1 not 2 because DbSet already carries HasQueryFilter). Reusable pattern = **active-count = plain DbSet · all-count = `IgnoreQueryFilters()`**. Mirror = `HrmConfigFilteredUniqueTests` GROUP B / `HolidayTests Case 7` (seed-soft-deleted-slot origin).

**BẤT-NGỜ:** `AuditingInterceptor` (prod soft-delete Deleted→Modified+IsDeleted=true) NOT wired in `SqliteDbFixture` → `Remove+SaveChanges = HARD delete` (can't test soft-delete via Remove) → must seed row `IsDeleted=true` manually to simulate the occupied Code-slot.

→ substring:"all-count PHẢI" (IgnoreQueryFilters nuance, S52-Master record) + substring:"AuditingInterceptor (prod soft-delete" (soft-delete-in-test surprise, S51-HRM record)

## C-B · round-robin assign + SLA-due [vừa · S52]

**VIỆC:** +9 test `ItTicketAssignSlaTests.cs` (Infra 133→142, baseline 191→**200**); proxy by em main (agent killed session-limit before MEMORY step).

**KẾT-LUẬN:** Round-robin assigns lowest-load `IsActive` user in Dept `Code="IT"` (A=1-open ticket vs B=0 → assign **B**, load 0<1); tie `A=B → ThenBy(Id)`; edge no-dept-IT / no-user-IT / user-outside-IT / `IsActive=false` → unassigned. SLA-due `Urgent→+4h / High→+8h / Medium→+24h / Low→+72h`, assert `SlaDueAt == CreatedAt + SlaWindow[priority]`. Regression P11-F: code-gen still `^IT/\d{4}/\d{3}$`.

**BÀI-HỌC:** assign + per-priority SLA-window unit-testable via direct handler; window = flat lookup table.

**BẤT-NGỜ:** `ItTicketSlaJob` BackgroundService SKIP unit-test — breach-query inline, hard to test directly (REPORTED, not covered).

→ substring:"load 0<1"

## C-C · codegen day-type + Serializable-SQLite-OK [vừa · S52]

**VIỆC:** +5 test (Infra 128→133, 191 PASS): `ItTicketCodeGenTests` (3) + `AttendanceReportTests` (2), P11-E/F test-after. Handler `CreateItTicketHandler(db, cu, clock)` 3-dep MediatR.

**KẾT-LUẬN:** Codegen `^IT/\d{4}/\d{3}$` sequential `001→002` per-prefix `IT/{year}` LastSeq++, `2027 reset 001`. Day-type: holiday check runs BEFORE weekend/weekday → seed `2026-06-01 (thứ Hai)` into holidaySet → asserts **Holiday** despite weekday (override day-of-week); `Holiday.Date=DateOnly` → `BuildHoliday` uses `DateOnly.FromDateTime`. `OtWeighted = 2×1.5+3×2.0+1×3.0 = 12.0m`. DepartmentId filter joins Users `u.DepartmentId==deptId` with `DefaultIfEmpty` (dept row optional).

**BÀI-HỌC:** holiday-precedence-over-day-of-week = core day-type rule. MSBuild-OOM on full parallel → `-maxcpucount:1 -p:BuildInParallel=false` (env resource, NOT test fail).

**BẤT-NGỜ:** **Serializable-on-SQLite GOTCHA = NON-ISSUE (confirmed)** — `BeginTransactionAsync(IsolationLevel.Serializable)` in `WorkflowAppCodeGen.GenerateMaDonTuAsync` runs CLEAN on SQLite (provider maps isolation gracefully, no throw); format+seq+per-year hold without try/skip. Also proven by `WorkflowAppApproveV2Tests` (DT/LR path).

→ substring:"Serializable-on-SQLite GOTCHA = NON-ISSUE"
