Files
solution-erp/.claude/agent-memory/implementer-backend/archive/2026-06.gist.md
pqhuy1987 c2d01f0dce
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m30s
[CLAUDE] Docs: S152 closeout — bookend hình B trọn 2 đầu + owner (30)(31)(32) + queue memory-ops TRỌN (squash 20 wal:)
- Owner 3-quyết: (30) JUMP để-nguyên-theo-nhịp · (31) 4 persona +NEVER-block (restart ĐÃ THOẢ) · (32) vòng 1→5 AUTO — lô consent gỡ 9 site (5 H1 + ring1-tự-bắt + 3 trio; E-013 EXT)
- Memory-ops TRỌN: A1-A4/D1/D2/R1/B-series — A7 392/392 · mfe 20/20-100% lần đầu · lead-gap drain 24.728→16.485B · strike-ledger + WARN-latch (-Ack = owner-action)
- V4 sleep AUTO đầu: 1 shard thật/16 KÊU · ring4 đầu-đời TRƯỢT-4/5 → 4 fix gist (scribe sub-ring4-close vì return-only)
- Thư model re-stamp: 59ce1d0f → 9c909007 published (G-024a) — adap-report Đính-chính @S152 + sweep 13 bề-mặt LIVE
- Bookend @close: stale 6 FLAG + gap 2 FLAG → vá 8/8; ring2 8/8 ĐẠT (4-A pin-bản-cắt → slot 33) · ring1 48Đ/5T/59-claim
- M9 13 đơn-vị diary (3 S151-deep hồi-tố) · #53 ×10-chắc → tally 60-cận-dưới + errata subject-1b85713
- Session-log 2 run= + completeness-gate 5-vòng ĐẠT · HANDOFF segment S152 (carry 3-đóng/3-mới/23-giữ; slot 33/34)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 01:58:30 +07:00

18 KiB
Raw Blame History

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+AdjustmentUsed 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"

CỤM BỔ SUNG [coverage-diff @S152 D2 — máy-trích nguyên-văn + lead-review; KHÔNG phải re-distill, distill-gen giữ]

