All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m23s
FINALIZE review wf_73de399d-753 (3 reviewer, R1/R2 no-StructuredOutput->self-gate, R3 thorough): audit 21 checklist item A1-A9/B1-B4/C1-C8 on-disk = 0 code-defect, adoption SOUND, 3 deferred-gap. Closed: G1 curate wf_f32987b8-03f (reviewer 36.7->24.8KB + investigator 29.8->23.2KB <25600 cap, archive +N -0 0-byte-loss, +reviewer-gist gen:2, budget.json re-measure) + G2 (2 stale user-memory claims) + G3 (feedback_harness10_run_trace.md new) + minor (gitignore check-ignore exit-trap corrected). Race root-cause fixed structurally. State unchanged (Mig 53/88 tables/306 test). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
16 KiB
16 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"