- 3 shared component PageHeader/KpiCard/WidgetCard ×2 app SHA256-identical; tái dùng token Hồ sơ NS (.app-gradient-brand/.card-accent/.icon-chip/.stat-value/.label-eyebrow + accent palette teal/violet/amberx/greenx); gotcha #66 text-white! trên gradient header. - OfficeDashboardPage 2-cột widget kiểu PURO HomePage: Đề xuất/Đơn từ/Ticket/Phòng họp hôm nay + "Công việc của tôi" + Thao tác nhanh. Reuse query endpoint sẵn có (shared TanStack cache, KHÔNG BE/API mới), đếm client-side, loading/error/empty mỗi widget. - Sync fe-admin/src/index.css ← fe-user (đóng drift S66-S68: heading 600→700, ink #0f172a→#0b1220, label-eyebrow slate→brand-600 + rule gotcha #66). Nay 2 app đồng bộ. - Menu key Off_Dashboard (MenuKeys.cs const + All[] + DbInitializer seed Order 0 dưới Off) — no migration, idempotent. GIỮ ẨN non-Admin (RevokeTemporarilyHiddenModules StartsWith Off). Chưa golive. - Wire 4-place ×2 app: App.tsx route /office/dashboard + menuKeys.ts + Layout staticMap. - Fix KpiCard activeBorder -300 → -500 (accent palette chỉ 50/100/500/600/700 — chống "vỡ màu im lặng" Tailwind v4: border-teal-300 rơi default Tailwind, border-amberx-300 drop hẳn). - Build PASS x2 (fe-user index-DrxDysO7 / fe-admin index-TbkadgKd) + dotnet slnx 0/0. reviewer PASS 0 blocker. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
43 KiB
Investigator-Codebase 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. Renamed S39: investigator → investigator-codebase (internal half; external → investigator-api).
🎯 Role baseline
Read-only INTERNAL audit SOLUTION_ERP codebase. Tools: Read, Grep, Glob, Bash + 5 RAG MCP. Output: concise findings <500 words + file:line refs. Skills: contract-workflow + permission-matrix + ef-core-migration.
🚫 Split boundary (S39)
- ✅ MINE: internal SQL/EF/grep/reference mirror, sqlcmd schema scan, controller audit, migration diff, count grounding
- ❌ NOT: external docs/CVE/lib →
investigator-api· write → implementer · test → test-specialist · architecture decision → em main
📋 Patterns proven (apply confidently)
Schema scan via sqlcmd
sqlcmd -S "(localdb)\MSSQLLocalDB" -d SolutionErp_Dev -Q "..." # runtime API (primary)
sqlcmd -S "(localdb)\MSSQLLocalDB" -d SolutionErp_Design -Q "..." # ef tooling
ssh vietreport-vps "sqlcmd -S .\SQLEXPRESS -d SolutionErp -U vrapp -P '...' -Q '...'" # prod
Queries: sys.columns, sys.triggers, __EFMigrationsHistory, COUNT(*), sys.indexes.
Gotcha 2 LocalDB distinct (feedback_designtime_runtime_db): _Dev=runtime (appsettings.Development), _Design=dotnet ef default. Prod password fallback C:\inetpub\solution-erp\api\appsettings.Production.json khi $env:PROD_DB_PASSWORD empty.
Controller / wire-claim audit
- Grep
\[Route\("api/[a-z]+"\)\]enumerate controllers ·\[Authorize(Policy = "..."per-action policy (gotcha #44 silent 403 class-level quá strict) - Grep
// Mock/alert(/setEditing(null) // close UI— wire claim bugs ·IActionResultvsActionResult<T>
Smoke verify catalog
Bearer từ POST api.solutions.com.vn/api/auth/login → status matrix expected vs actual + file:line evidence.
Memory cross-reference
27 user-memory tại C:\Users\pqhuy\.claude\projects\D--...\memory\MEMORY.md (index). Key: per_chunk_commit · uat_skip_verify · audit_reuse_before_clone · designtime_runtime_db · per_nv_permission_scope · ef_migration_backfill_reorder · status_handoff_tiering (S40) · 7agent_split_upgrade (S39).
External research → DEFER investigator-api (split S39)
⚠️ Anti-patterns
❌ Skip MEMORY update · ❌ Vague "seems like/probably" · ❌ Missing file:line · ❌ >500 words · ❌ Scope drift to architecture recommendation (em main decides)
🧠 SOLUTION_ERP context essentials (S40 verified — re-grounded)
- DB: Dev
SolutionErp_Dev· DesignSolutionErp_Design(distinct) · Prod.\SQLEXPRESS/SolutionErp/vrappvia SSHvietreport-vps - Migration path:
src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/*.cs(⚠️ NOT root/Migrations/). 40 mig, last20260528090839_AddAttendances. - Counts S40: 40 mig · 84 SQL tables (77 DbSet + 7 Identity, count
.ToTable()ModelSnapshot NOT DbSet) · ~211 endpoints · 65 FE pages (36 admin + 29 user*Page.tsx) · ~53 menu keys (BEMenuKeysconst) · 130 test (58 Domain + 72 Infra) · 55 gotchas (format### N.highest #55) · 27 user-memory · 6 skills · 7 sub-agents - Tech: .NET 10 Clean Arch (Api→Application←Domain + Infra) + CQRS MediatR + EF Core 10 + 2 React 19 Vite 8 TS 6 (fe-admin :8082 + fe-user :8080) + SQL Server + Gitea CI + IIS
- Prod: api/admin/eoffice.solutions.com.vn · Gitea
git.baocaogiaoduc.vn/vietreport-admin/solution-erp(Actions API/api/v1/repos/.../actions/tasksNOT/runs404, cache stale ~2min gotcha #46 — cross-check VPS mtime) - Auth:
admin@solutions.com.vn/Admin@123456(full) /nv.test@solutions.com.vn/TestUser@123456(Drafter). ResponseaccessToken+refreshToken+user. Password ≥12 chars.
🔄 Active workflow schemas (V1 + V2 coexist post-S17)
- V1 Mig 21 flat —
WorkflowDefinitionpin PE/Contract cũ. Match Dept+PositionLevel. - V2 Mig 22-31 —
ApprovalWorkflowpin PE/Contract mới, matchApproverUserId1-1 OR-of-N cùng Cấp. Steps (Phòng) > Levels (Cấp). PE + Contract đã wire V2 (Mig 32+33 S29). Proposal V2 (Mig 38 S37 inline ApproveV2Async). WorkflowApps skeleton (Mig 39 S38 — ApproveV2 advance DEFER Phase 11).- Mig 25 IsUserSelectable · Mig 26 PE LevelOpinions UPSERT · Mig 29 Allow* per-NV (F1/F3 per Approver slot + F2
Users.AllowDrafterSkipToFinal) · Mig 30 F4 AllowApproverEditBudget · Mig 31 SkipToFinal→ApproverLevel
- Mig 25 IsUserSelectable · Mig 26 PE LevelOpinions UPSERT · Mig 29 Allow* per-NV (F1/F3 per Approver slot + F2
- State machine PE 5 trạng thái: Nháp / Đã gửi duyệt / Trả lại (TraLai=98) / Từ chối / Đã duyệt
- Mode Trả lại 4 option per-Level: OneLevel (lùi 1 Cấp) / OneStep (lùi Bước trước) / Assignee (pick NV đã ký) / Drafter (Phase=TraLai). 3 mode đầu giữ ChoDuyet lùi pointer. Admin bypass
level.Allow*.
📅 Recent activity (FIFO — older → archive/git)
-
2026-06-17 (S69 recon — Office-module inventory + Hồ sơ-NS CSS-contract, on-disk): ⭐ PART A Office: 21
Off_*keys (MenuKeys.cs:99-121): rootOff+ DanhBa(card-grid),Off_PhongHop{View=cal/Manage=room-CRUD-admin/Book},Off_DeXuat{List/Create/Inbox=Proposal-V2},Off_DonTu{Leave/Ot/Travel},Off_DatXe,Off_ItTicket,Off_ChamCong(re-parent→Personal S57),Off_AttendanceReport(admin). 10 office pages{fe-admin,fe-user}/src/pages/office/ALL SHA256-MIRROR except MyAttendancePage DIFFERS + AttendanceReportPage ADMIN-ONLY. RoutesApp.tsxuser:70-80/admin:88-100; staticMapLayout.tsx:87-103(workflow-apps :kind/workflow-apps/{leave,ot,travel,vehicle}); menuKeys.ts:45-63. HIDE-FLAGRevokeTemporarilyHiddenModulesAsync(DbInitializer.cs:2157-2190called :2040 LAST) wipes CRUD onMenuKey.StartsWith("Off")||"Hrm"||==Personalnon-Admin, idempotent. Golive flip: remove :2040 call (+ re-add prefix InReviewScope grant). Office already S55-shell polished NOT bare. PART B Hồ sơ-NS CSS: layout=3-col flex (EmployeesListPage.tsxSHA256-identical x2, 1597 LOC): cây-tổ-chức TRÁI(:178) + NV-list MID(:244) + detail PHẢI = avatar-headerapp-gradient-brand(:643)+text-white!(:653)+initials chip bg-white/15 → 5-TAB(:507 Tổng quan/Thân nhân/Trình độ/Kinh nghiệm/Hợp đồng) →Card(:1526 left-rail+icon-chip) w/Field(:1572 label uppercase accent-tint + valuefont-medium text-brand-800, empty=text-slate-300 —).ACCENTmap :497-503 Record<5,{chipBg/chipFg/head/rail/labelText}> accent∈{brand,teal,violet,amberx,greenx}, palettes stops 50/100/500/600/700 only no-800→headings -700 (brand -800 OK). Tokensindex.css: brand-600=#1f7dc1 brand-800=#175685 @theme:5-55, font Be-Vietnam-Pro:53; classes.app-gradient-brand(:105 120deg b600→700→800),.card-accent(:112),.icon-chip(:128 --chip-bg/--chip-fg),.stat-value(:140),.label-eyebrow(:89). ⚠️ GOTCHA #66 =index.css:79-83h1,h2,h3,h4{color:#0b1220;font-weight:700}OUTSIDE @layer → TW-v4 unlayered wins → heading-tag inside gradient MUSTtext-white!. ⚠️ CROSS-APP DRIFT: fe-user=S68 (h1-4 #0b1220/700, label-eyebrow brand-600, 175L); fe-admin STILL OLD (h1-4 #0f172a/600, label-eyebrow #64748b slate, 167L) — fe-admin NOT synced S66-68 heading bump → mirror Office to fe-admin needs index.css sync. Tag[s69, office-inventory, hoso-css-contract, gotcha66, fe-admin-css-drift]. -
[→ git pre-S60] S60 recon#2 V2-engine-map (ApprovalWorkflow.cs Step/Level Order 1-based per-step; OR-of-N=N rows cùng Order service GroupBy:475; ApproveV2Async:446-634 guard+UPSERT+advance; notify DRAFTER-only:748; skipToFinal F2:561-602 = precedent advance-không-ghi-opinion) · S60 PE Section-3 submit-guard (submit path POST/pe/{id}/transitions→TransitionAsync:38 ROLE-only guard NO data-check; Section-3 mục a/b/c/d map — SUPERSEDED bởi S65ter post-Mig50 Budget-drop; test mirror PurchaseEvaluationWorkflowServiceGuardTests). Full text git.
-
2026-06-11 (S59 recon — prod test-data wipe + PE tree Hạng mục, prod+on-disk): ⭐ Prod: PE=10 active (1 Nháp + 1 DaDuyet(7) + 8 ChoDuyet(10), MaPhieu A/031-040, ALL WorkItemId NULL) + child 20/10/20/28/138/18/18 (Sup/Det/Quote/Appr/Chg/Att/LvlOp); Contracts=7 ALL
[DEMO]05-08 pin V1 (AwId NULL) + Appr15 + details15; Budgets/WorkflowApps/Proposals/Attendances/Meetings ALL 0; Notifications 64. Seq: PE/2026/A=40 B=1; CT=7 demo prefix LastSeq=1. FK: PE child CASCADE trừQuotes→PE NO_ACTION(multi-path; Plan R S23 proved singleDELETE FROM PurchaseEvaluationsOK — NO_ACTION check end-of-statement sau cascade Details→Quotes). Contract child ALL CASCADE. PE.ApprovalWorkflowId Restrict → wipe PE trước khi xóa AW QT-DN-V2-001 v1 (inactive, còn 1 PE pin). AW V2=8: 7 ghim KEEP. Uploads orphan: purchase-evaluations/ 19 folder vs 10 PE → ~10 orphan từ S23 (file không xóa); contracts/ 1. Demo gate OK: SeedDemoContracts/PE TRONGDemoSeed:Disabled(DbInitializer:80,131-132) → wipe không resurrect. Surprise: Users 55 total / 21 active — 20 user THẬT batch 2026-06-11 06:01 (S58 seed fix ăn; thanh.lethanh NOW EXISTS — stale S57bis mem; chuong.phan typo-domain VẪN active song song twin). FE tree:pe/PurchaseEvaluationsListPage.tsx:138-179Project>Year(createdAt :150)>Supplier; SHA256 identical 2 app; PeListItem ĐÃ có workItemId/Name (types :116-118, BE Features :514/570/644) → đổi tree FE-only. Tag[s59-recon, prod-wipe, pe-tree-workitem]. -
2026-06-11 (S57bis lock no-op — prod user census, on-disk+prod): ⭐
LockDemoSampleUsersAsync(DbInitializer.cs:1552, chạy CUỐI :98) hardcode 14 named-person email (bod.huynh/pm.nguyen/fin.do/qs.hoang…) = population CHỈ CÓ TRÊN DEV. Prod 34 user ALL-active: 20 UAT-matrix placeholder hand-created batch 2026-05-13 15:04-05, scheme{act,equ,fin,hra,pm,qs}.{nv,pp,tp}@+bod.{1,2}@(FullName tự khai "ACT NV - Drafter+Accounting", "[Bypass]"/"[SkipFinal]" = test Mig 29-31 flags) + 9 real staff hand-created 05-04→05-12 +binh.lethanh@(người thật Lê Thanh Bình — seed dùngthanh.lethanh@KHÔNG tồn tại prod) +chuong.phan@solution.com.vnTYPO-domain dup (twin đúng tạo 05-12) + admin/catalog.manager/nv.test. ROOT CAUSE seed-user never-on-prod: prodIdentity:Password:RequiredLength=12(appsettings.Production.json) vsDemoUserPassword="User@123456"=11 chars → CreateAsync silent-fail MỌI startup từ prod-init 04-21 (code comment :1675-79 đã biết); Dev fallback 8 (DependencyInjection.cs:67?? 8, Development.json no Identity section) → Dev đủ 33 user named-person.bod.1@NEVER in git pickaxe = tạo tay qua admin UI, không phải seed. Surprise: _Dev hiện CŨNG chưa khóa (Locked=0; LockoutEnd=MaxValue sẽ persist qua reconcile re-activate :1714 nếu từng chạy) → lock chưa từng execute against _Dev runtime. Fix cần 20 email prod-thật; GIỮ binh.lethanh + 9 real + admin/catalog.manager;nv.test@= creds smoke-verify (khóa = vỡ cicd smoke). Tag[s58, s57bis-lock-noop-recon, prod-user-census, pwd-policy-env-divergence]. -
2026-06-11 (S57bis PE recon — 4 đầu việc sếp, on-disk): ⭐ PE entity NO Year, NO WorkItem link (
PurchaseEvaluation.cs:15ProjectId req; Detail free-textPurchaseEvaluationDetail.cs:10-13). Create cmdPurchaseEvaluationFeatures.cs:19-30; MaPhieu gen-AT-CREATE:114-116formatPE/{YYYY}/{A|B}/{Seq:D3}(PurchaseEvaluationCodeGenerator.cs:23). Main create UI =PeWorkspaceCreateView.tsx(:151 workflow-select isUserSelectable ĐẦU TIÊN → tenGoiThau → projectId → DiaDiem → MoTa → PaymentTerms → budget; canSubmit :129 = wf+project+ten). PE controller class-[Authorize]ONLY no policy → mở menu là đủ, no silent-403. Pe_* leaves NOT inMenuKeys.All(chỉ root :156); PE defaults 7 role × 11 key (root + 2type×{group,WfView,List,Create,Pending})DbInitializer.cs:2098-2160. S57SeedAllRolesReviewReadPermissionsAsync:1993-2001InReviewScope EXCLUDES Pe; extend đúng =key == MenuKeys.PurchaseEvaluationsEXACT (prefix "Pe" sẽ dính PeWorkflows admin!) — root inherit cascade (GetMyMenuTreeQuery.cs:49-82). Demo gate: prodappsettings.json:35 DemoSeed:Disabled=true→ 7[DEMO]HĐ + 4[DEMO]PE (MaPhieu[DEMO]-A-001) KHÔNG lên prod; UNGATED trên prod = 31 users + 18 demo NCC + 8 demo project (:2244-2315) + real 62/71/3 (:2329-2522). ⚠️ Clear-demo gotcha: seed re-add per-code idempotent MỖI startup → xóa DB-only sẽ resurrect, phải gỡ khỏi DbInitializer code. WorkItem write Admin-only (CatalogsController:113-130) — CatalogManager có menu-perm nhưng API write bị chặn. Tag[s57bis, pe-recon, demo-inventory]. -
2026-06-10 (S57 perm-broaden blocks A/B/E/F — on-disk): ⭐ BE AUTHZ SPLIT (decision-critical for E): Config controllers gate WRITE behind
[Authorize(Roles="Admin")], READ open to any-authed:HrmConfigsController.cs:15class[Authorize]+GET open:19-21, all POST/PUT/DELRoles="Admin";CatalogsController.cs:14same (write:23+Admin);MeetingRoomsController.cs:15same (comment:9-10explicit). → granting FE read on Hrm_Config/Catalogs/MeetingRooms = pure UI-visibility, BE already correct. BUT Master 3 controllers = class[Authorize]ONLY, no per-action role:SuppliersController.cs:17,ProjectsController.cs:11,DepartmentsController.cs:11— ANY authed user can POST/PUT/DELETE via API (FE menu perm is only gate, not BE-enforced). Flag em-main: making Suppliers/Projects/Departments visible-to-all ⇒ staff can write master incl. S55 prod data unless add[Authorize(Roles="Admin,CatalogManager")]or per-action policy. S55 PROD DATA location:SeedRealMasterDataAsync:2267-2460writes to Projects(62,:2270), WorkItems(71=CatalogWorkItems key,:2430-2438), Suppliers(3,:2440-2456) — all ungated idempotent per-code. 10 departments now (SeedDepartmentsAsync:2104): 9 orig + IT (:2115). 31 demo users (SeedDemoUsersAsync:1553-1609): dept spread BOD4/PM2/CCM9/PRO6/QS2/FIN2/ACT1/EQU1/HRA2; roles = mostly Drafter/CostControl/Procurement + 1 CatalogManager (catalog.manager@, dept PRO,:1608). NO user has zero-role; every demo user authenticatable. Inherit-root display:GetMyMenuTreeQuery.cs:96 HasAccess = CanRead OR Children.Any(HasAccess)→ a non-inherit root (Hrm/Off/Master) auto-shows if ANY child has CanRead, so granting leaves is enough to reveal the root node (root row itself optional for display, but grant it too for cleanliness). Inherit-roots (Contracts/Pe/Wf/PeWf) cascade root→child so root-row-only suffices there. Menu already S57-edited:Personalgroup +Off_ChamCongre-parent Off→Personal viaparentBackfill:1908. Tag[s57-perm, be-authz-split, master-write-open, s55-data, 31user]. -
2026-06-10 (S57 perm-broaden RECON blocks C/D — RAG down, on-disk): ⭐ SEED MODEL:
SeedAdminPermissionsAsyncDbInitializer.cs:1939-1977Admin loopsMenuKeys.AllCRUD=true skip-existing dedup (:1950/:1952). Calls 2 sub: (a)SeedPurchaseEvaluationPermissionDefaultsAsync:2036-2098→ 7 roles {Drafter,DeptManager,Procurement,CostControl,ProjectManager,Director,AuthorizedSigner} Read+Update on PE keys only; (b)SeedCatalogManagerPermissionsAsync:1984-2029→ role CatalogManager full-CRUD 9 master keys. NO generic per-employee Read seed — plain Drafter user sees ONLY PE keys; DOESN'T see Off_/Hrm_/Master/Contracts. Most non-admin staff today see ~nothing but PE. GetMyMenuTree:96filters CanRead=true. Permission entityPermission.cs:3-15: RoleId/MenuKey/CanRead/Create/Update/Delete. Dedup=app-level skip-existing per(RoleId,MenuKey), NO DB upsert. 13 AppRolesAppRoles.cs:23: Admin(system)+12 employee. 4 inherit-rootsGetMyMenuTreeQuery.cs:56-84: Contracts/Workflows/PurchaseEvaluations/PeWorkflows — root grant auto-cascades to child IF child no own row (:66). Hrm/Off/Master NOT inherit → each leaf needs own row or add to switch. GRANT-ALL pattern (block D): mirror CatalogManager seeder but looproleManager.Roles(all 13) × chosen key-set, CanRead=true only, insertion AFTER SeedCatalogManagerPermissionsAsync:1976. Tag[s57-perm-recon, seed-model, no-employee-default, inherit-4root]. -
2026-06-10 (menu-order cross-repo recon SE↔NAMGROUP, RAG down, on-disk): ⭐ SE menu seed =
SeedMenusAsyncDbInitializer.cstuple-list (NOT partial). Văn phòng số rootOff(Order=29):1769-1792; HR rootHrm"Nhân sự" (Order=28):1754-1767(+Hrm_Dashboardappended out-of-order:1791). ⚠️ HR SCATTERED 2 roots:Hrmholds only Hồ sơ+Cấu hình HRM(6 leaf)+Dashboard; transactional HR (Nghỉ phép/OT/Công tác/Đặt xe/Chấm công/Báo cáo CC) live underOffasOff_DonTu_*/Off_DatXe/Off_ChamCong/Off_AttendanceReport. SEED = UPSERT that RE-SETS Order (:1845-1871if(existing.Order!=o){existing.Order=o}) → reorder in code propagates to Dev/prod next deploy, NO migration. BUT Label/ParentKey/Icon NOT touched on existing rows (:1855comment) — rename needs separatelabelBackfilldict:1874. Order = BE-only:GetMyMenuTreeQuery.cs:35 OrderBy(m.Order); both FELayout.tsxrenderuseAuth().menuas-is,staticMap+menuKeys.ts= key→route ONLY (no sort). FE needs NO edit for pure reorder. NAMGROUP "Puro" = hardcoded FE array (NOT DB seed), index=order: clientInternalLayout.tsx:83-118(Nhân sự 3-item / Văn phòng số 6-item FLAT / Chấm công under separate "Cá nhân" group); adminAdminLayout.tsx:87-119splits by function not HR/office. NAMGROUPĐơn từ/Phòng họp/Đề xuất= flat single links (no sub-children) vs SE deep-nested. Tag[menu-order, se-namgroup, seed-upsert-order, fe-be-driven, s57]. -
2026-06-09 (S56 pre-golive verify — 4 logic streams, all PASS): Audited P11-B/D/E/F + ApproveV2 + catalogs + S55 master-wiring. LeaveBalance deduction exactly-once (terminal DaDuyet, guard
Status!=DaGuiDuyet:296 blocks re-approve), FK guard Create+UpdateDraft→Conflict; AttendanceReport classify day-type IN-MEMORY (Holiday DateOnly HashSet), OtPolicy multiplier; MaTicket gen-on-Create Serializable IT/2026/NNN. Tests cover (LeaveBalance 9 + AttReport 2 + codegen 3 = 29 green). ApproveV2 4-module flatten Steps→Levels correct; Travel/Vehicle ApproveV2 = 0 test (cookie-cutter of tested Leave/OT — add 2 smoke post-golive). master-data idempotency PROVEN (DbInitializer re-run → counts identical, per-code guard :2310/:2404/:2422). ⚠️ PROD FACT (corrects stale S52 mem): dept "IT"/Phòng CNTT DOES exist (Id 65CC6307…) but has 0 active users on prod → ItTicket auto-assign no-ops, reassign dropdown empty, SLA job no notify-target. Pre-golive ops fix: assign ≥1 real user to dept IT (1 UPDATE, no code). Tag [s56, pre-golive-verify, logic-pass, dept-IT-empty-prod, travel-vehicle-untested]. -
2026-06-09 (S56 Phase 2 FE-redesign RECON — 25 page audit, on-disk): ⭐ NOT a rewrite — S55 already redesigned ui-primitives + DataTable + shell → any page importing
ui/{Button,Input}+DataTableAUTO-inherits density. Hover-hidden quick-win nearly absent: repo-wide grepopacity-0×group-hover= only 1 real siteContractCreatePage.tsx:196(EXCLUDED scope) + DataTable.tsx:15 is a comment forbidding it (good). In-scope = 0 hover-hidden fixes. DataTable adoption split: only 5/25 use<DataTable>(Suppliers/Projects/Departments/Users/Forms); 12 pages roll RAW<table>(MeetingRooms/Catalogs/HrmConfigs/EmployeesList/Proposals/WorkflowApps/Attendance×2/MenuVisibility/Roles) → custom density pass needed. Drawer ≥8-field candidates = 3: Suppliers (9 fld, Dialog), Projects (10 fld, Dialog), Users-CREATE (~8 fld w/ roles multiselect, 4 Dialogs total but only create is big). All currently big-Dialog → convert. NODrawer.tsxexists (ui/= Button/Dialog/Input/Label/Select/Textarea only) → build first. Bậc-thang reference ALREADY EXISTS:EmployeesListPage.tsx(1200L) = canonical inline add/edit-row for 5 satellites (setEditing{X}Id+addingXmutex, :256-356) → extractInlineEditRowpattern from here, reuse for Catalogs/HrmConfigs/MeetingRooms (these 3 currently edit via Dialog :251/:316/:232, ≤7 cols → bậc-thang candidates). Modal-detail = NONE: ProposalDetail:275 + WorkflowAppDetail:424 Dialogs are tiny action-confirm (just<Textarea>ý kiến), detail body already inline 2-col grid (:181) → NOT convert. fe-user mirrors office/master/hrm (SHA256-identical per comments) but NO system/forms/reports mirror. Custom-layout heavy: InternalDirectory (card-grid :124 not table), MeetingCalendar (693L FullCalendar), EmployeesList (2-panel), HrmConfigs (declarative KIND_CONFIG :45). Effort: Master 3×Drawer=M, Catalogs/HrmConfigs/MeetingRooms bậc-thang=M, Users Drawer=M, rest S (auto-inherit polish). Surprise: hover-hidden NAMGROUP-win essentially pre-solved (team already avoids opacity-0 pattern, DataTable comment enforces) → quick-win section nearly empty. Tag[fe-redesign-p2, recon, drawer-3, basc-thang-ref-exists, s56]. -
2026-06-09 (S55 master-data Excel-import recon — 3 master + seed mechanism, on-disk): ⭐ "Hạng mục"/WorkItem master TỒN TẠI —
Domain/Master/Catalogs/WorkItem.cs:6-14(Code(50)UNIQUE-filtered/Name(200)/Category(100,idx)/DefaultUnit(50)/Description/IsActive), configCatalogsConfiguration.cs:60-74, full CRUDCatalogsFeatures.cs:260-324→ group(VẬT TƯ/THẦU PHỤ/MEP)→Category, "1 Mat"→Code, item→Name. KHÔNG cần table/migration mới. PE detail = pure free-text (PurchaseEvaluationDetail.csGroupCode/GroupName/ItemCode/NoiDung strings, NO FK→WorkItem) → load WorkItems non-breaking. Project (Project.cs:5-14, cfg:14-21): Code(50,UNIQUE[IsDeleted]=0Mig47)+Name(200) REQUIRED, StartDate/EndDate/BudgetTotal(18,2)/Note(1000)/ManagerUserId optional. ❌ THIẾU Year/Investor/Location/Package — chỉ Note free-text catch-all. Create cmdProjectFeatures.cs:67dup-check:87 AnyAsync(Code==). Supplier (Supplier.cs:5-16, cfg:14-27): Code/Name req + Type enum + TaxCode(20)/Phone/Email/Address/ContactPerson/Note.SupplierType.cs: NhaCungCap=1/NhaThauPhu=2/ToDoi=3/DonViDichVu=4/ChuDauTu=5. ❌ THIẾU Status/TinhTrang (KHÔNG có field/enum nào) + bank-acct + legal-rep (≠ContactPerson) + quality-score; "Cả hai" PHÂN LOẠI unmappable (Type single-valued). CreateCreateSupplierCommand.cs:10dup:45. Seed = idempotentexistingCodes.Contains→skip(DbInitializer.SeedDemoMasterDataAsync:2149, today 18 supplier:2155+ 8 project:2222; WorkItems 15 rows tuple-loopSeedCatalogsAsync:576-599). NO bulk import — Master chỉ single CRUD; Import/Upload hits = Forms/PE/Employees attachment only; POST one-at-a-time. Seed→prod:DbInitializer.InitializeAsyncchạy MỌI startup (Program.cs:197unless--no-db-init) →MigrateAsyncTHEN seed; demo gatedconfig.GetValue<bool>("DemoSeed:Disabled")(:80) NHƯNG SeedDemoMasterData+SeedCatalogs chạy BẤT KỂ flag (ngoài if-block :108/:115) → seed method mới auto-reach prod next deploy. Rec: idempotent DbInitializer mirror (NOT API loop). Surprise: real+demo data sẽ trộn chung Suppliers/Projects/WorkItems (18/8/15 demo rows) → cân nhắc gate demo off prod. Tag[master-import, workitem-exists, seed-idempotent, s55]. -
2026-06-16 (S65bis recon — Employee profile master-detail vs NamGroup, on-disk): ⭐ STALE-PREMISE CORRECTION: fe-user
/employeesKHÔNG list-only —hrm/EmployeesListPage.tsx(1201 LOC) ĐÃ master-detail 2-panel (filter sidebar :117 + list table :197 + inline detail :234) với 6 collapsible section (<details>:1157, KHÔNG tab) + 5 satellite inline CRUD (WorkHistory/Education/FamilyRelation/Skill/Document,setEditing{X}Id+adding{X}mutex pattern 12-ter S35). fe-admin == fe-userdiff -qIDENTICAL (SHA256 same). Entity gần đủ screenshot:Domain/Hrm/EmployeeProfile.cs(137 LOC) CÓ: DOB/Gender/Ethnicity/Religion/Nationality/Height/Weight(:98-99)/IdCard(số+ngàycấp+nơicấp :52-54)/permanent+temporary addr/phone/personalEmail/code/hireDate/qualification/salary(Base+Total)/bank/4×leave-days. THIẾU vs screenshot: (a) BloodType CÓ nhưng "sức khỏe loại" (health-grade A/B/C) KHÔNG; (b) thâm niên = DERIVED từ HireDate (no column); (c) chức danh =User.Position/PositionLevel(Identity, KHÔNG ở EmployeeProfile) — list/detail JOIN Users (EmployeeFeatures.cs:467); (d) "lương BHXH/phụ cấp" tách riêng KHÔNG có (chỉ Base+Total); đơn vị=DepartmentName JOIN. 5 satellite entity + 15 endpoint FULL (EmployeesController.cs:75-2335 region×Create/Update/Delete; GET detail Include cả 5 :455-459). Skill polymorphic gộp 3 NamGroup table (Computer/Language/Other Kind :69). GAP THẬT: (1) NO org-tree —Department.csFLAT (Code/Name/ManagerUserId/Note, KHÔNG ParentId),DepartmentsControllerchỉ GET list+byId (NO /tree), KHÔNG endpoint count-per-dept → cây trái + badge phải build CLIENT-side group-by departmentId từ list; (2) 5-tab layout screenshot = 6-section<details>hiện tại (re-skin UI, data đủ); (3) "Hợp đồng lao động" tab = chỉ cóEmployeeDocumenttype=LaborContract(5), KHÔNG entity HĐLĐ riêng (3 HĐLĐ table DEFER Plan H2 perEmployeeProfile.cs:10). NamGroup source:D:\...\NAMGROUP\SOURCECODE_CÔNG_TY\find .tsx/.razor = 0 hit (KHÔNG phải React/archived) — RAGproj_namgroup_main0 component; tham khảo layout = screenshot anh gửi, KHÔNG có code mirror trực tiếp. ⇒ Wire-lại-là-xong: data + API + satellite CRUD 100% sẵn. Build mới: Department.ParentId migration + /tree + count endpoint (nếu muốn org-tree thật thay client-group). Re-skin: 6-section→5-tab + avatar header. No new field bắt buộc trừ health-grade nếu anh cần. Tag[s65bis, employee-profile, master-detail-EXISTS, dept-flat-no-tree, stale-list-only-corrected]. -
2026-06-17 (S69 recon — NamGroup "PURO" digital-office layout, CROSS-REPO
D:\...\NAMGROUP\): ⭐ PURO = UI design-language/skin (ref ERP demo.purocorp.vn), KHÔNG phải app riêng — NamGroup mirror sidebar/typography của nó (commentsInternalLayout.tsx:33,74,109,200,332"PURO exact spec"). Digital-office sống trongnamgroup.client/(app NV; admin = config-only). Shell =components/layout/InternalLayout.tsx(724L): sidebar trái fixed h-screen +<main flex-1 overflow-auto p-2.5..lg:p-4>:609 chứa<Outlet/>. Sidebar =navTreehardcoded array :76-122 (KHÔNG DB), flat 2-tier group→leaf, 4 group: "Văn phòng số" :90-100 = 6 leaf {Danh bạ/danhba· Phòng họp/phonghop· Đề xuất/dexuat· Đơn từ/dontu· Đặt xe công/xecong· Ticket CNTT/ticket}; + Nhân sự(3) + Cá nhân{Chấm công}(1) + Hệ thống(5). Routing =App.tsx:81-140flat<Route element={InternalLayout}>><RouteGuard>(perm) > index=HomePage. Landing/=internal/HomePage.tsx(296L): grid 2-col (LEFT 2/3 stack 4 WidgetCard: Đề xuất/Nghỉ phép/Bình luận/Truyền-thông · RIGHT 1/3 Công-việc-của-tôi); WidgetCard = gradient-blue header + inline stat-chips + body/EmptyState (shared comp tại :219). Layout pattern mỗi feature (KHÔNG dùng tab — dùng KpiCard-row + view-toggle): (a)<PageHeader>shared (icon-badge accent + breadcrumb + actions slot,components/shared/PageHeader.tsx); (b) KPI stat-cards clickable filter (DeXuat :1643 6-card / Ticket :197 5-card — "PURO KPI cards" comment); (c) body = list-table (DeXuat/Ticket:<table>master + right detail panel grid-cols-3) HOẶC list↔calendar ViewToggle (DonTu :683 + XeCong + PhongHop: custom month-grid Sun-Sat, NO FullCalendar — comment :PhongHop "saves install friction"); DanhBa = dept-tree trái + card-grid phải. Shared comp tái dùng: PageHeader · DataTable · KpiCard · CrudToolbar · ActionLogList · DatePickerVN · MasterDataPage · DonutChart/MiniBarChart (components/shared/16 file). Top files mirror: InternalLayout.tsx(shell+nav) · HomePage.tsx(dashboard) · PageHeader.tsx · DeXuatPage.tsx(1676 KPI+table+detail) · DonTuPage.tsx(1269 +DonTuCalendar.tsx toggle) · XeCongPage/PhongHopPage(month-grid) · TicketPage.tsx(595) · DanhBaPage.tsx(756 tree+grid) · ChamCongPage.tsx(809). ⚠️ Style/màu lấy chỗ khác per task — chỉ structure. SE đã có analog (fe-userInternalLayout/office pages) nhưng layout khác (deep-nested vs PURO flat). Tag[s69, namgroup-puro-recon, digital-office-layout, hardcoded-navtree, kpicard-not-tab, cross-repo]. -
2026-06-16 (S66 recon — mirror Hồ sơ NS fe-user→fe-admin, on-disk): ⭐ VERDICT (B): vá
fe-admin/src/index.cssTRƯỚC rồi cookie-cutter SẠCH. Copy page thuần = VỠ MÀU. fe-admin index.css = 86 dòng (chốt7feb53e, TRƯỚC redesign S58e959f72/c98030f) → THIẾU: 4 accent paletteteal/amberx/violet/greenx(mỗi cái 50/100/500/600/700) + 3 utility.icon-chip/.app-gradient-brand/.card-accent/.stat-value. CÓ SẴN:--color-brand-50..900(hex y hệt fe-user, incl brand-800 #175685 :15),.label-eyebrow:54, font Be Vietnam Pro :22. ⚠️ heading-weight CẦN CHECK (fe-user S66h1-h4 font-weight:700 color:#0b1220; fe-admin có thể còn 600). Page fe-user phụ thuộc THẬT: text-brand-800 ×9, teal/amberx/violet/greenx-50/500/700 ×4 mỗi, icon-chip ×3, app-gradient-brand ×1. Wiring fe-admin ĐỦ SẴN (0 đụng): route/employees+/newApp.tsx:82-83·EmployeeCreatePage.tsxidentical (diff rỗng) · menuHrm_HoSomenuKeys.ts:33+staticMapLayout.tsx:53. Lib parity ✅: ui/{Input,Select,Textarea,Button} prop-sig identical (HTMLAttributes passthrough, content khác chỉ className=chủ ý non-breaking) · EmptyState/cn/api/apiError ✅ ·types/employee.tsidentical · Pagedtypes/master✅ ·DepartmentTreeNodeđịnh nghĩa INLINE trong page (:65 mirror BE DepartmentTreeNodeDto, đi theo copy — không cần type file). KHÔNG khác chủ ý admin/user — mirror y hệt như S35 (9616ae2/c3cd343cùng commit 2 app); write Admin-gated ở BE controller. Cấu trúc: admin=1200 dòng (S35 cũ, 2-panel + 5<details>+ 5 satellite mutex); user=1602 dòng (redesign: cây "SOLUTION COMPANY" đệ quy + list cột trái dọc · detail phải 5 tab accent + avatar gradient). Scope: 3 file = index.css (chèn ~40 dòng token+class block từ fe-user :29-51+:100-160) + overwrite EmployeesListPage.tsx (import path KHÔNG chỉnh, đều@/) + (tùy chọn heading-700). KHÔNG đụng route/menu/types/primitives/CreatePage. Tag[s66, mirror-employee-page, accent-token-missing-fe-admin, verdict-B, cookie-cutter-after-css]. -
2026-06-16 (S65ter recon — Mục E "Link hồ sơ" phiếu PE, on-disk): ⭐ Anh Kiệt: chèn mục E "Link hồ sơ" NGAY DƯỚI mục D "Bản so sánh". Render 4 file (SHA256-identical 2 app):
components/pe/PeDetailTabs.tsx(detail+edit, 2770 LOC) +PeWorkspaceCreateView.tsx(create) × {fe-user,fe-admin}. KHÔNG tabs — 5<Section>dọc, tiêu đề "1./2./3./4." + sub-item chữ thường "a./b./c./d." (label cột trái w-44). Mục D ∈ Section "3. Đơn vị NCC/TP" =ChonNccSection(PeDetailTabs.tsx:1302-1375): a.NCC(:1321) · b.Tổng hợp NS trình ký(:1324PeBudgetSummaryTable— S61 thay Budget) · c.Giá chào thầu(:1326 auto) · d.Bản so sánh(:1337-1348) =GeneralAttachmentsSection(:2613) upload N FILE filtersupplierId===nullpurpose=ComparisonTable(4). INSERT E:PeDetailTabs.tsx:1348(sau</div>mục D, trước paymentTerms :1350); mẫu = block :1337-1348. Create:PeWorkspaceCreateView.tsx:277(sau FormRow d). BE:PurchaseEvaluation.cs(:1-72) KHÔNG có field URL — DiaDiem/MoTa/PaymentTerms semantic khác. 1 link →string? HoSoLink(1000)+Mig AddColumn+cmd+DTO+validator; nhiều link → entity conPurchaseEvaluationLink+CREATE TABLE+CRUD (nặng). Attachment KHÔNG reuse URL —PurchaseEvaluationAttachmentFeatures.cs:18-55IFormFile thuần (FileSize>0+ContentType whitelist+IFileStorage). Mục D multi-row → E nên multi-row đối xứng. ⚠️ Surprise: comment :1314 nói "purpose=ComparisonTable hoặc supplier-row null" SAI — filter thực :1315-17 CHỈsupplierId===null. Tag[s65ter, pe-section-e-link, attachment-file-only, insert-1348]. -
2026-06-16 (S65 recon — public HRM module for all-role, on-disk): ⭐ Mục 6 CRITICAL (gotcha #44 family) RESOLVED-FAVORABLE:
EmployeesController.cs:23-25= class[Authorize(Policy="Hrm_HoSo.Read")](NOTRoles="Admin") + per-actionHrm_HoSo.{Create/Update/Delete}(:45/:54). Policy resolves THROUGH permission matrix (MenuPermissionHandler.cs:40-52baseQuery role×menuKey CanRead; Admin-bypass :27) → seed CanRead row = API ALSO unlocked, NO 403.HrDashboardController.cs:8-11=[Authorize]any-auth only (/api/hr/dashboard). GET list =/api/employees(:28). ⇒ seed BE permission ĐỦ, không cần đụng controller. Menu keys dướiHrm(prefix THẬT =Hrm_): rootHrm="Nhân sự" parent=null Order=28 (DbInitializer.cs:1805);Hrm_Dashboard="Dashboard NS" parent=Hrm Order=1 (:1850);Hrm_HoSo="Hồ sơ Nhân sự" parent=Hrm Order=2 (:1806). Hrm_Config* (6 leaf: LeaveTypes/Holidays/Shifts/OtPolicies/Vehicles/Drivers) parent=Master Order=25 (S57 re-parent :1812 — KHÔNG dưới Hrm). Revoke (Mục 2):RevokeTemporarilyHiddenModulesAsync:2151 — matchStartsWith("Hrm")||StartsWith("Off")||==PersonalAND role!=Admin AND any-flag-true (:2162-67) → set 4 cờ CRUD=false. ⚠️ THỨ TỰ: gọi CUỐI CÙNG:2040trong SeedAsync, SAU grant:2033→ revoke THẮNG mọi grant trước nó. Mở Hrm = phải (a) sửa revoke loại trừ Hrm_HoSo/Hrm_Dashboard HOẶC (b) thêm grant SAU :2040. Pe pattern (Mục 3):SeedAllRolesReviewReadPermissionsAsync:2055—roleManager.Roles.ToListAsync():2090loop ALL role × reviewKeys, upsert CanRead (+CanCreate cho Pe_), additive idempotent (skip-existing non-Pe :2115). Seed entity (Mục 4):Permission(RoleId,MenuKey,4 CRUD); idempotent = app-level skip per (RoleId,MenuKey); 13 roleAppRoles.All(Admin/Drafter/DeptManager/ProjectManager/Procurement/CostControl/Finance/Accounting/Equipment/Director/AuthorizedSigner/HrAdmin/CatalogManager).Hrm_HoSo+Hrm_DashboardĐỀU ∈MenuKeys.All:153,160(khác Pe_ leaf NOT in All). FE (Mục 5): menu-tree-API-driven viaGetMyMenuTreeQuery.cs(/api/menus/me); Hrm NOT inherit-root (chỉ 4: Contracts/Workflows/Pe/PeWf :51-59) → MỖI leaf cần CanRead row riêng, NHƯNG rootHrmauto-hiện nếu child có access (HasAccess:96CanRead OR child).Layout.tsx:145 USER_HIDDEN_KEYS={System,Users,Roles,Permissions,Forms,Reports} — KHÔNG chứa Hrm → fe-user auto-render;staticMap Hrm_HoSo→/employees :75,Hrm_Dashboard→/hr/dashboard :104. NO PermissionGuard per-route fe-user. ⇒ chỉ seed BE, FE tự hiện. Tag[s65-recon, public-hrm, policy-based-authz-not-roles, revoke-runs-last]. -
[→ archive/2026-06.md + git] S52 P11-D/E/F 6-gap recon (IT-pool absent → S56 corrected: dept IT exists 0 user · SlaExpiryJob HostedService DI:46 pattern · OtPolicy 3-multiplier · ClosedXML exporter reuse · Attendance API cá nhân-only · FE skeleton state — full text git pre-S60) · S50 P11-C HrmConfigs add-kind 11-chỗ pattern · S50 wave h2-verify B6 gitignore ordering + POSIX-not-pwsh (curated S57bis) · S51 gotcha #57 EXT reachability 3-Master-fix/3-skip global-filter-makes-bug (curated S59).
-
2026-06-07 (Harness 1/2/3 adap-apply recon — 3 slice, HMW wave): Governance recon AI_INFRA broadcast harness-1/2/3. H1/H2 (Harness 1): roster 8→10 — CREATE 2 sub TÁCH BIỆT
tooling-auditor(H1 freshness 4-mặt skill/sub-role/plugin/docs) +harvest-curator(H2 integrity 5-trục). H2 PARTIAL sẵn:session-end.mdPhase 1.5 §L.b(d) spawn-record 4-field + (f) double-check moved-not-cut + (c) 0-byte AS-8 = Coverage+Completeness+Corruption (3/5); THIẾU Fidelity-escalate + Placement. RE-REPORT @session-start = 0 (chỉ generic Phase 2.7). 2 sub mirror inv-codebase read-set + store_memory strip + NO Write/Edit; color brown+teal (8 màu cũ hết). H2 wave (Harness 2): SEhmw.js= OLD pre-wave (no subMdPath/writeGuard/wave-block); AI_INFRAhmw.js= canonical template. ⭐git check-ignore -v= ground-truth B6:.claude/workflows/wave-test/wave.mdHIỆN match.gitignore:83 !.claude/**= TRACKED → wave pattern PHẢI đặt AFTER!.claude/**(last-match-wins, mẫuhmw-mode.on:87). Read-only sub (4)=inv-cb/inv-api/reviewer/cicd; Write sub (4)=impl×2/test/fe-designer. B5 depends H2 harvest-curator. H3 email (Harness 3): broadcasts/ absent; id authoritative =se(NOT solution_erp), 6 others short{ai_infra,vipix,dyd,namgroup,ashico,bvaau}từAI_INFRA/broadcasts/sister-commands/send-email.md:13-22(folder name = 2nd source-truth);adap-apply.md:14base-path STALE flat →outbox/all/*.md(latent bug). broadcasts/ ở root → commit OK (no gitignore rule). Containment post-P2: git-diff bắt 1 file-write (inv-api self-MEMORY), chunk-count 2414=2414 (0 RAG-write) = defense-in-depth proven. Tag [harness-recon, governance, hmw-wave, 2026-06-07]. -
2026-06-01 (P11-C Vehicle+Driver catalog pre-flight): Mig 44 next (latest=Mig 43
FilterHolidayUniqueIndexByIsDeletedS45). 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.csmega 4-region +HrmConfigsController([Authorize]read /[Authorize(Roles="Admin")]write) — add Region 5 Vehicle + 6 Driver (kindvehicles/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.tsxdeclarative KIND_CONFIG Record — add 2 entry vào KIND_CONFIG + KINDS[] +renderCellsbranch + smart-defaults; NO new page. Menu+perm: add 6 constMenuKeys.cs(+Hrm_Config_Vehicles/Drivers), thêm vàoAll[](:140) → Admin auto-grant quaSeedAdminPermissionsAsyncloop (:1909 idempotent), +2 MenuItemDbInitializer:1757, +2menuKeys.tsmirror. 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 FKVehicleBooking.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 S4078c9de3, 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ạiApplication/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-486ApproveHandler = flattenSteps.OrderBy(Order).SelectMany(Levels.OrderBy(Order))global level index →allLevels[CurrentApprovalLevelOrder-1]→ actor matchLevel.ApproverUserId==uid||Admin→ UPSERT LevelOpinion → advance++/DaDuyet. ⚠️ApprovalWorkflow.cs:72nó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}LevelOpinionmới (Mig 41+), 3 route/controller, 4 seed WF, FE Detail+Opinion (chỉ có WorkflowAppsListPage chung). ⚠️ enumApplicableTypeTHIẾU Travel (có Leave=5/OT=6/Vehicle=7/ItTicket=8);ExtendApplicableTypeForWorkflowAppsmig 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, lastAddAttendances). ✅ 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]. -
2026-05-29 (S39 BVAAU 7-agent extract): Đọc 8 file BVAAU
.claude/agents/~22K. Split 4→7 trục research(2)/implement(2)/quality(3). Boundary: repo interface=domain, EF config=infra, test=test-specialist. Tool: cả 7 agent 5 RAG MCP (+search_code BM25 +store_memory +list_projects). BVAAU Phase 0 codebase RỖNG → aspirational template chưa battle-test; SOLUTION_ERP giữ 6 skill + backend/frontend split (thay domain/infra cho 2-FE fit). VIPIX guide claim KHÔNG verify được (file miss). Tag[cross-project, bvaau, port]. -
Archived S29-S37 →
archive/2026-05-q4.md+ gitd2f52ba(S40 curate): S36 G-O2 Phòng họp clean-room + FullCalendar v6 MIT eval · S36 startup MEMORY-size audit · S35 G-H2 HRM clean-room verdict · S33 G-H1 NamGroup TblNhanVien 10-bảng (105 cols main) · S33 startup RAG verify · S32 Plan G 11-module backlog · S29 Plan CA+B pre-flight (3 patterns: 9-menu terrain, V1+V2 coexist, reference-template-paths cite line-range ROI). KEY absorbed: clean-room > NamGroup port verified 4× · Pattern 12-bis cross-module mirror · FK+freetext dual-write.
🔄 Curate trigger
-
~30KB → archive recent → L2
archive/<period>.md. Stale >3mo → remove. - Last curate: 2026-05-29 S40 em main proxy (35.7→~20KB): archived 7 FIFO S29-S36 → q4 + git
d2f52ba, refreshed stale essentials S25→S40 numbers, trimmed memory-list. Prev: S34 q3 · S32 q2 · S22 q1.