- STATUS.md + HANDOFF.md: S70 entry (Harness-9 L2 dark-matter recovery + adap 2-workflow mandate, 0 production code, 3-stage Workflow run-id, 4 over-cap sub <25KB cap, P1 curate-debt CLOSED). - docs/changelog/sessions/2026-06-17-S70-harness-9-l2-recovery.md: full session log. - §L.g doc-drift 4-flush (H1 tooling CHỐT): dependency-audit-erp/SKILL.md:153 (65->68) + skills/README.md:20 (Mig 52->53) + :90 (65->68) + session-start.md:44 (58->68). - §L.d/f agent-memory provenance (H2 harvest GATE CONDITIONAL PASS): 4 curate-log refresh -> S70 (was stale S40/S66/S69) + read-side fix MEMORY.md L5 header -> _INDEX.md (3/4 had 0 refs; reviewer already had it). All 4 MEMORY.md still <25KB auto-inject cap. State unchanged (0 production code): Mig 53 / 88 tables / 306 test / 68 gotchas / menu 54 / bundle BgNCjwsG-CBvh0vtf. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
24 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+archive/2026-06.md. 🗺️ Lookup map (Harness-9 S70):archive/_INDEX.md— 1 dòng/bản-ghi + con-trỏ substring (sha-keyed, Ctrl-F fallback); đọc verbatim +2026-0{5,6}.gist.md(nén 4-field) theo nhu cầu. Renamed S39: 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 (PE-workflow recon for FDC feature-plan — urgent flag + value-threshold routing, on-disk): ⭐ PE VALUE: NO stored "giá trị gói thầu" column. Best-fit = winner-quote-total
SUM(Quote.ThanhTien WHERE supplier==SelectedSupplierId)— COMPUTED (submit-guardPurchaseEvaluationWorkflowService.cs:188-190+CurrentProposalTotalinPeBudgetSummaryDto). Other amounts:PE.BudgetPeriodAmount(:40 drafter NS kỳ này)/ExpectedRemainingAmount(:41)/PeWorkItemBudget.FullAmount=(Initial??0)+(Adjustment??0) (PeWorkItemBudget.cs:29-30) — all budgets, not deal-value. ROLES PRO/CCM/CEO = domain shorthand NOT constants (AppRoles.cshas Procurement/CostControl/Director; PRO=Procurement CCM=CostControl CEO=Director). V2 routing IGNORES roles — approvers = specificApproverUserId(ApprovalWorkflow.cs:80), OR-of-N = N Level rows sameOrder(GroupBy :687). "Phòng CCM" = seed Step NAME + non-strict DeptId hint only (:67). CEO = positional (last level/last step), NOT conditional. ROUTING 100% LINEAR (level→step,DaDuyetwhennextIdx>=steps.Count). ZERO value/threshold/conditional config anywhere (grep 0 on AW/Step/Level/PEType). ⭐ HOOK B (value-threshold) =ApproveV2Asyncadvance block lines 816-845 (:817levelOrder++ /:828-837terminal DaDuyet /:838-845next step). Precedent:skipToFinal :773-814already "jump pointer to last step+level" — reuse mechanic conditioned on value. HOOK A (urgent): addIsUrgent bit/PePriorityenum (mirrorItTicketPriority{Low,Medium,High,Urgent}Office/Enums.cs:48-54) AddColumn no-new-table; notifyINotificationService.NotifyAsync(userId,type,title,desc?,href?,refId?)(INotificationService.cs:10)+SignalR interceptor; LogTransition notifies DRAFTER-only on terminal (:960-980), NO approver-notify yet. Badge DTOs:PurchaseEvaluationListItemDto(PurchaseEvaluationDtos.cs:6)+DetailBundleDto(:201). Type A/B (PurchaseEvaluationType.cs:6-10) constrains pinnable ApplicableType only — ZERO type-conditional routing. ⚠️ "Từ chối" REMOVED S60 hard-guard:80-85(throws even Admin; only Duyệt/Trả lại). ⚠️ drafter-in-chain bypass:543auto-approves drafter's own step-1 levels on submit (interacts w/ value-finalize). Tag[pe-workflow-recon, value-threshold-hook, urgent-flag, fdc-feature-plan]. -
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-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).
-
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-06-17 S70 Harness-9 (em-main + Stage-B workflow) (47.0→24.1KB): L2 dark-matter recovery — 15 entries →
archive/2026-06.md(additive) + 3 oldest → NEWarchive/2026-05-q4.md(was git-onlyd2f52ba) +_INDEX.md(substring sha-keyed) +2026-0{5,6}.gist.md(distill-gen:1). Also Stage-A recon actor (wf_be952f3c-97f— substring-pointer-design decision). 0-byte-loss verified (em-main self-gate, reviewer no-StructuredOutput). Prev: S40 (35.7→~20KB q4+d2f52ba) · S34 q3 · S22 q1.