wal: flush 20260803T1053

This commit is contained in:
pqhuy1987
2026-08-03 10:53:28 +07:00
parent 9b8a5e3255
commit 4af3414ed1
31 changed files with 1318 additions and 30 deletions

View File

@ -0,0 +1,81 @@
# investigator-codebase/2026-05-q1 — gist
distill-gen: 1
source-verbatim: .claude/agent-memory/investigator-codebase/archive/2026-05-q1.md (11 entry [10 FIFO S21→S24 + 1 setup] → 7 cụm)
pointer-style: substring
## TỔNG-HỢP (3-5 ý)
- Refactor cardinality Mig 29 (1-row-per-Cấp → 1-row-per-NV) làm mọi phép `FirstOrDefault` theo khoá cũ sai âm-thầm: bug BE `PurchaseEvaluationFeatures.cs:765` ẩn TỪ NGÀY DEPLOY, chỉ lộ khi tick chọn-lọc per-NV; cùng bộ 7 cờ `Allow*` hỏng 2 kiểu KHÁC nhau ở 2 lớp (F4 FE đoản-mạch readOnly ⟂ BE lấy nhầm hàng) + 1 báo-động-giả (mental-model "Trả lại"≠"nháp").
- Ranh giới kiểu dữ-liệu = điểm gãy câm: record `TransitionPeBody` 3 field nuốt im-lặng 3 field FE gửi đủ — phép thử rẻ = đếm field body ↔ đếm field command; 1 lượt audit FE thuần BÁC được giả-thiết, chặn em-main sửa mù xuyên tầng.
- Pre-flight có bản-đồ đổi được quyết-định: Plan K neo 20+ điểm + GAP fe-user (phá luật "mirror 2 app"); Plan R 4 sqlcmd → chốt Option A; Plan F HUỶ ngay ở pre-flight vì Contract còn hoàn-toàn V1.
- Trí-nhớ/docs tự sai, phải re-ground: "3-panel" đo lại là 2-panel; Dev DB 2 user vs prod 33 user đều KHÔNG khớp spec; entry đếm-cumulative (B1-G) hết hạn nhanh — giá-trị còn lại chỉ là mốc thời-gian.
- Tailwind JIT đòi chuỗi class ĐẦY ĐỦ trong mảng (STEP_PALETTE/LEVEL_PALETTE liệt nguyên chuỗi); sidebar widen an-toàn `w-72 xl:w-80` @1280px, ngưỡng rủi-ro `w-80 xl:w-96`.
## Entries
**[cao] B1-A — chuỗi per-NV Allow* (F1-F4): 2 bug tách lớp FE/BE + 1 báo-động-giả** *(gộp 3 entry: S23 t2 L2 + S23 t3 + S23 t2 M0 — giữ 3 verdict phân-biệt)*
- VIỆC: 3 lượt audit UAT liên-tiếp trên cùng bộ 7 cờ `Allow*` per-slot (`ApprovalWorkflowLevels`) sau Mig 29/30/31: (i) admin tick F3+F4 mà menu "Duyệt" vẫn read-only; (ii) admin tick 7 cờ cho riêng 1 NV mà FE không hiện checkbox; (iii) rà 4 BE + 4 FE × 2 app xem F1-F4 có tự đẩy phiếu về "nháp".
- KẾT-LUẬN: (i) F3 ĐÚNG, F4 HỎNG — `PeDetailTabs.tsx:118` `itemsReadOnly` override được, nhưng `:245` truyền `readOnly={readOnly}` xuống BudgetAdjustSection và `:973` `canAdjust = !readOnly && (...)` đoản-mạch TRƯỚC khi F4 kịp xét ⇒ nút bút chì ẩn `:1030`; fix ~3-5 LOC 1 file: đảo vế `isAdmin || (!readOnly && isDrafter && isDrafterPhase) || isApproverChoDuyet` (land, thấy lại `:977`). (ii) BE lấy nhầm hàng: `PurchaseEvaluationFeatures.cs:765` `FirstOrDefault(l => l.Order == curLevelOrder)` — sau Mig 29 tách 1 Level-row PER ApproverUser thì `Order` trùng nhau trong cùng Cấp ⇒ EF trả row đầu theo PK; 2 token (admin + nv.test) gọi API ra CÙNG `currentLevelOptions`; fix 1 dòng `&& l.ApproverUserId == currentUser.UserId` + fallback row-đầu cho admin. (iii) KHÔNG bug: F1.OneLevel/OneStep/Assignee + F2 + F3 + F4 đều giữ Phase=ChoDuyet; chỉ F1.Drafter `:268-275` đặt `Phase=TraLai=98` — đúng thiết-kế.
- BÀI-HỌC: hai lớp cùng tính-năng hỏng theo hai kiểu khác nhau ⇒ verdict lớp này không suy ra lớp kia. Refactor cardinality làm mọi phép `FirstOrDefault` theo khoá cũ sai âm-thầm — phải rà toàn bộ site tra-cứu cùng khoá, không vá theo từng báo-cáo lỗi.
- BẤT-NGỜ: (a) bug BE CÓ TỪ NGÀY deploy Mig 29, không phải hồi-quy S23 — ẩn vì 13 user UAT cũ hầu hết ở row đầu của Cấp + admin tick toàn FALSE. (b) route `/inbox` đẩy DetailPage mobile mặc-định `readOnly=false`, còn Danh sách `?pendingMe=1` desktop hard-code `readOnly={true}` ⇒ CÙNG phiếu, 2 lối vào, 2 hành-vi. (c) lượt (iii) chốt "code đã đúng" — cái sai là mô-hình trong đầu người dùng (`TraLai=98``DangSoanThao=1`).
→ substring:"PurchaseEvaluationFeatures.cs:765"
**[cao] B1-B — Plan P: Controller record nuốt 3 field, ASP.NET im lặng**
- VIỆC: em-main giả-thiết Plan P chỉ đụng BE; investigator đối-chiếu FE × 2 app xác nhận trước khi sửa.
- KẾT-LUẬN: FE `PeWorkflowPanel.tsx:113-124` — nguồn ghi "SEND ĐÚNG **7 fields**" nhưng LIỆT KÊ 6 (TargetPhase + Decision + Comment + ReturnMode + ReturnTargetUserId + SkipToFinal, object literal không typed-service) — *lấy 6 theo danh-sách; số "7" của nguồn chưa xác-minh, tự nó là bằng-chứng sống cho bài "đếm field body so đếm field command là phép thử rẻ" (chính người viết verbatim cũng đếm nhầm — nhãn @S170 P2d, KHÔNG lặng-lẽ chọn một phía)*; BE `PurchaseEvaluationsController.cs:267` record `TransitionPeBody` chỉ 3 field ⇒ model-binder BỎ IM LẶNG 3 field còn lại. Verdict: Plan P = BE Controller ONLY ~6 LOC, không cần test mới (Mig 28/31 Domain test đã phủ handler).
- BÀI-HỌC: "FE gửi rồi" và "BE nhận được" là hai mệnh-đề khác nhau — điểm gãy ở ranh giới kiểu dữ-liệu (record thiếu field = mất dữ-liệu không lỗi, không log). Đếm field body ↔ đếm field command là phép thử rẻ.
- BẤT-NGỜ: một lượt audit FE thuần chặn được em-main sửa mù xuyên tầng — giá trị của spawn nằm ở chỗ nó BÁC BỎ giả-thiết, không phải xác nhận.
→ substring:"TransitionPeBody"
**[vừa] B1-C — Pre-flight Plan K (Mig 31): bản-đồ neo + GAP fe-user + lệch Dev/prod**
- VIỆC: rà trạng-thái trước khi chuyển `Users.AllowDrafterSkipToFinal``ApprovalWorkflowLevels.AllowApproverSkipToFinal` (đổi cả ngữ-nghĩa Drafter-Nháp → Approver-ChoDuyet).
- KẾT-LUẬN: neo đủ 20+ điểm (`User.cs:38` · `ApprovalWorkflow.cs:86-105` 6 cờ · nhánh Drafter/APPROVE service · DTO + bundle · UsersController · Designer panel 6 checkbox · UsersPage · PeDetailTabs admin/user — chi tiết file:line ở phụ-lục). Path migration thật = `Persistence/Migrations/` (KHÔNG phải `Migrations/`).
- BÀI-HỌC: GAP fe-user (không có UsersPage/Designer — quản-trị chỉ ở fe-admin) ⇒ "mirror 2 app" KHÔNG phải luật phổ-quát; xác định trước app nào có bề-mặt nào rồi mới khoanh phạm-vi UI.
- BẤT-NGỜ: Dev DB 2 user / 0 flagged, prod 33 user / 4 flagged — cả hai đều KHÔNG khớp spec (S22+2) ⇒ 4 user mất giá-trị khi DROP cột, chấp-nhận được vì ngữ-nghĩa mới làm nó vô-nghĩa.
→ substring:"User.cs:38"
**[vừa] B1-D — Pre-flight Plan R: xoá dữ-liệu prod, 3 phương-án**
- VIỆC: 4 truy-vấn sqlcmd đo trước khi xoá: 35 PE (28 active + 7 soft) · 17 V2 (15 chưa-ghim + 2 ghim) · 4 V1 (2 active + 2 inactive).
- KẾT-LUẬN: `PE.ApprovalWorkflowId` FK Restrict + `ApprovalWorkflow` kế-thừa `BaseEntity` (KHÔNG soft-delete) ⇒ buộc hard-DELETE; `LevelOpinion → ApprovalWorkflowLevel` Restrict chặn cascade; ước ~620 row con (446 PE-children + ~140 V2 + ~37 V1). Chốt Option A (hard-DELETE PE + bỏ V2 chưa-ghim + V1 inactive; GIỮ V2 ghim + V1 active).
- BÀI-HỌC: tiền-lệ Plan F nhắc lại đúng lúc — KHÔNG drop V1 active vì còn phiếu pin vào, drop là BE crash lúc khởi-động.
- BẤT-NGỜ: hạ-tầng ra điều-kiện: SQL Express không có COMPRESSION, `RESTORE VERIFYONLY` đòi sysadmin, index filtered (Mig 29) đòi `SET QUOTED_IDENTIFIER ON` mới chạy sqlcmd.
→ substring:"RESTORE VERIFYONLY"
**[vừa] B1-E — Plan AA: 5Q audit → 7 commit `a1a910f..ee0902a`** *(gộp 2 entry cùng Plan: Pre-A spawn + wrap 4 polish)*
- VIỆC: audit 5 câu hỏi mở đường (endpoint filter · menu seed · Designer · sidebar widen · enum) rồi em-main tự làm 4 vòng polish theo UAT.
- KẾT-LUẬN: `ApprovalWorkflowsV2Controller.cs:16-19` đã `[Authorize]` trần từ S18 (gotcha #44 vá vĩnh-viễn) ⇒ chỉ thêm param filter `IsUserSelectable`; menu seed `DbInitializer.cs:1429-1437` + quyền `:1541-1547` cho 7 role (Accounting KHÔNG có, admin cấp tay); Designer `ApprovalWorkflowsV2Page.tsx` 975 dòng, 13 field DTO / 7 cờ; sidebar `w-60 xl:w-72``w-72 xl:w-80` an-toàn @1280px (`w-80 xl:w-96` = ngưỡng rủi-ro); enum {DuyetNcc=1, DuyetNccPhuongAn=2, Contract=3}. 7 commit: `ee776d5` BE+Layout · `c667802` FE Page · `ac2c859` docs · 4 polish `da218f1`/`4d60598`/`fbbd361`/`ee0902a`.
- BÀI-HỌC: Tailwind JIT đòi chuỗi class ĐẦY ĐỦ trong mảng — `bg-${color}-100` bị purge ⇒ STEP_PALETTE/LEVEL_PALETTE phải liệt nguyên chuỗi (tái dùng cho mọi cây menu phân-cấp). Bỏ `truncate` KHÔNG đủ cho nhãn dài xuống dòng: đổi cả cấu-trúc NavLink (flex → block + inline) + `text-[12px] leading-snug`.
- BẤT-NGỜ: trí-nhớ dự-án SAI về chính UI của mình — memory khai "PE Workspace 3-panel", đo lại là 2-panel (`[260px_1fr] xl:[320px_1fr]`); chiến-lược Order=2 cho "Luồng duyệt" buộc UPDATE Order các leaf cũ, không thể chỉ INSERT-if-not-exists; `Contract=3` khai trong enum nhưng FE chưa nối.
→ substring:"STEP_PALETTE"
**[vừa] B1-F — 6 phát-hiện rời S22 (hạ-tầng/identity/script)**
- VIỆC: phiên em-main solo seed 20 user role-based lên prod (13→33 user); investigator ghi lại các va-chạm.
- KẾT-LUẬN: mật-khẩu Identity đòi ≥12 ký-tự (`User@123456` 11 ký-tự FAIL 400, `TestUser@2026` PASS); đổi tên user = 4 trường nguyên-tử (Email + NormalizedEmail + UserName + NormalizedUserName + FullName — gotcha #38); sqlcmd cần `SET QUOTED_IDENTIFIER ON`; login trả `accessToken`/`refreshToken`/`user` — KHÔNG có field `token` (ví-dụ Bash dùng `.token` sẽ fail); script PS 5.1 phải ASCII-only (gotcha #30).
- BÀI-HỌC: khuôn "admin tick opt-in TỪNG slot" củng-cố lần 2 (Mig 30 F4 lặp Mig 29 F1+F3) sau khi anh sửa lưng em-main: mặc-định là KHÔNG mở rộng quyền.
- BẤT-NGỜ: Plan F (drop V1) HUỶ ngay ở pre-flight vì sqlcmd lộ Contract còn HOÀN TOÀN V1 (chưa có cột `ApprovalWorkflowId`) + 4 PE V1-only + 19 PE lai ⇒ phạm-vi drop phải tính cả thực-thể liên-đới.
→ substring:"TestUser@2026"
**[thấp] B1-G — ảnh-chụp trạng-thái no-spawn (3 mốc)**
- VIỆC: 3 mốc không spawn: 2026-05-11 khởi-tạo agent · S21 t3-t5 · S22.
- KẾT-LUẬN: baseline 2026-05-11 = 44 gotcha + 14 memory + 6 skill + 27 mig + 81 test; S21 = 84 test / 29 mig / 45 gotcha / 19 memory; S22 = 104 test (+20) / 30 mig / 46 gotcha / ~146 endpoint (+3) / 33 user prod.
- BÀI-HỌC: entry loại "đếm cumulative" hết hạn nhanh — giá-trị còn lại chỉ là mốc thời-gian, không phải con số.
- BẤT-NGỜ: khai thẳng "Investigator KHÔNG spawn" mà vẫn ghi entry — diary vai từng ghi cả phiên nó không làm gì = nguồn nhiễu khi đếm coverage.
→ substring:"Awaiting first SendMessage"
## Phụ-lục token (coverage)
*(chép TRỌN token-list section investigator-codebase/2026-05-q1 của proposal curator — lưới coverage-diff)*
- **gotcha#:** `#45` (bug fix S21 t3) · `#38` (Identity rename atomic 4 field) · `#30` (PS 5.1 ASCII-only script discipline) · `#44` (class-level `[Authorize]` bare fixed permanent từ S18 2026-05-08)
- **surprise (nhãn nguyên-văn):** "Surprise: Inbox `/inbox` route (InboxPage.tsx) navigate sang `/purchase-evaluations/:id` (mobile DetailPage route, default readOnly=false)" · "**Surprise**: 'Trả lại' trong UI memory docs đôi khi gọi 'draft' colloquial — bro confuse 2 khái niệm" · "Surprise: bug PRESENT từ deploy Mig 29 (S21 t5 2026-05-13) — không phải regression S23" · "Surprises: (1) memory responsive breakpoint stale '3-panel' (2) Order strategy shift +1 (3) Contract=3 chưa wire FE" · "**Memory drift confirmed**: … S24 verify ACTUAL **2-panel**" · "**Plan AA color palette success** … Tailwind JIT yêu cầu full class strings array" · "**Drift Dev DB**: Total=2 user … NOT match 33-user prod seed" · "**Prod actual**: Total=33 / Flagged=4 (NOT 2 per S22+2 spec)" · "**GAP fe-user**: KHÔNG có UsersPage + ApprovalWorkflowsV2Page"
- **guard:** `PurchaseEvaluationDraftGuard.EnsureEditableForDetailsAsync` (`PurchaseEvaluationDetailFeatures.cs:42`, 8 callsite) · guard line 485 `ConflictException` non-admin + flag off (F2 skipToFinal) · `ApplyReturnModeAsync` gate per-slot per-mode `PurchaseEvaluationWorkflowService.cs:252-265` (Admin bypass :252) · entry guard `:760-762` (pointer null khi DaDuyet/TraLai/TuChoi) · BE budget-adjust `ConflictException` nếu `Allow=false` · FK **Restrict** `PE.ApprovalWorkflowId` + `LevelOpinion → ApprovalWorkflowLevel` · `SET QUOTED_IDENTIFIER ON` (filtered index Mig 29+) · class-level `[Authorize]` bare (gotcha #44) · Designer KHÔNG có `usePermission`/`PermissionGuard` (route guard là đủ)
- **root-cause:** ① "F4 = BROKEN at readOnly short-circuit" — `canAdjust = !readOnly && (…)` đoản-mạch TRƯỚC khi `isApproverChoDuyet` được xét (`PeDetailTabs.tsx:973`, nút ẩn `:1030`) ② "BE line 765 lookup semantic broken sau Mig 29" — `Level.Order` trùng nhau sau khi tách 1 row/ApproverUser ⇒ `FirstOrDefault(Order==X)` lấy row đầu theo PK (`PurchaseEvaluationFeatures.cs:765`) ③ Plan P — `TransitionPeBody` record 3 field ⇒ **ASP.NET silent DROP** 3 field còn lại (`PurchaseEvaluationsController.cs:267`) ④ Plan M0 — KHÔNG có root-cause code, "DISCONNECT bro mental model vs code reality post-Mig 28/29/30/31"
- **file:line:** `User.cs:38` · `ApprovalWorkflow.cs:86-105` · `ApprovalWorkflow.cs:45-50` · `PurchaseEvaluationWorkflowService.cs:119-161` (:125) · `~:393-525` · `:268-275` · `:285-312` · `:303-310` · `:314-333` · `:335-360` · `:364` · `:441-468` · `:483-524` (:485) · `:252-265` (:252) · `PurchaseEvaluationFeatures.cs:393-402` · `:272-329` (:283-290 / :291-323) · `:735-770` · `:765` · `:281-329` · `PurchaseEvaluationDtos.cs:86-92` · `PurchaseEvaluationDetailBundleDto` `:214` + `:217` · `UsersController` `:91-98` + `:105` · `UserFeatures.cs:332` · `PurchaseEvaluationDetailFeatures.cs:42` + `:42-99` (:49-51 / :54-94) · `PurchaseEvaluationsController.cs:267` · `ApprovalWorkflowsV2Controller.cs:16-19` · `DbInitializer.cs:1429-1437` + `:1541-1547` · `system/ApprovalWorkflowsV2Page.tsx:873` + `:853-933` + `:889-946` + `:892-948` (975 dòng) · `system/UsersPage.tsx:306-318` + `:365-372` + `:181-186` · `PeDetailTabs.tsx:113-118` + `:118` + `:224` + `:245` + `:957-973` + `:973` + `:977` + `:1030` + `:287-297`(admin) / `:294-304`(user) + `:109-115` + `:967-979` · `PeWorkflowPanel.tsx:51` + `:60-68` + `:113-124` + `:331-422` (:343-396, :343/:357/:371/:397) + `:425-442` (:425) · `fe-user/src/pages/pe/PurchaseEvaluationsListPage.tsx:256-261` · `fe-user Layout.tsx:325` + `fe-admin Layout.tsx:218` · `Persistence/Migrations/` (path thật) · mig `20260513160703_AddAllowApproverEditBudgetToLevels`
- **định-danh khác:** commit `a1a910f..ee0902a` (7): `ee776d5` · `c667802` · `ac2c859` · `da218f1` · `4d60598` · `fbbd361` · `ee0902a` · bug UAT prod `409a967` · phiếu `PE/2026/A/026` · WF `QT-DN-V2-001 v12` · user `nv.test@solutions.com.vn` · `TransitionAsync` param `skipToFinal` thứ 8 (position 47:47) · `w-60 xl:w-72``w-72 xl:w-80` (ngưỡng rủi-ro `w-80 xl:w-96`) · grid `[260px_1fr] xl:[320px_1fr]` · `STEP_PALETTE`/`LEVEL_PALETTE` 5 màu · token-cost 32k (Pre-A)
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `ApprovalWorkflowsV2Page.tsx:889` — Bro UAT login `nv.test@solutions.com.vn` vào menu eoffice "Duyệt NCC → Duyệt" phiếu PE/2026/A/026 (Phase=ChoDuyet, WF=QT-DN-V2-001 v12, ở B…
- `Mig 27` — 7 commits total `a1a910f..ee0902a`: BE+Layout (`ee776d5`) + FE Page (`c667802`) + Docs (`ac2c859`) + 4 polish iter UAT (`da218f1` px-2 + `4d60598` v1 …
- `PeDetailTabs.tsx:245` — Bug UAT prod 409a967: admin tick F3+F4 cho slot Approver, login actor user, vào menu "Duyệt" (`?pendingMe=1` cùng `PurchaseEvaluationsListPage` 3-…
- `PurchaseEvaluationFeatures.cs:272` — Bro UAT post-Plan L deploy 2026-05-15 ~02:00: "Hiện logic cũ là khi trả lại 1 cấp hoặc chỉ định hoặc edit là trạng thái draft -…
- `PurchaseEvaluationFeatures.cs:735` — Bug UAT prod 409a967: admin tick F3+F4 cho slot Approver, login actor user, vào menu "Duyệt" (`?pendingMe=1` cùng `PurchaseEvaluationsListPage` 3-…
- `PurchaseEvaluationWorkflowService.cs:285` — Bro UAT post-Plan L deploy 2026-05-15 ~02:00: "Hiện logic cũ là khi trả lại 1 cấp hoặc chỉ định hoặc edit là trạng thái draft -…

View File

@ -0,0 +1,60 @@
# investigator-codebase/2026-05-q2 — gist
distill-gen: 1
source-verbatim: .claude/agent-memory/investigator-codebase/archive/2026-05-q2.md (4 entry → 4 cụm, 0 cặp trùng — dedup = nén câu chữ)
pointer-style: substring
## TỔNG-HỢP (3-5 ý)
- Cùng triệu-chứng "Lịch sử thay đổi trống" nhưng 2 root-cause KHÁC trục: một bug GHI-thiếu (`ApplyReturnModeAsync` ZERO dòng log) ⟂ một bug nghi ĐỌC-thiếu (handler ĐÃ log, nghi FE lọc) — phải tách trục ghi/đọc trước khi đặt giả-thiết chung.
- Nối module thứ 2 vào schema duyệt dùng-chung: chi-phí thật nằm ở Service + terminal-state khác nhau (`DaPhatHanh``DaDuyet`), KHÔNG ở migration — Contract thừa-hưởng 7 cờ `Allow*` miễn phí; nhưng prod 7 HĐ 100% pin V1 (6 đang giữa quy-trình) ⇒ BẮT BUỘC cùng-tồn V1+V2, KHÔNG drop V1.
- Ghost-wire dạng schema lộ 2 lần: `EntityType.Workflow=5` khai trong enum nhưng 0 code ghi suốt 4+ migration; prod 0 row `ApplicableType=3` ⇒ nối xong không seed = dropdown rỗng.
- Thiếu component thư-viện KHÔNG chặn việc: fe-user GAP Accordion/Collapsible/Tree/Card/Badge → đường vòng `<details>/<summary>` native; quyết-định kiến-trúc đứng trên số ƯỚC (~50-200 dự-án) phải khai rõ (SSH prod fail auth).
- Khuôn hạ-tầng phải quy-chiếu đúng hình-thái người dùng: 1 người 5 dự-án ≠ team 5 dev ⇒ chốt Pattern C (MCP global localhost); Voyage AI 200M token/tháng free làm đổ luôn phép so chi-phí cũ.
## Entries
**[cao] B2-A — "Lịch sử thay đổi" trống: 1 bug hiển-thị + 1 bug thiếu-ghi**
- VIỆC: 2 báo lỗi UAT gộp 1 lượt audit 5Q: (1) chỉnh ngân-sách 2 lần → lịch-sử không hiện 2 dòng; (2) trả-lại mode "Người chỉ định" → lịch-sử không hiện.
- KẾT-LUẬN: Bug 1 KHÔNG phải lỗi ghi: `PurchaseEvaluationFeatures.cs:379-387` ĐÃ log changelog (EntityType=Header + Action=Update + diff) ⇒ nghi FE lọc bỏ Header, hoặc DB UAT cũ. Bug 2 thiếu ghi thật: `PurchaseEvaluationWorkflowService.cs:215-378 ApplyReturnModeAsync` ZERO dòng log — 4 nhánh chỉ đổi con-trỏ rồi trả summary; người gọi `TransitionAsync:100 LogTransitionAsync` chỉ log chuyển-phase. Query handler `ListPurchaseEvaluationChangelogsQueryHandler:1050-1064` không lọc gì ⇒ nếu mất thì mất ở FE. Đường vá: B2 trước (thêm `Changelogs.Add()` mỗi mode, 15-25 LOC 1 file), B1 sau (Option A enum Kind 30 LOC / B mở EntityType 20 LOC / C nới filter FE 10 LOC).
- BÀI-HỌC: cùng triệu-chứng nhưng một cái ghi-thiếu, một cái đọc-thiếu — tách hai trục trước khi đặt giả-thiết chung; `EntityType` quá thô (Header/Update dùng chung cho "sửa ngân sách" lẫn "sửa mục 2") nên không phân-biệt được ở tầng hiển-thị.
- BẤT-NGỜ: `EntityType.Workflow=5` tồn-tại trong enum nhưng KHÔNG dòng code nào ghi vào suốt 4+ migration — schema khai một khả-năng chưa bao giờ dùng (khai-mà-không-nối, dạng ghost-wire).
→ substring:"1050-1064"
**[cao] B2-B — Plan B Contract V2: 80% schema dùng lại, chốt cùng-tồn V1+V2**
- VIỆC: pre-flight 5Q + tái-chia chunk cho việc nối Contract vào bộ máy duyệt V2 (đang chỉ có PE).
- KẾT-LUẬN: `Contract.cs:8-58` 25 field, sẵn V1 (`WorkflowDefinitionId:22`, `CurrentWorkflowStepIndex:39`, `Phase:12`, `RejectedAtStepIndex:40`) thiếu `ApprovalWorkflowId` + `CurrentApprovalLevelOrder` ⇒ Mig 32 chỉ ADD COLUMN vì Mig 22 (`20260508053749_AddApprovalWorkflowsV2.cs:14-127`) đã tạo 3 bảng DÙNG CHUNG cho PE lẫn Contract; `ContractPhase.cs:14-28` đã có ChoDuyet=10 / TraLai=98 / TuChoi=99 soi gương PE; `ContractWorkflowService.cs:23-219` 220 LOC chỉ V1, sinh mã HĐ `:148-155` ở bước cuối → `DaPhatHanh` (terminal KHÁC PE `DaDuyet`). Prod: 7 HĐ 100% pin V1, 6 đang giữa quy-trình (Phase 2-8) ⇒ BẮT BUỘC cùng-tồn, KHÔNG drop V1. Mẫu clone = `ApproveV2Async` `PurchaseEvaluationWorkflowService.cs:446-634` (189 LOC: match actor → UPSERT opinion → advance → skipToFinal), điểm rẽ nhánh `:167-171`. Chia 6 chunk A1/A2/B/C/D/E theo tay-nghề (cookie-cutter → implementer, xuyên-tầng → em-main).
- BÀI-HỌC: khi schema đã tách sẵn dùng-chung, chi-phí thật của "nối module thứ 2" nằm ở Service + terminal-state, không ở migration — 7 cờ `Allow*` trên `ApprovalWorkflowLevels:86-114` Contract thừa-hưởng miễn phí.
- BẤT-NGỜ: (a) prod 0 row `ApplicableType=3` ⇒ nối xong mà không seed thì dropdown rỗng, phải gài seed vào chunk A2 *(chú-thích curator S170, không thuộc verbatim: đúng thứ sau này thành sự-cố Run #231 ở lane cicd)*; (b) `RejectedAtStepIndex` Contract `:40` đã chết mà chưa đánh dấu obsolete, lệch với PE `:50`; (c) 7 `ContractType` đối đầu 1 `ApplicableType=3` chung — có thể phải thêm `ContractTypes int[]` sau.
→ substring:"V1Pinned"
**[vừa] B2-C — Plan AG: PE List "đám rừng" → cây theo dự-án**
- VIỆC: anh gửi ảnh chụp phàn nàn danh sách phẳng; audit 5Q để chọn hướng.
- KẾT-LUẬN: `PurchaseEvaluationsListPage.tsx:133` 3-panel `lg:grid-cols-[340px_1fr_360px]`, thân phẳng `<ul><li>` `:199-252`, pageSize 50; `Master/Project.cs:5-14` thiếu MaxLength + thiếu navigation sang PE ⇒ sẵn cho bảng ProjectPackage sau; fe-user THIẾU Accordion/Collapsible/Tree/Card/Badge (chỉ có Button/Dialog/Input/Label/Select/Textarea). Chốt Approach C: Phase 1 FE-only ~160 LOC × 2 app, Phase 2 schema hoãn tới sau UAT.
- BÀI-HỌC: thiếu component thư-viện KHÔNG chặn việc — `<details>/<summary>` native + badge `<div>` là đường vòng đã kiểm-chứng ở Plan AA; chọn Phase-1-FE-only giữ quyền đổi ý về schema.
- BẤT-NGỜ: không đo được quy-mô dữ-liệu thật (SSH prod fail auth) nên phải ƯỚC ~50-200 dự-án × 5-15 phiếu — quyết-định kiến-trúc đứng trên con số ước, có khai rõ.
→ substring:"340px_1fr_360px"
**[vừa] B2-D — Plan AI: 4 case công-nghiệp cho hạ-tầng RAG 5 dự-án**
- VIỆC: nghiên-cứu 7Q cách chia-sẻ RAG cho 5 dự-án cùng máy.
- KẾT-LUẬN: 4 case — Cursor (Merkle tree + Turbopuffer + tái-dùng 92% similarity, khoá dẫn-xuất từ commit hash) · Cline Memory Bank (markdown + JIT + git-native) · Continue.dev Hub (YAML slug) · Sourcegraph Cody (phản-mẫu: BỎ embedding về Search API + graph IR cho 100K+ repo). 3 khuôn multi-tenant (pool / silo / hybrid base+delta). Chốt Pattern C — MCP global cấp user, 1 server localhost phục-vụ 5 dự-án, KHÔNG theo khuôn VPS-team.
- BÀI-HỌC: khuôn tốt nhất cho team 5 dev KHÔNG phải khuôn tốt nhất cho 1 người 5 dự-án — quy-chiếu về đúng hình-thái người dùng trước khi bê kiến-trúc về.
- BẤT-NGỜ: Voyage AI cho 200M token/tháng miễn phí, đủ cho cả 5 dev ⇒ toàn bộ bài toán chi-phí thu về còn tiền VPS ($15/tháng), làm đổ luôn phép so "$20 vs Qdrant cloud $50".
→ substring:"Turbopuffer"
## Phụ-lục token (coverage)
*(chép TRỌN token-list section investigator-codebase/2026-05-q2 của proposal curator — lưới coverage-diff)*
- **gotcha#:** **0 token** (file không dẫn số gotcha nào — chỉ dẫn chéo "pattern memory: S23 t3 lookup bug")
- **surprise (nhãn nguyên-văn):** "Surprise: `EntityType.Workflow=5` enum value design-only, unused 4+ mig history" · "**Surprise:** Voyage AI **200M tokens/month free tier** cover 5 devs comfortably" · "**Surprises:** (a) ApprovalWorkflows Prod ZERO ApplicableType=3 (b) `RejectedAtStepIndex` Contract line 40 DEPRECATED chưa marked obsolete drift với PE line 50 (c) ContractType 7 variants vs ApprovalWorkflow generic ApplicableType=3" · "Q1-DB state Prod SSH fail (auth), fallback code inspect" (×2 entry) · "**MISSING MaxLength + navigation tới PE**" · "shadcn fe-user component GAP **THIẾU Accordion/Collapsible/Tree/Card/Badge**"
- **guard:** `ConflictException` (Contract V2 chưa có nhánh — nêu dạng thiếu) · nhánh rẽ `if (evaluation.ApprovalWorkflowId is Guid awId) ApproveV2Async else ApproveV1Legacy` (`:167-171`) · "MUST coexist V1+V2, KHÔNG drop V1" (rào tiền-lệ Plan F) · Auth JWT RS256 + role scope + document-level ACL (thiết-kế RAG) · Risk LOW / Risk MEDIUM (ContractType discriminator)
- **root-cause:** ① Bug1 — "FE filter skip Header updates OR Schema gap (Header.Update collision: budget vs section 2 edit)" ② Bug2 — "Handler **intentionally skip logging** (no companion audit table like PurchaseEvaluationApprovals for return history)"
- **file:line:** `PurchaseEvaluationFeatures.cs:379-387` · `PurchaseEvaluationWorkflowService.cs:215-378` (ApplyReturnModeAsync) · `TransitionAsync:100` (LogTransitionAsync) · `PurchaseEvaluationChangelog.cs` (EntityType=Workflow(5)) · `ListPurchaseEvaluationChangelogsQueryHandler:1050-1064` · `Master/Project.cs:5-14` · `PurchaseEvaluationsListPage.tsx:133` + `:199-252` · `Contract.cs:8-58` (:12 Phase, :22 WorkflowDefinitionId, :39 CurrentWorkflowStepIndex, :40 RejectedAtStepIndex) · `ContractPhase.cs:14-28` · `ContractWorkflowService.cs:23-219` (:113-126 match Dept+PositionLevel, :148-155 gen mã HĐ) · `20260508053749_AddApprovalWorkflowsV2.cs:14-127` · `20260508072821_AddApprovalWorkflowIdToPurchaseEvaluation.cs:14-31` · `20260508074937_AddCurrentApprovalLevelOrderToPe.cs:14-17` · `PurchaseEvaluationWorkflowService.cs:446-634` (:484-495, :522-546, :561-602, :605-633) + `:167-171` · `ApprovalWorkflowLevels` entity `:86-114` · PE `RejectedAtStepIndex:50`
- **định-danh khác:** tag schema `[audit, phase-9, contract]` · `source_path: solution_erp/audit/investigator-plan-b-contract-v2-preflight-2026-05-22` · token-cost 28k/30k/40k/25K · prod 35 PE·17 V2·4 V1 (entry q1) vs "7 Contracts 100% V1Pinned" (entry này) · Mig 32 tên đề-xuất `AddProjectPackageTable` (Plan AG) vs Mig 32 thực-dụng cho Contract (Plan B) — **2 nghĩa của "Mig 32" trong CÙNG file** (xem `_lienfile-notes-batchB.md` #4)
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `Mig 21` — Em main spawn Investigator pre-flight Plan B priority HIGH. 5Q audit + Bonus chunk refinement. Tag schema: `[audit, phase-9, contract]`. **Findings:**
- `Mig 23` — Em main spawn Investigator pre-flight Plan B priority HIGH. 5Q audit + Bonus chunk refinement. Tag schema: `[audit, phase-9, contract]`. **Findings:**
- `Mig 24` — Em main spawn Investigator pre-flight Plan B priority HIGH. 5Q audit + Bonus chunk refinement. Tag schema: `[audit, phase-9, contract]`. **Findings:**
- `Mig 29` — Em main report 2 bug UAT: (1) Budget Adjust 2×click → "Lịch sử thay đổi" KHÔNG show 2 entry. (2) Return Assignee mode F1 click → "Lịch …
- `Mig 33` — Em main spawn Investigator pre-flight Plan B priority HIGH. 5Q audit + Bonus chunk refinement. Tag schema: `[audit, phase-9, contract]`. **Findings:**
- `Mig 34` — Em main spawn Investigator pre-flight Plan B priority HIGH. 5Q audit + Bonus chunk refinement. Tag schema: `[audit, phase-9, contract]`. **Findings:**

View File

@ -0,0 +1,43 @@
# investigator-codebase/2026-05-q3 — gist
distill-gen: 1
source-verbatim: .claude/agent-memory/investigator-codebase/archive/2026-05-q3.md (4 entry → 4 cụm, 2 cụm là housekeeping/bản-sao)
pointer-style: substring
## TỔNG-HỢP (3-5 ý)
- Diary khai việc ≠ bằng-chứng công-cụ: các entry cũ ghi "spawn Investigator" kèm chi-phí 28-40K token, nhưng trong S27 spawn agent type `investigator` NOT FOUND ⇒ hồ-sơ spawn của chính vai có thể sai — tạm tin + GẮN CỜ nghi-vấn (token loại Fidelity, giữ nguyên không tự phán).
- Pre-flight hạ-tầng PHẢI spawn: miss CRITICAL "Qdrant Web UI static thiếu" bị người dùng cuối (anh) phát-hiện qua trình duyệt thay vì lượt pre-flight — tuyên "X hoạt động" trong docs thì phải có ai đó đã CHẠM vào X.
- Bug 1 changelog chốt là lỗi bộ lọc FE (HistoryTab lọc cứng TraLai-only): lượt audit trước đoán đúng nhưng phải sang phiên sau mới cầm được bằng-chứng FE; CI run #215 FAIL (SQLite tie-break tái phát) → #216-#221 PASS liền chuỗi; gotcha #48 còn nợ docs.
- Quy-trình curate tự nhân bản sao: baseline 2026-05-11 xuất hiện trong 2 file archive khác nhau của cùng vai — entry housekeeping nên nằm ở header file, không chiếm slot dòng thời-gian.
## Entries
**[cao] B3-A — S27: hai bài học, một là tự nghi-ngờ hồ-sơ của chính mình**
- VIỆC: hồi-cố phiên S27 (em-main solo, không spawn) + rà lại các entry S20-S26 từng khai "spawn Investigator".
- KẾT-LUẬN: (i) miss CRITICAL: nếu có spawn pre-Plan A.3 thì đã bắt "Qdrant Web UI static thiếu" TRƯỚC khi em-main viết link `localhost:6333/dashboard` vào `rag-dashboard.ps1` — anh phát-hiện qua trình duyệt rồi báo ngược. (ii) Trong S27, spawn agent type `investigator` NOT FOUND ⇒ registry chưa từng nạp trong phiên đó; nhưng diary S23 t1 K0 / S25 t1 / S26 Plan AG / Plan AI đều ghi "spawn Investigator" kèm chi-phí 28-40K token. Hai khả-năng: (a) phiên trước nạp được (bản Claude Code cũ / định-dạng khác / hot-reload sau khi mở UI `/agents`), hoặc (b) em-main quy-sai, thực ra spawn `general-purpose`. Kết: tạm tin entry cũ nhưng GẮN CỜ nghi-vấn cho lần điều-tra sau.
- BÀI-HỌC: kiểm-tra phụ-thuộc-ngoài (binary ≠ web UI ở repo khác) là việc của lượt pre-flight, không phải của người dùng cuối; tuyên "X hoạt động" trong tài-liệu thì phải có ai đó đã CHẠM vào X.
- BẤT-NGỜ: vai tự phát-hiện hồ-sơ spawn của chính nó có thể sai — chi-phí token ghi trong diary không chứng-minh danh-tính agent đã chạy. Ca "diary khai việc mà không khai bằng-chứng công-cụ" — đúng loại rủi-ro Fidelity mà H2 phải soi.
→ substring:"rag-dashboard.ps1"
**[vừa] B3-B — S25 wrap: chốt root-cause 2 bug + chuỗi CI hồi phục**
- VIỆC: khép phiên S25 sau audit ~28K: xác nhận nguyên-nhân 2 bug rồi em-main tự làm 6 kế-hoạch tiếp.
- KẾT-LUẬN: Bug 1 = handler ĐÃ log (Header+Update) nhưng HistoryTab lọc cứng chỉ TraLai nên loại mất; Bug 2 = `ApplyReturnModeAsync` 4 mode không `Changelog.Add()`, chỉ người gọi log chuyển-phase. Đường vá: BE thêm log + FE nới filter + tách badge Decision. 7 commit `e23f51c..506cada`; run #215 FAIL (test Plan M vỡ vì SQLite tie-break tái phát) → #216-#221 PASS liền chuỗi; gotcha #48 còn nợ docs.
- BÀI-HỌC: "em-main solo từ Plan AC" vì chuỗi lý-luận xuyên tầng — implementer sẽ TỪ CHỐI, nên phân việc phải xét độ-xuyên-tầng trước khi xét khối-lượng.
- BẤT-NGỜ: bản này chốt Bug 1 là lỗi bộ lọc FE (giả-thiết ở lượt audit) — lượt audit trước đoán đúng nhưng chưa cầm được bằng-chứng FE, phải sang phiên sau mới xác nhận.
→ substring:"e23f51c"
**[thấp] B3-C + B3-D — housekeeping & bản sao** *(rút narrative — token sống ở phụ-lục)*
- VIỆC: ghi lại phiên curate S29-era + chép lại baseline khởi-tạo agent 2026-05-11.
- KẾT-LUẬN: curate: 10 entry S21→S24 chuyển sang `archive/2026-05-q1.md`, KEEP S25/S26/setup, MEMORY 34.9 KB → mục-tiêu 20-22 KB; baseline: 44 gotcha + 14 memory + 6 skill + 27 mig + 81 test, "No investigations performed yet".
- BÀI-HỌC: entry housekeeping nên nằm ở header file archive, không chiếm slot dòng thời-gian hoạt-động — chính nó gây trùng khi curate lần sau.
- BẤT-NGỜ: baseline 2026-05-11 xuất-hiện 2 lần trong 2 file archive khác nhau của cùng vai ⇒ bản sao nhân lên bởi chính quy-trình curate, không phải bởi việc thật (trùng 100% với entry đầu q1 — xem `_lienfile-notes-batchB.md` #1).
→ substring:"Awaiting first SendMessage"
## Phụ-lục token (coverage)
*(chép TRỌN token-list section investigator-codebase/2026-05-q3 của proposal curator — lưới coverage-diff)*
- **gotcha#:** `#48` (SQLite tie-break — "pending docs")
- **surprise (nhãn nguyên-văn):** không có nhãn "Surprise:" — token cùng loại: "Investigator pre-flight CRITICAL miss" · "spawn `investigator` agent type NOT FOUND trong session S27" · "**For now: trust prior memory entries but FLAG uncertainty cho future investigation.**" · "Possibility: (a) … hoặc (b) Em main misattribute - actually spawn `general-purpose` agent default" · "runs #215 FAIL Plan M tests SQLite tie-break re-emerge"
- **guard:** "**pre-flight infrastructure audit MUST spawn Investigator** trước khi em main claim 'X work' trong docs" · "cross-stack reasoning Implementer would REFUSE" · Rule §6.5 (trong header các file chị em; ở file này là "Patterns proven + Active workflow schemas foundation preserved untouched")
- **root-cause:** ① "Bug 1 Budget Adjust Handler ĐÃ log Changelog (Header+Update) nhưng FE HistoryTab filter strict TraLai-only loại" ② "Bug 2 ApplyReturnModeAsync 4 mode KHÔNG add Changelog.Add() — chỉ caller LogTransitionAsync log phase transition" ③ (meta) "registry chưa bao giờ load trong session này"
- **file:line:** **0 token file:line** trong toàn file (chỉ tên file `rag-dashboard.ps1` + URL `localhost:6333/dashboard`)
- **định-danh khác:** commit range `e23f51c..506cada` (7 commit) · CI run `#215` FAIL → `#216`-`#221` PASS · MEMORY 34.9 KB → 20-22 KB · baseline "44 gotchas + 14 memory entries + 6 skills + 27 mig + 81 test pass cumulative" (trùng q1) · token-cost ~28K

View File

@ -0,0 +1,54 @@
# investigator-codebase/2026-05-q4 — gist
distill-gen: 1
source-verbatim: .claude/agent-memory/investigator-codebase/archive/2026-05-q4.md (3 entry + 1 header-sự-kiện integrity → 4 cụm)
pointer-style: substring
## TỔNG-HỢP (3-5 ý)
- Trỏ-tới ≠ tồn-tại: MEMORY.md từng trỏ file archive này khi nó VẮNG trên đĩa — nội-dung chỉ còn trong git `d2f52ba`, dựng lại S69; lô S29-S37 vẫn khai là chỉ nằm trong git chưa kéo về đĩa. Chỉ mục trí-nhớ phải kiểm bằng phép CHẠM ĐĨA.
- Mỗi chỉ-số phải khai kèm CÁCH ĐẾM: DbSet=77 nhưng bảng SQL=84 (đếm `.ToTable()` trong ModelSnapshot, KHÔNG đếm DbSet — sót 7 Identity); docs sai CẢ HAI CHIỀU trong cùng lượt đo (FE pages 53<65 under-count, menu key 85>53 over-count); 3 số tin được nhất = mig / gotcha / git.
- Khuôn tốt nhất khi có module anh em đã chạy thật = đọc thẳng handler của nó (`ProposalFeatures.cs:403-486`) rồi liệt phần thiếu; nhưng spec tự mâu-thuẫn phải gắn cờ ngay: `ApprovalWorkflow.cs:72` khai Level KHÔNG OR-of-N (ngược trí-nhớ cũ), enum `ApplicableType` THIẾU Travel + mig `ExtendApplicableTypeForWorkflowApps` Up/Down RỖNG.
- Port khuôn phải soi hình-thái repo đích: BVAAU 7-agent là bản vẽ Phase 0 codebase RỖNG chưa thực-chiến — SOLUTION_ERP chọn split backend/frontend (thay domain/infra) cho hợp 2 FE.
## Entries
**[cao] B4-A — META: con-trỏ chết đã phục-hồi (file archive vắng đĩa, nội-dung git-only)**
- VIỆC: S69 phát-hiện `MEMORY.md` dẫn `archive/2026-05-q4.md` (kèm stub curate S40 + footer) mà đĩa không có file.
- KẾT-LUẬN: nội-dung thực nằm git-only ở `d2f52ba`; file được dựng lại, chứa 3 entry FIFO tháng 5 tràn ra; lô S29-S37 VẪN nằm nguyên trong git `d2f52ba` chứ chưa kéo về đĩa.
- BÀI-HỌC: trỏ-tới ≠ tồn-tại. Chỉ mục trí-nhớ phải được kiểm bằng phép chạm đĩa — đọc chỉ mục thấy "có mục" là cảm-giác đủ giả.
- BẤT-NGỜ: hai phần của cùng một lô archive kết-thúc ở HAI NƠI khác nhau (đĩa + git) mà chỉ mục chỉ khai một — còn một nửa dữ-liệu chỉ tra được nếu biết đúng commit (đầu mối cho lead — xem `_lienfile-notes-batchB.md` #6).
→ substring:"d2f52ba"
**[cao] B4-B — S40 STATE GROUNDING: đo lại 7 con số, docs sai 4**
- VIỆC: đối-chiếu 7 chỉ-số trạng-thái giữa docs và mã nguồn.
- KẾT-LUẬN: khớp: Migrations=40 (`.../Persistence/Migrations/*.cs`, cuối = `AddAttendances`) · Gotchas=55 (đếm `### N.`) · git clean. Lệch: DbSet=77 nhưng bảng SQL=84 (77 + 7 Identity — docs 84 ĐÚNG, phép đếm DbSet sai 7) · Endpoints=211 (docs ~223) · FE pages 36+29=65 (docs 53, đếm thiếu) · Menu key=53 hằng (docs 85, đếm thừa). 3 số tin được nhất = mig / gotcha / git.
- BÀI-HỌC: đếm bảng phải đếm `.ToTable()` trong ModelSnapshot, KHÔNG đếm DbSet — DbSet bỏ sót bảng Identity. Rộng hơn: mỗi chỉ-số cần khai kèm cách đếm, vì cùng một tên ("số bảng") có nhiều phép đếm cho nhiều số.
- BẤT-NGỜ: docs SAI theo cả hai chiều trong cùng một lượt đo (under-count FE pages 53<65, over-count menu key 85>53) ⇒ không thể suy "docs luôn lạc-hậu theo hướng thiếu".
→ substring:"AddAttendances"
**[cao] B4-C — P11-A WorkflowApps pre-flight: khuôn có sẵn, spec tự mâu-thuẫn 2 chỗ**
- VIỆC: pre-flight nối 4 module Leave/OT/Travel/Vehicle vào bộ máy duyệt.
- KẾT-LUẬN: schema pin sẵn từ Mig 39 (`Office/{Module}.cs` đều có `ApprovalWorkflowId?` + `CurrentApprovalLevelOrder?` + `WorkflowAppStatus` 5-state khớp ProposalStatus); code mới là SKELETON `Application/Office/WorkflowAppsFeatures.cs:11-15` (chỉ Create+List). Mẫu soi gương HOÀN HẢO = `ProposalFeatures.cs:403-486` (flatten `Steps.OrderBy(Order).SelectMany(Levels.OrderBy(Order))` → chỉ số cấp toàn-cục → `allLevels[CurrentApprovalLevelOrder-1]` → khớp actor → UPSERT opinion → tăng con-trỏ/DaDuyet). Thiếu: 4 bảng `{Module}LevelOpinion` (Mig 41+), 3 route/controller, 4 seed WF, FE Detail+Opinion.
- BÀI-HỌC: khi có module anh em đã chạy thật, khuôn tốt nhất là đọc thẳng handler của nó rồi liệt phần thiếu — rẻ hơn thiết-kế lại từ spec.
- BẤT-NGỜ: 2 mâu-thuẫn gắn cờ ngay: (a) `ApprovalWorkflow.cs:72` khai Level KHÔNG OR-of-N (1 ApproverUserId/Level) — NGƯỢC trí-nhớ cũ "OR-of-N", đòi verify lại; (b) enum `ApplicableType` THIẾU Travel (có Leave=5/OT=6/Vehicle=7/ItTicket=8) và mig `ExtendApplicableTypeForWorkflowApps` có Up/Down RỖNG (enum-only, không đụng DB).
→ substring:"ExtendApplicableTypeForWorkflowApps"
**[vừa] B4-D — S39: rút khuôn 7-agent từ BVAAU**
- VIỆC: đọc 8 file `.claude/agents/` của BVAAU (~22K) để tách trục vai.
- KẾT-LUẬN: 4→7 trục research(2)/implement(2)/quality(3); ranh giới: repo interface = domain, EF config = infra, test = test-specialist; cả 7 vai đều có 5 RAG MCP (+`search_code` BM25 +`store_memory` +`list_projects`). SOLUTION_ERP giữ 6 skill và chọn split backend/frontend (thay domain/infra) cho hợp 2 FE.
- BÀI-HỌC: port khuôn phải soi hình-thái repo đích — trục chia của dự-án nguồn không tự đúng ở đích.
- BẤT-NGỜ: BVAAU Phase 0 codebase RỖNG ⇒ toàn bộ khuôn 7-agent là bản vẽ chưa qua thực-chiến; "VIPIX guide" được dẫn nhưng không verify được vì file miss.
→ substring:"BVAAU"
## Phụ-lục token (coverage)
*(chép TRỌN token-list section investigator-codebase/2026-05-q4 của proposal curator — lưới coverage-diff)*
- **gotcha#:** **0 token dạng `#N`** — chỉ có phép đếm "Gotchas=**55** (`### N.`)"
- **surprise (nhãn ⚠️/đối-lập nguyên-văn):** "⚠️ `ApprovalWorkflow.cs:72` nói Level **KHÔNG OR-of-N** (1 ApproverUserId/Level) — KHÁC memory cũ 'OR-of-N', verify lại" · "⚠️ enum `ApplicableType` THIẾU Travel (có Leave=5/OT=6/Vehicle=7/ItTicket=8); `ExtendApplicableTypeForWorkflowApps` mig empty Up/Down (enum-only)" · "DbSet=77 nhưng **SQL tables=84** … DbSet count sai 7" · "Endpoints=**211** (docs ~223)" · "FE pages … =**65** (docs 53 under-count)" · "Menu keys=**53** const (docs 85 over-count)" · "BVAAU Phase 0 codebase RỖNG → aspirational template chưa battle-test" · "VIPIX guide claim KHÔNG verify được (file miss)" · header: "the file was absent on disk (content was git-only `d2f52ba`)"
- **guard:** "Boundary: repo interface=domain, EF config=infra, test=test-specialist" · "Verbatim — byte-exact, no reflow" (rào chép archive) · "3 số tin cậy nhất = mig/gotcha/git" (rào tin-cậy chỉ-số)
- **root-cause:** "tables phải count ToTable KHÔNG DbSet" (phép đếm sai nguồn ⇒ lệch 7 bảng Identity)
- **file:line:** `Application/Office/WorkflowAppsFeatures.cs:11-15` · `ProposalFeatures.cs:403-486` · `ApprovalWorkflow.cs:72` · `Office/{Module}.cs` (không số dòng) · `.../Persistence/Migrations/*.cs` · `.claude/agents/` (8 file ~22K)
- **định-danh khác:** commit `d2f52ba` (nguồn git-only) · Mig 39 · Mig 41+ (dự-kiến) · mig `AddAttendances` (mới nhất) · mig `ExtendApplicableTypeForWorkflowApps` (Up/Down rỗng) · `WorkflowAppStatus` 5-state ↔ `ProposalStatus` · tag `[cross-project, bvaau, port]` · `[state-grounding, docs-drift, s40]` · `[pre-flight, p11-a, workflowapps]`
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `Mig 38` — - **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ó `A…