[CLAUDE] Docs: S45 session-end — test-gap + Mig 43 sync
STATUS/HANDOFF S45 (154->181 test, Mig 43) + gotcha #57 (soft-delete UNIQUE must filter [IsDeleted]=0) + session log + root CLAUDE counts + ef-core skill Mig 43 row + flush 3 agent MEMORY (test-specialist proxy after #53 truncation + cicd Run #368 + investigator P11-C recon). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -47,8 +47,8 @@ Read-only CI/CD + post-deploy verifier SOLUTION_ERP. Polls Gitea Actions API, ve
|
||||
- **Gitea:** `git.baocaogiaoduc.vn/vietreport-admin/solution-erp` · workflow `.gitea/workflows/deploy.yml` · paths-ignore `['docs/**','**/*.md','.claude/skills/**']`
|
||||
- **Prod:** api/admin/eoffice `.solutions.com.vn` · SSH `ssh vietreport-vps` (Administrator, id_ed25519) · IIS site phys paths (S42 verified): API `C:\inetpub\solution-erp\api` · admin `\fe-admin` · user `\fe-user` (3 sites Started). DB `.\SQLEXPRESS`/`SolutionErp`/`vrapp` SQL-auth. **Conn string key = `ConnectionStrings.Default` (NOT `DefaultConnection`!)** — read pw from prod appsettings.Production.json when `$env:PROD_DB_PASSWORD` empty.
|
||||
- **SSH→PS quoting (S42 lesson):** nested bash→ssh→powershell mangles `$var`/`\"`. Use `iconv UTF-16LE | base64` → `powershell -EncodedCommand $B64`. Single-quote literal paths.
|
||||
- **Tests baseline:** **154 PASS** (S42 P11-B Run #367 sha 82d7fcf; +11 LeaveBalance +13 P11-A vs prev 130). CI gate runs both test projects BEFORE build/deploy → status=success ⟹ test gate passed (`tasks` endpoint reports terminal as `status:success`, `conclusion` field NOT populated). Local grep undercounts (Theory/InlineData) — trust CI conclusion. Phase 9 UAT mode skip per chunk OK.
|
||||
- **Mig latest repo:** **Mig 42 `20260530034336_AddLeaveBalances`** (S42 P11-B; prod tables 91). Path `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/`. Prod check `sqlcmd __EFMigrationsHistory ORDER BY MigrationId DESC TOP 5`.
|
||||
- **Tests baseline:** **181 PASS** (S45 Run #368 sha 0c5a014; Domain 58 + Infra 123 = +27 HRM coverage gaps: HrmConfigHolidayTests + EmployeeSatelliteTests + AuthorizePolicyRegressionTests-ext vs prev 154). CI gate runs both test projects BEFORE build/deploy → status=success ⟹ test gate passed (`tasks` endpoint reports terminal as `status:success`, `conclusion` field NOT populated). Local grep undercounts (Theory/InlineData) — trust CI conclusion. Phase 9 UAT mode skip per chunk OK.
|
||||
- **Mig latest repo:** **Mig 43 `20260601064128_FilterHolidayUniqueIndexByIsDeleted`** (S45; index-only change, prod tables stay 90-by-sys.tables / 91-by-doc — NO new table). Path `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/`. Prod check `sqlcmd __EFMigrationsHistory ORDER BY MigrationId DESC TOP 5`. ⚠️ Table-count drift: `sys.tables` count = 90 (verified S42 #364 + S45 #368), CLAUDE.md narrative = 91 — counting-convention diff, NOT missing table. Don't FAIL on 90.
|
||||
- **Bearer:** admin `admin@solutions.com.vn/Admin@123456` (full) · UAT `nv.test@solutions.com.vn/TestUser@123456` (Drafter CCM, gotcha #44 check)
|
||||
- **Bundle hash live S42:** admin `Krjvg_3j` · user `6sNStgxa` (Run #367 sha 82d7fcf). Prev admin `BU8FTBRi` · user `tepE4jvR` (#366/ffb2062). Bundle size ~800KB/750KB gz.
|
||||
- **DB pw (S42, when `$PROD_DB_PASSWORD` empty):** `vrapp/buKL3TGBkD0wDDbYVw65QeX9` read from `C:\inetpub\solution-erp\api\appsettings.Production.json`→`ConnectionStrings.Default`. ⚠️ Skill-doc path `C:\inetpub\apps\SolutionErp\Api` is STALE → real path `C:\inetpub\solution-erp\api`. sqlcmd over SSH works direct (no UTF-16 encode needed). ⚠️ sys-catalog string-concat queries hit collation conflict (`Latin1_General_CI_AS_KS_WS` vs `SQL_Latin1_General_CP1_CI_AS`) → add `COLLATE DATABASE_DEFAULT` per concatenated column.
|
||||
@ -68,6 +68,7 @@ BE (test+build) ~90s · FE × 2 ~60s/app · deploy ~30s · **total ~3min code /
|
||||
|
||||
## 📅 Recent runs (FIFO — older → archive/git)
|
||||
|
||||
- **2026-06-01 Run #368 (run_number 254) sha=`0c5a014` PASS ~4m20s (S45 Mig 43 filter Holiday UNIQUE by IsDeleted + 3 HRM test gaps — BE+tests ONLY, ZERO FE):** Push range `dbbed15..0c5a014` 2 commits: `051b62b` Tests +27 (HrmConfigHolidayTests + EmployeeSatelliteTests + AuthorizePolicyRegressionTests-ext → baseline 154→**181**) + `0c5a014` Mig 43 `20260601064128_FilterHolidayUniqueIndexByIsDeleted` (drops+recreates `IX_Holidays_Year_Date` as filtered UNIQUE `WHERE [IsDeleted]=0`, was unfiltered) + HolidayConfiguration.cs edit + Case-7 test flip. 7 files, all BE+tests, none in paths-ignore → CI ran. Poll iter4 status=success (started 13:43:47 → 13:48:07). **Bundle hashes UNCHANGED admin `Krjvg_3j` + user `6sNStgxa`** (= #367) — CORRECT for BE-only push, NOT ship-fail (Run #243 precedent; ship-proof = Mig 43 applied, not bundle rotate). **Mig 43 auto-applied prod** (history top = `...FilterHolidayUniqueIndexByIsDeleted` ✓). **THE FIX VERIFIED prod:** `IX_Holidays_Year_Date | unique=1 | filter=([IsDeleted]=(0))` — filter_definition non-NULL = filtered UNIQUE live (soft-deleted holidays no longer collide on UNIQUE). Health live+ready 200 Healthy. `Holidays` table exists, 10 rows, 2 named idx (PK + filtered UNIQUE). Prod tables=90-by-sys.tables (index-only change, NO new table — consistent #364 delta). NEW LESSON: filtered-index migration verify = check `sys.indexes.filter_definition` non-NULL (NOT just mig-history row); index-only mig = bundle unchanged + table-count unchanged both EXPECTED. Tag `[s45, run368, pass, mig43-filtered-index, be-only-bundle-unchanged]`.
|
||||
- **2026-05-30 Run #367 (run_number 253) sha=`82d7fcf` PASS ~4m08s (S42 P11-B LeaveBalance business logic, Mig 42):** Code commit 22 files (4 BE: Domain `LeaveBalance.cs` + App `LeaveBalanceFeatures.cs`/`LeaveOtApprovalFeatures` deduction hook + `LeaveBalancesController` + IApplicationDbContext + DbContext + Config + Mig42 3-file + 2 FE `WorkflowAppDetailPage`×2 +`workflowApps.ts`×2 + 2 tests + 4 agent-memory .md). Started 11:11:40 → success iter4 11:15:48. **Bundle rotate admin `BU8FTBRi→Krjvg_3j` + user `tepE4jvR→6sNStgxa`** (both changed ✓ FE shipped, verified AFTER status=success — pre-deploy snapshot still showed old hash, correct timing). **Mig 42 `20260530034336_AddLeaveBalances` auto-applied prod** (tables 90→**91**, `LeaveBalances` EXISTS). Schema ✓: UserId/LeaveTypeId/Year/EntitledDays/UsedDays/AdjustmentDays decimal + AuditableEntity soft-delete. **UNIQUE `IX_LeaveBalances_UserId_LeaveTypeId_Year`** + **FK→LeaveTypes del=NO_ACTION** (=Restrict) ✓. New endpoint smoke: `GET /api/leave-balances/my` unauth=**401** (route live not 404) + admin auth=**200** lazy-default 5 LeaveTypes (ANNUAL12/COMPASSIONATE3/MATERNITY180/SICK30/UNPAID0, all Used=0, `remainingDays`=entitled ✓ DTO shape has remainingDays/entitledDays) + `?year=2026` admin route 401 unauth + `PUT /adjust`=411 (route reg). health live/ready 200 Healthy. **NO seed gate concern** (plain table, lazy DTO — Stage 4.6 N/A). 0 regression. Note: prev run #366 (ffb2062 docs STATUS update) was a CODE-path push w/ status=success — NOT docs-only-skipped (commit touched only .md but Gitea still ran since prior range?); actually #366 display_title is Docs but ran full → confirms agent-memory .md NOT in paths-ignore (`.claude/skills/**` ignored, `.claude/agent-memory/**` NOT). Tag `[s42, run367, pass, p11b-leavebalance, mig42]`.
|
||||
- **2026-05-30 Run #365 sha=`75df04e` PASS ~4m05s (S42 P11-A fix workflow picker 2-bug + SetWorkflow endpoint, NO migration):** Code commit 11 files (4 BE controllers + 2 App features `LeaveOtApprovalFeatures`/`TravelVehicleApprovalFeatures` +125 lines + 2 FE `WorkflowAppDetailPage` ×2 + 1 test +79 lines). Status=success iter5 (started 10:15:45). **Bundle rotate admin `BLA09-qv→6D4k-aRi` + user `CXvejOE-→DkME-974`** (both changed ✓ FE fix shipped, verified AFTER status=success). +4 endpoint `PUT /api/{leave,ot,travel,vehicle-bookings}/{id}/workflow` (`Set{Module}WorkflowCommand`, route `[HttpPut("{id:guid}/workflow")]` body record `SetWorkflowBody(Guid ApprovalWorkflowId)`). Unauth smoke leave+ot/workflow → **401** (route exists, NOT 404 ✓). health live+ready 200 Healthy. Test gate **144** (CI both proj pre-deploy; grep undercounts InlineData=14 Fact at WorkflowAppApproveV2Tests). **NO migration** → skipped Stage 4.6 seed (verified #250). **NAMING RECONCILE:** Gitea task IDs are real #364 (e7b66cd, mem-labeled "#250") + #365 (this). Going forward use actual Gitea task id. **HEADS-UP em main:** follow-up commit `e47ef1d` (FE-User ProposalCreatePage workflow dropdown shape, latent S37 bug) pushed 10:19:17 DURING poll — NOT yet triggered CI run, will redeploy FE shortly (bundle may re-rotate). Out of scope this verdict. Tag `[s42, run365, pass, p11a-setworkflow]`.
|
||||
- **2026-05-30 Run #364 (mem #250) sha=`e7b66cd` PASS ~4m07s (S42 P11-A wire ApproveV2+LevelOpinions 4 WorkflowApps):** 1 commit BE+FE×2+Mig41+Tests. Status=success iter3. Bundle rotate admin `cWAXid0q→BLA09-qv` + user `CX79e2kZ→CXvejOE-`. **Mig 41 auto-applied prod** (latest=`20260530021936_WireWorkflowAppsApprovalV2`). Tables 84→**90** (+5: Leave/Ot/Travel/VehicleRequest LevelOpinions + WorkflowAppCodeSequences — ALL EXIST). 4 new endpoint smoke 200 auth (leave/ot/travel/vehicle-requests) + unauth 401 (route exists) + POST .../approve=411 (route reg). health live/ready 200. **Stage 4.6 seed gate PASS** (gotcha #51): 4 WF seeded prod despite DemoSeed:Disabled — QT-NP/OT/CT/XE-V2-001 AppType=5/6/7/9, verified call-site L142-145 OUTSIDE `if(!demoSeedDisabled)` gate. Test gate 141 (CI runs both proj pre-deploy). Note: table count 90 vs spec-expected 89 = baseline-count diff, NOT missing table (all 5 present). Stale doc drift deploy.yml comments "54/17 test" (cosmetic, flag em main). Tag `[s42, run250, pass, p11a-approvev2-workflowapps]`.
|
||||
|
||||
@ -70,6 +70,7 @@ Bearer từ `POST api.solutions.com.vn/api/auth/login` → status matrix expecte
|
||||
|
||||
## 📅 Recent activity (FIFO — older → archive/git)
|
||||
|
||||
- **2026-06-01 (P11-C Vehicle+Driver catalog pre-flight):** Mig 44 next (latest=Mig 43 `FilterHolidayUniqueIndexByIsDeleted` S45). **NO Vehicle/Driver master exists** — chỉ `Office/VehicleBooking.cs` (request, Mig 39) dùng FREE-TEXT (`VehicleLicense`/`VehicleName`/`DriverName?` strings, :13-19 comment "defer catalog Phase 11"). **RECOMMEND home = extend HrmConfigs** (NOT new module): `Application/Hrm/HrmConfigFeatures.cs` mega 4-region + `HrmConfigsController` (`[Authorize]` read / `[Authorize(Roles="Admin")]` write) — add Region 5 Vehicle + 6 Driver (kind `vehicles`/`drivers`), pattern proven 12-bis. ⚠️ HRM entities KHÔNG global HasQueryFilter → manual `.Where(!IsDeleted)` + UNIQUE soft-delete cần `.HasFilter("[IsDeleted]=0")` (Holiday Mig 43 lesson, LeaveType/Shift UNIQUE Code chưa có filter → nếu Vehicle BienSo UNIQUE phải add filter). **FE cheap:** `HrmConfigsPage.tsx` declarative KIND_CONFIG Record — add 2 entry vào KIND_CONFIG + KINDS[] + `renderCells` branch + smart-defaults; NO new page. **Menu+perm:** add 6 const `MenuKeys.cs` (+`Hrm_Config_Vehicles/Drivers`), thêm vào `All[]` (:140) → Admin auto-grant qua `SeedAdminPermissionsAsync` loop (:1909 idempotent), +2 MenuItem `DbInitializer` :1757, +2 `menuKeys.ts` mirror. Hrm_Config KHÔNG inherit-root (4 root=Contracts/Workflows/Pe/PeWf only) → leaf cần row riêng (loop lo). **Fields (NamGroup XeCong DROPPED Mig 2026-05-15, ref response shape only):** Vehicle{Code/BienSo UNIQUE, Hang, MauXe, SoCho int, TrangThai, GhiChu}; Driver{Code/Hoten, SDT, GPLX, Hang bằng, TrangThai}. FK link defer: P11-C = catalog only, optional FK `VehicleBooking.VehicleId?/DriverId?` giữ free-text back-compat (Mig sau). Tag `[pre-flight, p11-c, vehicle-driver-catalog]`.
|
||||
- **2026-06-01 (MONTHLY DRIFT AUDIT):** Ground truth code: **migrations=42** (last `AddLeaveBalances`, path `.../Persistence/Migrations/*.cs`) · **gotchas highest=#56** (file header NO self-count → drift chỉ ở file *reference* gotchas.md) · tests=154 (58 Domain+96 Infra, em main verified) · tables≈91 (45 config class nhưng Catalogs=4+ContractDetails=7+7 Identity untracked → khớp STATUS 91, KHÔNG cheap-exact). **Biggest drift: ef-core-migration SKILL** (frontmatter:3 "31 migration"→42, :19 history "31"→42 + thiếu rows Mig 27-42, :50 "59 bảng"→91, :80 "111 test"→154, :258/:267 "59 bảng"). dependency-audit SKILL:153 "49 bẫy"→56. CLAUDE.md:53 "40 mig→84 bảng"→42/91, :66 "130 test"→154, :133 "52 bẫy"→56. docs/CLAUDE.md:65 "52"→56. **schema-diagram GAP:** migration TABLE dừng Mig 16 (line 487); detail § cuối =§15=Mig 26 (§16=Related KHÔNG phải mig) → thiếu § cho **Mig 27-42** (16 mig). database-guide:4 "47 bảng/13 mig"→91/42. STATUS:97 backlog "Curate 4 agent MEMORY 35.7/35.3/30.9/28.4" STALE (đã curate S40 `78c9de3`, all ≤16KB) → REMOVE. NO-CHANGE: contract-workflow (historical counts OK), form-engine, iis-deploy (no count), HANDOFF (S43 current), PROJECT-MAP (no count). Tag `[drift-audit, monthly, 2026-06]`.
|
||||
- **2026-05-30 (P11-A WorkflowApps wire pre-flight):** 4 module Leave/OT/Travel/Vehicle. Schema pin ĐÃ CÓ SẴN (Mig 39): `Office/{Module}.cs` đều có `ApprovalWorkflowId?`+`CurrentApprovalLevelOrder?`+`WorkflowAppStatus` (5-state khớp ProposalStatus). SKELETON tại `Application/Office/WorkflowAppsFeatures.cs:11-15` (chỉ Create+List, KHÔNG Approve/Reject/Return/GetById). **Proposal = mirror HOÀN HẢO** (cùng Office ns, Mig 38): `ProposalFeatures.cs:403-486` ApproveHandler = flatten `Steps.OrderBy(Order).SelectMany(Levels.OrderBy(Order))` global level index → `allLevels[CurrentApprovalLevelOrder-1]` → actor match `Level.ApproverUserId==uid||Admin` → UPSERT LevelOpinion → advance++/DaDuyet. ⚠️ `ApprovalWorkflow.cs:72` nói Level **KHÔNG OR-of-N** (1 ApproverUserId/Level) — KHÁC memory cũ "OR-of-N", verify lại. Gap: 4 bảng `{Module}LevelOpinion` mới (Mig 41+), 3 route/controller, 4 seed WF, FE Detail+Opinion (chỉ có WorkflowAppsListPage chung). ⚠️ enum `ApplicableType` THIẾU Travel (có Leave=5/OT=6/Vehicle=7/ItTicket=8); `ExtendApplicableTypeForWorkflowApps` mig empty Up/Down (enum-only). Tag `[pre-flight, p11-a, workflowapps]`.
|
||||
- **2026-05-29 (S40 STATE GROUNDING):** 7 metric verify. ✅ Migrations=**40** (path `.../Persistence/Migrations/*.cs`, last `AddAttendances`). ✅ Gotchas=**55** (`### N.`). ✅ git clean. DbSet=77 nhưng **SQL tables=84** (em main verify `.ToTable()` ModelSnapshot — 77+7 Identity, "84 docs ĐÚNG", DbSet count sai −7). Endpoints=**211** (docs ~223). FE pages fe-admin **36**+fe-user 29=**65** (docs 53 under-count). Menu keys=**53** const (docs 85 over-count). 3 số tin cậy nhất = mig/gotcha/git. Lesson: tables phải count ToTable KHÔNG DbSet. Tag `[state-grounding, docs-drift, s40]`.
|
||||
|
||||
@ -15,7 +15,8 @@ WRITE specialist độc quyền `tests/**`. xUnit + FluentAssertions 7.2 + EF SQ
|
||||
- ❌ NOT: production code `src/Backend/**` + `fe-*/**` → test reveal bug → REPORT em main, KHÔNG fix
|
||||
- ❌ NOT: decide WHAT to test (test plan) → em main + reviewer chốt priority
|
||||
|
||||
## 📊 Baseline 152 PASS (58 Domain + 94 Infra) ← S43 +8 LeaveBalance + repaired 2 template terminal FK-fail
|
||||
## 📊 Baseline 181 PASS (58 Domain + 123 Infra) ← S45 +27 HRM coverage (Gap1/2/3) · S43 +8 LeaveBalance
|
||||
> ⚠️ Entry S45 dưới = **em main proxy** (agent final msg truncated mid-MEMORY-update, gotcha #53 recurrence).
|
||||
Run: `dotnet test SolutionErp.slnx --nologo --verbosity minimal`
|
||||
|
||||
### ⚠️ Pattern: deduction hook FK → seed LeaveType cho terminal test (S43)
|
||||
@ -45,16 +46,18 @@ Test theo CODE (single source truth), document mismatch header comment + report.
|
||||
`OrderByDescending(CreatedAt).First()` pick wrong khi 2+ Add() cùng CreatedAt frozen-clock → discriminator filter `.Where(Summary.Contains("Chuyển phase"))` BEFORE OrderBy.
|
||||
|
||||
## 🎯 Coverage gap backlog (priority — Reviewer flagged S36)
|
||||
1. **CRITICAL:** HrmConfig 16 endpoint (S35) — Holiday composite UNIQUE (Year,Date) Update 0 test
|
||||
2. **MAJOR:** EmployeeSatellite 15 endpoint (S34) — cascade + FK invariant 0 test
|
||||
3. **MAJOR:** gotcha #44 regression MISS EmployeesController + HrmConfigsController
|
||||
1. ✅ **DONE S45** — HrmConfig Holiday composite UNIQUE (Year,Date): 7 test (`HrmConfigHolidayTests.cs`) + surfaced Mig 43 filtered-index fix
|
||||
2. ✅ **DONE S45** — EmployeeSatellite FK invariant + soft-delete + cascade: 10 test (`EmployeeSatelliteTests.cs`)
|
||||
3. ✅ **DONE S45** — gotcha #44 authz regression EmployeesController + HrmConfigsController: 10 test (extend `AuthorizePolicyRegressionTests.cs`)
|
||||
4. Phase 10.3 Proposal ApproveV2 (S37) + Workflow Apps skeleton (S38) — test-after khi UAT confirm
|
||||
5. **NEW (gotcha #57):** LeaveType.Code + ShiftPattern.Code UNIQUE chưa filter `[IsDeleted]=0` (cùng bug class Holiday đã fix Mig 43) — recreate-on-soft-deleted-slot → 500. Test-before khi fix.
|
||||
|
||||
## 📅 Recent activity (last 10 FIFO)
|
||||
|
||||
- **2026-05-29 (S40 baseline audit smoke):** CONFIRMED 130 PASS (Domain 58 + Infra 72), 0 fail/skip, ~15s. Runner count authoritative; raw `[Fact]/[Theory]` attr = 48+70 (Theory→InlineData expand). Infra spread 15 files. Gap re-verified vs prod: EmployeesController+HrmConfigsController EXIST, authz regression chỉ ApprovalWorkflowsV2Controller (gotcha #44 gap real). Proposal = Domain entity + EF config only, CHƯA có ApproveV2Async service (S37 skeleton, defer đúng). Agent load OK. AUDIT-only, no write.
|
||||
- **2026-05-30 (S42 P11-A Wave4):** +11 test `tests/.../Application/WorkflowAppApproveV2Tests.cs` → **141 PASS** (Infra 72→83). LeaveRequest 8 case full (Submit happy/guard×2, Approve advance/terminal/UPSERT-invariant/forbidden/empty-comment-placeholder, Reject→TuChoi, Return→TraLai+RejectedFromStatus) + OtRequest smoke (submit→approve single-level→DaDuyet). **No prod bug** — LeaveOt ApproveV2 wire correct, all PASS first run. **NEW Pattern:** WorkflowApps handlers = CQRS MediatR (KHÔNG service) → instantiate handler trực tiếp `new ApproveLeaveRequestHandler(db, AsUser(u), clock).Handle(cmd,ct)`, chỉ 3 dep (IApplicationDbContext + TestCurrentUser + FixedDateTime) — nhẹ hơn 6-dep Contract service. MaDonTu format "DT/LR/2026/001". Gap #4 (Workflow Apps) PARTIAL done — Travel/Vehicle mirror pending. ⚠️ Lesson: CWD drift (fe-user) → ghi MEMORY nhầm path, em main relocate. Verify CWD root trước Write memory.
|
||||
- **2026-05-30 (S43 P11-B Wave3 LeaveBalance):** +8 test `tests/.../Application/LeaveBalanceTests.cs` → **152 PASS** (Infra 86→94). Deduction hook (ApproveLeaveRequestHandler terminal) full: deduct single-level (create row from DaysPerYear), only-at-terminal multi-level (advance no-deduct + 1× terminal), accumulate UPSERT (5+2=7 no new row), negative allowed (Used20>Entitled12 → Remaining−8 no throw), Reject+Return no-deduct (split 5a/5b), GetMyLeaveBalances lazy synth (2 active type filter inactive), AdjustLeaveBalance upsert. **⚠️ FOUND + FIXED 2 pre-existing RED** in S42 template (`Approve_LastLevel_TransitionsToDaDuyet` + `Approve_EmptyComment_StoresPlaceholder`): Wave 1 deduction hook (uncommitted, prod) làm terminal insert LeaveBalance FK→LeaveTypes Restrict FAIL vì BuildLeave dùng `LeaveTypeId=Guid.NewGuid()`. **NOT prod bug** (prod đơn luôn pin LeaveType thật) — fix tại test: BuildLeave +optional leaveTypeId, seed LeaveType ở 2 test đó. Baseline thật trước S43 = 142-pass/2-RED (KHÔNG phải 144-green). REPORTED em main.
|
||||
- **2026-06-01 (S45 HRM coverage gaps + Holiday drift) [em main proxy]:** +27 test → **181 PASS** (Infra 96→123). 3 file: HrmConfigHolidayTests (7 — composite UNIQUE Create/Update, ⭐self-update giữ key đổi Name no-false-positive, soft-delete exclusion) + EmployeeSatelliteTests (10 — 5× FK-invariant parent `AnyAsync(!IsDeleted)` guard + soft-delete + cascade-non-behavior Case5 + EF model `DeleteBehavior.Cascade` config assertion) + AuthorizePolicyRegressionTests extend (10 — HrmConfigs bare-`[Authorize]`+writes `Roles=Admin`; Employees class-`Policy=Hrm_HoSo.Read`+per-action). **FOUND drift** (test theo CODE = single source): Holiday DB UNIQUE (Year,Date) unfiltered vs handler `!IsDeleted` → recreate-on-soft-deleted-slot `DbUpdateException(500)`. REPORTED → em main fixed Mig 43 `.HasFilter("[IsDeleted]=0")` (Case 7 flipped assert SUCCESS). New pattern: EF model-metadata assertion `db.Model.FindEntityType(typeof(X)).GetForeignKeys()...DeleteBehavior` lock schema intent. ⚠️ gotcha #57 backlog: LeaveType.Code + ShiftPattern.Code vẫn unfiltered.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
name: ef-core-migration
|
||||
description: Tạo/sửa/revert EF Core 10 migration cho SOLUTION_ERP. Dùng khi thêm entity mới, thay đổi schema, rollback migration, debug DesignTimeDbContextFactory fail. Đã có 42 migration sẵn (Init → AddLeaveBalances Mig 42, S43 Phase 11 P11-B). Snapshot + Designer + Migration 3-file rule bắt buộc commit đủ.
|
||||
description: Tạo/sửa/revert EF Core 10 migration cho SOLUTION_ERP. Dùng khi thêm entity mới, thay đổi schema, rollback migration, debug DesignTimeDbContextFactory fail. Đã có 43 migration sẵn (Init → FilterHolidayUniqueIndexByIsDeleted Mig 43, S45). Snapshot + Designer + Migration 3-file rule bắt buộc commit đủ.
|
||||
when-to-use:
|
||||
- "thêm migration"
|
||||
- "EF Core migration"
|
||||
@ -16,7 +16,7 @@ when-to-use:
|
||||
|
||||
> **Context:** .NET 10 + EF Core 10 + SQL Server. DbContext: `ApplicationDbContext` ở `Infrastructure/Persistence/`. Startup: `SolutionErp.Api`.
|
||||
|
||||
## Migration history (42 migration hiện có)
|
||||
## Migration history (43 migration hiện có)
|
||||
|
||||
| # | Name | Tables added / changed |
|
||||
|---|---|---|
|
||||
@ -62,8 +62,9 @@ when-to-use:
|
||||
| **40** | **`AddAttendances`** | **Chấm công (Attendance) tables + Dashboard NS skeleton. Session 38.** |
|
||||
| **41** | **`WireWorkflowAppsApprovalV2`** | **🎯 Phase 11 P11-A (S42) — +4 `{Leave,Ot,Travel,Vehicle}LevelOpinions` (UNIQUE composite + Cascade/Restrict) + `WorkflowAppCodeSequences` (atomic MaDonTu) + 4 ALTER `RejectedFromStatus` + enum `TravelRequest=9`.** |
|
||||
| **42** | **`AddLeaveBalances`** | **🎯 Phase 11 P11-B (S43) — 1 bảng `LeaveBalances` (User×LeaveType×Year + Entitled/Used/Adjustment, UNIQUE composite + FK LeaveTypes Restrict). Trừ phép tự động khi đơn nghỉ duyệt cuối.** |
|
||||
| **43** | **`FilterHolidayUniqueIndexByIsDeleted`** | **🎯 S45 — DropIndex + CreateIndex `IX_Holidays_Year_Date` UNIQUE filtered `WHERE [IsDeleted]=0` (was unfiltered). Fix reachable 500 khi recreate ngày lễ trên slot soft-deleted. Không bảng mới (table vẫn 91). gotcha #57 — soft-delete UNIQUE phải filter.** |
|
||||
|
||||
Total: **91 bảng** dbo + `__EFMigrationsHistory` (last Mig 42 AddLeaveBalances). Xem `docs/database/schema-diagram.md` migration table + §11-15 module ERD (§16+ Mig 27-42 chi tiết pending).
|
||||
Total: **91 bảng** dbo + `__EFMigrationsHistory` (last Mig 43 FilterHolidayUniqueIndexByIsDeleted, index-only). Xem `docs/database/schema-diagram.md` migration table + §11-15 module ERD (§16+ Mig 27-42 chi tiết pending).
|
||||
|
||||
## N-stage workflow pattern (Mig 18-20 — Session 12-13)
|
||||
|
||||
|
||||
12
CLAUDE.md
12
CLAUDE.md
@ -50,7 +50,7 @@ Kiến trúc: **.NET 10 Clean Architecture + 2 React FE (admin + user) + SQL Ser
|
||||
- Audit fields: `CreatedAt`, `UpdatedAt`, `CreatedBy`, `UpdatedBy` (`BaseEntity`)
|
||||
- Soft delete: `IsDeleted`, `DeletedAt`, `DeletedBy` (`AuditableEntity`)
|
||||
- Migrations: `dotnet ef migrations add <Name> --project src/Backend/SolutionErp.Infrastructure --startup-project src/Backend/SolutionErp.Api`
|
||||
- **Hiện có 42 migration → 91 bảng** (Phase 10 COMPLETE + Phase 11 P11-A/B — Mig 34-42 HRM/Office/WorkflowApps/Attendance + Contract V2 (32-33) + WireWorkflowApps V2 (41) + LeaveBalance (42). V2 schema history S29-era bên dưới giữ nguyên — Mig 32+33 Plan B Contract V2 cookie-cutter mirror PE Mig 22-26 (S29). Mig 26 `AddPeLevelOpinionsForV2`: bảng mới `PurchaseEvaluationLevelOpinions` UNIQUE composite (PEId, LevelId), FK Cascade Pe + Restrict Level. Section 5 "Ý kiến cấp duyệt" V2 dynamic theo workflow đã pin: forEach Step (Phòng) → forEach Level (Cấp) → forEach NV → 1 OpinionBox. Service `ApproveV2Async` UPSERT auto khi NV duyệt — Q1=1B (sync gắn với Duyệt, KHÔNG form input rời). SignedByUserId track signer thật, FE banner "Admin duyệt thay" khi !== ApproverUserId. Comment empty → "(duyệt — không ý kiến)" placeholder. Phiếu V1 legacy fallback Mig 15 4 box readOnly (data history). Mig 25 `AddIsUserSelectableToApprovalWorkflows`: ALTER `ApprovalWorkflows` +`IsUserSelectable bit` (admin pin/unpin workflow nào cho user pick lúc create phiếu, multi-select độc lập IsActive). Backfill `WHERE IsActive=1 SET 1` giữ behavior cũ. Designer +badge "Cho user chọn" + button Ghim/Bỏ ghim. Workspace filter dropdown chỉ workflows `IsUserSelectable=true`. Mig 22-24 V2 schema (Session 17): `ApprovalWorkflows`/Steps/Levels — Quy trình > Bước (Phòng) > Cấp (N NV cụ thể qua ApproverUserId, OR-of-N cùng cấp). PE.ApprovalWorkflowId pin V2. PE.CurrentApprovalLevelOrder track. State machine 5 trạng thái: Nháp / Đã gửi duyệt / Trả lại (Phase riêng TraLai=98) / Từ chối / Đã duyệt. PE Service V2 wire match `actor.Id == ApproverUserId`. Contract V2 ĐÃ WIRE (Mig 32+33 Plan B S29 — cookie-cutter mirror PE V2: `ApproveV2Async` + `ContractLevelOpinions` UPSERT + Workspace V2 Select dropdown). Mig 21 V1 flat workflow vẫn live cho phiếu cũ.)
|
||||
- **Hiện có 43 migration → 91 bảng** (Phase 10 COMPLETE + Phase 11 P11-A/B — Mig 34-42 HRM/Office/WorkflowApps/Attendance + Contract V2 (32-33) + WireWorkflowApps V2 (41) + LeaveBalance (42) + Holiday filtered-unique (43, S45). V2 schema history S29-era bên dưới giữ nguyên — Mig 32+33 Plan B Contract V2 cookie-cutter mirror PE Mig 22-26 (S29). Mig 26 `AddPeLevelOpinionsForV2`: bảng mới `PurchaseEvaluationLevelOpinions` UNIQUE composite (PEId, LevelId), FK Cascade Pe + Restrict Level. Section 5 "Ý kiến cấp duyệt" V2 dynamic theo workflow đã pin: forEach Step (Phòng) → forEach Level (Cấp) → forEach NV → 1 OpinionBox. Service `ApproveV2Async` UPSERT auto khi NV duyệt — Q1=1B (sync gắn với Duyệt, KHÔNG form input rời). SignedByUserId track signer thật, FE banner "Admin duyệt thay" khi !== ApproverUserId. Comment empty → "(duyệt — không ý kiến)" placeholder. Phiếu V1 legacy fallback Mig 15 4 box readOnly (data history). Mig 25 `AddIsUserSelectableToApprovalWorkflows`: ALTER `ApprovalWorkflows` +`IsUserSelectable bit` (admin pin/unpin workflow nào cho user pick lúc create phiếu, multi-select độc lập IsActive). Backfill `WHERE IsActive=1 SET 1` giữ behavior cũ. Designer +badge "Cho user chọn" + button Ghim/Bỏ ghim. Workspace filter dropdown chỉ workflows `IsUserSelectable=true`. Mig 22-24 V2 schema (Session 17): `ApprovalWorkflows`/Steps/Levels — Quy trình > Bước (Phòng) > Cấp (N NV cụ thể qua ApproverUserId, OR-of-N cùng cấp). PE.ApprovalWorkflowId pin V2. PE.CurrentApprovalLevelOrder track. State machine 5 trạng thái: Nháp / Đã gửi duyệt / Trả lại (Phase riêng TraLai=98) / Từ chối / Đã duyệt. PE Service V2 wire match `actor.Id == ApproverUserId`. Contract V2 ĐÃ WIRE (Mig 32+33 Plan B S29 — cookie-cutter mirror PE V2: `ApproveV2Async` + `ContractLevelOpinions` UPSERT + Workspace V2 Select dropdown). Mig 21 V1 flat workflow vẫn live cho phiếu cũ.)
|
||||
|
||||
### Modules
|
||||
|
||||
@ -63,7 +63,7 @@ Kiến trúc: **.NET 10 Clean Architecture + 2 React FE (admin + user) + SQL Ser
|
||||
| Identity (User/Role/Permission/MenuItem) | `Domain/Identity/` | 1, 3, 11 | Feature-complete (30 demo user — 16 sample + 14 Solutions thật) |
|
||||
| Forms (Template + Clause) | `Domain/Forms/` | 4 | Feature-complete |
|
||||
| Notifications | `Domain/Notifications/` | 6 | In-app + SignalR OK, email SMTP TODO |
|
||||
| **Tests** | `tests/SolutionErp.{Domain,Infrastructure}.Tests/` | — | **154 test pass** (58 Domain + 96 Infra) — CI gate + path filter docs-only skip |
|
||||
| **Tests** | `tests/SolutionErp.{Domain,Infrastructure}.Tests/` | — | **181 test pass** (58 Domain + 123 Infra) — CI gate + path filter docs-only skip |
|
||||
|
||||
### Commit convention
|
||||
|
||||
@ -78,13 +78,13 @@ Kiến trúc: **.NET 10 Clean Architecture + 2 React FE (admin + user) + SQL Ser
|
||||
```
|
||||
tests/
|
||||
├── SolutionErp.Domain.Tests/ (58 test — Domain policy: Workflow / PE / Budget)
|
||||
└── SolutionErp.Infrastructure.Tests/ (96 test)
|
||||
└── SolutionErp.Infrastructure.Tests/ (123 test)
|
||||
├── Common/ (SqliteDbFixture + TestApplicationDbContext + IdentityFixture S9)
|
||||
├── Services/ (17 codegen + 6 PE 2-stage approval S9)
|
||||
└── Application/ (6 test - PeWorkflowDefinition versioning)
|
||||
```
|
||||
|
||||
**154 unit test pass** (58 Domain + 96 Infra). CI gate + path filter live. (Coverage gap S35-S38: HrmConfig Holiday UNIQUE + EmployeeSatellite cascade + gotcha #44 regression — backlog `docs/STATUS.md`.)
|
||||
**181 unit test pass** (58 Domain + 123 Infra). CI gate + path filter live. (S45: 3 coverage gap S35-S38 ĐÓNG +27 test + Mig 43 Holiday filtered-unique fix. Backlog gotcha #57: LeaveType/Shift Code unfiltered.)
|
||||
|
||||
```bash
|
||||
dotnet test SolutionErp.slnx # chạy cả 2 test project
|
||||
@ -108,7 +108,7 @@ dotnet test SolutionErp.slnx # chạy cả 2 test project
|
||||
| `form-engine` — render docx/xlsx + PDF | `ef-core-migration` — EF migration + 3-file rule |
|
||||
| `permission-matrix` — role × menu × CRUD | `iis-deploy-runbook` — 3 site IIS + win-acme + runner |
|
||||
|
||||
**Audit định kỳ:** đầu mỗi tháng — combined skill + doc drift audit theo `docs/rules.md §6.4 + §9.4`. Cron `solution-erp-skill-audit-monthly` fire 9:00 ngày 1. Lần kế: **2026-06-01**.
|
||||
**Audit định kỳ:** đầu mỗi tháng — combined skill + doc drift audit theo `docs/rules.md §6.4 + §9.4`. Cron `solution-erp-skill-audit-monthly` fire 9:00 ngày 1. Lần kế: **2026-07-01** (S44 đã chạy 06-01).
|
||||
|
||||
Quy tắc:
|
||||
- KHÔNG bulk-clone repo skill 3rd party. Chỉ thêm skill PROJECT-SPECIFIC. Xem `docs/rules.md §9` đầy đủ.
|
||||
@ -130,7 +130,7 @@ Quy tắc:
|
||||
| [`docs/database/database-guide.md`](docs/database/database-guide.md) | DB conventions + migration workflow + cheatsheet |
|
||||
| [`docs/database/schema-diagram.md`](docs/database/schema-diagram.md) | ⭐ ERD + luồng DB + data flow 91 table (+ §11 PE module + §12 Budget module + §13 PEDeptOpinions + §14 Contract V2 LevelOpinions) |
|
||||
| [`docs/flows/README.md`](docs/flows/README.md) | Index 6 flow (auth, permission, contract, form, SLA) |
|
||||
| [`docs/gotchas.md`](docs/gotchas.md) | ⭐ 56 bẫy đã gặp — đọc trước khi debug tương tự |
|
||||
| [`docs/gotchas.md`](docs/gotchas.md) | ⭐ 57 bẫy đã gặp — đọc trước khi debug tương tự |
|
||||
| [`.claude/skills/`](.claude/skills/README.md) | 6 skill: contract-workflow, form-engine, permission-matrix, dependency-audit-erp, ef-core-migration, iis-deploy-runbook |
|
||||
| [`docs/guides/vps-setup.md`](docs/guides/vps-setup.md) | ⭐ Master runbook deploy VPS shared với VIETREPORT |
|
||||
|
||||
|
||||
@ -2,7 +2,22 @@
|
||||
|
||||
> **Tiering rule (S40):** giữ **2-3 session gần nhất**. Cũ hơn → `docs/changelog/sessions/`. Full brief history pre-S40 → `docs/_archive/HANDOFF-preS40-fullhistory.md`.
|
||||
|
||||
**Last updated:** 2026-06-01 (Session 44 — Monthly drift audit + AI_INFRA bundle 06-01 adopt, docs-only, 2-way VERIFIED. 154 test unchanged. Chain ae30f8f→5dbcad3)
|
||||
**Last updated:** 2026-06-01 (Session 45 — HRM test-gap stabilization +27 (154→181) + Mig 43 Holiday filtered-unique fix. 2 commit 051b62b+0c5a014, cicd Run #368 PASS prod. Prev: S44 monthly drift audit.)
|
||||
|
||||
---
|
||||
|
||||
## S45 (2026-06-01) — HRM test-gap stabilization + Holiday drift fix (Mig 43)
|
||||
|
||||
**2 commit `051b62b` (Tests +27) + `0c5a014` (Mig 43) → push → cicd Run #368 PASS, verified prod. 154→181 test.**
|
||||
|
||||
**Done:**
|
||||
- **Đóng 3 test-gap deferred S35-S38** (stabilize before extend): Gap1 Holiday composite UNIQUE (7) + Gap2 EmployeeSatellite FK-invariant/soft-delete/cascade (10) + Gap3 gotcha #44 authz regression HrmConfigs+Employees (10). 🟪 test-specialist viết (return truncated #53 → 👤 em main verify-on-disk + proxy MEMORY).
|
||||
- **Mig 43 `FilterHolidayUniqueIndexByIsDeleted`** (👤 em main solo) — Gap1 test lòi bug thật: Holiday DB UNIQUE (Year,Date) unfiltered vs handler `!IsDeleted` → admin xoá + thêm lại ngày lễ cùng date = **500 reachable**. Fix `.HasFilter("[IsDeleted]=0")` (khớp pattern 13×). 🟩 cicd verify filtered index live prod. Table vẫn 91 (index-only), FE bundle unchanged (đúng — BE-only).
|
||||
- **🟦 P11-C recon sẵn:** Vehicle+Driver = extend HrmConfigs +2 kind declarative (Mig 44), giữ VehicleBooking free-text.
|
||||
|
||||
**gotcha #57 NEW:** soft-delete + UNIQUE → MUST `.HasFilter("[IsDeleted]=0")`. ⚠️ **LeaveType.Code + ShiftPattern.Code vẫn unfiltered** (cùng bug, backlog test-before). Process: `feedback_background_spawn_visibility` (foreground spawn im lặng "looks frozen" → đẩy background + report ngay).
|
||||
|
||||
**Next S46 (anh pick):** P11-C Vehicle+Driver (Mig 44, recon ready) · gotcha #57 fix LeaveType/Shift filtered-unique · P11-D/E/F · Phase 9 Ops.
|
||||
|
||||
---
|
||||
|
||||
@ -40,21 +55,9 @@
|
||||
|
||||
---
|
||||
|
||||
## S42 (2026-05-30) — Phase 11 P11-A: wire ApproveV2 + LevelOpinions 4 WorkflowApps + Max re-review fix
|
||||
## S42-S38 (older) → session logs
|
||||
|
||||
**4 commit `e7b66cd`+`75df04e`+`e47ef1d`+docs → #364-366 PASS.** 7-agent.
|
||||
|
||||
**Done:**
|
||||
- **Mig 41 `WireWorkflowAppsApprovalV2`** — 4 `{Module}LevelOpinions` + `WorkflowAppCodeSequences` (atomic MaDonTu) + 4 RejectedFromStatus + enum TravelRequest=9. 84→89 tables. Cookie-cutter mirror Proposal (Mig 38).
|
||||
- **BE** 30 handler + 8 route × 4 module (Leave/OT/Travel/Vehicle) + seed 4 WF. **FE** WorkflowAppDetailPage declarative 4-kind.
|
||||
- **⚠️ Max re-review (agents High) bắt + fix 2 bug FE picker:** (#1) pinWorkflow PUT partial→400 (#2) fetch shape sai (copy nhầm ProposalCreatePage hỏng). Fix: dedicated `PUT /{id}/workflow` + fetch mirror PE/Contract. **Bonus** fix ProposalCreatePage latent S37 bug.
|
||||
- **Test** +14 (ApproveV2 11 + SetWorkflow 3). 130→144.
|
||||
|
||||
**Lesson:** WIRE FE phải đọc reference proven (không đoán); Max re-review cross-stack bắt buộc (→ gotcha #56 CWD-drift + memory `feedback_high_to_max_multiagent_quality`). Detail → `docs/changelog/sessions/2026-05-30-S42-S43-phase11-p11a-p11b.md`.
|
||||
|
||||
---
|
||||
|
||||
## S41-S38 (older) → session logs
|
||||
- **S42** (2026-05-30) Phase 11 P11-A: Mig 41 WireWorkflowAppsApprovalV2 (4 LevelOpinions + WorkflowAppCodeSequences) + 30 handler 4 module + Max re-review fix 2 FE picker bug. 130→144 test. → `docs/changelog/sessions/2026-05-30-S42-S43-phase11-p11a-p11b.md`.
|
||||
|
||||
- **S41** (2026-05-29) RAG corpus cleanup w/ AI_INFRA: `.claude/rag.json` exclude `**/`-anchored fix (gotcha #10) + store_memory reconcile 5/5 (anti-data-loss, at-risk rule) + re-bootstrap 3080→2406 (−674 junk, zero-loss verified). Standing infra backlog → AI_INFRA. Concurrency carry-over (BROADCAST-2 + `.mcp.json`) originated here → **resolved S44** (`5b8736d`). → memory `feedback_store_memory_rebootstrap_protection`.
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
> **Update rule:** trước khi bắt đầu 1 task → ghi row `🔥 In Progress`. Xong → `✅ Recently Done`.
|
||||
> **Tiering rule (S40):** chỉ giữ **state hiện tại + 3 session gần nhất** ở file này. Session cũ hơn → `docs/changelog/sessions/`. Full history pre-S40 → `docs/_archive/STATUS-preS40-fullhistory.md`. (Tránh over-context — xóa double, không cắt nội dung.)
|
||||
|
||||
**Last updated:** 2026-06-01 (Session 44 — **Monthly drift audit + AI_INFRA bundle 06-01 adoption**: 42 doc/skill count-drift fixes (40→42 mig · 84→91 tables · 130→154 test · 52→56 gotcha across CLAUDE/skills/schema-diagram/database-guide) + agent-mem "25KB"→"~30KB tiered" wording ×7 + /session-end #4 adoption-report step. Docs-only, CI-skip. Prev: S42 P11-A Gitea #364/#365, S43 P11-B #367.)
|
||||
**Last updated:** 2026-06-01 (Session 45 — **HRM test-gap stabilization + Holiday drift fix**: +27 test đóng 3 coverage gap deferred S35-S38 (154→181) + **Mig 43** `FilterHolidayUniqueIndexByIsDeleted` (soft-delete slot reusable, fix reachable 500). 2 commit `051b62b`+`0c5a014` deploy+verified prod (cicd Run #368, filtered index live). Prev: S44 monthly drift audit + AI_INFRA bundle 06-01.)
|
||||
|
||||
---
|
||||
|
||||
@ -11,30 +11,30 @@
|
||||
|
||||
| Metric | Value | Note |
|
||||
|---|---|---|
|
||||
| Migrations | **42** | last `AddLeaveBalances` (20260530034336) |
|
||||
| Migrations | **43** | last `FilterHolidayUniqueIndexByIsDeleted` (20260601064128, S45) |
|
||||
| SQL tables | **91** | +1 S43 LeaveBalances (verified prod, UNIQUE composite + FK LeaveTypes Restrict) |
|
||||
| API endpoints | **~241** | +3 S43 (leave-balances my/admin/adjust) |
|
||||
| FE pages | **67** | WorkflowAppDetailPage (admin+user SHA256 identical) |
|
||||
| Menu keys | **~53** | BE `MenuKeys` const (FE menuKeys.ts mirror 54) |
|
||||
| Tests | **154 PASS** | 58 Domain + 96 Infra · 0 fail / 0 skip · +11 LeaveBalance/guard S43 |
|
||||
| Gotchas | **56** | `docs/gotchas.md` (latest #56 CWD-drift stray memory S42-43) |
|
||||
| Tests | **181 PASS** | 58 Domain + 123 Infra · 0 fail / 0 skip · +27 HRM coverage S45 (Holiday/EmployeeSatellite/authz) |
|
||||
| Gotchas | **57** | `docs/gotchas.md` (latest #57 soft-delete UNIQUE phải filter [IsDeleted]=0, S45) |
|
||||
| User memory | 27 | + `MEMORY.md` index |
|
||||
| Skills | 6 | 3 domain + 3 ops |
|
||||
| Sub-agents | **7** | Opus 4.8 1M (S39 split 4→7) |
|
||||
| RAG chunks | **2406** | ✅ S41 re-bootstrap clean (3080→2406, −674 junk: node_modules+_archive now excluded; user-memory 60 chunks/10 files slug-fixed + S38-S41 indexed) |
|
||||
|
||||
**Bundle hash live (prod):** admin `Krjvg_3j` · user `6sNStgxa` (Gitea #367, S43 P11-B). Auth-verified prod: `/leave-balances/my?year=2026` → 5 LeaveType lazy-default (Used=0, remaining=entitled).
|
||||
**Phase:** ✅ Phase 10 COMPLETE · 🔄 **Phase 11 IN PROGRESS** — P11-A + P11-B DONE (deployed prod) · ⬜ P11-C..F pending · 🚫 Phase 9 Ops blocked (anh main coordinate).
|
||||
**Phase:** ✅ Phase 10 COMPLETE · 🔄 **Phase 11 IN PROGRESS** — P11-A + P11-B DONE (deployed prod) · ⬜ P11-C..F pending (P11-C Vehicle+Driver recon ready S45) · 🧪 S45 test-gap stabilization + Mig 43 Holiday fix shipped · 🚫 Phase 9 Ops blocked (anh main coordinate).
|
||||
|
||||
> ⚠️ **Count drift fixed S40:** endpoints ~223→**211**, FE pages 53→**65**, menu keys 85→**~53**. Tables **84 confirmed correct** (DbSet 77 + Identity 7). 3 số "khó fake" (mig/gotcha/git) luôn đúng. Cause: số "incremented mỗi session" over/under-count optimistic — re-ground định kỳ.
|
||||
|
||||
---
|
||||
|
||||
## 🔥 In Progress (S43)
|
||||
## 🔥 In Progress (S45)
|
||||
|
||||
| Task | Owner | Status |
|
||||
|---|---|---|
|
||||
| _(none — P11-A + P11-B SHIPPED + verified prod. Next: P11-C..F khi anh chọn)_ | — | ✅ |
|
||||
| _(none — S45 test-gaps + Mig 43 Holiday fix SHIPPED + verified prod. Next: P11-C..F khi anh chọn — recon sẵn)_ | — | ✅ |
|
||||
|
||||
**S40 done:** ✅ Consolidation (`d2f52ba`) · ✅ Curate 4 agent MEMORY >25KB→<8.4KB (`78c9de3`) · ✅ RAG catch-up chunk S37-S40 (rerank 0.867) · ✅ **AI_INFRA bulletin 2026-05-29 adopt 4/4** (MỤC2 Tiered Memory Policy v1 `6f08d1f` + MỤC3 /session-start+/session-end slash commands `c8ff5e1`). ⏳ Full RAG re-index = AI_INFRA op (cần VOYAGE_API_KEY).
|
||||
|
||||
@ -44,6 +44,14 @@
|
||||
|
||||
## ✅ Recently Done (newest on top — 3 session; cũ hơn → session logs)
|
||||
|
||||
### S45 (2026-06-01) — 🧪 HRM test-gap stabilization + Holiday drift fix (Mig 43)
|
||||
- **2 commit `051b62b` (Tests) + `0c5a014` (Mig 43) → push → Gitea Run #368 PASS, verified prod.** "Stabilize before extend": đóng 3 test-gap deferred S35-S38 TRƯỚC khi chồng schema mới.
|
||||
- **+27 test (154→181):** Gap1 Holiday composite UNIQUE (7) · Gap2 EmployeeSatellite FK-invariant+soft-delete+cascade (10) · Gap3 gotcha #44 authz regression HrmConfigs+Employees (10). 🟪 test-specialist viết (return truncated gotcha #53 → 👤 em main verify-on-disk + proxy MEMORY).
|
||||
- **Mig 43 `FilterHolidayUniqueIndexByIsDeleted`** (👤 em main solo, bug-fix chain): Gap1 test lòi drift — Holiday DB UNIQUE (Year,Date) unfiltered vs handler `!IsDeleted` → admin xoá+thêm lại ngày lễ cùng date = **500 reachable**. Fix `.HasFilter("[IsDeleted]=0")` (khớp pattern 13× sẵn có). Flip Case 7 assert success. Table vẫn 91 (index-only).
|
||||
- **🟩 cicd Run #368 PASS** ~4m20s: test gate 181 · Mig 43 applied prod · `IX_Holidays_Year_Date` filter=`([IsDeleted]=(0))` live (was NULL) · FE bundle UNCHANGED `Krjvg_3j`/`6sNStgxa` (đúng — BE-only) · health 200 · 0 regression.
|
||||
- **🟦 investigator P11-C pre-flight** (Vehicle+Driver catalog): chưa có master → extend HrmConfigs +2 kind declarative, Mig 44, giữ VehicleBooking free-text. **gotcha #57 caught:** LeaveType.Code + ShiftPattern.Code cũng unfiltered (backlog).
|
||||
- **gotcha #57 NEW** (soft-delete UNIQUE phải `.HasFilter`). Process: foreground spawn = im lặng = "looks frozen" → đẩy background + report-ngay (`feedback_background_spawn_visibility`).
|
||||
|
||||
### S44 (2026-06-01) — 🗓️ Monthly drift audit + AI_INFRA bundle 06-01 adoption (docs-only, CI-skip)
|
||||
- **Cadence audit** (cron 2026-06-01) + adopt AI_INFRA bundle 06-01 (federated, team chọn full scope). 🟦 investigator-codebase drift scan (read-only, ground-truth verified) → 👤 em main patch. No code touched → **154 test PASS unchanged**.
|
||||
- **42 count-drift fixes:** CLAUDE.md root + docs/CLAUDE.md + `ef-core-migration` SKILL + `dependency-audit-erp` SKILL + schema-diagram + database-guide — `40→42 mig · 84/59/55/47→91 tables · 130/111→154 test · 52/49→56 gotcha`.
|
||||
@ -101,7 +109,7 @@
|
||||
|
||||
### 🔧 Maintenance backlog
|
||||
- RAG re-ingest `solution_erp` S42-S43 content (store_memory stopgap live; full re-index = AI_INFRA op)
|
||||
- Test coverage gaps: **Gap1** HrmConfig Holiday composite UNIQUE (🔴 CRITICAL) · **Gap2** EmployeeSatellite cascade · **Gap3** gotcha #44 regression `EmployeesController`+`HrmConfigsController`
|
||||
- ✅ **Test coverage gaps Gap1/2/3 DONE S45** (+27 test). NEW backlog (gotcha #57): **LeaveType.Code + ShiftPattern.Code UNIQUE chưa filter `[IsDeleted]=0`** (cùng class Holiday Mig 43 — recreate-on-soft-deleted-slot 500; test-before khi fix). Cân nhắc gộp vào P11-C khi đụng HrmConfigs.
|
||||
- Skill + doc drift audit cron — ✅ **2026-06-01 DONE (S44)** — 42 count-drift fixes + AI_INFRA bundle adopt; next **2026-07-01**
|
||||
|
||||
### 🚫 Phase 9 Ops (blocked — anh main coordinate)
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
# Session 45 — HRM test-gap stabilization + Holiday drift fix (Mig 43)
|
||||
|
||||
**Date:** 2026-06-01
|
||||
**Scope:** Tests + Infra (schema)
|
||||
**Commits:** `051b62b` (Tests) → `0c5a014` (Mig 43) → docs (this session-end)
|
||||
**CI:** Gitea Run #368 (run_number 254) PASS ~4m20s — verified prod
|
||||
**Test:** 154 → **181 PASS** (58 Domain + 123 Infra, 0 fail / 0 skip)
|
||||
|
||||
---
|
||||
|
||||
## Bối cảnh + quyết định
|
||||
|
||||
Anh giao quyền chọn thứ tự việc. Em main chọn **"stabilize before extend"**: đóng 3 test-gap deferred từ S35-S38 TRƯỚC khi chồng schema Phase 11 mới (P11-C). Lý do: 1 gap CRITICAL-flagged, nhỏ + low-risk, deferred nhiều session; làm xong baseline vững rồi mới thêm Mig.
|
||||
|
||||
## Việc done
|
||||
|
||||
### 1. Đóng 3 test-gap (commit `051b62b`, +27 test)
|
||||
🟪 **test-specialist** viết (em main chốt test plan sau recon 8 file):
|
||||
|
||||
- **Gap1 (CRITICAL) — `HrmConfigHolidayTests.cs` (7 test):** Holiday composite UNIQUE (Year, Date). Create duplicate → Conflict; same-date-diff-year → OK; Update→occupied-slot → Conflict; ⭐ **self-update giữ (Year,Date) đổi Name → KHÔNG false-positive** (guard short-circuit `entity.Year!=req.Year || entity.Date!=req.Date`); Update→empty-slot OK; Update non-existent → NotFound; soft-delete exclusion.
|
||||
- **Gap2 (MAJOR) — `EmployeeSatelliteTests.cs` (10 test):** 5× FK-invariant (mọi Create satellite guard parent `AnyAsync(Id && !IsDeleted)` → NotFound); soft-deleted parent → NotFound; happy path FK đúng; Delete soft + DeletedBy + re-operate NotFound; **DeleteEmployeeProfile soft KHÔNG cascade satellite** (behavior hiện tại — handler chỉ soft-delete parent); EF model `DeleteBehavior.Cascade` config assertion (lock schema intent).
|
||||
- **Gap3 (MAJOR) — extend `AuthorizePolicyRegressionTests.cs` (10 test):** 2 controller shape KHÁC nhau lock đúng intent — `HrmConfigsController` (class `[Authorize]` trần Policy/Roles null + writes `Roles="Admin"`) vs `EmployeesController` (class `Policy="Hrm_HoSo.Read"` + per-action Create/Update/Delete + 1 satellite representative).
|
||||
|
||||
⚠️ **gotcha #53 recurrence:** test-specialist return truncated mid-MEMORY-update ("let me read it first"). → em main verify-on-disk (glob + git status + đọc 3 file line-by-line + dotnet test) thay vì tin output cụt. MEMORY proxy-updated bởi em main.
|
||||
|
||||
### 2. Holiday drift fix (commit `0c5a014`, Mig 43)
|
||||
👤 **em main solo** (bug-fix reasoning chain + schema decision tightly coupled → tránh spawn-truncation cho change nhỏ).
|
||||
|
||||
- **Bug Gap1 lòi ra:** `HolidayConfiguration` DB UNIQUE `(Year,Date)` plain `.IsUnique()` KHÔNG filter, trong khi handler check `!IsDeleted` → admin xoá (soft) 1 ngày lễ rồi tạo lại cùng (Year,Date) → app-check PASS nhưng DB UNIQUE reject → `DbUpdateException` **500 reachable**.
|
||||
- **Fix:** `.HasFilter("[IsDeleted] = 0")` — khớp pattern **13× sẵn có** (Catalogs ×4, Contract/PE/Proposal/Budget/WorkflowApps code-unique). **Mig 43 `FilterHolidayUniqueIndexByIsDeleted`** (DropIndex + CreateIndex filter; Down reverse sạch). Applied Dev + Design LocalDB.
|
||||
- **Test flip:** Case 7 từ assert `DbUpdateException` → assert SUCCESS (reuse slot, 1 active + 1 soft-deleted). Spec-change → update test cùng commit.
|
||||
- Table count vẫn **91** (index-only mig, no CREATE TABLE).
|
||||
|
||||
### 3. cicd verify (🟩 Run #368 PASS)
|
||||
test gate 181 · Mig 43 applied prod (`__EFMigrationsHistory` top) · `IX_Holidays_Year_Date` `filter_definition = ([IsDeleted]=(0))` live (was NULL) · FE bundle UNCHANGED `Krjvg_3j`/`6sNStgxa` (đúng — BE-only push, KHÔNG flag ship-fail) · health 200 · 0 regression.
|
||||
|
||||
### 4. P11-C pre-flight (🟦 investigator-codebase)
|
||||
Vehicle+Driver catalog: chưa có master entity (chỉ VehicleBooking free-text Mig 39). **Recommend:** extend HrmConfigs +2 kind (`vehicles`+`drivers`) declarative KIND_CONFIG, Mig 44, giữ VehicleBooking free-text (FK link defer). Caught gotcha #57 backlog (LeaveType/Shift unfiltered).
|
||||
|
||||
## Learnings
|
||||
|
||||
- **gotcha #57 NEW:** soft-delete + UNIQUE → MUST `.HasFilter("[IsDeleted]=0")`. Backlog: LeaveType.Code + ShiftPattern.Code vẫn unfiltered.
|
||||
- **`feedback_background_spawn_visibility`:** spawn agent foreground = im lặng vài phút = "looks frozen" (anh phản hồi). → đẩy long work background + report "đã launch X" ngay. Cũng có 1 dead turn ("No response requested") = hiccup thật, em main nhận.
|
||||
- **Test theo CODE = single source of truth** → test lòi drift thật (Holiday) → REPORT → fix proper. Closed loop: gap test → bug surfaced → fixed + verified prod cùng session.
|
||||
- **EF model-metadata assertion** (`db.Model.FindEntityType(...).GetForeignKeys()...DeleteBehavior`) = cách lock schema intent (cascade) không cần DB round-trip — Pattern-10-style cho EF model.
|
||||
|
||||
## Next (S46, anh pick)
|
||||
P11-C Vehicle+Driver (Mig 44, recon ready) · gotcha #57 fix LeaveType/Shift filtered-unique (gộp P11-C) · P11-D ItTicket SLA / P11-E AttendanceReport / P11-F MaTicket · Phase 9 Ops.
|
||||
@ -1044,6 +1044,20 @@ for h in resp.points: # ← .points không phải iterable trực tiếp
|
||||
|
||||
---
|
||||
|
||||
### 57. Soft-delete entity + UNIQUE index PHẢI filter `[IsDeleted] = 0` (Session 45)
|
||||
|
||||
**Triệu chứng:** Entity soft-delete (AuditableEntity) có UNIQUE index trên business key (Code / composite). Handler check trùng đã loại soft-deleted (`AnyAsync(x => x.Key == k && !x.IsDeleted)`) → định cho phép reuse slot. NHƯNG nếu DB UNIQUE index KHÔNG filter → xoá (soft) 1 row rồi tạo lại cùng key → handler PASS app-check nhưng `SaveChangesAsync` ném `DbUpdateException` (SQL Server 2627 / SQLite Error 19) → HTTP **500** (không phải Conflict sạch hay insert OK). Reachable thật: admin xoá nhầm 1 ngày lễ / mã catalog rồi nhập lại đúng.
|
||||
|
||||
**Root cause:** UNIQUE index mặc định tính CẢ row IsDeleted=1 → mâu thuẫn app-level `!IsDeleted` intent.
|
||||
|
||||
**Fix:** EF config filtered index — `e.HasIndex(x => x.Key).IsUnique().HasFilter("[IsDeleted] = 0")` (composite: `new { x.A, x.B }`). Migration DropIndex + CreateIndex(filter). SQL Server + SQLite test đều honor (bracket-quote + partial index OK).
|
||||
|
||||
**Đã áp 13× sẵn:** Catalogs ×4, Contract/PE/Proposal/Budget/WorkflowApps code-unique. **Fixed S45:** Holiday `(Year,Date)` Mig 43. ⚠️ **CÒN unfiltered (backlog test-before):** `LeaveTypeConfiguration.cs` Code + `ShiftPatternConfiguration.cs` Code — cùng bug class.
|
||||
|
||||
**References:** Mig 43 `FilterHolidayUniqueIndexByIsDeleted` · `HolidayConfiguration.cs` · `HrmConfigHolidayTests.cs` Case 7 · surfaced bởi test-specialist Gap1 S45.
|
||||
|
||||
---
|
||||
|
||||
## Checklist debug bug mới
|
||||
|
||||
1. Build pass không? → fail → check using + package version compat
|
||||
|
||||
Reference in New Issue
Block a user