Token load-bearing có trong verbatim 2026-06.md mà gist thiếu — mỗi dòng = token + TRÍCH NGUYÊN-VĂN dòng chứa nó (Ctrl-F token trong verbatim để đọc trọn). Chuẩn gate: gotcha#/Mig/file-ref/guard phải sống trong gist.

  • gotcha #73 · trích: "6 #5) + absolute-set null=clear (gotcha #73) + changelog Header (sibling-consistency; spec min=SaveChanges). (6) Controller `[HttpPatch('{id:guid}/bud"
  • Mig Mig 11 · trích: "field nằm trên User.DepartmentId (Mig 11) → GROUP BY db.Users.Where(DepartmentId!=null && IsActive).GroupBy(DepartmentId).ToDictionary = Direc"
  • Mig Mig 43 · trích: "#57 4th+5th+6th cumulative (S45 Holiday Mig 43, S51 HRM×3 Mig 45 → now Department/Project/Supplier). Edit 3 config Code unique: `b.HasIndex(x=>x.Code)"
  • Mig Mig 45 · trích: "mulative (S45 Holiday Mig 43, S51 HRM×3 Mig 45 → now Department/Project/Supplier). Edit 3 config Code unique: b.HasIndex(x=>x.Code).IsUnique() → `+."
  • Mig Mig 49 · trích: "proposed):** Return-truncated #53 TRƯỚC Mig 49 + projection-3 → em main solo hoàn tất (fix CS7036 + CS8019, Mig 49 AddWorkItemToPurchaseEvaluation 3"
  • Mig Mig 51 · trích: "- **2026-06-16 (S65b PE +HoSoLink BE — Mig 51 AddHoSoLinkToPurchaseEvaluation 3-file, 6 file edit + 0 new file, em-main CHỐT spec 100% → ACCEPT Case"
  • Mig Mig 52 · trích: "olumn, snapshot verified — clean mirror Mig 52. (4) DTO PurchaseEvaluationDetailBundleDto +decimal? CcmBudgetPeriodAmount sau ExpectedRemainingAmo"
  • Mig Mig 55 · trích: "mn, snapshot ×2 verified — clean mirror Mig 55. (4a) UpdatePeSuggestedPriceProCommand +trailing string? Note=null + validator `MaximumLength(1000)"
  • Mig Mig 57 · trích: "-19 (S77 PE +2 ghi-chú giá đề xuất BE — Mig 57 AddPeSuggestedPriceNotes 3-file, 6 edit/0 new file, COOKIE-CUTTER S73 suggested-price + S74 CcmNote,"
  • Mig Mig 58 · trích: "Suppliers.Where(s=>s.IsWinner)` (multi, Mig 58) → sửa CHỈ comment-text 'MỌI đơn vị IsWinner (multi-winner Mig 58)'. Field/code UNCHANGED. LESSON:"
  • Mig Mig 59 · trích: "vity 2026-06-25..19 (S88 · PE Section-B Mig 59 · PE chuông) -->"
  • file-ref AttendanceReportFeatures.cs · trích: "/001. **P11-E** report chấm công: NEW AttendanceReportFeatures.cs(DTO 3 record EXACT + GetAttendanceReportHandler) + NEWIAttendanceReportExcelEx"
  • file-ref DbInitializer.cs · trích: "Reportafter OffChamCong:158-159 · (3) DbInitializer.cs menu tuple(OffAttendanceReport, 'Báo cáo chấm công', Off, 8, 'FileBarChart')` after OffCham"
  • file-ref Department.cs · trích: "y tổ chức nền trang Hồ sơ Nhân sự. (1) Department.cs +Guid? ParentId loose-Guid KHÔNG physical FK (convention PE.ProjectId/WorkItemId/SelectedSupp"
  • file-ref DepartmentConfiguration.cs · trích: "Id/WorkItemId/SelectedSupplierId). (2) DepartmentConfiguration.cs +HasIndex(x=>x.ParentId) only — KHÔNG HasOne self-FK (em main chốt loose)."
  • file-ref DepartmentFeatures.cs · trích: "eself-FK** (em main chốt loose). (3)DepartmentFeatures.cs +GetDepartmentTreeQuery+DepartmentTreeNodeDto`+Handler (append existing file, NO new"
  • file-ref DepartmentsController.cs · trích: "e; slnx lists projects-not-files). (4) DepartmentsController.cs +[HttpGet('tree')]. KEY recon finding (spec asked verify): EmployeeProfile h"
  • file-ref MasterCatalogFilteredUniqueTests.cs · trích: "real code+year only, OK). Compile-fix MasterCatalogFilteredUniqueTests.cs +4 null args CreateProjectCommand (necessary build-green). **Runtime Dev"
  • file-ref MenuKeys.cs · trích: "potent seed (not schema). 3 insert: (1) MenuKeys.cs const OffAttendanceReport = 'Off_AttendanceReport' after OffChamCong:124 · (2) MenuKeys.cs All[]"
  • file-ref PEFeatures.cs · trích: "ote` sau CcmSuggestedPrice + projection PEFeatures.cs positional-insert ĐÚNG order (DTO positional → order khít projection BẮT BUỘC). **Call-site grep"
  • file-ref PeSuggestedPriceFeatures.cs · trích: "file → move về budget file. (a) REMOVE PeSuggestedPriceFeatures.cs lines 144-199 (blank + ===== CCM NS kỳ này ===== header → `SetPeCcmBudgetPeriod"
  • file-ref PeWorkItemBudgetFeatures.cs · trích: "ce PRO/CCM trên giữ nguyên. (b) APPEND PeWorkItemBudgetFeatures.cs END sau line 200 (UpdatePeBudgetCcmCommandHandler }) → cạnh budget setters `U"
  • file-ref ProjectFeatures.cs · trích: "len 250/500/300 ProjectConfiguration). ProjectFeatures.cs DTO+CreateCmd+UpdateCmd+validators+handlers+List/Get projections +4 (all nullable, appende"
  • file-ref PurchaseEvaluation.cs · trích: "nullable, no table/index/backfill. (1) PurchaseEvaluation.cs +string? ProSuggestedPriceNote/CcmSuggestedPriceNote ngay sau CcmSuggestedPrice. (2"
  • file-ref PurchaseEvaluationConfiguration.cs · trích: "eNotengay sau CcmSuggestedPrice. (2)PurchaseEvaluationConfiguration.cs +HasMaxLength(1000)` ×2 sau ApprovedPriceSource (KHÔNG index — free-text)"
  • file-ref PurchaseEvaluationDtos.cs · trích: "G đụng. T2 stale-comment fix: Dtos/PurchaseEvaluationDtos.cs ~250-254 comment trên decimal WinnerQuoteTotal ghi SUM 'của SelectedSupplierId' ("
  • file-ref PurchaseEvaluationFeatures.cs · trích: "ngle-winner) → verified actual compute PurchaseEvaluationFeatures.cs:1093-1099 = Suppliers.Where(s=>s.IsWinner) (multi, Mig 58) → sửa CHỈ comment-"
  • file-ref PurchaseEvaluationWorkflowService.cs · trích: "= +1 block trong LogTransitionAsync (PurchaseEvaluationWorkflowService.cs ~line 1058) NGAY SAU drafter-notify, KHÔNG endpoint mới (Pattern 4 — noti"
  • file-ref TravelVehicleApprovalFeatures.cs · trích: "— wire ApproveV2 CQRS Travel+Vehicle (TravelVehicleApprovalFeatures.cs ~830 LOC + 2 controller edit):** Cookie-cutter mirror Wave 2a / ProposalFeat"
  • file-ref WorkflowAppsFeatures.cs · trích: "ullName)capability endpoint (REGION 5 WorkflowAppsFeatures.cs) + MODIFIEDAssignItTicketHandler: authz Admin-OR-dept-IT → ForbiddenException`; as"
  • file-ref menuKeys.ts · trích: "ing DocxRenderer warn). KHÔNG touch FE (menuKeys.ts/Layout = implementer-frontend) / tests / commit. Tag `[s54, task-d, menu-leaf, no-mig, admin-perm-"

BỔ-SUNG-2 [F1/F2 sau Fidelity-gate @S152 — mở đuôi infra {yml,yaml,json,slnx,csproj,sql,config} + guard IsUnique/optimistic. N/A CỐ-Ý (F5): bundle-hash xoay #69 · tên-giả phép-thử ZZfakehash* · đuôi .md]

  • file-ref SolutionErp.Application.csproj · trích: "mirror S35 EF-source-of-truth). Build SolutionErp.Application.csproj -v q 0 warn 0 err (4s). KHÔNG test (test-specialist)/FE/mig/commit. Tag `["