distill-gen: 1 # Gist — 2026-06 archive (S41-S55) · 4-field distillation > **distill-gen: 1** — already-distilled; do NOT re-compress. Each block = VIỆC · KẾT-LUẬN(+Mig/file:line) · BÀI-HỌC · BẤT-NGỜ, ending `→ substring:"…"` grep-UNIQUE in `2026-06.md` (14 bullets moved byte-exact from MEMORY.md L87-L104, S? Harness-9 curate). Pointer-style = substring (Ctrl-F), Mig-name / phrase keyed. value tag: cao/vừa/thấp. > The final subsection distills L1-HOT markers (S56/S57bis/S65) that REMAIN in MEMORY.md (not moved) — included here only so the coverage checklist resolves in one place; their verbatim lives in MEMORY.md, pointer notes say so. --- ## P11-A WorkflowApps wave (S41-S42) — schema + app + seed **[cao] Mig 41 schema — 4 WorkflowApps ApproveV2 + LevelOpinions (S41 P11-A W1).** VIỆC: cookie-cutter mirror Proposal Mig 38 — 5 entity (4 {Leave,Ot,Travel,Vehicle}RequestLevelOpinion + shared WorkflowAppCodeSequence Prefix-PK) + 5 EF config (auto-discover ApplyConfigurationsFromAssembly) + 4 parent nav + enum TravelRequest=9 + 2 DbSet. KẾT-LUẬN: Mig 41 WireWorkflowAppsApprovalV2; diff CLEAN 5 CreateTable+4 AddColumn; FK Cascade parent + Restrict Level + UNIQUE composite; both DB. BÀI-HỌC: Pattern 12-bis 13× cumulative. BẤT-NGỜ: none (spec deterministic 100%). → substring:"S41 P11-A Wave 1 SCHEMA — wire ApproveV2+LevelOpinions" **[cao] App-layer ApproveV2 CQRS (S42 Wave 2a LeaveOt stub + 2b Travel/Vehicle).** VIỆC: per-module DetailDto+LevelOpinionDto+GetById(JOIN Step/Level)+UpdateDraft+Submit+Approve(UPSERT+advance)+Reject+Return; shared internal CodeGen Serializable-tx. KẾT-LUẬN: TravelVehicleApprovalFeatures ~830 LOC; codes `{prefix}/{seq:D3}` (D3 NO year segment per spec) vs Leave/Ot `{prefix}/{year}/{seq:D3}`. BÀI-HỌC: **WorkflowAppCodeGen Serializable-tx Prefix-keyed**; **WorkflowAppStatus enum DIFFERS ProposalStatus int values (DaGuiDuyet=2 not 1, TraLai=3) → mirror by SEMANTIC enum member NOT literal**; Owner=RequesterUserId (not DrafterUserId); Submit verify wf.ApplicableType else Conflict. BẤT-NGỜ: 2a content absorbed (stub→git) into Pattern 4 + this 2b entry. → substring:"S42 P11-A Wave 2b APP — wire ApproveV2 CQRS Travel+Vehicle" **[cao] Seed 4 sample workflow V2 (S42 P11-A) — gotcha #51.** VIỆC: DbInitializer mirror SeedSampleProposalWorkflowV2Async EXACT ×4 (Leave/Ot/Travel/Vehicle), each idempotent AnyAsync(ApplicableType) guard → 1 Workflow+1 Step+1 Level, codes QT-NP/OT/CT/XE-V2-001. KẾT-LUẬN: wired 4 calls; build 0 err 0 warn. BÀI-HỌC (marker #4): **gotcha #51 — infra seed NOT gated by DemoSeed:Disabled** (by-design reaches prod, like SeedDemoMasterData/Catalogs). BẤT-NGỜ: Bash tool runs bash NOT PowerShell despite env hint → use `cd && cmd | grep`. → substring:"S42 P11-A SEED — 4 sample ApprovalWorkflow V2" ## P11-B/C HRM business + catalogs (S43, S51) **[cao] LeaveBalance deduct exactly-once (S43 P11-B W1) — Mig 42.** VIỆC: entity LeaveBalance:AuditableEntity (UserId/LeaveTypeId/Year + Entitled/Used/Adjustment decimal(5,2)); FK LeaveType Restrict + UNIQUE composite (UserId,LeaveTypeId,Year); deduction hook in ApproveLeaveRequestHandler terminal DaDuyet branch ONLY. KẾT-LUẬN: Mig 42 AddLeaveBalances; RemainingDays = Entitled+Adjustment−Used COMPUTED (not stored); GetMy/GetUser lazy-merge in-memory (no EF LEFT JOIN). BÀI-HỌC (marker #12): **deduct exactly-once GUARANTEED by early guard `Status != DaGuiDuyet throw`**; HRM admin convention `[Authorize(Roles="Admin")]` not menu policy. BẤT-NGỜ: OtRequest/Travel/Vehicle untouched — only Leave has balance. → substring:"S43 P11-B Wave 1 — LeaveBalance business logic" **[cao] Vehicle+Driver catalogs (S51 P11-C W1) — Mig 44, gotcha #57.** VIỆC: 2 entity + 2 EF config (mirror filtered HolidayConfiguration NOT buggy bare LeaveType) + 2 DbSet + HrmConfigFeatures Region5/6 + Controller 8 endpoint (GET public, write Admin) + MenuKeys + DbInitializer seed. KẾT-LUẬN: Mig 44 AddVehicleAndDriverCatalogs; diff CLEAN 2 CreateTable+2 filtered IX. BÀI-HỌC (markers #57,#13,#14): **Code UNIQUE `.HasFilter("[IsDeleted]=0")`**; admin perm AUTO via MenuKeys.All (SeedAdminPermissions + Program.cs:78 both iterate); HRM no HasQueryFilter→`.Where(!IsDeleted)` manual. BẤT-NGỜ: RAG/Qdrant DOWN → all Read/Grep on-disk. → substring:"S51 P11-C HMW W1 — Vehicle+Driver catalogs HrmConfigs" ## P11-D/E/F WorkflowApps wave2 (S52) **[cao] ItTicket round-robin + SLA (S52 P11-D W2) — Mig 46.** VIỆC: entity +SlaDueAt/SlaWarnedSent/SlaBreached; CreateItTicketHandler SlaWindow static map (Urgent4/High8/Medium24/Low72h) shared w/ ItTicketSlaJob; round-robin least-loaded assign (itDept=Departments.Code=="IT"); new ItTicketSlaJob:BackgroundService (breach+warn only, NO auto-transition). KẾT-LUẬN: Mig 46 AddSlaFieldsToItTicket 3 AddColumn no-table; AssignItTicketCommand PUT /{id}/assign [Authorize(Roles=Admin)]. BÀI-HỌC: **SeedItDepartmentStaffAsync MUST run AFTER SeedDemoUsersAsync** (that method reconciles 2 users to PRO/CCM each boot → override to IT after, end-state deterministic). BẤT-NGỜ: agent killed session-limit before MEMORY → proxy by em main. → substring:"S52 P11-D Wave2 BE — ItTicket round-robin + SLA" **[cao] Attendance report + IT codegen (S52 P11-E+F) — NO mig.** VIỆC: P11-F MaTicket gen at Create (kanban no-workflow) `IT/2026/001`; P11-E AttendanceReportFeatures + IAttendanceReportExcelExporter (ClosedXML mirror ContractExcelExporter). KẾT-LUẬN: 2 endpoint [Authorize(Roles=Admin)] GET `/api/attendances/report` + `/report/excel`. BÀI-HỌC: **DayOfWeek+holidaySet NOT EF-translate → `.ToListAsync()` then group/classify IN-MEMORY C#**; **Holiday.Date = DateOnly NOT DateTime (spec wrote HashSet wrong) → HashSet**; day-type prio holiday→weekend→weekday; OtWeighted=Σ(level×coef). BẤT-NGỜ: spec type-error caught (DateOnly). → substring:"S52 P11-E+F Wave1 BE migration-FREE" ## Master + cross-stack (S53, S54, S55) **[cao] Filter 3 Master unique indexes (S53 gotcha #57 EXT) — Mig 47 + Mig 46 catch-up.** VIỆC: test-before RED→GREEN; edit 3 config Code unique `+.HasFilter("[IsDeleted] = 0")` (Department/Project/Supplier); Supplier ONLY Code filtered, Type index left untouched. KẾT-LUẬN: Mig 47 FilterMasterCatalogUniqueIndexesByIsDeleted; Mig 46 local catch-up closed LocalDB gap; 203 GREEN. BÀI-HỌC (markers #2,#57): **gotcha #57 — soft-delete UNIQUE must filter [IsDeleted]=0**; **copied filter string BYTE-FOR-BYTE from HolidayConfiguration:18 + LeaveTypeConfiguration:19** (spaces around `=`, not guessed) → snapshot+SQL consistent w/ 13 existing filtered idx. BẤT-NGỜ: Master HAS global HasQueryFilter (unlike HRM) → app-check passes but bare DB index counts soft-deleted → 500; filter aligns. → substring:"S53 gotcha #57 EXT BE — filter 3 Master Code unique indexes" **[cao] ItTicket reassign capability + fail-closed authz (S54 cross-stack) — NO mig.** VIỆC: new GetAssignableItStaffQuery capability endpoint + AssignItTicketHandler authz Admin-OR-dept-IT→ForbiddenException, assignee-must-be-IT→ConflictException; controller /assign lowered Roles=Admin→[Authorize]. KẾT-LUẬN: 2 patterns saved (controller-lower-authorize-handler-finegrained + scoped-capability-endpoint-anti-silent-403); reviewer chain-verified role-string "Admin" real (AppRoles→SeedRoles→JWT→cu.Roles). BÀI-HỌC (marker #11): **fail-closed authz — Forbidden BEFORE NotFound** (avoid existence-oracle); ICurrentUser lacks DepartmentId → query db.Users. BẤT-NGỜ: em main reconciled 2 patterns from stray src/Backend/.claude (cwd-relative Write mishap). → substring:"S54 ItTicket reassign cross-stack — IT-staff self-service" **[vừa] Menu leaf AttendanceReport (S54 Task D) — NO mig.** VIỆC: 3 insert — MenuKeys const Off_AttendanceReport + All[] + DbInitializer menu tuple (Order 8, parent Off). KẾT-LUẬN: admin perm auto via MenuKeys.All 2-point (SeedAdminPermissions :1916 + Program.cs:78). BÀI-HỌC: idempotent seed upsert (existing prod gets leaf on restart, existing rows only Order-reconciled). BẤT-NGỜ: none. → substring:"S54 Task D BE — promote AttendanceReport to sidebar menu leaf" **[cao] Real master-data import (S55) — Mig 48 + ungated seed.** VIỆC: Project +4 prop (Year int?, Investor/Location/Package, maxlen 250/500/300); SeedRealMasterDataAsync 3 tuple-loop per-code idempotent, wired UNGATED after SeedCatalogsAsync. KẾT-LUẬN: Mig 48 AddProjectMasterFields 4 AddColumn no-table; seeds 62 Project+71 WorkItem+3 Supplier; runtime Dev proof landed. BÀI-HỌC: real-data import reaches prod by-design (DemoSeed:Disabled NOT gate); FLOCK01 collision demo↔real → per-code skip (demo wins). BẤT-NGỜ: WorkItem divider row "THIẾT BỊ" dropped; agent return truncated #53 → proxy by em main. → substring:"S55 master-data import (Mig 48 `AddProjectMasterFields`" ## [stub→git d2f52ba] older absorbed (S35/S40 trim) **[thấp] S35 BE-CRUD-4-catalog + S29 Contract-V2-mirror.** KEY absorbed into L1 Pattern 12-bis foundation (catalog-mega + HRM `.Where(!IsDeleted)` manual + Validator MaxLength=EF). → substring:"Archived S35 G-H2 BE-CRUD-4-catalog + S29 Plan-B-Chunk-C Contract-V2-mirror" **[thấp] S40 curate — FE/test + older BE → git d2f52ba.** S35 FE inline forms 5 satellite + S34 test +10 (130 PASS) + S33 EmployeesListPage + S32 wrap/startup. → substring:"Archived FE/test + older BE entries → `archive/2026-05-q4.md` + git d2f52ba (S40 curate)" --- ## L1-HOT markers (still in MEMORY.md — NOT moved; distilled here for coverage completeness) > These live VERBATIM in MEMORY.md (kept hot). Pointers below target MEMORY.md, not 2026-06.md. - **[cao] S56 GOLIVE-HARDEN — ExecuteUpdate atomic + fail-closed (markers #10,#11).** LeaveBalance lost-update → `ExecuteUpdateAsync` server-side row-lock inside tx (em-main post-review bumped to `IsolationLevel.Serializable`); AssignItTicket existence-oracle → Forbidden guard BEFORE NotFound (fail-closed). STALE-TRACKED caveat: ExecuteUpdate bypasses tracker → don't re-add `bal.UsedDays +=` (double-count). → substring(MEMORY.md):"S56 GOLIVE-HARDEN 3 BE fix" - **[cao] S57bis/S65 loose-Guid FK guard (marker #13).** loose-Guid FK (WorkItemId S57bis, Department.ParentId S65) = NO physical FK; guard `AnyAsync(x.Id==id && x.IsActive)`→Conflict (mirror S43); UpdateDraft null-safe `if (request.WorkItemId is not null)` to avoid null-ing bug-class S42. → substring(MEMORY.md):"FK-guard loose-Guid" - **[vừa] L1 Pattern foundation markers (#3,#5,#9b).** gotcha #17 = EF migration 3-file rule (Pattern 2, BẮT BUỘC commit `.cs`+`.Designer.cs`+snapshot); gotcha #65 = `dotnet build SolutionErp.slnx` includes 2 test projects; Mig 29/30/31 per-NV admin opt-in (Pattern 7, proven 4×). → substring(MEMORY.md):"EF migration 3-file rule (gotcha #17"