- 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>
36 KiB
Gist 2026-06 — investigator-codebase (2026-06.md distilled)
distill-gen: 2(already-distilled — do NOT re-compress). 4-field per record: VIỆC · KẾT-LUẬN (+file:line) · BÀI-HỌC · BẤT-NGỜ. Same-topic merged. Confidence cao/vừa/thấp. Each line ends with a back-resolvesubstring:"…"(unique Ctrl-F) into2026-06.md. Covers all 18 records (3 S57bis-era + 12 moved by Harness-9 curate 2026-06-17 + 3 S65-series moved by S71 curate 2026-06-18). 22-marker coverage gate satisfied across this + 2026-05.gist.md.
gotcha #57 family — soft-delete + bare unique index (reachable 500)
- [cao] gotcha #57 EXTENSION reachability audit · VIỆC: 6 candidates for soft-delete + bare
.IsUnique()on Code → recreate-after-delete throws DbUpdateException 500. KẾT-LUẬN: FIX 3 Master = Department/Supplier/Project (Department/Supplier/ProjectConfiguration.cs:18/24/19bare unique), all CONFIRMED-reachable (DepartmentFeatures.cs:76+125,ProjectFeatures.cs:87+147,CreateSupplierCommand.cs:45+DeleteSupplierCommand.cs:20). SKIP 3 = ContractClause (no Create/Update/Delete handler anywhere — not CRUD-reachable), MeetingRoom (Delete setsIsActive=falseNOT IsDeleted,MeetingFeatures.cs:178; Create also&& !IsDeleted), EmployeeProfile (Create BLOCKS reuse by design — UserId check sees soft-deleted → throws "Cần khôi phục" :160-163; EmployeeCode auto-gen atomic). Mig 46 = exactly 3 indexes. BÀI-HỌC: every OTHER bare-unique is safe (composite junction, nullable-code already filtered, or no-soft-delete). BẤT-NGỜ: Master's GLOBALHasQueryFilter(!IsDeleted)MAKES the bug — auto-hides soft-deleted from the Create dup-check so it passes, then the unfiltered index throws 500 — opposite of HRM where the bug needs a manual!IsDeleted; either way the unfiltered index is the fault. · 2026-06.md · substring:"S51 gotcha #57 EXTENSION reachability audit" - [cao] add-kind 11-spot stack + bare-unique confirm · VIỆC: add a HrmConfigs kind (Vehicle/Driver). KẾT-LUẬN: HrmConfigs has NO kind-enum/registry backend — 4 separate entities (LeaveType/Holiday/ShiftPattern/OtPolicy), "kind" is FE-only union+route param; adding 1 kind = mirror full entity stack across 11 spots (Domain/Configuration/DbContext×2/Features-region/Controller-4-routes/DbInitializer/MenuKeys+All/Page-KIND_CONFIG/App-route/menuKeys+staticMap). gotcha #57 CONFIRMED still bare:
LeaveTypeConfiguration.cs:19+ShiftPatternConfiguration.cs:19+OtPolicyConfiguration.cs:22.IsUnique()lack.HasFilter("[IsDeleted]=0")— onlyHolidayConfiguration.cs:18fixed (Mig43) → Vehicle/Driver Code UNIQUE must add the filter from day one. BÀI-HỌC: each kind = its own table (NOT discriminated) → Mig 44 must CREATE TABLE. · 2026-06.md · substring:"S50 P11-C Vehicle+Driver — HrmConfigs add-kind pattern VERIFIED" - [vừa] P11-C Vehicle/Driver catalog pre-flight · VIỆC: where to home the catalog. KẾT-LUẬN: extend HrmConfigs (NOT new module) — add Region 5/6 (kind vehicles/drivers); FE = +2 KIND_CONFIG entries; VehicleBooking stays free-text (
Office/VehicleBooking.cs:13-19, no VehicleId/DriverId FK). BÀI-HỌC: HRM entities have NO global HasQueryFilter → manual.Where(!IsDeleted)+ UNIQUE soft-delete needs.HasFilter("[IsDeleted]=0")(Holiday Mig43 lesson). · 2026-06.md · substring:"P11-C Vehicle+Driver catalog pre-flight"
Permission / authz / seed model
- [cao] BE authz split — Master write-open · VIỆC: assess making modules visible to all roles (blocks A/B/E/F). KẾT-LUẬN: config controllers gate WRITE behind
[Authorize(Roles="Admin")], READ open (HrmConfigs/Catalogs/MeetingRooms) → FE-grant is pure UI-visibility there. 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 is the only gate). S55 prod data lives inSeedRealMasterDataAsync :2267-2460→ Projects(62 :2270), WorkItems(71 :2430-2438), Suppliers(3 :2440-2456), all ungated idempotent. BÀI-HỌC: making Suppliers/Projects/Departments visible-to-all needs[Authorize(Roles="Admin,CatalogManager")]or the staff can overwrite S55 master data. 10 departments now (9 + IT); 31 demo users, none zero-role. · 2026-06.md · substring:"S57 perm-broaden blocks A/B/E/F" - [cao] seed model — no per-employee default Read · VIỆC: blocks C/D recon. KẾT-LUẬN:
SeedAdminPermissionsAsync DbInitializer.cs:1939-1977loops MenuKeys.All CRUD=true (skip-existing); 2 sub-seeders give 7 roles Read+Update on PE keys and CatalogManager full-CRUD 9 master keys. NO generic per-employee Read seed → a plain Drafter sees ONLY PE keys; most non-admin staff see ~nothing but PE. 4 inherit-roots (Contracts/Workflows/PurchaseEvaluations/PeWorkflows) cascade root→child; Hrm/Off/Master NOT inherit (each leaf needs own row). BÀI-HỌC: grant-all pattern = mirror CatalogManager seeder but loop ALL 13 roles × key-set, CanRead-only, inserted AFTER the catalog seeder. · 2026-06.md · substring:"S57 perm-broaden RECON blocks C/D" - [cao] menu seed = UPSERT that re-sets Order · VIỆC: menu reorder cross-repo SE↔NAMGROUP. KẾT-LUẬN: SE menu =
SeedMenusAsync DbInitializer.cstuple-list; seed UPSERT re-sets Order (:1845-1871) → reordering in code propagates to Dev/prod next deploy, NO migration; but Label/ParentKey/Icon are NOT touched on existing rows → rename needs a separate labelBackfill dict. Order = BE-only (GetMyMenuTreeQuery.cs:35 OrderBy); both FE render menu as-is → no FE edit for pure reorder. BÀI-HỌC: HR is SCATTERED across 2 roots —Hrm(Hồ sơ+Config+Dashboard) and transactional HR underOff(DonTu/DatXe/ChamCong/AttendanceReport). BẤT-NGỜ: NAMGROUP "Puro" = hardcoded FE array (NOT DB seed), index=order, flat single links vs SE deep-nested. · 2026-06.md · substring:"menu-order cross-repo recon SE↔NAMGROUP" - [cao] public-HRM for all-role — seed-only · VIỆC: open HRM module (Hồ sơ/Dashboard) to every role. KẾT-LUẬN:
EmployeesController.cs:23-25is[Authorize(Policy="Hrm_HoSo.Read")](NOTRoles="Admin") — policy resolves THROUGH the permission matrix (MenuPermissionHandler.cs:40-52), so seeding a CanRead row also unlocks the API, NO 403 → seed BE permission is enough, no controller edit; FE auto-renders (menu-tree API-driven,HrmNOT inUSER_HIDDEN_KEYS, root auto-shows if a child has access).Hrm_HoSo+Hrm_DashboardARE inMenuKeys.All(unlike Pe_ leaves); 13 roles inAppRoles.All.* BÀI-HỌC: Pe-style grant-all =SeedAllRolesReviewReadPermissionsAsync :2055loops all roles × keys, upsert CanRead, idempotent. BẤT-NGỜ:RevokeTemporarilyHiddenModulesAsyncis called LAST:2040in SeedAsync (after the grant:2033) and wipes CRUD on everyStartsWith("Hrm")||"Off"||==Personalnon-Admin row → it BEATS any earlier grant; opening Hrm needs either excluding Hrm_HoSo/Dashboard from the revoke OR granting AFTER :2040. · 2026-06.md · substring:"S65 recon — public HRM module for all-role"
Prod facts / census / wipe
- [cao] prod test-data wipe + FK + PE tree · VIỆC: wipe prod test PE + retree by Hạng mục. KẾT-LUẬN: prod PE=10 active (MaPhieu A/031-040, all WorkItemId NULL) + Contracts=7 all
[DEMO]V1; FK: PE children CASCADE exceptQuotes→PE NO_ACTION(multi-path) — Plan R proved a singleDELETE FROM PurchaseEvaluationsworks (NO_ACTION checked end-of-statement after Details→Quotes cascade); PE.ApprovalWorkflowId Restrict → wipe PE before deleting AW. Demo gate OK (SeedDemoContracts/PE insideDemoSeed:Disabled). FE tree =pe/PurchaseEvaluationsListPage.tsx:138-179Project>Year>Supplier, PeListItem already has workItemId/Name → FE-only change. BÀI-HỌC: ~10 orphan upload folders from S23 (files not deleted on wipe). BẤT-NGỜ: 20 REAL users batched 2026-06-11 06:01 (S58 seed-fix landed;thanh.lethanhnow exists, correcting stale S57bis memory). · 2026-06.md · substring:"S59 recon — prod test-data wipe + PE tree" - [cao] prod user census + pwd-policy env divergence · VIỆC: why is the demo-user lock a no-op. KẾT-LUẬN:
LockDemoSampleUsersAsync DbInitializer.cs:1552hardcodes 14 named-person emails = a population that exists ONLY on Dev; prod has 34 all-active users (20 UAT-matrix placeholders hand-created 2026-05-13, 9 real staff,binh.lethanh, a typo-domainchuong.phan@solution.com.vndup, admin/catalog.manager/nv.test). ROOT CAUSE seed-users-never-on-prod = prodIdentity:Password:RequiredLength=12vsDemoUserPassword="User@123456"=11 chars → CreateAsync silent-fails every prod startup since 04-21; Dev fallback length 8 → Dev gets the 33 named users. BÀI-HỌC: fix needs 20 real prod emails; keep nv.test (breaking it breaks CI smoke). BẤT-NGỜ:bod.1@never appears in git pickaxe → created by hand via admin UI, not seed. · 2026-06.md · substring:"S57bis lock no-op — prod user census" - [cao] PE entity recon — 4 đầu việc · VIỆC: PE Year/WorkItem/create-UI/perm. KẾT-LUẬN: PE has NO Year, NO WorkItem link (free-text detail); MaPhieu gen-AT-CREATE
PurchaseEvaluationCodeGenerator.cs:23formatPE/{YYYY}/{A|B}/{Seq:D3}; PE controller class-[Authorize]only (no policy → opening the menu is enough, no silent-403); Pe_* leaves NOT inMenuKeys.All. BÀI-HỌC: extending InReviewScope must matchkey == MenuKeys.PurchaseEvaluationsEXACT — the prefix "Pe" would also catch PeWorkflows (admin); root inherit cascades. BẤT-NGỜ: WorkItem write is Admin-only (CatalogsController:113-130) so CatalogManager has the menu but the API write is blocked. · 2026-06.md · substring:"S57bis PE recon — 4 đầu việc sếp"
Pre-golive verify / FE-redesign / master-import
- [cao] pre-golive logic verify — 4 streams PASS · VIỆC: audit P11-B/D/E/F + ApproveV2 + catalogs + S55 wiring. KẾT-LUẬN: LeaveBalance deduction exactly-once (terminal DaDuyet, guard
Status!=DaGuiDuyet :296); AttendanceReport classifies day-type in-memory; MaTicket gen-on-Create Serializable; ApproveV2 4-module flatten correct; master-data idempotency PROVEN (re-run → identical counts). BÀI-HỌC: Travel/Vehicle ApproveV2 = 0 tests (cookie-cutter of tested Leave/OT) — add 2 smoke post-golive. BẤT-NGỜ: dept "IT"/Phòng CNTT DOES exist on prod (Id 65CC6307…) but has 0 active users → ItTicket auto-assign no-ops, SLA job no target (corrects stale S52 "no IT dept"); ops fix = assign ≥1 user to dept IT (1 UPDATE). · 2026-06.md · substring:"S56 pre-golive verify — 4 logic streams" - [vừa] FE-redesign Phase-2 recon · VIỆC: 25-page redesign audit. KẾT-LUẬN: NOT a rewrite — S55 already redesigned ui-primitives+DataTable+shell so importers auto-inherit density; hover-hidden quick-win ~absent (only 1 real
opacity-0×group-hover site, excluded); only 5/25 use<DataTable>(12 roll raw<table>); 3 Drawer candidates (Suppliers/Projects/Users-create); noDrawer.tsxexists yet; bậc-thang inline-edit reference already exists (EmployeesListPage.tsx). BÀI-HỌC: effort mostly S (auto-inherit) + a few M (Drawer/bậc-thang). · 2026-06.md · substring:"S56 Phase 2 FE-redesign RECON — 25 page audit" - [cao] master-data Excel-import recon · VIỆC: import 3 masters from Excel. KẾT-LUẬN: WorkItem/"Hạng mục" master EXISTS (
Domain/Master/Catalogs/WorkItem.cs, full CRUD) — no new table needed; PE detail is pure free-text (no FK→WorkItem); Project MISSING Year/Investor/Location/Package (only Note free-text); Supplier MISSING Status/bank-acct/legal-rep + "Cả hai" unmappable (Type single-valued); seed = idempotentexistingCodes.Contains→skip; no bulk import (Master is single-CRUD, POST one-at-a-time). BÀI-HỌC: nạp via idempotent DbInitializer mirror (NOT API loop) → reaches prod by design. BẤT-NGỜ: SeedDemoMasterData + SeedCatalogs run REGARDLESS ofDemoSeed:Disabled(outside the if-block) → real+demo data mix on prod unless gated. · 2026-06.md · substring:"S55 master-data Excel-import recon"
FE mirror / UI-insert recon
- [cao] mirror Hồ sơ-NS fe-user→fe-admin = patch CSS first · VIỆC: replicate the redesigned employee page from fe-user into fe-admin. KẾT-LUẬN: VERDICT B — a plain page-copy BREAKS COLORS; must patch
fe-admin/src/index.cssFIRST then cookie-cutter. fe-admin index.css = 86 lines (pinned7feb53e, pre-S58 redesign) → missing 4 accent palettes (teal/amberx/violet/greenx, each 50/100/500/600/700) + utilities.icon-chip/.app-gradient-brand/.card-accent/.stat-value; brand-50..900 hex already present (incl brand-800 #175685). The fe-user page really depends on text-brand-800 ×9, the 4 accents ×4 each, icon-chip ×3, app-gradient-brand ×1. Wiring already complete in fe-admin (0 changes): route, identicalEmployeeCreatePage.tsx, menuHrm_HoSo+staticMap; ui-primitives/types/api all parity. Scope = 3 files (index.css insert ~40 lines + overwrite EmployeesListPage.tsx + optional heading-700). BÀI-HỌC: mirror exactly like S35 (both apps committed together); write stays Admin-gated at BE. · 2026-06.md · substring:"S66 recon — mirror Hồ sơ NS fe-user" - [cao] PE Section-E "Link hồ sơ" insert point · VIỆC: add mục E "Link hồ sơ" right under mục D "Bản so sánh" in the PE form. KẾT-LUẬN: render in 4 files (SHA256-identical 2 apps):
components/pe/PeDetailTabs.tsx+PeWorkspaceCreateView.tsx× {fe-user,fe-admin}; NOT tabs — 5 vertical<Section>. Mục D lives inChonNccSection(PeDetailTabs.tsx:1302-1375), d.Bản so sánh at :1337-1348 =GeneralAttachmentsSectionupload filteredsupplierId===nullpurpose=ComparisonTable. INSERT E atPeDetailTabs.tsx:1348(after mục D's</div>, before paymentTerms :1350); create-view atPeWorkspaceCreateView.tsx:277. BEPurchaseEvaluation.cshas NO URL field — 1 link = addstring? HoSoLink(1000)+Mig+cmd+DTO+validator; many links = child entity (heavy). BÀI-HỌC: attachments are IFormFile-only (PurchaseEvaluationAttachmentFeatures.cs:18-55) — cannot reuse for a URL. BẤT-NGỜ: comment :1314 claims "purpose=ComparisonTable OR supplier-row null" but the real filter :1315-17 is ONLYsupplierId===null. · 2026-06.md · substring:"S65ter recon — Mục E"
Governance / wave / harness
- [vừa] Harness 1/2/3 adap-apply recon · VIỆC: apply AI_INFRA broadcast. KẾT-LUẬN: roster 8→10 (+tooling-auditor H1, +harvest-curator H2); SE
hmw.jsis pre-wave (AI_INFRA = canonical); email id authoritative =se;git check-ignore -v= ground-truth B6 (wave patterns must sit AFTER!.claude/**last-match-wins). BÀI-HỌC: git-diff + chunk-count = defense-in-depth (caught 1 self-MEMORY write, 0 RAG-write). · 2026-06.md · substring:"Harness 1/2/3 adap-apply recon — 3 slice" - [cao] wave h2-verify — B6 isolation + Bash surprise · VIỆC: audit wave write-isolation. KẾT-LUẬN: B6 = 2 rules — transient
wave-*/+agent-teams/gitignored (audit-noise=0) AND canonicalagent-memory/**/MEMORY.mdTRACKED (rogue writes surface in git status); all 10 MEMORY.md tracked. BÀI-HỌC: ordering gotcha — wave/team patterns MUST sit after!.claude/**or it un-ignores everything. BẤT-NGỜ: Bash tool =/usr/bin/bashNOT PowerShell despite env=PowerShell →Get-ChildItem/Select-String/Test-Pathfail (exit 2/127); read-only Bash-only subs MUST use POSIX. · 2026-06.md · substring:"S50 waveh2-verify— B6 guardrail audit"
Monthly drift audit
- [vừa] 2026-06 monthly drift audit · VIỆC: ground-truth counts vs docs. KẾT-LUẬN: migrations=42 (last AddLeaveBalances) / gotchas highest=#56 (file header has no self-count) / tests=154 / tables≈91; biggest drift = ef-core-migration SKILL (says 31 mig / 59 bảng / 111 test). BÀI-HỌC: schema-diagram migration table stops at Mig 16 → missing §§ for Mig 27-42. BẤT-NGỜ: STATUS backlog "Curate 4 agent MEMORY 35.7/…" is STALE (already curated S40) → remove. · 2026-06.md · substring:"MONTHLY DRIFT AUDIT"
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.mdmà 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. (Đính-chính nhãn @lead-review:#N > 83= Run/PR-ref CI, KHÔNG phải gotcha — repo max #83; đổi nhãnref.)
- ref #346 · trích: "finalize/price audit (FROZEN Mig 59 Run#346 — patchwork hunt):** ⭐ TWO finalize mechanisms COEXIST + BOTH LIVE (NOT dead): (1) `AllowApproverFinalize"
- gotcha #44 · trích: "on-disk):** ⭐ Mục 6 CRITICAL (gotcha #44 family) RESOLVED-FAVORABLE:
EmployeesController.cs:23-25= class[Authorize(Policy='Hrm_HoSo.Read')]" - gotcha #66 · trích: "140),
.label-eyebrow(:89). ⚠️ GOTCHA #66 =index.css:79-83h1,h2,h3,h4{color:#0b1220;font-weight:700}OUTSIDE @layer → TW-v4 unlayered wins →" - gotcha #70 · trích: "lti-winner + mirror-drift + dead-code + #70):** ⭐ Post-Mig58 multi-winner (
isWinnerbool) NOW implemented (supersedes 06-24 'SINGLE' recon). **All 6" - gotcha #72 · trích: "notify/TZ audit (FROZEN Mig 59 — gotcha #72):** ⭐ (a-TZ)
UtcDateTimeJsonConverterregistered ONLY onAddControllers().AddJsonOptions`Program.cs:3" - gotcha #73 · trích: "datePeDraft edit-preserve audit (gotcha #73-class hunt, FROZEN Mig 59):** ⭐ LATENT #73-CLASS BUG FOUND:
UpdatePeDraftHandlerabsolute-sets `enti" - Mig Mig 26 · trích: "g 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"
- Mig Mig 29 · trích: "nting', '[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ù" - Mig Mig 39 · trích: "hỉ
Office/VehicleBooking.cs(request, Mig 39) dùng FREE-TEXT (VehicleLicense/VehicleName/DriverName?strings, :13-19 comment 'defer catalog Ph" - Mig Mig 43 · trích: "chỉ
HolidayConfiguration.cs:18đã fix Mig 43). → Vehicle/Driver Code UNIQUE PHẢI add filter ngay từ đầu. Mig 44 BẮT BUỘC CREATE TABLE (mỗi kind" - Mig Mig 52 · trích: "oLink → editing header CLEARS HoSoLink (Mig 52) to null. HoSoLink set separately in
PeDetailTabs.tsx:2012-2020HoSoLinkRow (re-sends all absolute fi" - Mig Mig 53 · trích: "s column-PE(per-phiếu) = em-main quyết. Mig 53 CeoApprovalThreshold+cờ-gấp đã có khung CEO-duyệt-theo-ngưỡng. Tag `[pe-price-model, gia-chao-thau-deri"
- Mig Mig 54 · trích: "legation
OPT-IN param threshold-gated (Mig 54):879-915`. AUTO runs FIRST + returns → if both eligible, OPT-IN flag silently ignored (coherent, no d" - Mig Mig 56 · trích: "REA-2 (budget matrix + Section B 3-col, Mig 56/59):** ⭐ BAND-AID CONFIRMED
SetPeCcmBudgetPeriodCommand+Validator+Handler(`PeSuggestedPriceFeatu" - Mig Mig 58 · trích: "pproverFinalize
per-Level AUTO config (Mig 58)PurchaseEvaluationWorkflowService.cs:861-877+ (2)finalizeByCcmDelegation` OPT-IN param threshold-" - Mig Mig 59 · trích: "ve audit (gotcha #73-class hunt, FROZEN Mig 59):** ⭐ LATENT #73-CLASS BUG FOUND:
UpdatePeDraftHandlerabsolute-sets `entity.HoSoLink = request.H" - Mig Mig47 · trích: "14-21
): Code(50,UNIQUE[IsDeleted]=0` Mig47)+Name(200) REQUIRED, StartDate/EndDate/BudgetTotal(18,2)/Note(1000)/ManagerUserId optional. ❌ **THIẾU Ye" - Mig Mig50 · trích: "/b/c/d map — SUPERSEDED bởi S65ter post-Mig50 Budget-drop; test mirror PurchaseEvaluationWorkflowServiceGuardTests). Full text git."
- Mig Mig58 · trích: "rror-drift + dead-code + #70):** ⭐ Post-Mig58 multi-winner (
isWinnerbool) NOW implemented (supersedes 06-24 'SINGLE' recon). **All 6 PE components" - guard-kw race · trích: "eBudgetSummaryDto:290-291
; auto-create race-safePeWorkItemBudgetEnsurer.EnsureTrackedAsync:34`; KHÔNG ràng Phase. NO AutoMapper (DTO project tay)." - file-ref AdminLayout.tsx · trích: "under separate 'Cá nhân' group); admin
AdminLayout.tsx:87-119splits by function not HR/office. NAMGROUPĐơn từ/Phòng họp/Đề xuất= flat singl" - file-ref App.tsx · trích: "faults
+ types/hrm-config.ts DTO (10)App.tsx:90route/hrm/configs/:kindSẴN catch-all → KHÔNG cần sửa, chỉ +menuKeys (11)menuKeys.ts:38-42` +" - file-ref AppRoles.cs · trích: "enuKey), NO DB upsert. 13 AppRoles
AppRoles.cs:23: Admin(system)+12 employee. 4 inherit-rootsGetMyMenuTreeQuery.cs:56-84: Contracts/Workf" - file-ref ApplicationDbContext.cs · trích: "oTable+.HasIndex(Code).IsUnique()
(3)ApplicationDbContext.cs:95-98DbSet (4)IApplicationDbContext.cs:102-105DbSet (5)HrmConfigFeatures.cs` +" - file-ref ApprovalWorkflow.cs · trích: "approvers = specific
ApproverUserId(ApprovalWorkflow.cs:80), OR-of-N = N Level rows sameOrder(GroupBy :687). 'Phòng CCM' = seed Step NAME + n" - file-ref ApprovalWorkflowsV2Page.tsx · trích: "L both DTOs.** (A) Designer fe-admin
ApprovalWorkflowsV2Page.tsx: read-only renderDefinitionCard:446-454(level group → approver `{approverUs" - file-ref AuditingInterceptor.cs · trích: "Deleted)
** + Delete via.Remove()→AuditingInterceptor.cs(State Deleted→Modified, IsDeleted=true) + CreateAnyAsync(x=>x.Code==req.Code)NO" - file-ref CatalogsConfiguration.cs · trích: "Unit(50)/Description/IsActive), config
CatalogsConfiguration.cs:60-74, full CRUDCatalogsFeatures.cs:260-324→ group(VẬT TƯ/THẦU PHỤ/MEP)→Category" - file-ref CatalogsController.cs · trích: "21
, all POST/PUT/DELRoles='Admin';CatalogsController.cs:14same (write:23+Admin);MeetingRoomsController.cs:15same (comment:9-10` expl" - file-ref CatalogsFeatures.cs · trích: "logsConfiguration.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/m" - file-ref ChamCongPage.tsx · trích: "(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-user `InternalL"
- file-ref ContractClauseConfiguration.cs · trích: "). SKIP 3: (a) ContractClause (
ContractClauseConfiguration.cs:18) — NO Create/Update/Delete handler ANYWHERE (only `IApplicationDbContext.cs" - file-ref ContractCreatePage.tsx · trích: "
×group-hover= **only 1 real site**ContractCreatePage.tsx:196` (EXCLUDED scope) + DataTable.tsx:15 is a comment forbidding it (good). In-scope =" - file-ref DanhBaPage.tsx · trích: "age(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"
- file-ref DataTable.tsx · trích: "tCreatePage.tsx:196` (EXCLUDED scope) + DataTable.tsx:15 is a comment forbidding it (good). In-scope = 0 hover-hidden fixes. **DataTable adoption"
- file-ref DateTimeService.cs · trích: "e:
n.CreatedAt=clock.UtcNow=Kind.Utc (DateTimeService.cs:7) → default STJ writes 'Z' for Utc-kind, FEnew Date(iso)NotificationBell.tsx:23 OK. La" - file-ref DeXuatPage.tsx · trích: "ePage.tsx(dashboard) · PageHeader.tsx · DeXuatPage.tsx(1676 KPI+table+detail) · DonTuPage.tsx(1269 +DonTuCalendar.tsx toggle) · XeCongPage/PhongHopPag"
- file-ref Department.cs · trích: "). GAP THẬT: (1) NO org-tree —
Department.csFLAT (Code/Name/ManagerUserId/Note, KHÔNG ParentId),DepartmentsControllerchỉ GET list+byId" - file-ref DependencyInjection.cs · trích: "ment :1675-79 đã biết); Dev fallback 8 (DependencyInjection.cs:67
?? 8, Development.json no Identity section) → Dev đủ 33 user named-person. `bod.1@" - file-ref DonTuCalendar.tsx · trích: "KPI+table+detail) · DonTuPage.tsx(1269 +DonTuCalendar.tsx toggle) · XeCongPage/PhongHopPage(month-grid) · TicketPage.tsx(595) · DanhBaPage.tsx(756 tre"
- file-ref DonTuPage.tsx · trích: "DeXuatPage.tsx(1676 KPI+table+detail) · DonTuPage.tsx(1269 +DonTuCalendar.tsx toggle) · XeCongPage/PhongHopPage(month-grid) · TicketPage.tsx(595) · Da"
- file-ref Dtos.cs · trích: "alLevelApproverDto
(PurchaseEvaluationDtos.cs:129-132= UserId/FullName/Email, **NO role**). **(C) Role-resolve for LIST userId:** codebase usesu" - file-ref EmployeeFeatures.cs · trích: "6
EmployeeCode+UserId) — Delete soft (EmployeeFeatures.cs:437) BUT Create BLOCKS reuse by design: UserId checkAsNoTracking().FirstOrDefault(UserI" - file-ref EmployeeProfile.cs · trích: "Entity gần đủ screenshot:**
Domain/Hrm/EmployeeProfile.cs(137 LOC) CÓ: DOB/Gender/Ethnicity/Religion/Nationality/Height/Weight(:98-99)/IdCard(số+ng" - file-ref EmployeeProfileConfiguration.cs · trích: "eleted
:113. (c) **EmployeeProfile** (EmployeeProfileConfiguration.cs:24/26EmployeeCode+UserId) — Delete soft (EmployeeFeatures.cs:437`) BUT Crea" - file-ref Enums.cs · trích: "iority{Low,Medium,High,Urgent}
Office/Enums.cs:48-54) AddColumn no-new-table; notifyINotificationService.NotifyAsync(userId,type,title,desc?,href" - file-ref HomePage.tsx · trích: "dex=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 · RI" - file-ref HrDashboardController.cs · trích: "nRead row = API ALSO unlocked, NO 403.
HrDashboardController.cs:8-11=[Authorize]any-auth only (/api/hr/dashboard). GET list = `/api/employees" - file-ref HrmConfigFeatures.cs · trích: "icationDbContext.cs:102-105
DbSet (5)HrmConfigFeatures.cs` +Region N (DTO+List/Create/Update/Delete handler+validator, mega 4-region :30/125/222/32" - file-ref HrmConfigsController.cs · trích: "or, mega 4-region :30/125/222/328) (6)
HrmConfigsController.cs+4 route hardcode[HttpGet/Post/Put/Delete('{kind}')](Post/Put/Del `[Authorize(Rol" - file-ref HrmConfigsPage.tsx · trích: "issionsAsync
loop idempotent). FE (9)HrmConfigsPage.tsx:45 KIND_CONFIG+entry +:114 KINDS[]+:379 renderCellsbranch +:166 smart-defaults`" - file-ref IApplicationDbContext.cs · trích: "plicationDbContext.cs:95-98
DbSet (4)IApplicationDbContext.cs:102-105DbSet (5)HrmConfigFeatures.cs` +Region N (DTO+List/Create/Update/Delete ha" - file-ref INotificationService.cs · trích: "serId,type,title,desc?,href?,refId?)
(INotificationService.cs:10)+SignalR interceptor; LogTransition notifies DRAFTER-only on terminal (:960-980`)" - file-ref InternalLayout.tsx · trích: "y (NOT DB seed),** index=order: client
InternalLayout.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' gro" - file-ref Layout.tsx · trích: "ỉ +menuKeys (11)
menuKeys.ts:38-42+Layout.tsx:60-63 staticMap. gotcha #57 CONFIRMED còn trần:LeaveTypeConfiguration.cs:19+ `ShiftPattern" - file-ref MeetingRoomConfiguration.cs · trích: "t CRUD-reachable. (b) MeetingRoom (
MeetingRoomConfiguration.cs:20) — Delete setsIsActive=falseNOT IsDeleted (MeetingFeatures.cs:178, comme" - file-ref MeetingRoomsController.cs · trích: "ller.cs:14
same (write:23+Admin);MeetingRoomsController.cs:15same (comment:9-10` explicit). → granting FE read on Hrm_Config/Catalogs/Meeti" - file-ref MenuKeys.cs · trích: "hải +
&& OtPoliciesNew.AnyAsync()(8)MenuKeys.cs:88-92+const +:149 All[](Admin auto-grantSeedAdminPermissionsAsyncloop idempotent). FE (9" - file-ref NotificationBell.tsx · trích: "es 'Z' for Utc-kind, FE
new Date(iso)NotificationBell.tsx:23 OK. Latent: any future SignalR push of an EF-READ (Unspecified) DateTime would skew. N" - file-ref NotificationPushInterceptor.cs · trích: "no
.AddJsonProtocol, separate STJ):NotificationPushInterceptor.cs:77pushescreatedAt=n.CreatedAtoverSendAsync. BENIGN at runtime: `n.Crea" - file-ref PageHeader.tsx · trích: "rumb + actions slot,
components/shared/PageHeader.tsx); (b) KPI stat-cards clickable filter (DeXuat :1643 6-card / Ticket :197 5-card — 'PURO KP" - file-ref PeCcmBudgetPeriodSetterAuthzTests.cs · trích: "sit). Test+comments already self-flag (PeCcmBudgetPeriodSetterAuthzTests.cs:14). ⚠️ **Role-gate ASYMMETRY (intentional, NOT a bug — but inconsistent"
- file-ref PeHeaderForm.tsx · trích: "e
PeWorkspaceCreateView.tsx· headerPeHeaderForm.tsx. FE typetypes/purchaseEvaluation.tsPeBudgetSummary:292-307+winnerQuoteTotal:445. ⚠️" - file-ref PeListPanel.tsx · trích: "RESIDUAL single-winner BUG (low):
PeListPanel.tsx:168rendersp.selectedSupplierName(list-card winner label) → BLANK when ≥2 joint winners (" - file-ref PeSuggestedPriceFeatures.cs · trích: "udgetPeriodCommand+Validator+Handler
(PeSuggestedPriceFeatures.cs:153-199) is a BUDGET setter (writespe.CcmBudgetPeriodAmount`) MISPLACED in a pr" - file-ref PeUrgentFeatures.cs · trích: "
GetUsersInRoleAsync(role)(reverse,PeUrgentFeatures.cs:74).IApplicationDbContextexposesDbSet<Role> Roles:29 but **NO UserRoles join-tabl" - file-ref PeWorkItemBudget.cs · trích: "h
toànsrc/Backend=0 + FE=0. EntityPeWorkItemBudget.cs6 field plain, KHÔNG cờIsInitialLocked/SubmitCount`; record per-cặp(Project×WorkItem)" - file-ref PeWorkItemBudgetFeatures.cs · trích: "||CostControl
(DTO arg :856). HandlerPeWorkItemBudgetFeatures.cs: PRO:86-91CCM:152-157` fail-closed ForbiddenException role-only TRƯỚC side-" - file-ref PeWorkflowPanel.tsx · trích: "
:474,:526· IPe…Service:32 · FE bothPeWorkflowPanel.tsx:184,584ô-tích) — NOT orphan. ALL 3 DaDuyet paths bind giá chốt via `ApplyApprovedPriceOn" - file-ref Permission.cs · trích: "rs CanRead=true. Permission entity
Permission.cs:3-15: RoleId/MenuKey/CanRead/Create/Update/Delete. Dedup=app-level skip-existing per(RoleId,Men" - file-ref Program.cs · trích: "zer.InitializeAsync
chạy MỌI startup (Program.cs:197unless--no-db-init) →MigrateAsyncTHEN seed; demo gatedconfig.GetValue('DemoSeed" - file-ref Project.cs · trích: "d WorkItems non-breaking. Project (
Project.cs:5-14, cfg:14-21): Code(50,UNIQUE[IsDeleted]=0Mig47)+Name(200) REQUIRED, StartDate/EndDate/B" - file-ref PurchaseEvaluationCreatePage.tsx · trích: "ctive. PeHeaderForm reachable as EDIT (
PurchaseEvaluationCreatePage.tsx:13,26editId from URL?id=, deep-link 'Sửa header'). Field table: TenGoiTh" - file-ref PurchaseEvaluationDetail.cs · trích: "n mới. PE detail = pure free-text (
PurchaseEvaluationDetail.csGroupCode/GroupName/ItemCode/NoiDung strings, NO FK→WorkItem) → load WorkItems no" - file-ref PurchaseEvaluationDetailFeatures.cs · trích: "seEvaluationWinnerCommand(PEId,Guid?)
PurchaseEvaluationDetailFeatures.cs:392handler:398-423(null=clear S85-C5); ctrl/select-winnerPurcha" - file-ref PurchaseEvaluationDtos.cs · trích: "seEvaluationApprovalLevelApproverDto
(PurchaseEvaluationDtos.cs:129-132` = UserId/FullName/Email, NO role). **(C) Role-resolve for LIST userId:*" - file-ref PurchaseEvaluationFeatures.cs · trích: "valuationDetail.cs:10-13
). Create cmdPurchaseEvaluationFeatures.cs:19-30; MaPhieu gen-AT-CREATE:114-116formatPE/{YYYY}/{A|B}/{Seq:D3}(Pur" - file-ref PurchaseEvaluationQuote.cs · trích: "at/ChuaVat/ThanhTien
decimal non-nullPurchaseEvaluationQuote.cs:12-14· PE-headerBudgetPeriodAmount(row3 drafter)/ExpectedRemainingAmount`(row" - file-ref PurchaseEvaluationType.cs · trích: "6
)+DetailBundleDto(:201). Type A/B (PurchaseEvaluationType.cs:6-10`) constrains pinnable ApplicableType only — ZERO type-conditional routing. ⚠️ '" - file-ref PurchaseEvaluationUrgentFeatures.cs · trích: "t. (b-authz) Urgent SET/UNSET branches
PurchaseEvaluationUrgentFeatures.cs:54-73SYMMETRIC + fail-CLOSED both (if(!allowed)throwBEFORE mutate); 1" - file-ref PurchaseEvaluationWorkflowService.cs · trích: "ted 3 nơi đồng-predicate: submit-guard
PurchaseEvaluationWorkflowService.cs:188-192· detail-GETPurchaseEvaluationFeatures.cs:818-826(→`CurrentPr" - file-ref PurchaseEvaluationsController.cs · trích: "roller class-level
[Authorize]only (PurchaseEvaluationsController.cs:15) → handler gates ARE the boundary. Tests solid (PeWorkItemBudgetTests 30+" - file-ref Supplier.cs · trích: "
:87 AnyAsync(Code==). Supplier (Supplier.cs:5-16, cfg:14-27): Code/Name req + Type enum + TaxCode(20)/Phone/Email/Address/ContactPerson/No" - file-ref SupplierType.cs · trích: "hone/Email/Address/ContactPerson/Note.
SupplierType.cs: NhaCungCap=1/NhaThauPhu=2/ToDoi=3/DonViDichVu=4/ChuDauTu=5. ❌ **THIẾU Status/TinhTrang (KHÔN" - file-ref TicketPage.tsx · trích: "· XeCongPage/PhongHopPage(month-grid) · TicketPage.tsx(595) · DanhBaPage.tsx(756 tree+grid) · ChamCongPage.tsx(809). ⚠️ Style/màu lấy chỗ khác per tas"
- file-ref config.ts · trích: "nfigKind
unionfe-admin/src/types/hrm-config.ts:4+ route param. **Add 1 kind = mirror FULL entity stack 11 chỗ:** BE (1) DomainHrm/{X}.cs` Audit" - file-ref detectors.ps1 · trích: ") BASELINE-DRIFT ALIGNED:**
governance-detectors.ps1staleness = CANONICAL-ANCHOR vs docs/STATUS.md (Get-StatusValue:133parse `| label | *N" - file-ref employee.ts · trích: "· EmptyState/cn/api/apiError ✅ ·
types/employee.tsidentical · Pagedtypes/master✅ ·DepartmentTreeNodeđịnh nghĩa INLINE trong page (:65 m" - file-ref gate.ps1 · trích: "ale) — ZERO age-window.
memory-archive-gate.ps1over-cap = byte-MEASURE+2-strike-hysteresis (:106 bytes>cap), A7 = substring-pointer-resolve. Neit" - file-ref memory.ps1 · trích: "ined headroom' +
scripts/measure-agent-memory.ps1real-bytes), bump-not-cut khi curate drops markers. Hysteresis drain-to-BELOW-low-water (gate:33" - file-ref menuKeys.ts · trích: "ll → KHÔNG cần sửa, chỉ +menuKeys (11)
menuKeys.ts:38-42+Layout.tsx:60-63 staticMap. gotcha #57 CONFIRMED còn trần: `LeaveTypeConfiguration." - file-ref purchaseEvaluation.ts · trích: "Panel3 separate.
PeApprovalFlowDTOpurchaseEvaluation.ts+ BEPurchaseEvaluationApprovalLevelApproverDto(PurchaseEvaluationDtos.cs:129-132"
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 appsettings.Production.json · trích: "
Identity:Password:RequiredLength=12(appsettings.Production.json) vsDemoUserPassword='User@123456'=11 chars → CreateAsync silent-fail MỌI startu" - file-ref appsettings.json · trích: "uTreeQuery.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" - file-ref memory-budget.json · trích: "ỗi team-size). (1) BUDGET ALIGNED:
memory-budget.jsongrep decay|recency|retention|age|TTL|expire=0 hit. Params =autoinject_cap 25600/`so" - file-ref trial-state-lock.json · trích: "recall@5 gate 0.7, rerank≥0.7) +
eval/trial-state-lock.json(baseline recall@5=1.0, chunk-drift 5% threshold) +eval/runs/*.json. BUT: weekly-Fri"