# Session 53 — gotcha #57 EXT Master (Mig 47) + P11-D reassign-UI + P11-E menu-key + database-agent verified-runtime

**Date:** 2026-06-08 · **Mode:** HMW-mode ON · **Theme:** "làm hết luôn đi" — full close of remaining Phase 11 follow-ups + governance + doc-drift, all prod-verified.

**User flow:** `/session-start` → "Workflow làm nhanh" (Task B gotcha #57 EXT) → "làm hết luôn đi" (Task C + D + doc-drift E + `/session-end`).

---

## Outcome

| Metric | Before (S52) | After (S53) |
|---|---|---|
| Migrations | 46 | **47** (Mig 47 `FilterMasterCatalogUniqueIndexesByIsDeleted`) |
| Tests | 200 | **203** (+3 `MasterCatalogFilteredUniqueTests`) |
| Menu keys | ~55 | **~56** (+`Off_AttendanceReport`) |
| Sub-agents | 11 (database-agent executed-file) | 11 (database-agent **verified-runtime**) |
| Bundle admin | `DYfjnpY0` | **`DfCfHUE9`** (rotated by C+D FE) |
| Bundle user | `_3S0BPJ2` | `_3S0BPJ2` (unchanged — fe-user untouched) |
| Tables | 92 | 92 (Mig 47 index-only) |

**2 code commits, both Gitea-verified prod:**
- `44b9e54` — Task B (Mig 47, gotcha #57 EXT Master) → Run #260 success
- `dbf6648` — Task C + D (ItTicket reassign-UI + AttendanceReport menu-key) → Run #261 success

---

## Part 1 — Bootstrap (3 governance agents)

- **🔵 database-agent → VERIFIED-RUNTIME** (first real spawn since S52 adopt). CLI restart confirmed (registry loaded it). Spawn-test: connected LocalDB `(localdb)\MSSQLLocalDB` Dev+Design, read `__EFMigrationsHistory`, introspected `ItTickets`, queried `sys.tables`. **Caught 2 real drifts** the rest of the toolchain missed:
  1. **Mig 46 committed-but-unapplied-local** — the migration file + snapshot were on disk (3-file committed S52) and prod had it (CI-applied), but local `SolutionErp_Dev`/`_Design` were stuck at Mig 45 (S52 session-limit killed the local `database update`). 203 SQLite tests + green prod both **miss** this — local DB is the one surface nothing else verifies. → Closed as a bonus when Mig 47's `database update` applied 46+47 to both local instances.
  2. table-count: raw `sys.tables` = 93 = 92 domain + `__EFMigrationsHistory` (docs' "92 domain" correct).
- **🟫 tooling-auditor (H1)** re-report: top-5 doc-drift (ef-core skill 43, roster 10→11, CLAUDE.md root 45, docs/CLAUDE.md 56→57/91→92). Patched at session-end (Part 4).
- **⬜ harvest-curator (H2)** re-report: 🟢 clean. S52 proxy-append (Wave-2 killed-mid implementer-backend + test-specialist diaries) **verified present, 0 orphan**. 11 diaries byte>0. 1 cosmetic flagged (`s??`→`s52` placeholder).

## Part 2 — Task B: gotcha #57 EXT Master (Mig 47)

**Workflow:** 🟪 test-specialist (RED) → 🟨 implementer-backend (fix+Mig47+GREEN) → 🟥 reviewer (PASS, 0 issues).

- 3 Master configs (Department:18 / Project:19 / Supplier:24) → unique Code index `.HasFilter("[IsDeleted] = 0")` (byte-for-byte from the existing 13 filtered indexes). Mig 47 = 3× DropIndex+CreateIndex filtered (Up) / reverse (Down).
- **Root cause** (test-specialist pinned it): app-level dup-check `db.X.AnyAsync(x => x.Code == req.Code)` runs *through* `HasQueryFilter(!IsDeleted)` → ignores the soft-deleted row → says "Code free"; the **bare** DB unique index counts the soft-deleted row → UNIQUE-500. Admin delete+re-add same Code = reachable 500. `.HasFilter` aligns the index with the query filter.
- test-before RED confirmed (3× `SqliteException UNIQUE`) → GREEN after fix. Test 200→203.
- Prod (Run #260): 3 indexes `filter_definition` NULL → `([IsDeleted]=(0))` live (cicd sqlcmd).
- **gotcha #57 EXT backlog CLOSED** — cumulative 6× (Holiday Mig 43 S45 + 3 HRM Mig 45 S51 + 3 Master Mig 47 S53).

## Part 3 — Task C+D (one commit `dbf6648`)

**Workflow:** 🟨 implementer-backend (D-BE) → 🟧 implementer-frontend (C + D-FE) → 🟥 reviewer (PASS, 0 issues).

- **Task C — ItTicket admin reassign-UI** (fe-admin ONLY, intentional mirror-break): per-card pencil → Dialog with user Select (reuse existing `GET /users` pagedSize 200) → `PUT /it-tickets/{id}/assign` (Admin-only, 204 handled) → invalidate `['it-tickets']`. fe-user `ItTicketsPage` untouched (employees don't reassign). Top comment updated to flag divergence.
- **Task D — AttendanceReport menu-key** (no migration): `MenuKeys.OffAttendanceReport = "Off_AttendanceReport"` + `All[]` + DbInitializer seed `(…, "Báo cáo chấm công", MenuKeys.Off, 8, "FileBarChart")`. Admin-perm auto via `SeedAdminPermissionsAsync` iterating `All`. Idempotent seed → prod gets leaf on restart. FE: `menuKeys.ts` const + `Layout.tsx` staticMap → existing `/attendance/report` route (only 2 of 4-place needed; page+route from S52).
- **Menu-key = 5 mirror points** (gotcha #50) — reviewer byte-verified `"Off_AttendanceReport"` identical at: BE const, BE `All[]`, DbInitializer seed, FE `menuKeys.ts`, FE Layout staticMap.
- Prod (Run #261): `Off_AttendanceReport` MenuItems row seeded (ParentKey=Off, Order=8) · admin bundle `DfCfHUE9` rotated / user `_3S0BPJ2` unchanged · smoke health 200, /users + /it-tickets 401.

## Part 4 — Closeout (doc-drift E + session-end)

- **Doc-drift E (H1 top-5 patched):** ef-core SKILL.md + skills/README (43→47, Mig 47, S45→S53) · agents/README roster 10→11 (×3) + plugin "18 enabled"→"18 registered/15 enabled" · CLAUDE.md root 45→47 mig + 186→203 test + Mig list +46+47 · docs/CLAUDE.md 56→57 gotcha + 91→92 ERD + Mig 27-47.
- database-agent adap-report: executed-file → **verified-runtime** (§2 nấc + §5 caveat #1 + commit-sha `e9ee97f`).

---

## §L.b(d) Spawn-records (4-field {agent · task · nấc · evidence})

| Agent | Task | Nấc | Evidence |
|---|---|---|---|
| 🔵 database-agent | verified-runtime spawn-test (introspect ItTickets/migrations) | **verified** | LocalDB connect + `__EFMigrationsHistory` read + caught Mig 46-unapplied-local + table-count 93 |
| 🟫 tooling-auditor (H1) | session-start re-report (4-mặt freshness diff vs S50) | verified | top-5 doc-drift coords → all patched Part 4 |
| ⬜ harvest-curator (H2) | session-start re-report + session-end gate (5-trục) | verified | 11 diaries byte>0, S52 proxy-append present, 0 orphan |
| 🟪 test-specialist | Mig 47 test-before (3 `MasterCatalogFilteredUniqueTests`) | verified | RED 3× SqliteException → GREEN; 200→203 |
| 🟨 implementer-backend | Mig 47 (configs+migration) + D-BE (MenuKeys+DbInitializer) | verified | Run #260 + #261; dotnet build 0-err; em-main re-verified |
| 🟧 implementer-frontend | C reassign-UI + D-FE menu wiring (fe-admin) | verified | npm build 0-err; admin bundle `DfCfHUE9` rotated prod |
| 🟥 reviewer | Mig 47 pre-commit + C+D pre-commit | verified | 2× PASS 0-issues; em-main confirmed builds + git scope |
| 🟩 cicd-monitor | Mig 47 prod verify + C+D prod verify | partial (truncated 2×) | Run #260/#261 success + filter_definition + menu-row; **gaps curl-self-recovered** |

## §L.a — Deterministic detect (AS scan)

- **AS-truncation (gotcha #53/#55):** cicd-monitor truncated output 2× (C+D verify cut mid-Q3 on its own `AspNetRoles` query typo). **Guard held** (not a new RCA — existing `feedback_agent_kill_recovery` guard): em-main curl-self-verified the gaps (Gitea run #261 + bundle hashes via public curl; menu-row from agent's pre-truncation sqlcmd). No prod impact, no re-spawn needed. Active-Guard "curl-self-verify recovery" → marked verified (2nd consecutive session held: S52 session-limit + S53 truncation).
- No bug-production (all 203 green, both deploys verified). No new RCA entry.

## Lessons

1. **Read-advisory DB lens earns its seat on first spawn.** database-agent caught a committed-but-unapplied-local migration that *every other green signal missed* (SQLite tests build from snapshot, prod uses CI-applied DB). Local DB is the unverified surface.
2. **cicd-monitor truncation is now a 2-session pattern** → curl-self-verify (public Gitea API + bundle HTTP) is the reliable recovery; don't re-trust a 2nd agent round-trip. Bundle-hash diff is content-addressed ground truth for "right FE shipped".
3. **Hybrid scout→pipeline** (em-main recon inline → Workflow with exact coords) kept both workflows tight + 0-issue reviews.

## Next session (anh pick)
- **Phase 9 Ops** (anh main coordinate): SMTP · rotate creds · SQL auto-backup register · UAT real user.
- **Monthly drift audit 2026-07-01** (cron).
- Optional: mirror ItTicket reassign to fe-user · RAG re-index S42-S53 (AI_INFRA op).
