Closeout buoi san pham lon (anh Kiet FDC + Tra Sol + Bich Phuong, HMW-mode ON): 10 deploy prod-verified #320->#329, 10/10 cicd PASS. STATUS + HANDOFF + session log 2026-06-19-S77. State: Mig 56->57 (AddPeSuggestedPriceNotes) · test 344->354 (+10) · bundle cuoi BqKD3Y23/Cn-i349D · 88 tables · gotcha 70. Viec: co GAP pill moi danh sach+inbox · focus->revert list · Mig 57 ghi chu gia de xuat PRO/CCM + so phan cach + chinh ta + guard #70 · so am do-ngoac · muc con thut-gach · co gap GAN=NV/GO=Truong phong bat-doi-xung · tach chon-phieu(inline) khoi mo-rong(overlay)+nut Xem mo rong · chuong bao nguoi duyet · banner Tra-lai. 3 loi em tu bat review-truoc-deploy (guard#70 · asymmetric · double-mount). FD process-death Task H->recover-disk. Flush 5 sub-agent MEMORY (self-flush khi return). CARRY: curate L1 over-cap reviewer 45KB+cicd 37.6KB+inv 35.6KB keep-floor-hit manual (archive-gate A7 GATE PASS 186/186). Docs+memory only -> CI skip (gotcha #41). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
19 KiB
Implementer-Backend Agent — Persistent Memory
Persistent diary cross-session. Auto-injected first ~200 lines at spawn (L1 HOT). Update BEFORE every stop. Tiered Memory v1: L1 HOT soft-cap ~30KB · L2
archive/on-demand · L3 RAGsearch_memoryjust-in-time. Keep entry ≤ 1.5K chars (gotcha #53). Full verbatim history pre-S40 → gitd2f52ba+archive/2026-05-q1..q4.md+archive/2026-06.md. 🗺️ Lookup map (Harness-9 S70):archive/_INDEX.md— 1 dòng/bản-ghi + con-trỏ substring (sha-keyed, Ctrl-F fallback); đọc verbatim +2026-0{5,6}.gist.md(nén 4-field) theo nhu cầu. Renamed S39: implementer → implementer-backend (.NET half). FE patterns →implementer-frontendMEMORY. Test patterns →test-specialistMEMORY.
🎯 Role baseline
WRITE specialist .NET backend SOLUTION_ERP (Domain+Application+Infrastructure+Api). Case 1+2+3+5 only. Tools: Read, Edit, Write, Bash, Skill, Grep, Glob + 5 RAG MCP. Skills: ef-core-migration + permission-matrix + contract-workflow + form-engine. Output: commits + verify report.
🚫 Split boundary (S39) + auto-refuse
- ✅ MINE:
src/Backend/SolutionErp.{Domain,Application,Infrastructure,Api}/** - ❌ NOT:
fe-*/**→implementer-frontend·tests/**→test-specialist· schema/UX/architecture decision → em main - REFUSE if ANY: 1 schema design (FK/nullable/discriminator) · 2 UX flow · 3 cross-stack >2 layer · 4 bug fix reasoning chain · 5 integration multi-component · 6 <30min trivial · 7 first-time no precedent · 8 spec ambiguity >20%
📋 BE Patterns proven (apply confidently)
Pattern 1: Per-chunk discipline A-E
A Domain+Mig (3-file) · B Application CQRS (Command/Query/Validator) · C Service (workflow logic) · D Api Controller · E commit. Build+test pass mỗi chunk. Commit [CLAUDE] <scope>: Chunk X — ... + Co-Authored-By Claude Opus 4.8 (1M context).
Pattern 2: EF migration 3-file rule (gotcha #17 — BẮT BUỘC commit đủ)
{TS}_{Name}.cs + .Designer.cs + ApplicationDbContextModelSnapshot.cs. Path src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/.
dotnet ef migrations add <Name> --project src/Backend/SolutionErp.Infrastructure --startup-project src/Backend/SolutionErp.Api
# Apply Dev (runtime): --connection "Server=(localdb)\MSSQLLocalDB;Database=SolutionErp_Dev;Trusted_Connection=True;TrustServerCertificate=true"
# Apply Design (ef default): không cần --connection
Apply BOTH DB per feedback_designtime_runtime_db.
Pattern 3: Audit reuse trước khi clone (feedback_audit_reuse_before_clone)
"Clone X→Y": grep discriminator (ApplicableType/Type/Kind) → check Service/Handler hardcode → check FE route dynamic → check menu key (BE const + FE menuKeys.ts thường thiếu) → default reuse 80%, chỉ thêm menu key + sample seed.
Pattern 4: Service hook vs CRUD endpoint cho derived state (feedback_service_hook_vs_endpoint)
State X derived của action Y → UPSERT trong handler Y, KHÔNG endpoint /X riêng. VD ApproveV2Async UPSERT LevelOpinion qua match ApproverUserId==actorUserId (fallback first khi Admin override). 0 endpoint mới.
Pattern 7: Per-NV admin opt-in flag (Mig 29/30/31)
ApprovalWorkflowLevel +1 bool DEFAULT 0 (opt-in). EF HasDefaultValue(false). DTO extend. FE Designer checkbox inline. Scope role-context → table mapping (Approver→Level table carry ApproverUserId FK, Drafter→User table direct, feedback_per_nv_permission_scope). Reusable F5/F6.
Pattern 8: Tách endpoint riêng cho narrow scope
1 action 2 scope theo role → tách endpoint (guard tự nhiên + audit). VD Drafter UpdatePeDraft (Section 1 rộng, phase Nháp/Trả lại) vs Approver AdjustBudget (Budget rows hẹp, phase Đang duyệt + per-NV flag). KHÔNG default expand Drafter scope cho Approver.
Pattern 9: Defense-in-depth FE+BE guard pair
UI disabled={!canX} + BE helper EnsureCanXAsync(id, userId) throw 403 (NOT inline handler) — tránh forge qua DevTools. Bất kỳ action sensitive (approve/reject/adjust).
Pattern 12-bis: Cross-module entity cookie-cutter mirror (S29 Plan B, proven 3×)
"Mirror entity X từ module A→B": 6 file MAX — (1) new entity Domain/<Mod>/<Entity>.cs rename FK+nav · (2) parent +nav collection · (3) IApplicationDbContext +DbSet · (4) ApplicationDbContext +Set<X>() · (5) new <Entity>Configuration.cs (separate file mirror PE, NOT inline) · (6) dotnet ef migrations add 3-file. AuditableEntity inherit. FK: parent Cascade + 3rd-party Restrict + User skip-nav (denorm <Type>ByFullName). Apply 2 DB. ⚠️ Catalog-mega variant (S35 HrmConfig): HRM entities KHÔNG có global HasQueryFilter(!IsDeleted) (vs Master) → list query MUST .Where(!IsDeleted) thủ công (verify Grep HasQueryFilter Configurations FIRST). Validator MaxLength MATCH EF config (verify source-of-truth, KHÔNG trust spec blind).
Pattern 12-ter: N≤7 satellite CRUD scaffold same parent (S34, feedback_within_module_n_satellite_scaffold)
"N satellite cùng parent" → 1 mega file <Parent>SatelliteFeatures.cs N region cookie-cutter (Create verify parent AnyAsync → Update FirstOrDefault !IsDeleted → Delete soft IsDeleted+DeletedAt+DeletedBy ICurrentUser) + 1 Controller extend (3 verb × N). Endpoint verify parentId==cmd.ParentId BadRequest mismatch. Per-action policy override class-level Read.
Patterns moved (split S39)
- FE patterns (5 mirror 2-app · 6 VND helpers · 13 read-only Designer · 14 Tailwind JIT palette · 15 rowSpan builder · 16-bis 4-place mirror) →
implementer-frontendMEMORY (seeded). - Test patterns (10 reflection authz · 11 test infra helper · 12 InternalsVisibleTo) →
test-specialistMEMORY (seeded).
⚠️ Anti-patterns (DO NOT)
- ❌ Skip MEMORY · 2. ❌
--no-verify· 3. ❌git add -A/git add .(specific files) · 4. ❌ Touch outside spec scope · 5. ❌ Push remote autonomous (em main pushes) · 6. ❌ ModifySolutionErp.slnxautonomous · 7. ❌ Lower bar match em main (Smart Friend) · 8. ❌ Proceed khi ambiguity >20% → REFUSE
🧠 SOLUTION_ERP BE conventions (S40)
- BE .NET 10: PascalCase entities + DTO records + command names. CQRS+MediatR+FluentValidation+AutoMapper. Repository qua
IApplicationDbContext.GlobalExceptionMiddleware→ ProblemDetails (NO try-catch controllers). - State S53: 47 mig (last
FilterMasterCatalogUniqueIndexesByIsDeletedMig 47, index-only) · 93 SQL tables · ~224 endpoints · 203 test (58 Domain + 145 Infra, test-specialist owns). Phase 9 UAT skip per chunk (feedback_uat_skip_verify). - Build:
dotnet build SolutionErp.slnxclean 0 err. Commit[CLAUDE] <scope>: <msg>+ Co-Authored-By Claude Opus 4.8 (1M context). - Pin (KHÔNG
*/latest): MediatR12.4.1(14 fail DI) · Swashbuckle6.9.0· Node CI20.x· LibreOffice25.8.6· @microsoft/signalr8.0.7.
📅 Recent activity (FIFO — older → archive/git)
-
2026-06-19 (PE chuông báo approver BE — NO migration, 1 edit/1 file, em-main spec deterministic 100% → ACCEPT Case 1): Tra Sol (Zalo) "không thấy chuông — việc có hồ sơ cần duyệt": approver Cấp hiện tại KHÔNG nhận notify khi phiếu ENTER/ADVANCE tới ChoDuyet (chỉ drafter báo terminal). FIX = +1 block trong
LogTransitionAsync(PurchaseEvaluationWorkflowService.cs ~line 1058) NGAY SAU drafter-notify, KHÔNG endpoint mới (Pattern 4 — notify = side-effect của Submit/Approve-advance, cả 2 đã gọi LogTransitionAsync + end ChoDuyet). Resolution mirror EXACT canonicalEnsureActorInLevel(line ~301):db.ApprovalWorkflows.AsNoTracking().Include(Steps).ThenInclude(Levels)→stepsOrdered[CurrentWorkflowStepIndex](bounds-check) →.Levels.Where(Order==CurrentApprovalLevelOrder && ApproverUserId!=Guid.Empty && !=actorUserId).Select(ApproverUserId).Distinct()→NotifyManyAsync(ids, Generic, "Phiếu cần bạn duyệt: {MaPhieu??TenGoiThau}", "Có phiếu Duyệt NCC đang chờ bạn duyệt.", /purchase-evaluations/{Id}, refId:Id, ct). Recon: (1)ApprovalWorkflowLevel.ApproverUserId= non-nullGuid(NOT Guid?) → "exclude null" =!=Guid.Emptydefensive (OR-of-N rows mỗi 1 NV). (2)NotifyManyAsyncsig verified =(IEnumerable<Guid>, NotificationType, title, string? desc, string? href, Guid? refId, CT)khớp urgent-feature ref. (3) actor-exclude qua!= actorUserId(Guid != Guid? lift OK). Guard V2-only:ApprovalWorkflowId is Guid && CurrentWorkflowStepIndex is int && CurrentApprovalLevelOrder is int→ V1/no-workflow skip. Best-effort try/catch nuốt lỗi (phiếu đã SaveChanges; notify fail KHÔNG rollback). Block đọc pointer SAU set → Submit (idx0/lvl1) + Approve-advance (pointer advance rồi LogTransition) đều fire đúng. Build SolutionErp.slnx (2 test project, gotcha #65) 0 warn 0 err. KHÔNG test/FE/mig/commit. Tag[pe-approver-bell, notify-many, log-transition-hook, no-mig, v2-only, best-effort-trycatch, pattern4-side-effect]. -
2026-06-19 (S77 PE +2 ghi-chú giá đề xuất BE — Mig 57
AddPeSuggestedPriceNotes3-file, 6 edit/0 new file, COOKIE-CUTTER S73 suggested-price + S74 CcmNote, em-main CONTRACT-locked names 100% → ACCEPT Case 1): PRO dải Min/Max + CCM 1 giá (S73) +ô GIẢI THÍCH vì-sao min vs max (anh Kiệt FDC + Tra Sol). 2 cột nullable, no table/index/backfill. (1)PurchaseEvaluation.cs+string? ProSuggestedPriceNote/CcmSuggestedPriceNotengay sau CcmSuggestedPrice. (2)PurchaseEvaluationConfiguration.cs+HasMaxLength(1000)×2 sau ApprovedPriceSource (KHÔNG index — free-text). (3) Mig: Up=2 AddColumn nvarchar(1000) nullable, Down=2 DropColumn, snapshot ×2 verified — clean mirror Mig 55. (4a)UpdatePeSuggestedPriceProCommand+trailingstring? Note=null+ validatorMaximumLength(1000)MATCH-EF (S35) + handler absolute-setpe.ProSuggestedPriceNote=request.Note(null=clear, gia-đình text-field) + changelog part khi đổi. (4b)UpdatePeSuggestedPriceCcmCommandmirror + changelog suffix "(kèm ghi chú)". Role-gate KHÔNG đụng (Note gated y giá: Forbidden fail-closed TRƯỚC side-effect đã có). (5) ControllerSuggestedPriceProBody/CcmBody+string? Note+ passbody.Notevào cmd (4th/3rd positional). (6)PurchaseEvaluationDetailBundleDto+ProSuggestedPriceNote/CcmSuggestedPriceNotesau CcmSuggestedPrice + projection PEFeatures.cs positional-insert ĐÚNG order (DTO positional → order khít projection BẮT BUỘC). Call-site grep verify (S65b lesson):new UpdatePeSuggestedPrice(Pro|Ccm)Command\(repo-wide → 2 controller (đã sửa) + 18 test named-arg/positional-dừng-trước-Note → trailing-optional fully backward-compat, 0 test edit. FE 0 ref (đúng — implementer-frontend next). Build SolutionErp.slnx (2 test project, gotcha #65) 0 warn 0 err. KHÔNG apply DB/FE/test/commit. Route:notecamelCase qua PUT/suggested-price/{pro,ccm}body + GET detail bundle. Tag[s77, pe-suggested-price-note, mig57, two-column-no-table, absolute-set, trailing-optional-callsite-safe, positional-dto-order]. -
2026-06-17 (S? Off_Dashboard menu leaf BE — NO migration, 3 edit/2 file, idempotent seed mirror S53/S54-TaskD, em-main spec deterministic 100% → ACCEPT Case 1): +1 menu key
Off_Dashboard("Bảng điều khiển Văn phòng số"), pattern = S53 Off_AttendanceReport EXACT. 3 insert: (1)MenuKeys.csconstOffDashboard = "Off_Dashboard"ngay sau rootOff:99· (2)MenuKeys.csAll[] lineOff, OffDanhBa→Off, OffDashboard, OffDanhBa· (3)DbInitializer.csSeedMenuTreeAsync tuple(OffDashboard, "Bảng điều khiển Văn phòng số", Off, **0**, "LayoutDashboard")trước OffDanhBa=1 (Order 0 = landing đầu nhóm, KHÔNG renumber children 1-7 hiện có). KEY recon — Off_ leaves ARE IN All[] (NOT factory-excluded):* task hint "leaf may be excluded+granted-via-factory" KHÔNG áp Off (chỉ Pe_* leaf sinh động). Off_AttendanceReport :160 in All → tôi follow SAME = +All. Admin auto 2-point verified:SeedAdminPermissionsAsync:2001+Program.cs:78both iterateMenuKeys.All→ +All = 4 policy {Read/Create/Update/Delete} + Admin Permission row auto, NO manual grant. Revoke verified KHÔNG sửa:RevokeTemporarilyHiddenModulesAsync:2170p.MenuKey.StartsWith("Off")→ Off_Dashboard tự nằm trong scope ẩn-non-Admin.InReviewScope:2070chỉ match Catalog*/Master-keys/Pe_* → Off_Dashboard KHÔNG re-grant non-admin. Idempotent: upsert loop :1909existingItems.TryGetValue(key)miss→Add / hit→chỉ reconcile Order (prod DB cũ nhận leaf next boot, re-run no-op). Build SolutionErp.slnx (gồm 2 test project, gotcha #65) 0 warn 0 err. KHÔNG touch FE (menuKeys.ts/Layout=implementer-frontend)/test/mig/commit. Tag[s?, off-dashboard, menu-leaf, no-mig, admin-perm-via-all, order-0-landing]. -
2026-06-16 (S65b PE +HoSoLink BE — Mig 51
AddHoSoLinkToPurchaseEvaluation3-file, 6 file edit + 0 new file, em-main CHỐT spec 100% → ACCEPT Case 1): Phiếu PE +1 cộtHoSoLink= 1 hyperlink tới thư mục hồ sơ NAS (anh Kiệt paste link, FE render bấm-mở). KHÔNG entity con/bảng mới — 1 cột nullable. (1)PurchaseEvaluation.cs+string? HoSoLinksau MoTa. (2)PurchaseEvaluationConfiguration.cs+HasMaxLength(1000)(KHÔNG index — hyperlink free-text, không filter/join). (3) Mig viadotnet ef migrations add→ Up=1AddColumn<string> nvarchar(1000) maxLength:1000 nullableNO table NO index, Down=1 DropColumn; snapshot HoSoLink nvarchar(1000) verified. (4a)CreatePurchaseEvaluationCommand+trailingstring? HoSoLink = null(sau WorkItemId — optional-param-after-required rule) + validatorMaximumLength(1000)MATCH EF (S35 lesson) + handlerHoSoLink = request.HoSoLink. (4b)UpdatePurchaseEvaluationDraftCommand+trailingstring? HoSoLink = null+ validator 1000 + handler absolute-setentity.HoSoLink = request.HoSoLink(Section-1 text-field family MoTa/DiaDiem pattern → null=clear, KHÔNG null-safe-keep như WorkItemId picker; deliberate: hyperlink user cần clear được). (5)PurchaseEvaluationDetailBundleDto+string? HoSoLinksau MoTa + projectione.HoSoLinkpositional-insert đúng vị trí. RANG-CUNG grep verify (bài học CreateDepartmentCommand CS7036):Grep CreatePurchaseEvaluationCommand|UpdatePurchaseEvaluationDraftCommandrepo-wide gồm tests → 0 manualnew ...Command(...)call-site (controller bind[FromBody]model-binding, KHÔNG manual ctor; tests dùng NAMED-ARG dừng ở WorkItemId) → trailing-optional-default fully backward-compat, KHÔNG sửa call-site nào. List DTO KHÔNG đụng (spec chỉ Detail/Get)..slnxKHÔNG update (chỉ 2 mig-file trong project có sẵn). KHÔNG apply DB/FE/test/commit. Build SolutionErp.slnx (gồm 2 test project) 0 warn 0 err (DocxRenderer warn cleared). Route:hoSoLinkcamelCase qua POST/PUT body + GET detail. Tag[s65b, pe-hosolink, mig51, one-column-no-table, trailing-optional-param, named-arg-callsite-safe]. -
2026-06-16 (S65 Department hierarchy BE — Mig
AddDepartmentParentId3-file, 4 file edit + 0 new file, em-main schema CHỐT → ACCEPT Case 1): Cây tổ chức nền trang Hồ sơ Nhân sự. (1)Department.cs+Guid? ParentIdloose-Guid KHÔNG physical FK (convention PE.ProjectId/WorkItemId/SelectedSupplierId). (2)DepartmentConfiguration.cs+HasIndex(x=>x.ParentId)only — KHÔNGHasOneself-FK (em main chốt loose). (3)DepartmentFeatures.cs+GetDepartmentTreeQuery+DepartmentTreeNodeDto+Handler (append existing file, NO new .cs →.slnxKHÔNG cần update; slnx lists projects-not-files). (4)DepartmentsController.cs+[HttpGet("tree")]. KEY recon finding (spec asked verify):EmployeeProfilehas NODepartmentId— links viaUserId; org-chart dept field nằm trênUser.DepartmentId(Mig 11) → GROUP BYdb.Users.Where(DepartmentId!=null && IsActive).GroupBy(DepartmentId).ToDictionary= DirectEmployeeCount (recon NOT schema-decision). Tree ráp in-mem: roots=ParentId-null OR orphan-parent (safe-root); TotalEmployeeCount=Direct+Σ(Children.Total) đệ quy rollup viarecord with{}; cycle-guard HashSet visited (node đã thăm→return null cắt vòng); Children sortOrderBy(Code, StringComparer.Ordinal)ổn định. Authz copy-từ-đâu:[HttpGet]List = CHỈ class-level[Authorize](no per-action attr) →/treecũng vậy (verified read controller). Mig diff CLEAN: AddColumn ParentId nullable + CreateIndex IX_Departments_ParentId, NO new table, Down DropIndex→DropColumn (SQL 5074 order). KHÔNG apply (prod/CI). Build SolutionErp.slnx 0/0. KHÔNG touch FE/test/seed-parent/commit (em main). RouteGET /api/departments/tree→List<DepartmentTreeNodeDto>. Tag[s65, dept-hierarchy, loose-guid-no-fk, in-mem-tree-rollup, cycle-guard, user-departmentid-source]. -
2026-06-11 (S57bis PE WorkItemId BE slice — PARTIAL, on-behalf em main ghi hộ, H2-proposed): Return-truncated #53 TRƯỚC Mig 49 + projection-3 → em main solo hoàn tất (fix CS7036 + CS8019, Mig 49
AddWorkItemToPurchaseEvaluation3-file, projection ListItemDto ×3 LEFT-join WorkItems, UpdateDraft null-safeif (request.WorkItemId is not null)chống null-hóa bug-class S42). LEARNED: FK-guard loose-GuidAnyAsync(w.Id==x && w.IsActive)→Conflict (mirror S43); validatorNotEmptycreate-only, DB nullable backward-compat 4 phiếu cũ;NotEmpty()trênGuid?KHÔNG chặnGuid.Empty→ handler guard bắt (defense-in-depth). SURPRISE: truncate 2 session liên tiếp (S55, S57bis) ở slice lớn cross-layer → cắt stage nhỏ hơn (entity+config / mig / projection tách spawn). Tag[s57bis, truncated-53, on-behalf]. -
2026-06-10 (S57-resume spawn-test H4.8 — Harness-4 two-tier): Mình bị DEMOTE pin
model: claude-opus-4-8(deterministic-scaffold class, double-gate reviewer+test+cicd sau lưng). Spawn-test echo model NGAY sau edit → self-reportclaude-fable-5[1m]= SE env (CCD harness) KHÔNG fresh-read frontmatter → pin ăn SAU restart CLI. Post-restart mình chạy Opus 4.8 (effort Max giữ env-wide); task hệ-trọng giao mình qua hmw có thể overridetier:'fable'. Tag [h4-demote, spawn-test, pending-restart].(S56 GOLIVE-HARDEN 3 BE fix — ExecuteUpdate-atomic LeaveBalance + fail-closed AssignItTicket authz + DocxRenderer CS8602 → FIFO'd S77; verbatim git/archive. em-main post-review bumped tx →
IsolationLevel.Serializableper database-agent. Test 228 green.)
🔄 Curate trigger
-
~30KB → archive recent → L2
archive/<period>.md. Stale >3mo → remove. - Last curate: 2026-06-17 S70 Harness-9 (em-main + Stage-B workflow) (33.2→17.4KB): L2 dark-matter recovery — 14 Recent-activity entries (S55→S35) → NEW
archive/2026-06.md+_INDEX.md(substring sha-keyed) +2026-0{5,6}.gist.md(distill-gen:1). 0-byte-loss md5 byte-exact (Stage C audit CONCERN → read-side-gap MEMORY-L5→_INDEX fixed). (cosmetic: 2 curate-meta lines carryS?worker-label.) Prev: S40 (30.9→~18KB dedup-split BE/FE/test) · S34 q3 · S22 q1.