Files
solution-erp/.claude/workflows/runs/2026-08-01-S169-bookend-open/sub-harvest-curator-sleep-batchB-S170.md
2026-08-03 09:57:05 +07:00

121 KiB
Raw Blame History

sub-harvest-curator — /sleep-recovery-memory-l2 Phase 1 (GATHER + DEDUP) — batch B/2 — S170

Vai: harvest-curator (H2) · PROPOSE-ONLY — file này là kênh run-trace B4/B6, KHÔNG phải ghi trí-nhớ. 0 chạm .claude/agent-memory/**. Batch B = investigator-codebase (4 verbatim) + reviewer (2) + test-specialist (1) + implementer-frontend (1) = 8 file gen-0. Phương-pháp: đóng-kín TỪNG file (chống cross-file bleed) — cụm-dedup + importance-tag + bản-nén-nháp 4-field SE + danh-sách token. Khung 4-field SE: VIỆC · KẾT-LUẬN(+commit/file:line) · BÀI-HỌC · BẤT-NGỜ — rút gọn câu chữ, KHÔNG bỏ field. Token-list = nguyên-liệu coverage-diff của lead: {surprise · guard · file:line · root-cause · gotcha#}. Thiếu = gate FAIL. 2 file đặc-biệt (theo lệnh lead): implementer-frontend/2026-H2 = nội-dung moved-not-cut đợt drain 08-01 ⇒ MỌI mục giữ danh-tính riêng, mục auto-refuse (dòng 78) tag cao · test-specialist/baseline-history = chuỗi số baseline là RUỘT ⇒ nén = gom giai-đoạn, giữ đủ mốc đầu/cuối mỗi giai-đoạn. Lưu ý đọc: mọi token dưới đây trích NGUYÊN VĂN từ verbatim gen-0; bản-nén là ĐỀ-XUẤT, chưa phải bản chốt.


investigator-codebase/2026-05-q1 (20.699 B — "Archive Recent Activity Q1 2026-05, S21-S24")

Đặc-điểm file: 10 entry verbose FIFO S21→S24 Plan AA, archive 2026-05-22 (nguồn MEMORY.md 34.9 KB → giữ <25KB). Header khai rõ §6.5 KHÔNG cắt narrative + danh-sách KEEP. Đây là file dày file:line nhất batch B (bản-đồ mã PE V2 thời Mig 28-31).

Cụm-dedup

# Cụm Thành-phần gộp Tag
B1-A Chuỗi bug per-NV Allow (F1-F4) — 3 lượt audit, 2 bug + 1 no-bug* S23 t2 spawn L2 (F4 FE readOnly short-circuit) + S23 t3 spawn (BE lookup :765 sai row) + S23 t2 spawn M0 (semantic ChoDuyet — KHÔNG bug, disconnect mental-model) cao
B1-B Cross-stack contract drop — Controller body ≠ Command record S23 t6 spawn Plan P FE-wire audit (xác nhận scope BE-only) cao
B1-C Pre-flight Plan K (Mig 31) — bản-đồ 20+ điểm neo + GAP fe-user + drift Dev/prod S23 t1 spawn K0 vừa
B1-D Pre-flight Plan R — cleanup destructive prod S23 t8 spawn (4 sqlcmd, FK Restrict, SQL Express limit, ~620 row cascade, 3 Option → chốt A) vừa
B1-E Plan AA (User Workflow Matrix + sidebar widen) — 5Q audit + wrap 7 commit S24 t1 spawn Pre-A + S24 t1-t4 em-main solo 4 polish (gộp: cùng 1 Plan, phần wrap chính là hậu-nghiệm của 5Q) vừa
B1-F Phát-hiện rời S22 (6 mục) — hạ-tầng/identity/script password ≥12 · rename atomic 4 field · accessTokentoken · PS 5.1 ASCII · Plan F drop V1 ABORTED · admin opt-in 2× vừa
B1-G Ảnh chụp trạng-thái no-spawn (setup + S21 + S22 cumulative count) 2026-05-11 setup baseline + S21 t3-t5 + phần đầu S22 thấp

Ghi-chú dedup: B1-A gộp 3 entry rời vì cùng một mặt-cắt tính-năng (7 cờ Allow* per-slot sau Mig 29/30/31); giữ nguyên 3 verdict phân-biệt (BUG FE · BUG BE · NO-BUG) — gộp verdict là mất danh-tính. B1-E gộp 2 entry cùng Plan AA. B1-G là ảnh-chụp số đếm, đã bị các mốc sau ghi đè ⇒ tag thấp.

Bản-nén-nháp 4-field

[cao] B1-A — chuỗi per-NV Allow (F1-F4): 2 bug tách lớp FE/BE + 1 báo-động-giả*

  • 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 nào; (iii) rà 4 BE + 4 FE × 2 app xem F1/F2/F3/F4 có tự đẩy phiếu về "nháp" không.
  • KẾT-LUẬN: (i) F3 ĐÚNG, F4 HỎNGPeDetailTabs.tsx:118 itemsReadOnly = readOnly && !approverEditMode override được, còn :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ế thành isAdmin || (!readOnly && isDrafter && isDrafterPhase) || isApproverChoDuyet (đã land, thấy lại ở :977). (ii) BE lấy nhầm hàng: PurchaseEvaluationFeatures.cs:765 Levels.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 ⇒ handler mù currentUser; fix 1 dòng && l.ApproverUserId == currentUser.UserId + fallback row-đầu cho admin. (iii) KHÔNG bug: F1.OneLevel :285-312 / F1.OneStep :314-333 / F1.Assignee :335-360 / F2 :483-524 / F3 PurchaseEvaluationDetailFeatures.cs:42-99 / F4 PurchaseEvaluationFeatures.cs:272-329 đề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 một tính-năng hỏng theo hai kiểu khác nhau ⇒ đọc verdict lớp này không suy ra lớp kia. Và: refactor cardinality (1-row-per-Cấp → 1-row-per-NV) làm mọi phép FirstOrDefault theo khoá cũ thành 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ó ẩn suốt vì 13 user UAT cũ hầu hết nằm ở row đầu của Cấp và admin tick toàn FALSE ⇒ mọi row hành-xử giống nhau; chỉ lộ khi tick CHỌN LỌC per-NV. (b) route /inbox (InboxPage.tsx) đẩy sang DetailPage mobile mặc-định readOnly=false, chỉ màn Danh sách ?pendingMe=1 desktop 3-panel mới 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: chữ "Trả lại" bị gọi lẫn thành "draft", trong khi TraLai=98DangSoanThao=1.

[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 api.post(/transitions, body) gửi ĐỦ (TargetPhase + Decision + Comment + ReturnMode + ReturnTargetUserId + SkipToFinal, object literal không typed-service); 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 nằm ở ranh giới kiểu dữ-liệu (record thiếu field = mất dữ-liệu không lỗi, không log). Đối-chiếu đế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 nó.

[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.AllowDrafterSkipToFinalApprovalWorkflowLevels.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 PurchaseEvaluationWorkflowService.cs:119-161 (:125) · nhánh APPROVE ~:393-525 · PurchaseEvaluationFeatures.cs:393-402 · PurchaseEvaluationDtos.cs:86-92 · bundle DTO :214/:217 · UsersController :91-98/:105 · UserFeatures.cs:332 · Designer :873 nhãn slot + :853-933 panel 6 checkbox · UsersPage.tsx:306-318/:365-372/:181-186 · PeDetailTabs :287-297 admin / :294-304 user). 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; phải 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 có 2 user / 0 flagged, prod có 33 user / 4 flagged — cả hai đều KHÔNG khớp con số spec (S22+2) ⇒ 4 user sẽ 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.

[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 được 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, và index filtered (từ Mig 29) đòi SET QUOTED_IDENTIFIER ON mới chạy được sqlcmd.

[vừa] B1-E — Plan AA: 5Q audit → 7 commit a1a910f..ee0902a

  • 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ỉ cần 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ờ, nhãn VI :892-948; sidebar Layout.tsx:325 (user) + :218 (admin) w-60 xl:w-72w-72 xl:w-80 an-toàn @1280px, w-80 xl:w-96 là ngưỡng rủi-ro; enum ApprovalWorkflow.cs:45-50 {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ảngbg-${color}-100 bị purge ⇒ bảng màu STEP_PALETTE/LEVEL_PALETTE phải liệt nguyên chuỗi (tái dùng được cho mọi cây menu phân-cấp). Bỏ truncate KHÔNG đủ để nhãn dài xuống dòng: phải đổ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 — feedback_responsive_laptop_breakpoint khai "PE Workspace 3-panel", đo lại là 2-panel ([260px_1fr] xl:[320px_1fr]); và chiến-lược Order=2 cho "Luồng duyệt" buộc phải 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.

[vừa] B1-F — 6 phát-hiện rời S22 (hạ-tầng/identity/script)

  • VIỆC: phiên em-main solo, investigator ghi lại các va-chạm khi seed 20 user role-based lên prod (13→33 user).
  • 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 là 4 trường nguyên-tử (Email + NormalizedEmail + UserName + NormalizedUserName + FullName — xác nhận gotcha #38), sqlcmd cần SET QUOTED_IDENTIFIER ON; login trả accessToken/refreshToken/userKHÔNG có field token (ví-dụ Bash trong spec 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 củng-cố lần 2 (Mig 30 F4 lặp lại Mig 29 F1+F3) sau khi anh sửa lưng em-main: mặc-định là KHÔNG mở rộng quyền, muốn mở thì tick.
  • BẤT-NGỜ: Plan F (drop V1) bị HUỶ ngay ở pre-flight vì sqlcmd lộ ra Contract còn HOÀN TOÀN V1 (chưa có cột ApprovalWorkflowId) + 4 PE V1-only + 19 PE lai V1+V2 ⇒ phạm-vi drop phải tính cả thực-thể liên-đới, không chỉ PE.

[thấp] B1-G — ảnh-chụp trạng-thái no-spawn

  • 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 — nghĩa là diary vai này từng ghi cả những phiên nó không làm gì, đây là nguồn nhiễu khi đếm coverage sau này.

Token-list (verbatim, investigator-codebase/2026-05-q1)

  • 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-72w-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)

investigator-codebase/2026-05-q2 (9.062 B — "Archive Recent Activity Q2 2026-05, S25-S29 verbose audit")

Đặc-điểm file: 4 entry verbose, archive 2026-05-26 S32 (MEMORY.md 27.7KB → ~22KB). Đây là file DUY NHẤT batch B có 0 token gotcha# — mọi phát-hiện đều dạng schema-gap / nghiên-cứu ngoài repo.

Cụm-dedup

# Cụm Thành-phần gộp Tag
B2-A 2 bug UAT "Lịch sử thay đổi" trống — 2 root-cause KHÁC NHAU S25 t1 spawn 5Q (Bug1 Budget-Adjust: code ĐÚNG, nghi FE filter · Bug2 Return-mode: handler KHÔNG log) cao
B2-B Pre-flight Plan B Contract V2 — 5Q + tái-chia 6 chunk 2026-05-22 (Q1-Q5 + Bonus re-chunk + 3 surprise) cao
B2-C Plan AG — PE List cây thư-mục kiểu Outlook S26 spawn 5Q (scale · Project entity · 3-panel · GAP shadcn · đặt tên Mig 32) vừa
B2-D Plan AI — nghiên-cứu phân-phối RAG, 4 case công-nghiệp S26 spawn 7Q (Cursor/Cline/Continue/Sourcegraph + multi-tenant + chi-phí + chốt Pattern C) vừa

Ghi-chú dedup: 4 entry = 4 chủ-đề rời, KHÔNG có cặp trùng ⇒ dedup ở đây = giữ nguyên 4, chỉ nén câu chữ.

Bản-nén-nháp 4-field

[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 theo 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, không log tác-dụng-phụ của mode. 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 thêm enum Kind 30 LOC / B mở EntityType 20 LOC / C nới filter FE 10 LOC).
  • BÀI-HỌC: cùng một triệu-chứng "không thấy lịch-sử" nhưng một cái là ghi-thiếu, một cái là đọc-thiếu — phải tách hai trục ghi/đọc trước khi đặt giả-thiết chung; và 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).

[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 cả 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 cái đ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 :484-495 · UPSERT opinion :522-546 · advance :605-633 · skipToFinal :561-602), đ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 việc "nối module thứ 2" nằm ở Service + terminal-state khác nhau, không ở migration — 7 cờ Allow* trên ApprovalWorkflowLevels:86-114 Contract thừa-hưởng miễn phí.
  • BẤT-NGỜ: (a) prod có 0 row ApplicableType=3 ⇒ nối xong mà không seed thì dropdown rỗng, phải gài seed vào chunk A2 (đúng thứ sau này thành sự-cố Run #231 ở lane cicd); (b) RejectedAtStepIndex bên 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.

[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 sàng 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 được việc — <details>/<summary> native + badge <div> là đường vòng đã kiểm-chứng ở Plan AA; chọn Phase-1-FE-only giữ được 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õ.

[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 — phải 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".

Token-list (verbatim, investigator-codebase/2026-05-q2)

  • 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

investigator-codebase/2026-05-q3 (3.089 B — "Archive Q3 2026-05, S34 curate")

Đặc-điểm file: 4 mục ngắn, trong đó 2 mục là bản sao/ghi-chú housekeeping. Mục S27 chứa flag tự-nghi-ngờ về chính lịch-sử spawn của vai — đắt nhất file, và là loại token mà harvest-curator phải giữ.

Cụm-dedup

# Cụm Thành-phần gộp Tag
B3-A S27 — 2 bài: (i) pre-flight hạ-tầng BẮT BUỘC spawn (ii) NGHI-VẤN lịch-sử spawn của chính vai 2026-05-22 S27 wrap retrospective (2 đoạn) cao
B3-B S25 wrap Plan AB — bản rút gọn của entry S25 ở q2 + kết-quả CI 2026-05-19 (root-cause 2 bug + 7 commit + run #215 FAIL → #216-#221 PASS + gotcha #48) vừa
B3-C Housekeeping: bản-ghi phiên curate S29-era 2026-05-22 (archive 10 entry → q1, 34.9KB → ~20-22KB) thấp
B3-D Trùng nguyên-văn: setup baseline 2026-05-11 y-hệt đoạn mở đầu file q1 thấp

Ghi-chú dedup: B3-D trùng 100% với entry đầu của q1 (cùng 44 gotcha/14 memory/6 skill/27 mig/81 test) — đề-xuất nén còn 1 dòng trỏ, KHÔNG chép lại. B3-B là bản NGẮN của cùng sự-kiện với entry S25 ở q2 (bản q2 dài hơn, có 5Q); KHÔNG gộp xuyên file theo lệnh đóng-kín, chỉ FLAG cho lead ở §Ghi-chú liên-file cuối bài.

Bản-nén-nháp 4-field

[cao] B3-A — S27: hai bài học, một trong đó 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 được "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 phiên S27, spawn agent kiểu 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 các 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) phải là việc của lượt pre-flight, không phải việc của người dùng cuối; và khi 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 vào diary không chứng-minh được danh-tính agent đã chạy. Đây là 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.

[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) — tức 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.

[thấp] B3-C + B3-D — housekeeping & bản sao

  • 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 (đúng chỗ), không nên chiếm slot trong 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 một vai ⇒ bản sao được nhân lên bởi chính quy-trình curate, không phải bởi việc thật.

Token-list (verbatim, investigator-codebase/2026-05-q3)

  • 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

investigator-codebase/2026-05-q4 (2.770 B — "Archive Q4 2026-05, S39-S40 era")

Đặc-điểm file: 3 bullet cô-đọng (đã ở dạng gần-nén sẵn) + header mang một sự-kiện integrity: file này từng được MEMORY.md trỏ tới nhưng VẮNG trên đĩa, nội-dung chỉ còn trong git d2f52ba, dựng lại ở S69. Đây là ca con-trỏ-chết đã được vá — giữ nguyên, đừng nén mất.

Cụm-dedup

# Cụm Thành-phần gộp Tag
B4-A META: con-trỏ chết đã phục-hồi (file archive vắng đĩa, nội-dung git-only d2f52ba) header file (dựng S69) cao
B4-B S40 STATE GROUNDING — 7 metric, 4/7 lệch docs 2026-05-29 cao
B4-C P11-A WorkflowApps pre-flight — mirror Proposal + 2 mâu-thuẫn spec 2026-05-30 cao
B4-D S39 BVAAU 7-agent extract (port khuôn từ dự-án khác) 2026-05-29 vừa

Ghi-chú dedup: 3 bullet rời chủ-đề, không trùng nhau; cụm B4-A tách ra từ header vì nó là sự-kiện về chính hồ-sơ, không phải nội-dung việc.

Bản-nén-nháp 4-field

[cao] B4-A — con-trỏ chết: MEMORY trỏ file không tồn-tại

  • 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, còn 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, vì đọ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 — nghĩa là còn một nửa dữ-liệu chỉ tra được nếu biết đúng commit.

[cao] B4-B — S40: đ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 cái tên ("số bảng") có nhiều phép đếm cho ra 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".

[cao] B4-C — P11-A: khuôn có sẵn, nhưng 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); phần 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 (làm phẳng 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). Còn 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 được gắn cờ ngay: (a) ApprovalWorkflow.cs:72 khai Level KHÔNG OR-of-N (1 ApproverUserId/Level) — NGƯỢC với 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à migration ExtendApplicableTypeForWorkflowAppsUp/Down RỖNG (enum-only, không đụng DB).

[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; và "VIPIX guide" được dẫn nhưng không verify được vì file miss.

Token-list (verbatim, investigator-codebase/2026-05-q4)

  • 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]

reviewer/2026-05-q1 (18.707 B — "Archive Recent Activity Q1 2026-05, S21-S24")

Đặc-điểm file: 7 entry, archive 2026-05-22 (MEMORY.md 34.5 KB). Ba entry là self-review em-main không spawn (chế-độ UAT), bốn entry là review thật. Xuyên suốt có 1 sợi chỉ: "Smart Friend guard active" lặp ở cuối mọi entry — dấu hiệu vai tự nhắc chuẩn không được hạ.

Cụm-dedup

# Cụm Thành-phần gộp Tag
B5-A Plan K1+K2 — PASS kèm 2 Major: "khuôn sentinel tạm" đẻ endpoint zombie S23 t1 spawn cao
B5-B Plan N→U (8 kế-hoạch self-review) — 3 phản-mẫu, gồm "vá theo báo-cáo lỗi" và vòng lặp re-seed S23 t4-t11 cao
B5-C Plan M — PASS, phạm-vi hẹp có chủ-đích + lệch chữ xuyên module S23 t3 spawn vừa
B5-D Plan AA — pre-commit PASS 0 issue + hậu-nghiệm ROI spawn S24 pre-commit spawn + S24 post-wrap (gộp: cùng Plan, phần sau là kết-luận chi-phí của phần trước) vừa
B5-E Chế-độ UAT không spawn (S21/S22) + 3 phản-mẫu + gotcha #47 treo S21 t3-t5 + S22 vừa

Ghi-chú dedup: B5-D gộp 2 entry cùng Plan AA (một trước commit, một sau khi anh UAT xong 4 vòng polish) vì kết-luận thật nằm ở chỗ nối hai phần: spawn 1 lần cho chunk nặng thì lãi, spawn cho polish thì lỗ.

Bản-nén-nháp 4-field

[cao] B5-A — Plan K: PASS nhưng để lại endpoint xác sống

  • VIỆC: review đối-kháng trước K3 cho Mig 31 (đổi chỗ + đổi nghĩa cờ skip-to-final) — 11 file BE, +4093/83 LOC (Designer migration chiếm 3938 dòng), 3 commit eb106f2..56868bf..db66253..364aef6.
  • KẾT-LUẬN: PASS với 2 Major + 2 Minor. Wire đúng: nhánh Approver F2 đặt ở :477 — SAU khi UPSERT opinion :441-468, TRƯỚC khi đẩy con-trỏ :502 ⇒ chữ ký người duyệt được ghi trước khi nhảy terminal; guard skipToFinal cho V1 đặt ở người gọi (:147-149 trong TransitionAsync) nên V1 giữ nguyên chữ ký cũ. sqlcmd Dev xác nhận 7 cột Allow* + Users.AllowDrafterSkipToFinal đã drop (count=0), snapshot regen sạch, HasDefaultValue(false) có nối, Mig 31 Up() đảo tay ADD→DROP đúng khuôn. Major-1: endpoint zombie PATCH /api/users/{id}/allow-skip-final còn sống nhưng thân rỗng (Task.CompletedTask) — admin tick, BE nuốt im ⇒ đòi chunk K5 dọn. Major-2: comment cũ ApprovalWorkflow.cs:78 vẫn nói cờ "đã move sang Users…" trong khi :107-113 là prop mới. Minor: PurchaseEvaluationFeatures.cs:401 còn chữ "Drafter"; ApprovalWorkflowConfiguration.cs:22 chưa ghi Mig 31.
  • BÀI-HỌC: đặt sentinel tạm kèm lời hứa "chunk sau dọn" là bẫy — chunk sau đổi phạm-vi thì sentinel hoá xác sống. Muốn dùng thì phải có chunk dọn được đặt tên trước, đứng trước cả chunk sửa test.
  • BẤT-NGỜ: cùng một lượt review vừa xác nhận wire ĐÚNG ở chỗ khó (thứ tự UPSERT-trước-skip) vừa bắt được lỗi ở chỗ dễ (endpoint quên xoá) — rủi-ro không tập trung ở phần phức-tạp mà ở phần bị coi là đã xong.

[cao] B5-B — Plan N→U: 8 kế-hoạch, 3 phản-mẫu đắt

  • VIỆC: em-main tự review liên-tiếp 8 kế-hoạch (N sửa lookup per-NV, O cascade 4 site, P Controller body, Q CSS, R+S+T5 xoá prod, T cờ DemoSeed, U sidebar).
  • KẾT-LUẬN: Plan N vá PurchaseEvaluationFeatures.cs:765; Plan O phát-hiện 4 site còn lại cùng khuôn (EnsureCanRejectV2Async:201 · ApplyReturnModeAsync:248 · EnsureEditableForDetailsAsync:72 · AdjustBudgetCommandHandler:311) + 3 test hồi-quy PurchaseEvaluationPerNvLookupRegressionTests.cs; Plan P thêm 3 field vào TransitionPeBody:267đây là gốc của 2 ngày lỗi prod F1+F2; Plan R+S+T5 xoá ~720 row prod mà BE vẫn khoẻ nhờ giữ V1 active; Plan T thêm cờ DemoSeed:Disabled tắt 5 hàm seed demo. Tổng: 111 test (+7 so với 104), 31 mig, 47 gotcha.
  • BÀI-HỌC: vá theo báo-cáo lỗi = vá 1/5; refactor đổi hình-dạng khoá thì phải liệt kê TOÀN BỘ site tra-cứu cùng khuôn rồi vá một lượt. Và bản ghi record ở Controller phải soi gương record Command — lệch field là mất dữ-liệu câm.
  • BẤT-NGỜ: dọn prod xong thì IIS recycle làm seeder chạy lại ⇒ dữ-liệu demo mọc lại thành vòng lặp; gốc không nằm ở lệnh xoá mà ở chỗ seeder không có công-tắc — Plan T mới thật sự đóng.

[vừa] B5-C — Plan M: PASS, hẹp có chủ-đích, còn lệch chữ xuyên module

  • VIỆC: review trước push 3 commit c2afef2..508b17a..4dd6f9c — sửa ca biên "đang ở Bước 1" + đổi nhãn hiển-thị Phase=TraLai.
  • KẾT-LUẬN: PASS. Hot path Service.cs:287-333: thay "đặt TraLai + xoá con-trỏ + return" bằng "đặt con-trỏ (0,1) + summary 'không lùi được' → rơi tiếp xuống reset SLA :364giữ ChoDuyet"; người gọi :94-100 truyền Phase mới vào log ⇒ ContextNote khớp assert của test. Drafter mode :268-275 giữ nguyên nghĩa; Assignee :335-360 vẫn ném nếu không khớp; F2/F3/F4 KHÔNG đụng. Build 0 err 2 warn (DocxRenderer có sẵn), test 106/106 (58 Domain + 48 Infra, +2), npm build 2 app sạch TS, không --no-verify. 2 test mới phủ đúng 2 ca biên.
  • BÀI-HỌC: phân-biệt động-từ hành-động ("Trả lại" trên nút) với nhãn kết-quả ("Cần chỉnh sửa lại" cho Phase=98) — đổi tên phải chọn đúng lớp, đổi nhầm lớp thì UI mâu-thuẫn.
  • BẤT-NGỜ: phạm-vi hẹp để lại 8 chỗ chữ "Trả lại" chưa đổi và 2 module anh em (contracts.ts:29, budget.ts:20) vẫn 98:'Trả lại' ⇒ nếu deploy chung thì người dùng thấy 3 module gọi khác nhau cho cùng một trạng-thái.

[vừa] B5-D — Plan AA: PASS sạch + bài toán ROI của việc spawn

  • VIỆC: review trước commit chunk A (BE+Layout) + chunk B (FE trang ma-trận, implementer làm), rồi ghi nhận 4 vòng polish sau đó KHÔNG spawn.
  • KẾT-LUẬN: PASS 0 critical / 0 major / 0 minor; wire xuyên suốt Controller :21-26 → Handler :114-117 (request.IsUserSelectable is bool ius → Where(...)) → FE :29-33 gọi thật (không mock) → route App.tsx:38 + Layout.tsx resolvePath:81-86; 0 migration (cột có từ Mig 25); seed menu idempotent (PK trên Key ⇒ ToDictionary an toàn); [Authorize] trần :18 được giữ (gotcha #44); quyền 7 role qua SeedPurchaseEvaluationPermissionDefaultsAsync:1573. Ghi chú rủi-ro thấp: người không phải admin có thể truyền isUserSelectable=false để xem workflow chưa ghim — dữ-liệu không nhạy-cảm nên không chặn commit. Plan AA tổng ~620 LOC / 7 commit a1a910f..ee0902a, spawn 1 lần ~25K.
  • BÀI-HỌC: gom lại review một lượt cho chunk nặng (wire BE / migration / xuyên tầng), bỏ qua với việc polish <30 phút (CSS/màu/layout) — 4 lần spawn polish ≈ 100K token đổi lấy gần như không phát-hiện.
  • BẤT-NGỜ: "Smart Friend guard" được khai là vẫn bật ngay trong lượt PASS-0-issue, kèm 3 ghi-chú rủi-ro treo lại — tức PASS ở đây không có nghĩa "không còn gì", mà là "không có gì đáng chặn"; và bất đối-xứng cấu-trúc giữa 2 app (fe-user có StaticLeaf "Hộp thư", fe-admin không) được chấp-nhận thay vì ép mirror.

[vừa] B5-E — chế-độ UAT không spawn (S21/S22): 3 phản-mẫu + 1 gotcha treo

  • VIỆC: 2 phiên em-main tự review theo feedback_uat_skip_verify (bỏ dotnet test mỗi chunk nhưng vẫn build + npm build 2 app); 11-12 commit cộng dồn.
  • KẾT-LUẬN: 4 điểm được xác nhận: phân quyền V2 siết theo actor.UserId (bỏ mệnh-đề lỏng || ApprovalWorkflowId != null); helper EnsureCanRejectV2Async chặn request giả mạo PATCH /transitions decision=Reject (phòng-thủ 2 lớp FE+BE); handler chỉnh ngân-sách 3 tầng (Drafter Nháp/Trả-lại · Approver ChoDuyet · Admin) rồi siết lại theo cờ AllowApproverEditBudget; mật-khẩu ≥12 ký-tự. Trạng-thái: 104 test / 30 mig / 46 gotcha / ~146 endpoint / 33 user.
  • BÀI-HỌC: 3 phản-mẫu — (a) mở rộng quyền theo mặc-định (S22+4) phải sửa thành admin-tick-từng-slot (S22+5); (b) giả-định field tồn-tại khi render (PeDetailBundle.changelogs) → TS2339, phải xem kiểu trước; (c) script PS 5.1 có dấu tiếng Việt → parser lỗi (gotcha #30).
  • BẤT-NGỜ: vai reviewer tự khai không spawn trong 2 phiên liên-tiếp mà vẫn viết entry đầy-đủ, và ghi nhận CICD Monitor đứng thay vai reviewer sau deploy; đồng thời phát-hiện gotcha #47 (paths-ignore bỏ sót agent-memory) nhưng để treo chờ anh quyết, không tự thêm vào docs.

Token-list (verbatim, reviewer/2026-05-q1)

  • gotcha#: #45 (fix + 3 test hồi-quy, test-before §7) · #44 (class-level [Authorize] bare — "protect", nhắc 2 lần) · #47 ("paths-ignore agent-memory gap … pending bro decide" — New gotcha discovered) · #30 (PS 5.1 Vietnamese diacritics)
  • surprise (nhãn nguyên-văn dạng Anti-pattern/Pattern caught/Discovery): "Pattern caught: 'Transient sentinel pattern' — đặt sentinel + comment commit chunk khác cleanup nhưng chunk đó scope SHIFT → zombie state" · "Anti-patterns observed: (a) Default scope expansion mistake S22+4 → S22+5 fix (b) History display field assumption … FAIL TS2339 (c) PS 5.1 Vietnamese diacritics gotcha #30 reinforced" · "Plan N point 9 chỉ catch 1/5 sites" · "Plan O caveat #1 surfaced Plan P pre-existing bug" · "DbInitializer auto re-seed loop — Plan R+S clean → IIS recycle → re-seed loop" · "Anti-pattern observed S24 polish chunks: UI/UX iteration thuần CSS/layout KHÔNG cần Reviewer spawn mỗi chunk" · "Discovery #3 anomaly re-tested S24 … KHÔNG reinforce docs-only anomaly hypothesis" · "Low note S24: BE filter IsUserSelectable=false non-admin leak workflow chưa ghim" · "Non-admin pass isUserSelectable=false BE không block → workflow chưa ghim leak risk — Low severity"
  • guard: "Smart Friend guard" (bật/nhắc ở 6/7 entry; "KHÔNG lower bar") · EnsureCanRejectV2Async (BE helper chặn forge Reject, defense-in-depth FE+BE) · entry guard :75-81 (đòi ChoDuyet mới Reject) · Reject guard :81 · Admin bypass :252-265 · anti-fiddle audit (scope drift 0%) · "No --no-verify bypass" (×2) · class-level [Authorize] bare :18 giữ nguyên · HasDefaultValue(false) EF config · MenuItemConfiguration HasKey(Key) PK unique ⇒ ToDictionary an toàn · PermissionGuard/usePermission KHÔNG có (route guard đủ) · Cat-checklist 5 mục (Wire BE + Schema + Security + Code quality + Test) · feedback_uat_skip_verify (rào chế-độ) · feedback_ef_migration_backfill_reorder (rào ADD→DROP)
  • root-cause: ① "Transient sentinel pattern" ⇒ orphan endpoint PATCH /api/users/{id}/allow-skip-final silent NoOp (Task.CompletedTask) ② "Plan P Controller TransitionPeBody:267 record +3 fields mirror TransitionPurchaseEvaluationCommand schema — root cause 2 ngày prod bug F1+F2 wire fail" ③ "DbInitializer auto re-seed loop … Plan T flag fix root cause" ④ "Plan N point 9 chỉ catch 1/5 sites → Plan O cascade fix"
  • file:line: ApprovalWorkflow.cs:78 + :107-113 · PurchaseEvaluationFeatures.cs:401 · :765 · ApprovalWorkflowConfiguration.cs:22 · K2 F2 branch :477 / UPSERT :441-468 / advance :502 / caller guard :147-149 · test ref :253 (K7-pending) · Service.cs:287-333 · :364 · :268-275 · :335-360 · caller :94-100 · entry guard :75-81 · :81 · :252-265 · :105-132 · EnsureCanRejectV2Async:201 · ApplyReturnModeAsync:248 · EnsureEditableForDetailsAsync:72 · AdjustBudgetCommandHandler:311 · TransitionPeBody:267 · Controller :21-26 · Handler :114-117 · WorkflowMatrixViewPage :29-33 · App.tsx:38 · Layout.tsx resolvePath:81-86 · SeedPurchaseEvaluationPermissionDefaultsAsync:1573 · [Authorize] bare :18 · PeListPanel.tsx:112 · PeWorkflowPanel.tsx:270 / :272 / :315 / :342 · PeDetailTabs.tsx:152 / :287 · ApprovalWorkflowsV2Page.tsx:639 · contracts.ts:29 · budget.ts:20 · file test PurchaseEvaluationPerNvLookupRegressionTests.cs
  • định-danh khác: commit eb106f2..56868bf..db66253..364aef6 · c2afef2..508b17a..4dd6f9c · a1a910f..ee0902a (da218f1/4d60598/fbbd361/ee0902a) · Run #210 sha=ac2c859 (4/4 wire + bundle 2 app rotate) · diff 11 file BE +4093/83 (Designer 3938 dòng) · test 84 → 104 → 106/106 (58 Domain + 48 Infra) → 111 · mig 29 → 30 → 31 · gotcha 45 → 46 → 47 · ~146 endpoint · 33 user · 2 test mới ApplyReturnMode_OneLevel_AtStep1Level1_ResetsToBuoc1Cap1_KeepsChoDuyet + ApplyReturnMode_OneStep_AtStep1_ResetsToBuoc1Cap1_KeepsChoDuyet · fe-admin npm build 1926 modules / 740ms / 0 TS err · ~720 row prod bị xoá (Plan R+S+T5) · spawn-cost ~25K/lượt vs ~100K nếu spawn 4 polish

reviewer/2026-05-q2 (3.526 B — "Archive Q2 2026-05, S34 curate")

Đặc-điểm file: 3 mục ngắn, mật-độ bài-học cao/byte. Mục S28 là bài về ranh-giới thẩm-quyền — sinh ra hẳn một hạng-mục kiểm mới (Cat 6), và tự khai một luật cũ bị BỎ.

Cụm-dedup

# Cụm Thành-phần gộp Tag
B6-A S28 — tự-cấp-phép luật xuyên dự-án ⇒ sinh Cat 6 "authority boundary" + huỷ luật cũ 2026-05-22 cao
B6-B S25 Plan AB — gotcha #48 (SQLite đồng-hồ đóng băng) + bổ checklist Cat 5 2026-05-19 vừa
B6-C S26 Plan AG — 12 phép kiểm đối-kháng PASS + củng-cố luật ROI spawn 2026-05-21 vừa

Bản-nén-nháp 4-field

[cao] B6-A — S28: "chú ý X" bị đọc thành "BẮT BUỘC X"

  • VIỆC: ghi lại quỹ-đạo phiên S28 dưới góc reviewer (thực tế em-main solo, không có việc review nào).
  • KẾT-LUẬN: t1 kết-luận ROI của RAG "ngắn hạn không đáng, dài hạn đổi chất" → t2 em-main tự ban luật xuyên dự-án "ghi RAG mọi tương tác" khi chưa có đồng-ý của anh → t4 anh bắt lỗi, thu phạm-vi về kỷ-luật nội-bộ SOLUTION_ERP → t5 chốt quản-trị 3 lớp (4 nhóm mặc-định + danh-sách bỏ qua + tag schema bắt buộc [lesson, phase-<N>, <bc>] với phase ∈ {phase-9, phase-9plus, phase-10} và BC ∈ {contract, pe, budget, workflow, identity, form, infra}). Luật t2 ABANDONED.
  • BÀI-HỌC: thêm Cat 6 — kiểm ranh-giới thẩm-quyền: phân-biệt "anh gợi ý phương án X" (tham-khảo) với "anh chốt X" (mệnh-lệnh); gắn cờ mọi thứ dạng "BẮT BUỘC … xuyên dự-án" mà nguồn là em-main tự quyết. 5 nhóm kiểm cũ giữ nguyên, Cat 6 cộng thêm từ S28.
  • BẤT-NGỜ: sai-phạm không nằm ở kỹ-thuật mà ở phép đọc ngôn-ngữ: một chữ "chú ý" biến thành chính sách; và vai reviewer phát-hiện được nó theo lối hồi-cố, tức lúc xảy ra thì không có ai chặn.

[vừa] B6-B — S25 Plan AB: đồng-hồ đóng băng của SQLite

  • VIỆC: ghi từ-khoá đúc kết sau đợt vá changelog (cdfd542 — PE chỉnh ngân-sách + trả-lại Người-chỉ-định có log).
  • KẾT-LUẬN: gotcha #48 — nhiều Changelog.Add() trong CÙNG một SaveChangesAsync nhận cùng dấu thời-gianOrderByDescending(CreatedAt).FirstAsync() cho kết-quả không tất-định trên SQLite; kèm rủi-ro lặp lại của chế-độ UAT bỏ dotnet test khi refactor BE > 100 LOC.
  • BÀI-HỌC: bổ vào Cat 5: bộ lọc trong test phải phân-biệt bằng thứ khác ngoài mốc thời-gian (EntityType + từ khoá trong Summary).
  • BẤT-NGỜ: một bài về test lại bắt nguồn từ đặc-tính đồng-hồ của engine DB dùng trong test — thứ không xuất-hiện ở prod (SQL Server), nên chỉ lộ ra khi test đỏ ngẫu-nhiên.

[vừa] B6-C — S26 Plan AG: PASS 0 issue, và bài ROI lặp lại

  • VIỆC: review trước commit 3 chunk A+B+C (~25K token, 12 phép kiểm đối-kháng).
  • KẾT-LUẬN: PASS 0 issue; commit 0bf6c7e 2 file +346/116 LOC, hai bản mirror SHA256 IDENTICAL (21001E90...); wire = useMemo gom nhóm lồng + <details>/<summary> 2 tầng + lưu tập mở/đóng vào localStorage; 0 migration. 5 vòng polish AG2-AG6 sau đó em-main tự verify (SHA256 giống nhau + npm build 2 app + dotnet test 111/111), không spawn lại.
  • BÀI-HỌC: củng-cố luật phân-bổ: spawn reviewer cho việc nặng xuyên tầng (~370 LOC + 4 sub-agent phối hợp), em-main tự lo vòng polish 50-100 LOC.
  • BẤT-NGỜ: phép chứng "2 app giống nhau" được nâng lên so SHA256 nguyên file thay vì đọc diff — rẻ và không cãi được; đồng thời cả 6 commit của S26 giữ nguyên baseline 111 test, tức thay đổi UI thuần không mua thêm rủi-ro hồi-quy.

Token-list (verbatim, reviewer/2026-05-q2)

  • gotcha#: #48 (SQLite frozen clock tie-break — mô-tả đầy đủ: "Multi-Changelog.Add same SaveChangesAsync transaction non-deterministic OrderByDescending(CreatedAt).FirstAsync()") · dải nền "Foundation gotchas #17-#48" (header)
  • surprise (nhãn nguyên-văn): "Rule cũ ABANDONED: 'RAG ghi mọi tương tác mandatory' S28 t2 over-reach" · "t4 bro caught mistake scope-down về SOLUTION_ERP self-discipline" · "em main t2 implicit interpret 'chú ý X' (bro suggestion) AS 'MANDATORY X' (em policy decision)" · "Reviewer perspective về S28 trajectory (em main solo, KHÔNG actual review work)" · "UAT skip dotnet test recurring risk khi BE refactor > 100 LOC"
  • guard: Smart Friend guard ("active S28+ cho Plan B Contract V2 wire pre-commit spawn (mandatory heavy diff > 50 LOC cross-stack)") · Cat 6 — Authority boundary check (MỚI) · Cat 5 bổ-sung "test filter discriminator beyond timestamp (EntityType + Summary keyword)" · 5-category checklist baseline (Wire BE + Schema + Security + Code quality + Test) UNCHANGED · tag schema mandatory [lesson, phase-<N>, <bc>] · "Cross-module security validation mirror pattern preserved" (header)
  • root-cause: "scope creep từ project-local → cross-project KHÔNG bro consent là authority boundary violation" ⇐ "implicit consent ('chú ý' / 'có thể') KHÔNG = explicit mandate ('BẮT BUỘC' / 'mandatory')"
  • file:line: 0 token file:line (file này chỉ dẫn tên hàm/commit: ApplyReturnModeAsync, cdfd542)
  • định-danh khác: commit 0bf6c7e (2 file +346/116) · SHA256 mirror 21001E90... · commit cdfd542 · dotnet test 111/111 (baseline 111 preserved, 6 commit S26, 0 prod regression) · spawn ~25K · phase enum {phase-9, phase-9plus, phase-10} · BC enum {contract, pe, budget, workflow, identity, form, infra}

test-specialist/baseline-history (32.069 B — "Baseline count history (verbatim) + aged FIFO")

Đặc-điểm file: file LỚN nhất batch B, có frontmatter YAML (name: baseline-history · type: project), 1 chuỗi số một dòng + 25 entry newest-first (S155 → S54). Tách khỏi MEMORY.md 2026-06-26 vì chỉ mục vượt trần đọc 24KB. ⚠️ Ràng buộc nén (lệnh lead): chuỗi số là RUỘT ⇒ giữ đủ mốc đầu/cuối mỗi giai-đoạn, chỉ được gom giai-đoạn, KHÔNG được rút gọn thành "254 → 561".

Cụm-dedup

# Cụm Thành-phần gộp Tag
TS-A CHUỖI SỐ BASELINE (ruột) — chuỗi gốc + hậu-chuỗi rải trong entry + 4 lỗ + 2 mâu-thuẫn nội-tại dòng "Count progression" + mọi mốc X→Y trong 25 entry cao
TS-B S155 — 1 FAIL GIỮ ĐỎ = bug prod thật + fixture đầu tiên wire AuditingInterceptor S155 (+10 → 561) cao
TS-C S153 — fault-inject máy mind-check (KHÔNG phải test C#), bắt 1 GAP + bài fixture-hỏng-cho-xanh S153 (18/18, baseline C# không đổi) cao
TS-D S133 budget-freeze RED→GREEN (INV-A T0 + INV-C T1-T9) + va-chạm sub-file 2 invocation S133 INV-A + S133 INV-C (gộp: cùng feature, 1 đỏ 1 xanh) cao
TS-E S134 — lũy kế TẠM TÍNH: khuôn "2 hàm cùng seed, khớp số CHÍNH XÁC" S134 (+8 → 528) cao
TS-F Cụm SECURITY/authz regression (reflection harness Pattern 10 + contrast chống vacuous) S118b (+4→509) · S118 (+4→505) · S92 (+3→434) · S69 Office (+6) · S54 ItTicket (+13) vừa
TS-G Cụm FINANCIAL / critical-algo (giá chốt, số âm, ngưỡng, multi-winner) S117 (+5→500) · S116 (+9→495) · S72 (+28) · S69b (+14) · D2 (+6→419) · S87 (+7) cao
TS-H Cụm Supplier Excel-import (case-collation + 1 BUG NRE báo-không-sửa) D3 (+10→450) · S113 (+19→477) vừa
TS-I Cụm bug-fix / spec-change / compile-fix arity D4 (+8→458) · S97 (+0) · S86 (+3) · S85b (+3) · S85 (+5) · S77c (rewrite 12→11) · S77 (+7) · S76 (+5) · S74 (+5) vừa
TS-J 2 ghi-chú kỹ-thuật lẻ giữ lại (S57bis / S56) mục "S57bis / S56 (notes kept)" thấp

Ghi-chú dedup: TS-D gộp 2 entry S133 vì là hai nửa của một chu-trình test-before (INV-A dựng đỏ, INV-C đẩy xanh) — gộp KHÔNG mất danh-tính vì giữ đủ 2 verdict. TS-F/TS-G/TS-H/TS-I gộp theo loại rủi-ro (security / financial / import / bug-fix) chứ không theo thời-gian, mỗi entry trong cụm vẫn giữ 1 dòng riêng có số delta.

Bản-nén-nháp 4-field

[cao] TS-A — chuỗi baseline: giữ nguyên mốc, gắn cờ 4 lỗ + 2 mâu-thuẫn

  • VIỆC: duy-trì một chuỗi số test-pass xuyên ~20 phiên làm bằng-chứng "không hồi-quy".
  • KẾT-LUẬN (giữ NGUYÊN VĂN mốc): 254 (S60) → 263 (S61: +22 PeWorkItemBudget, 14 BudgetPolicy Domain drop) → 286 (S67 +23 HRM) → 292 (S69 +6 Office) → 306 (S69b +14 PE) → 334 (S72 +28 Mig54) → 339 (S74 +5 CcmNote) → 344 (S76 +5 PRO-split) → 351 (S77 +7 suggested-price NOTE) → 354 (S77b symmetric, superseded) → 366 (S78-S84, MEMORY chưa ghi từng delta) → 371 (S85 +5) → 374 (S85b +3) → 377-395 (S86-S86d) → 402 (S87 +7) → 419 (D2 +6 Mig58 multi-winner + translation-smoke). Hậu-chuỗi nằm rải trong entry (chuỗi một-dòng KHÔNG được cập nhật tiếp): 431→434 (S92 +3) · 440 (S97, +0 extend in-place) · 440→450 (D3 +10) · 450→458 (D4 +8) · 458→477 (S113 +19) · 486→495 (S116 +9) · 495→500 (S117 +5) · 501→505 (S118 +4) · 505→509 (S118b +4) · →519 (S133, Domain 45 + Infra 474) · →528 (S134, 45D+483I) · →561 (S155 +10; 560 PASS / 1 FAIL; 45D + 516I).
  • BÀI-HỌC: chuỗi này chỉ có giá-trị khi mỗi mốc kèm delta + lý-do; đoạn "366 (S78-S84, MEMORY chưa ghi từng delta)" và "377-395 (S86-S86d)" cho thấy chỗ nào bỏ ghi thì chỗ đó vĩnh-viễn không truy được nữa. Khi nén, mốc đầu/cuối mỗi giai-đoạn là thứ không được phép mất.
  • BẤT-NGỜ / CỜ CHO LEAD (4 lỗ + 2 mâu-thuẫn nội-tại, trích nguyên văn): ① lỗ 419 → 431 (+12 không có mốc) · ② lỗ 434 → 440 (+6) · ③ lỗ 477 → 486 (+9) · ④ lỗ 500 → 501 (+1). Mâu-thuẫn (a): S133 tiêu-đề ghi "+10 -> 519" nhưng thân ghi "+9 test vào PeBudgetFreezeTests" — hoà-giải KHẢ DĨ, chưa xác-minh: T0 của INV-A (+1) + T1-T9 của INV-C (+9) = +10, khớp "464+10". Mâu-thuẫn (b): S153 (2026-07-26) ghi "baseline 509 KHÔNG đổi" trong khi S134 (2026-07-17, TRƯỚC đó) đã ghi 528 và S155 (2026-07-27, NGAY SAU) ghi 551→561 ⇒ con số 509 ở S153 không nằm cùng thang với hai mốc kẹp nó. Tôi CHỈ đọc file, KHÔNG chạy dotnet test — đây là mâu-thuẫn trong verbatim, không phải phán-quyết về baseline thật.

[cao] TS-B — S155: giữ nguyên 1 test ĐỎ vì đó là bug prod

  • VIỆC: đợt 2 tính-năng "xoá phiếu khi đang duyệt" + màn "Đã xoá" — +10 test PeDeleteByApproverTests.cs (T21 lũy-kế 4 hướng · T22/b/c màn Đã xoá · T23/T24/b cờ+lượt · T25 authz reflection · T25b chiều thuận · T26 changelog).
  • KẾT-LUẬN: 561 = 560 PASS / 1 FAIL (45 Domain + 516 Infra), 0 hồi-quy (506 Infra cũ nguyên vẹn). T26 GIỮ ĐỎ vì là bug prod thật: PeSoftDeleteFeatures.cs thêm changelog ở :126 rồi Remove(pe):142; CascadeDeleteTiming.Immediate cascade ngay tại Remove, thực-thể con đang ở trạng-thái Added bị đẩy sang Detached (không phải Deleted) ⇒ 0 row; AuditingInterceptor chạy ở SavingChanges = SAU điểm mất nên không cứu được. Đo bằng chẩn-đoán ChangeTracker (Added→Detached, rowsInDb=0), KHÔNG suy đoán. Grep cùng lớp: đây là site DUY NHẤT có hình "Add con → Remove cha → 1 SaveChanges". Báo lead, KHÔNG tự sửa.
  • BÀI-HỌC: hạ-tầng mới SoftDeleteFixture (~90 LOC, ngay trong file test) là fixture ĐẦU TIÊN của repo có wire AuditingInterceptor — bắt buộc khi đo Remove() nghĩa là xoá-mềm, vì TestApplicationDbContext không wire ⇒ mặc-định xoá cứng ⇒ màn "Đã xoá" rỗng ⇒ assert đúng ở CẢ HAI thế-giới = đúng bẫy vacuous.
  • BẤT-NGỜ: số học T21 phải seed thêm phiếu hàng-xóm (DaDuyet + TraLai) để hai đại-lượng "không đổi" (PrevSelected, PendingSubmitted) khác 0 ở cả hai phía — nếu để 0 thì "bất động" và "rỗng" nhìn giống hệt nhau.

[cao] TS-C — S153: tiêm lỗi vào máy mind-check, và bắt được chính fixture của mình nói dối

  • VIỆC: fault-inject #8-#17 + #21 lên scripts/session_ctx.py mind-check (KHÔNG phải test C#), mọi đột-biến ở cây tạm qua seam --root, repo read-only.
  • KẾT-LUẬN: 18/18 PASS 0 FAIL, baseline C# KHÔNG đổi. Bắt 1 GAP THẬT: ở chế-độ closed, xoá con-trỏ → cờ/exit 0 trong khi giữ con-trỏ đang-chờ một cách trung thực → trượt/exit 1 ⇒ đường rẻ nhất để xanh là đường SAI (chữ luật cấm, nhưng gradient thưởng). Báo em-main, KHÔNG tự sửa.
  • BÀI-HỌC: fixture draft-ts MALFORMED vẫn cho exit 0inject_A neo vào ## MIND-1 sau khi renum(...,2) đã đổi heading ⇒ str.find = 1 ⇒ chèn GIỮA heading ⇒ ts: không nằm đầu dòng ⇒ máy ĐÚNG mà vẫn xanh. Fixture-hỏng-cho-xanh trông Y HỆT máy-đúng-cho-xanh ⇒ phải soi RUỘT fixture (cat -A / repr từng dòng), không đọc mỗi exit code.
  • BẤT-NGỜ: 2 kỹ-thuật đem dùng lại — (i) cặp CÙNG-RUỘT khác đúng 1 biến (cap=32 vs cap=1 trên cùng tệp = chứng đọc config LIVE; _end giả _pause=9 vs không = chứng nguồn đếm là ĐĨA, dùng diff chứng 2 output BYTE-IDENTICAL); (ii) đo wall-clock phải đi nhánh ĐẮT — lệnh literal rơi vào nhánh thoát-sớm (repo chưa có _mind) là tự đo nhánh rẻ rồi gọi đó là chi-phí thật; đo 55-64ms/lượt so với ước 140-300ms ⇒ lệch 2.2-5.4×, trả lead điền lại.

[cao] TS-D — S133 budget-freeze: dựng ĐỎ rồi đẩy XANH, và 2 lượt cùng vai đè file của nhau

  • VIỆC: INV-A dựng test tái-hiện lỗi (test-before) → INV-C viết T1-T9 phủ tính-năng đóng băng ngân-sách phiếu đã duyệt.
  • KẾT-LUẬN: INV-A RED thật Failed 1/1 — kỳ vọng ProInitial 100tr, thấy 999tr @PeBudgetFreezeTests.cs:184; luồng chuẩn = đưa phiếu tới DaDuyet QUA svc.TransitionAsync (V2 1-Bước-1-Cấp AllowApproverFinalize=true + giá chốt), KHÔNG seed thẳng Phase (vì sau khi vá thì snapshot chỉ được đặt qua transition); đổi cặp giá trị qua UpdatePeBudgetPro/Ccm (2 handler không chặn theo phase = đúng cơ-chế lỗi); 2 sanity-assert chống vacuous (Phase==DaDuyet + bản ghi live đổi thật) đặt TRƯỚC assert chính. INV-C +9 test (:426 T1 đủ-11-cột · :480 T2 CCM-threshold · :524 T3 all-steps · :555 T4 V1-legacy · :638 T5 guard CCM+Admin · :687 T6 AdjustBudget · :730 T7 live-vs-frozen · :783 T8 null-safe · :821 T9 overwrite+refresh) → lead chạy full exit-0: Domain 45 + Infra 474 = 519.
  • BÀI-HỌC: INV-C ghi đè sub-test-specialist-0.md của INV-A — cùng vai + cùng chỉ-số task qua 2 lần gọi trong cùng run-folder = va-chạm tên sub-file; nội-dung INV-A sống sót nhờ journal + diary. Nhiều lần gọi trong một run phải có nhãn/chỉ-số phân biệt.
  • BẤT-NGỜ: spec ghi sai tên truy-vấn — truy-vấn thật là GetPurchaseEvaluationQuery (bundle .BudgetSummary), spec ghi GetPurchaseEvaluationDetailQuery; test-specialist bắt được và lead sửa spec (test soi ra lỗi tài-liệu, không phải lỗi mã).

[cao] TS-E — S134: hai hàm, một seed, khớp số CHÍNH XÁC

  • VIỆC: +8 test PePendingAccumulationTests.cs (C1-C8) cho lũy-kế TẠM TÍNH (mirror seed PeBudgetFreezeTests:277-336).
  • KẾT-LUẬN: C1 ChoDuyet+winner → delta2+PriorPes · C2 TuChoi/DaDuyet loại sạch · C3 nháp chỉ-liệt-kê / TraLai delta1 · C4 cửa-sổ CreatedAt · C5 delta âm có dấu · C6 bổ-sung-không-đếm-hai-lần: gọi CẢ ComputeAsync + ComputePendingAsync trên CÙNG seed, assert SỐ CHÍNH XÁC hai phía (PrevSelected 90tr/1 bất động ⟂ PendingSelected 50tr/1) · C7 frozen→0/null qua query handler · C8 changelog D4 dương + đối-chứng (lọc ContextNote.Contains("[S134 pending-prior]"), gotcha #48). Full 528/0 (45D+483I).
  • BÀI-HỌC: (1) gọi thẳng accumulator thì chỉ cần curPeId tổng-hợp + CreatedAt muộn, không cần row phiếu đang xét (chỉ lọc Id!= / CreatedAt<); (2) phép chống hồi-quy mạnh nhất cho đại-lượng "bất động" là kiểu C6 — số chính xác, 2 hàm, 1 seed; (3) FluentAssertions ContainInOrder(params T[]) đụng overload với chuỗi because khi T=Guid ⇒ bọc new[]{...}.
  • BẤT-NGỜ: helper D4 không gọi SaveChanges ⇒ row chỉ thực sự nằm trong DB sau TransitionAsync — nếu assert sớm sẽ thấy "không có row" và kết luận sai về tính-năng.

[vừa] TS-F — cụm SECURITY/authz: khuôn reflection + đối-chứng chống vacuous

  • VIỆC: 5 đợt khoá quyền bằng test hồi-quy: S118b Reports (+4 → 509) · S118 Procurement master-access (+4 → 505) · S92 admin-only revoke (+3 → 434) · S69 Office golive seed (+6) · S54 ItTicket reassign (+13).
  • KẾT-LUẬN: S118b mở rộng Api/AuthorizePolicyRegressionTests.cs (Pattern 10 reflection, file 15→19 [Fact]): Dashboard + ExportContracts nay [Authorize(Policy="Reports.Read")], còn MyDashboard cố ý để trần — T4 assert (attr?.Policy).Should().BeNull() để cấm mọi policy (thêm policy = khoá luôn dashboard cá-nhân của role không-Admin); csproj đã ref Api từ S45 nên không vướng. S118 ProcurementMasterAccessSeedTests.cs 4 [Fact], T2 ScopeIsolation là lõi: DeptManager đứng riêng, pre-grant → revoke → seed ⇒ 9 key VẪN all-false (row còn giữ vết) = cấp quyền cho role này không rò sang role khác, kèm đối-chứng Procurement thật sự được nâng. S92 AdminOnlyModulesRevokeTests.cs 3 test giữ bất-biến "PE KHÔNG bị revoke". S69 Office khác HRM ở chỗ cấp cả Read AND Create trên 16 key. S54 ItTicket = authz theo dữ-liệu (2 phòng ban + ma-trận role/dept).
  • BÀI-HỌC: khuôn chung = reflection vào private static + seed FK đúng thứ tự (MenuItem → Role → Permission) + luôn có nhánh đối-chứng dương để test không "xanh vì chẳng có gì xảy ra"; và authz theo dữ-liệu (handler) khác hẳn authz tĩnh ([Authorize] đọc bằng reflection) — hai điểm thực-thi khác nhau, phải test khác nhau.
  • BẤT-NGỜ: S92 — OfficeModulePermissionSeedTests + HrmProfilePermissionSeedTests VẪN xanh dù prod đã TẮT 2 lời gọi cấp quyền, vì test gọi thẳng method cô-lập qua reflection, không chạy chuỗi SeedAsynctest xanh không chứng minh đường chạy thật còn sống; đã thêm một dòng ghi chú đầu mỗi file.

[cao] TS-G — cụm FINANCIAL / critical-algo: giá chốt, số âm, ngưỡng, đa người thắng

  • VIỆC: 6 đợt test cho phần dính tiền: S117 ProMinMax (+5 → 500) · S116 số âm (+9 → 495) · S72 Mig 54 (+28 → 334) · S69b ngưỡng CCM + urgent (+14 → 306) · D2 multi-winner (+6 → 419) · S87 CCM budget-period (+7 → 402).
  • KẾT-LUẬN: S117 khoá bất-biến Max != null ⟺ Source=="ProMinMax" + ca biên min==max PHẢI OK (revert > thành >= là đỏ). S116 đổi guard nộp phiếu từ Sum()<=0 sang Count==0 / All(x==0) để cho báo giá ÂM (phát-sinh-giảm/hoàn tiền) đi qua; ranh-giới quyết-định là ca net==0 nhưng từng dòng ≠ 0 [+500k, 500k] → ChoDuyet, và ngưỡng CCM tính trên NET ([+1.2tỷ, 0.3tỷ]=0.9tỷ không chạm ngưỡng ⟂ đối-chứng [+1.2tỷ] một mình thì chạm). S72 chuyển CCM-finalize từ TỰ-ĐỘNG sang CHỌN-THAM-GIA + thứ tự fail-closed (threshold-null → Conflict / sai role → Forbidden / vượt ngưỡng → Conflict). D2 chuyển 1→N hợp-đồng cho N đơn-vị thắng, GiaTri per-winner = tổng ThanhTien báo giá của ĐÚNG winner (140/260) ⟂ ca single dùng legacy ThanhTienNganSach (800).
  • BÀI-HỌC: với phần tiền, đối-chứng ngược luôn đi kèm: mỗi assert "cho qua" phải có một seed "chặn lại" cạnh bên, nếu không thì không phân-biệt được "luật đúng" với "luật không chạy".
  • BẤT-NGỜ: ① S117 — đổi method prod sang 5 tham số khiến MethodInfo.Invoke không tự bind optional param ⇒ giữ 4 arg là TargetParameterCountException phá 3 test cũ (tự gây, tự tránh). ② S72 — nhánh isSystem KHÔNG với tới được qua ApproveV2Async công-khai (PE không có job SLA) ⇒ phòng-thủ/chết, phải test bằng unit + reflection, và ghi rõ đó là OBSERVATION. ③ D2 — PurchaseEvaluationQuote.PESupplierIdFK CỨNG → Supplier Restrict (cfg:135) nên Guid ngẫu-nhiên làm SQLite ném lỗi 19, phải seed row thật; trong khi Contract.SupplierId/ProjectId không có FK vật lý — hai quy-ước ngược nhau trong cùng một luồng. ④ S87 — Changelog.UserIdGuid? không FK nên Guid ngẫu-nhiên vô-hại.

[vừa] TS-H — cụm Supplier import: lỗi so-sánh hoa/thường và một NRE báo-mà-không-sửa

  • VIỆC: D3 (+10 → 450) test-before cho import Excel; S113 (+19 → 477) test-after cho dedup v2 + guard công-bố.
  • KẾT-LUẬN: Lỗi lõi = collation: dedup nằm ở SERVICE với OrdinalIgnoreCase, không dựa DB ⇒ fixture SQLite (BINARY) không bắt trùng khác-hoa-thường, còn prod SQL Server (CI) thì bắt — nên truonggiang vs TRUONGGIANG ra Inserted=0/Updated=1; nếu service dùng Ordinal thì prod nổ 500 cả lô. Thứ tự ưu-tiên dedup: existing-MST → existing-Code → batch-MST → batch-Code → New, và MST thắng, không âm thầm ghi đè (FillNulls bỏ qua Code). Guard công-bố: PublishSupplierCommand (KHÔNG đi qua UpdateSupplier — #73), chặn Code rỗng, chặn trùng chính-xác.
  • BÀI-HỌC: khuôn dựng header xlsx qua reflection từ ExpectedHeaderTokens ⇒ test tự đồng-bộ khi hằng đổi; ClosedXML in-memory gán .Value = string giữ nguyên chữ (0.105 không bị ép về số ⇒ mã có số 0 đứng đầu vẫn an toàn).
  • BẤT-NGỜ: S113 tìm ra BUG thật và KHÔNG sửa: NewSupplier Code = r.Code!.Trim()NRE khi row.Code == NULL (đường R4 nháp: parser sinh null cho ô rỗng, preview phân loại New không báo lỗi ⇒ tới confirm mới nổ ⇒ 500 cả lô, dbCount=0), trong khi chuỗi rỗng "" thì OK; viết characterization test chốt đúng hành-vi NRE hiện tại để khi vá null-safe thì test tự lật sang NotThrow. Và D3 khai ExpectedHeaderTokens chỉ là PHỎNG ĐOÁN ≠ 30 token thật ở dòng 4 ⇒ Case 9 so hai chiều động, hiện LayoutValid=false, sẽ tự lật true khi hằng được sửa.

[vừa] TS-I — cụm bug-fix / spec-change / compile-fix arity

  • VIỆC: 9 đợt nhỏ: D4 sign-off A1 (+8 → 458) · S97 cờ runtime (+0) · S86 loại người tạo khỏi chuông (+3) · S85b giữ workflow khi lưu nháp (+3) · S85 người khai tự gỡ cờ gấp (+5) · S77c đối-xứng → BẤT đối-xứng (12→11) · S77 note giá đề-xuất (+7) · S76 tách cột PRO (+5) · S74 CcmNote (+5).
  • KẾT-LUẬN: D4 đổi ApplyDrafterBypassOnSubmitAsync sang A1 — bypass CHỈ các cấp dưới thuần (Order < minOwn), con-trỏ DỪNG tại minOwn để người soạn ký TAY; 4 test cũ được đổi tên vì tên cũ gây hiểu nhầm. S97 = spec-change: đọc thẳng cờ EndedByLevelFinalize thay vì suy từ config, test cũ thêm param + ca tái-hiện mới peFinButWentCeo (cùng config nhưng cờ FALSE). S86 chứng RED-trên-code-cũ bằng git stash rồi khôi phục nguyên trạng. S77c là đảo chiều luật giữa chừng: SET = đủ role chức-năng / UNSET = role AND DeptManager.
  • BÀI-HỌC: đổi arity của record command thì compile-fix là một phần của việc test (S76 vá 5 call-site + đổi assert .ProEstimateAmount.ProInitialAmount; S74 vá 3 call-site thêm null) — bỏ qua thì không phải "test đỏ" mà là "không build được".
  • BẤT-NGỜ: S86 — phép loại-trừ theo actor KHÔNG che được bug (vì actor ở Cấp 1 còn người tạo ở Cấp 2) nên test mới thật sự có răng; S85 — nhánh isDrafter ưu tiên và độc lập với isProTp, nên người vừa là Drafter vừa là Procurement-không-DM vẫn gỡ được cả 2 cờ; S77c ghi thẳng "test theo CODE" (không đụng prod).

[thấp] TS-J — 2 ghi-chú kỹ-thuật lẻ

  • VIỆC: giữ lại 2 quan-sát không gắn với đợt test nào.
  • KẾT-LUẬN: S57bis — NotEmpty() trên Guid? KHÔNG bắt Guid.Empty (FluentValidation 7.2 coi default(Guid?) == null) ⇒ dựa vào FK-guard ở create-handler (phòng-thủ nhiều lớp, không phải bug prod). S56 — ExecuteUpdateAsync đi vòng qua change tracker ⇒ bản đọc đang tracked bị cũ, phải .AsNoTracking() đọc lại; transaction đã ship ở mức IsolationLevel.Serializable.
  • BÀI-HỌC: hai ca đều thuộc loại "API trông như có kiểm mà không kiểm" — validator im lặng và lệnh update im lặng; phải biết trước thì mới viết được assert đúng.
  • BẤT-NGỜ: cả hai ghi-chú sống sót qua mọi vòng curate dù không kèm test nào — tức giá-trị của chúng nằm ở chỗ chặn một giả-định sai, không ở số lượng test.

Token-list (verbatim, test-specialist/baseline-history)

  • gotcha#: #48 (S134 — lọc ContextNote.Contains("[S134 pending-prior]")) · #73 (S87 "null=clear (pre-seed 500tr, gotcha #73)"; S113 "PublishSupplierCommand (KHÔNG UpdateSupplier #73)") · các ⚠️/"Gotcha" không đánh số: S117 "⚠️ Gotcha (self-inflicted-avoided)" · S92 "Gotcha:" (test xanh dù prod tắt) · S56 "gotcha:" (ExecuteUpdateAsync)
  • surprise (nhãn nguyên-văn): "Surprise: D4 no-SaveChanges-in-helper → row chỉ persist sau TransitionAsync" (S134) · "⚠️ Lesson mới: INV-C ghi đè sub-test-specialist-0.md của INV-A … sub-file collision" (S133) · "Bài học mới quan trọng nhất: fixture draft-ts MALFORMED ra exit 0 … Fixture-hỏng cho-xanh trông Y HỆT máy-đúng cho-xanh" (S153) · "đo 55-64ms/lượt vs ước 140-300ms ⇒ lệch 2.2-5.4×" (S153) · "1 FAIL = T26 GIỮ ĐỎ = BUG PROD" (S155) · "🔴 1 BUG-FOUND (REPORTED em-main, NOT fixed)" (S113) · "Spec-drift FLAGGED (không phải bug): ExpectedHeaderTokens = BEST-GUESS ≠ 30 real row-4 token" (D3) · "⚠️SEED-FK: PurchaseEvaluationQuote HARD FK PESupplierId→Supplier Restrict (cfg:135)" (D2) · "⚠️OBSERVATION: isSystem KHÔNG reachable qua public ApproveV2Async … defensive/dead" (S72) · "⚠️ApprovalWorkflowId FK Restrict → PE seed PHẢI ref WF thật" (S85b) · "⚠️actor-exclusion KHÔNG che bug" (S86) · "⚠️Changelog.UserId Guid? no FK User → random-Guid an toàn" (S87) · "⚠️GUARD-FIRST: bypass-test PHẢI dựng PE đủ 4 ĐK" (S60) · "Bắt được lỗi spec: query THẬT = GetPurchaseEvaluationQuery … spec ghi nhầm" (S133 INV-A) · "[superseded by S77c for urgent SET/UNSET asymmetry]" (S69b) · "(1 FAIL GIU DO = bug prod)" (tiêu-đề S155) · "baseline 509 KHÔNG đổi" (S153 — xem mâu-thuẫn ở TS-A)
  • guard: SoftDeleteFixture (fixture ĐẦU TIÊN wire AuditingInterceptor) · "2 sanity-assert chống vacuous (Phase==DaDuyet + liveRec đổi thật) TRƯỚC assert chính" (S133) · "+CONTRAST assert … (chống vacuous-pass)" (S118) · "CONTROL/đối-chứng" ở S116 ([+1.2tỷ] alone) · "Forbidden red-able BY-CONTRAST (non-IT vs IT-staff identical-setup)" (S54) · submit-guard :202-208 (S116) · publish-guard (S113) · "guard R2-revised" (S116) · "wire-bug-guard" (S117) · "fail-closed order (832-851)" (S72) · "role-gate 152-157 TRƯỚC side-effect" (S74) · "GUARD-FIRST" (S60) · PurchaseEvaluationDraftGuard-tương-đương: "T5 guard CCM+Admin Conflict + ChoDuyet-allow" (S133 :638) · "authz-capability … CanReassign flag + Forbidden/Conflict guard + empty-staff 0-leak + no-mutation" (S54) · "Pattern 10 reflection static [Authorize]" (S118b) · "T4 (attr?.Policy).Should().BeNull()" (S118b — rào NGƯỢC: cấm thêm policy) · "invariant T6"-tương-đương: "Pe-module-NOT-revoked invariant" (S92) · "REPORT lead, KHÔNG tự sửa" (S155, S153) · "repo read-only … mọi mutation trong cây tạm qua seam --root" (S153) · "No prod touch — verify qua stash, restore exact" (S86) · "KHÔNG touch prod" (S113/S116/S117/S118/S118b)
  • root-cause: ① S155 — "Remove(pe) (:142) → CascadeDeleteTiming.Immediate cascade ngay tại Remove; dependent đang Added bị cascade ⇒ Detached ⇒ 0 row; AuditingInterceptor chạy ở SavingChanges = SAU điểm mất" ② S153 — "inject_A neo ## MIND-1 sau khi renum(...,2) đã đổi heading ⇒ str.find=-1 ⇒ chèn GIỮA heading ⇒ ts: không ở đầu dòng" ③ S113 — "NewSupplier Code=r.Code!.Trim() → NRE khi row.Code==NULL" ④ D3/S113 — "dedup ở SERVICE OrdinalIgnoreCase, KHÔNG dựa DB — SQLite fixture BINARY collation KHÔNG bắt CI-dup, prod SQL Server CI thì có" ⑤ S117 — "optional param KHÔNG auto-bind qua MethodInfo.Invoke" ⑥ S92 — "test invoke method-CÔ-LẬP qua reflection … KHÔNG chạy SeedAsync chain"
  • file:line: PeSoftDeleteFeatures.cs:126 + :142 · PeBudgetFreezeTests.cs:184 · :426 :480 :524 :555 :638 :687 :730 :783 :821 + seed :277-336 · CreateContractFromEvaluationFeatures.cs:196 · PurchaseEvaluationFeatures.cs:285 (UpdateDraft) + :849-864 (FullAmount) · PurchaseEvaluationWorkflowService.LogTransitionAsync (~:1083-1135) · submit-guard :202-208 · ApproveV2Async 816-854 (strict-< @838) + 832-851 · role-gate 152-157 · Quote FK cfg:135 · call-site PeWorkItemBudgetTests 388/407/421 · PurchaseEvaluationUrgentFeatures.cs:49-55 · Api/AuthorizePolicyRegressionTests.cs (15→19 [Fact]) · csproj line 25 · scripts/session_ctx.py mind-check
  • tên file test (định-danh): PeDeleteByApproverTests.cs · PePendingAccumulationTests.cs · PeBudgetFreezeTests.cs · SupplierImportV2DedupTests.cs · SupplierPublishAndListTests.cs · Services/SupplierExcelImportServiceTests.cs · ProcurementMasterAccessSeedTests.cs · AdminOnlyModulesRevokeTests.cs · OfficeModulePermissionSeedTests.cs · HrmProfilePermissionSeedTests.cs · PeSubmitGuardAndBypassTests.cs · PeCcmThresholdFinalizeTests.cs · PeApprovedPriceFinalizeTests.cs · PeSuggestedPriceSetterAuthzTests.cs · PeUrgentToggleAuthzTests.cs · PeCcmBudgetPeriodSetterAuthzTests.cs · PeApproverNotifyExcludesDrafterTests.cs · PeUpdateDraftWorkflowPreserveTests.cs · PeMultiWinnerTests · PeWorkItemBudgetTests · PeFinalizeProjectionTests · PeApproverFinalizeTests · CreateContractFromEvaluationMultiWinnerTests.cs · PeListWinnerNamesProjectionTests.cs · ItTicketReassignAuthzTests.cs · SqliteDbFixture / TestApplicationDbContext / IdentityFixture / CapturingNotificationService
  • định-danh run/engine: run 2026-07-27-S155-pe-delete-approver · sub-d2-test-4 (diary 13.9KB) · run 2026-07-26-S153-ctx-adopt [engine: opus-5] · wf_97d6a582-b7e [engine: opus-worker] (S134) · wf_75c6bfd3-754 (S133 INV-C, #53 return-fail lần 3) · wf_d9639faa-97a (S133 INV-A, return sạch) · Mig 54/55/56/57/58/59/60/61/65/66/67/68 được dẫn rải rác

implementer-frontend/2026-H2 (22.215 B — "verbatim L2, dựng @S166 2026-08-01")

Đặc-điểm file: TOÀN BỘ file là nội-dung moved-not-cut của đợt drain 08-01 (header: "Chuyển verbatim từ MEMORY.md khi L1 chạm trần đọc. Con-trỏ 1 dòng ở MEMORY.md; Ctrl-F tag để nhảy"), thứ tự mới→cũ, 14 mục, mỗi mục có tag Ctrl-F riêng = danh-tính tra-cứu. ⚠️ Ràng buộc nén (lệnh lead): mọi mục moved = load-bearing, tag tối-thiểu {cao} cho mục auto-refuse (dòng 78) và KHÔNG được nén mất danh-tính ⇒ đề-xuất dưới đây giữ 1 khối riêng cho từng mục, tuyệt đối không gộp 2 mục thành 1 dòng, và giữ nguyên chuỗi tag Ctrl-F (nếu mất tag thì con-trỏ ở MEMORY.md trỏ vào hư-không).

Cụm-dedup

# Cụm Thành-phần gộp Tag
FE-A S152 R1 — 2 ranh AUTO-REFUSE [s152, auto-refuse-trivial-30min, no-lower-bar-smart-friend] (dòng 78) 1 mục, KHÔNG gộp với gì cao
FE-B S166 K4b — harness đo nhầm cái render bị React vứt [s166, khkk-k4b-group-wire, harness-do-nham-render-bi-vut] 1 mục cao
FE-C S166 K4a — regex 42 leaf + single-source nhãn nhóm [s166, khkk-k4a-regex-42-leaf, tien-de-lead-sai-x2] 1 mục cao
FE-D S166 K5 — cây toàn-trình: forward-provision = đổi HÌNH DẠNG, không bịa field [s166, khkk-k5-8-sub-folder] 1 mục cao
FE-E S153 T2+T5 — writeGuard là 2 ĐIỂM, chỉ :235 có RĂNG [s153, t5-writeguard-2-diem-235-co-rang] 1 mục cao
FE-F S166 K4c — lọc WfView theo nhóm; rỗng-vì-lọc ⟂ rỗng-vì-chưa-có [s166, khkk-k4c-wfview-loc-nhom, rong-vi-loc-khac-rong-that] 1 mục vừa
FE-G S166 K6 — thu gọn menu admin: dựng lại cây thật rồi chạy bộ lọc đã ship [s166, k6-admin-thu-gon, …] 1 mục vừa
FE-H S164/S165 K2 — tiền-đề lead sai, việc thật ≈ 1/4; colSpan lỗi CÂM [s164, khkk-nhom-duyet, …] 1 mục vừa
FE-I S161 ARC W1+W2+W3đ1 — containment 2 lớp, types-theo-ENTITY, hợp-đồng phải LEAD định-nghĩa [s161, …] 1 mục (3 lượt gộp sẵn trong verbatim) vừa
FE-J1..J5 5 mục micro-lesson FE, mỗi mục GIỮ khối riêng: S134 slot-prop sub?: ReactNode · S133 anchor-by-visible-text · S117 spread-trước-cast · S116 sign-toggle mất-dấu-thầm-lặng · S113 publish-not-update 5 mục — chỉ xếp chung nhóm để tiện đọc, KHÔNG hợp nhất nội-dung vừa (S116: cao)
FE-M [CHỈ-MỤC, không thay thế mục nào] Sợi chỉ "TIỀN-ĐỀ ĐỀ-BÀI/LEAD SAI ⇒ đo đĩa TRƯỚC khi gõ" — xuất-hiện 5 lần liên-tiếp K2 · K4a (×2) · K4b (lần 3) · K5 · K6 cao

Ghi-chú dedup: file này gần như không có trùng-lặp để gộp — mỗi mục là 1 lượt việc riêng có tag riêng. Giá-trị dedup thật nằm ở FE-M: cùng một bài học lặp 5 lần trong 3 phiên ⇒ lead có thể viết 1 dòng luật ở L1 và để 5 mục chi-tiết ở L2, thay vì lặp bài học 5 lần.

Bản-nén-nháp 4-field

[cao] FE-A — S152 R1: hai ranh giới auto-refuse (mục dòng 78, giữ nguyên danh-tính)

  • VIỆC: ghi chú lưu-giữ do lead seed on-behalf (M9, hàng đợi memory-ops) — chốt 2 ranh giới vai implementer-frontend tự áp cho chính mình.
  • KẾT-LUẬN: (a) Việc < 30 phút / vặt (sửa 1 dòng CSS, chỉnh nhãn) = em-main tự làm còn nhanh hơn chi-phí spawn ⇒ TỪ CHỐI nhận, đúng vai, ghi vào role-file; nhận việc vặt là đốt spawn vô ích. (b) KHÔNG hạ chuẩn kiểu "Smart-Friend nể-nang": dù bị giục "làm nhanh" vẫn giữ sàn mirror-SHA × 2 app + npm build × 2 PASS; thiếu 1 trong 2 thì trả lại, không ship.
  • BÀI-HỌC: hai ranh này bảo vệ hai thứ khác nhau — (a) bảo vệ ngân-sách token, (b) bảo vệ sàn chất-lượng; và cả hai đều là quyền tự từ chối của vai, không cần xin phép từng lần.
  • BẤT-NGỜ: một ghi-chú "không làm gì cả" lại là mục đáng giữ nhất nhóm — vì nó định-nghĩa cái vai này KHÔNG nhận, thứ mà mọi mục còn lại (toàn là việc đã làm) không nói được.

[cao] FE-B — S166 K4b: phép đo bắt đúng cái render đã bị React vứt đi

  • VIỆC: nối ?group= vào trang danh sách + trang tạo, kèm vá-3/5/6. Artifact …/sub-implfe-k4b.md.
  • KẾT-LUẬN: BÀI TO NHẤT — suýt kết-luận NGƯỢC: gọi setState trong lúc render khiến React huỷ render đó và render lại với state mới TRƯỚC khi commit; harness bản 1 chỉ chạy 1 lượt nên đọc ra phase=3 (giá-trị của render đã bị vứt) — và control-âm cũng ra 3 ⇒ hai nhánh giống hệt nhau ⇒ phép đo mất khả-năng phân-biệt, trông y như PASS. Vá = lặp tới khi state đứng yên → A ra phase=2, control-âm giữ 3. Giải bài chính: override 3 trạng-thái chipPhase: T|null|undefined (undefined = "nghe URL" ≠ null = "user chọn Tất cả") + reset trong render theo navKey, KHÔNG dùng useEffect (effect vẽ một lượt dữ-liệu cũ rồi mới sửa = nháy).
  • BÀI-HỌC: mô-phỏng hành-vi framework thì phải mô-phỏng CẢ vòng đời, không chỉ một lượt gọi hàm. Và tiền-đề đề-bài sai lần thứ 3 liên-tiếp: làm đúng nguyên văn ("derive phase từ searchParams thay useState") sẽ giết 6 chip lọc (KpiCard onClick setPhase :153) — hỏng CÂM, build vẫn xanh.
  • BẤT-NGỜ: BE /deleted thiếu tham số group (Controller:61-66 so với List:42) ⇒ không lọc được ở client (server đếm total trên tập chưa lọc ⇒ số trang nói dối) ⇒ xử bằng cách không cho UI tuyên nhóm ở màn đã-xoá (đổi sai-thầm-lặng thành thiếu-nhìn-thấy-được) + gắn cờ cho lane BE; và staticMap lệch có chủ đích (fe-user 6/6 mang group=1, fe-admin 5/6 vì WfView admin trỏ Designer).

[cao] FE-C — S166 K4a: hai tiền-đề lead sai trong CÙNG một đề bài

  • VIỆC: nới regex cho 42 leaf KHKK + gom nhãn nhóm về một nguồn. Artifact …/sub-implfe-k4a.md.
  • KẾT-LUẬN: ① đề bài bảo export KHKK_GROUP_LABELS mới, nhưng đĩa đã có KHKK_APPROVAL_GROUP_LABELS (types/khkk.ts:129, land từ K2, 5 call-site) ⇒ đẻ map thứ 2 chính là phá đúng cái acceptance-5 (single-source) mà nó phục vụ; đo thêm thấy nhãn BE ≡ `${n}. ${short}` cho 8/8 ⇒ giải bằng DERIVE khkkGroupMenuLabel(n), 0 call-site cũ bị đụng (hardcode lần 2 sẽ ra "N2 - 2. …" đánh số kép ở select). ② comment vá-8 bảo "phải nới regex trước vì sắp gỡ KeHoachKyKet khỏi ẩn-admin", nhưng isAdminHidden:194 = key.startsWith('Ct_')KHKK chưa bao giờ bị ẩn ⇒ viết comment theo ĐĨA + gắn cờ "7 nhóm rỗng" (MenuGroup chỉ xét children.length>0, không xét con có resolve được ⇒ header hiện, thân rỗng).
  • BÀI-HỌC: chứng regex bằng new Function() chạy CHÍNH khối text đã ship (indexOf('const khkkMatch') → slice → eval), key sinh từ KhkkGroupNumbers/KhkkLeafActions đọc thẳng MenuKeys.cs ⇒ 42/42 resolve, 0 drop, +3 control-âm (G9→null · action rác→null · Khkk_List nhóm-1-không-infix→null vì bị chặn ở staticMap trước). Nhãn cũng kèm control-âm (chèn 'x' vào N4 ⇒ phép so phải FLAG) — 0-hit trần là vô nghĩa.
  • BẤT-NGỜ: Khkk_G1 + 6 leaf KHÔNG có infix (S159, cấm đổi kẻo mồ côi permission-row) ⇒ 42 leaf infix chỉ thuộc nhóm 2..8; giữ [1-8] rộng hơn tập seed một nấc là cố ý; và Layout 2 app cố ý KHÔNG mirror (user +34 dòng regex · admin +13 dòng comment).

[cao] FE-D — S166 K5: "chuẩn bị sẵn cho sau" nghĩa là đổi hình dạng, không phải bịa field

  • VIỆC: mở 8 ngăn nhóm ở GĐ2 của cây toàn-trình. Artifact …/sub-implfe-k5.md.
  • KẾT-LUẬN: đề bài đòi GĐ3/4 đi qua lines[].contractId pe.contractId; đo 4 nơi đều ÂM (PeListItem 0 hit chữ lines · PurchaseEvaluation.cs:31 chỉ có Guid? ContractId đơn · ContractListItem không có purchaseEvaluationId · Contract.cs 0 khoá ngược). Bằng-chứng đắt nhất là BE tự khai: CreateContractFromEvaluationFeatures.cs:143-145 pe.ContractId = contracts[0].Id + comment "N HĐ liên-danh truy qua changelog" — endpoint TRẢ contractIds[] (PeDetailTabs.tsx:2186) nhưng chỉ HĐ đầu được ghi ngược ⇒ HĐ thứ 2 trở đi VÔ HÌNH. ⇒ chỉ wire vế làm được: contractIdsOfPackage = new Set<string>() (hợp) thay vì đọc 1 ô, đánh dấu sẵn chỗ cắm nguồn-2 + trích nguyên văn BE; FLAG 2 đường cho lead.
  • BÀI-HỌC: stage.leaves giữ vai danh sách PHẲNG canonical (nguồn đếm duy nhất), groups chỉ chia lại chính nó ⇒ bất-biến tổng là chuyện CẤU TRÚC, không phải kỷ-luật nhớ-cộng-cho-khớp. Hiện đủ 8 ngăn kể cả rỗng = .map trên hằng chứ không .filter; ngăn "(chưa phân nhóm)" chỉ mọc khi thật có phiếu lạc (8 nhóm là khung nghiệp-vụ cố định ⟂ ngăn kia là chỗ chứa bất thường).
  • BẤT-NGỜ: CA4 control-âm tiêm lỗi nhân-đôi ⇒ console.warn NỔ — không có CA4 thì "0 cảnh báo" ở CA1-3 vô nghĩa (im-vì-đúng ⟂ im-vì-câm); và 2 hash ĐỨNG YÊN (PipelineTreePanel d70ac315, PePipelineStrip 6fb7337e) được dùng làm đối chứng dương cho phép đo SHA.

[cao] FE-E — S153 T5: writeGuard là 2 điểm, chỉ một điểm có răng

  • VIỆC: T2 dựng template session-mind-template.md + T5 sửa hmw.js. Lead seed on-behalf @closeout M9 (T2 return mất schema-fail — #53 +1, phải derive-from-body; template VẪN LAND 8.819 B).
  • KẾT-LUẬN: VALID_ROLES 20→21 (sau T10/T11 = 23); 🔴 writeGuard nằm ở 2 CHỖ: :228 là comment JS (tài-liệu) :235 là template-literal ĐI VÀO PROMPT của sub. Chỉ sửa :235 ⇒ tài-liệu lệch trong chính file đó; chỉ sửa :228 ⇒ prompt runtime KHÔNG đổi ⇒ vai read-only bị hướng-dẫn như vai được ghi ⇒ Bash-write MD ⇒ mojibake G-009. :235 = điểm có RĂNG. Vùng cấm đo bằng diff: WIDTH_CAP 0 hit · TIER2_EXPECTED_FULL_ID 0 hit.
  • BÀI-HỌC: T2 — ts sống trong HEADING sau em-dash nên 0 dòng key ts: (không phạm rào-2); placeholder <k> trong schema-comment để parser không khớp nhầm; kiểm _SECRET_RES để ví-dụ của rào-3 không tự khớp mẫu của chính nó.
  • BẤT-NGỜ: 2 kẽ báo lead — ① va tên cùng-vai 2 lần gọi (chỉ số i reset mỗi lần gọi ⇒ đợt 2 suýt đè sub-implementer-frontend-1.md của đợt 1) = kẽ CÒN LẠI sau S134; ② node --check trên file thô LUÔN SyntaxError (top-level await) ⇒ acceptance phải là parse-sau-khi-bọc + đối chứng HEAD.

[vừa] FE-F — S166 K4c: rỗng-vì-lọc phải khác câu với rỗng-vì-chưa-có-gì

  • VIỆC: lọc trang WfView theo nhóm (đóng FLAG-3 của K4b: ?group= chưa có ai tiêu-thụ ⇒ 8 leaf mở ra 8 trang y hệt). fe-user ONLY, 1 file (+68/5), git diff -- fe-admin RỖNG.
  • KẾT-LUẬN: đo trước 4 thứ, không đoán cái nào — ① AwDefinitionDto.code là tên field THẬT (types/approvalWorkflowV2.ts:41, đang render :165) ② trang tải trọn bộ theo type rồi đọc summary.historylọc CLIENT-side, KHÔNG thêm trục group vào queryKey (thêm = 8 lần gọi mạng cho cùng payload + 8 ô cache trùng) ③ DbInitializer.cs:609 code = $"KHKK-N{n}" + :623 IsUserSelectable = true ⇒ 8 workflow thực sự lọt bộ lọc của trang (seed false thì lọc kiểu gì cũng trắng trang) ④ PageHeader bản đang import CÓ actions (repo có ≥2 bản khác chữ ký — lặp bài S37).
  • BÀI-HỌC: chốt chặn KÉP typeInt === 10 && /^[1-8]$/group là khái-niệm riêng của KHKK, type khác hoặc rác thì bỏ qua hoàn toàn (thà hiện đủ còn hơn lọc theo số vô nghĩa ra trang trắng); regex trên chuỗi bắt được "03"Number() sẽ nuốt; lọc giữ đủ VERSION cùng code (nghĩa là "quy trình của nhóm", không phải "1 bản ghi").
  • BẤT-NGỜ: dùng chung một câu cho hai loại rỗng thì user nhóm N5 tưởng cả module chưa cấu hình trong khi 7 nhóm kia đang chạy — cùng họ bài "sai-thầm-lặng → thiếu-nhìn-thấy-được"; chứng 21/21 kèm 2 control-âm (gỡ vị-ngữ lọc ⇒ ra 8 = phép đo phân-biệt được · helper sai khuôn KHKK_N3 ⇒ 0 wf, không âm thầm khớp).

[vừa] FE-G — S166 K6: đếm bằng cách DỰNG LẠI CÂY THẬT rồi chạy chính bộ lọc đã ship

  • VIỆC: thu gọn menu admin về đúng vai "nơi cấu-hình". fe-admin ONLY, 1 file (+52/19), fe-user 0-diff.
  • KẾT-LUẬN: spec treo ẩn-số "N = 7 hay 8 tuỳ AwV2_Contract có tồn tại"; đo ra 0 HIT toàn repo ⇒ 2 seed leaf + 8 designer ⇒ chốt N = 1 Dashboard + 4 System-leaf + 2 AwV2 + 8 designer = 15 (spec tự quên cộng 8 designer vào công thức của chính nó). Phép đếm: parse 72 tuple literal từ DbInitializer + sinh 128 key từ vòng lặp (hằng đọc thẳng MenuKeys.cs) = 200 node → chạy ADMIN_HIDDEN_KEYS + isAdminHidden + filterForAdmin cắt từ chính file166 lá → 15 lá. CONTROL-ÂM: bỏ 'KeHoachKyKet' khỏi Set ⇒ 15→63 (+48 = đúng 8 nhóm × 6 leaf).
  • BÀI-HỌC: filterForAdmin lọc ĐỆ QUY ⇒ ẩn 1 ROOT là cả cây con chết theo ⇒ tập chỉ cần liệt 12 ROOT (thêm Workflows/PeWorkflows vì chúng là CON của System); liệt từng leaf = thừa và phải bảo trì mỗi lần BE thêm key. Vá-14 có 2 lần đảo (S29 ẩn → S57 bỏ ẩn → QĐ7 S164 ẩn lại rộng hơn) ⇒ comment cũ đang nói NGƯỢC luật đang chạy, phải gỡ và viết lại kèm câu "cả hai đều từng đúng, mốc sau thắng mốc trước".
  • BẤT-NGỜ: cây dựng lại tự kiểm chính nó — đối chứng "113 key trong MenuKeys.All → 0 key thiếu menu-row (invariant T6)", dựng sai thì chỉ số này vỡ ngay; và bẫy suýt lẫn: resolvePath:173 CÓ nhánh nhận code 'Contract' nhưng route-resolver có nhánh ≠ menu-row tồn tại.

[vừa] FE-H — S164/S165 K2: tiền-đề lead sai, việc thật chỉ ≈ 1/4 mô tả

  • VIỆC: relaunch p2 sau khi p1 chết vì #53 — nhóm duyệt N1-N8 cho KHKK.
  • KẾT-LUẬN: lead khai CreatePage "mid-wire, 8 khai báo chưa vào JSX" (nghĩa là TS6133 sẽ đỏ); đo git diff trước khi gõ thì 8/8 ĐÃ có call-site, Detail cũng đã land ⇒ việc thật ≈ 1/4 mô tả, chỉ ListPage còn dở. p2 sửa đúng 3 điểm: badge N{g} nhét vào ô "Mã kế hoạch" (không đẻ cột — bảng đã cuộn ngang @1366 do cây GĐ 19rem) · thêm cột "Tổng duyệt" (BE trả 0 cho phiếu chưa duyệt ⇒ ép hiển thị kẻo đọc nhầm "đã duyệt 0 đ") · colSpan 7→8 ở hai chỗ (loading + empty row).
  • BÀI-HỌC: khuôn "WRITE-lane garble ≠ mất code" (S114) đúng thêm một lần: lead đọc transcript dở-dang nên kết-luận theo Ý ĐỊNH đang chạy, không theo đĩa ⇒ viết lại phần đã đúng chính là phá. npm run build = tsc -b && vite build với noUnusedLocals:truebuild LÀ phép thử TS6133 thật, không cần tin IDE (#68).
  • BẤT-NGỜ: quên colSpanlỗi CÂM cho tới lúc bảng rỗng — thứ chỉ lộ ở trạng-thái ít ai test; và SHA-pair 5/5 IDENTICAL với Panel không sửa (hash 2B64144EF6E8 BẰNG baseline) làm đối-chứng dương.

[vừa] FE-I — S161 ARC W1+W2+W3đ1: containment 2 lớp và hợp-đồng types

  • VIỆC: 3 lượt FE cho KHKK (Designer type-10 · 3 page + types · panel workflow). Mọi lượt đều #53, đĩa cứu trọn.
  • KẾT-LUẬN: W1 — containment phải 2 LỚP render + payload (levelFlagsPayload(e, lock) ép false ở 2/2 call-site; chỉ dùng disabledcontainment GIẢcopyFromDefinition tuồn state ra); 2 tiền-đề đề-bài sai tự bẻ (FE không có bảng nhãn — nhãn đến từ BE applicableTypeLabel; khuôn type-9 không tồn tại ⇒ soi gương Contract:3). W2 — types theo ENTITY khi BE chạy song song; header types = HỢP-ĐỒNG 12 endpoint (reviewer đối chiếu ra 6 chỗ đứt → lead vá). W3đ1 — KhkkWorkflowPanel soi gương PE-panel 1063 dòng theo bảng GIỮ/BỎ tường minh; cây workflow THÔ nên FE tự suy ✓/●/○ từ con-trỏ đôi (StepIndex 0-based vs Level.Order là giá-trị).
  • BÀI-HỌC: hợp-đồng giữa 2 lane phải do LEAD định-nghĩa literal — để mỗi lane tự suy thì đứt ở chỗ không ai nhìn; và "place-3 menuKeys SKIP-có-khai (0 consumer)" cho thấy bỏ qua có khai là hành-vi hợp lệ, khác hẳn bỏ sót.
  • BẤT-NGỜ: W2 khai thiếu approverUserId trong opinion, tới W3đ1 mới bổ — lỗ hổng hợp-đồng chỉ lộ ra ở lượt tiêu-thụ, không lộ ở lượt định-nghĩa.

[vừa/cao] FE-J1..J5 — 5 mục micro-lesson (giữ khối riêng từng mục)

  • VIỆC: 5 lượt FE 2-app: J1 S134 lũy-kế tạm tính · J2 S133 badge budget-frozen · J3 S117 radio thứ 5 ProMinMax · J4 S116 cho nhập số ÂM (cao) · J5 S113 import v2 + publish.
  • KẾT-LUẬN: J1 sub?: ReactNode cắm vào BudgetSharedNumCell:1184 + BudgetColValue:1197 ⇒ chèn sub-dòng amber vào N chỗ render không nhân bản markup; gate showPending = !bs.budgetFrozen && (...); hmw đánh chỉ-số sub-file GLOBAL theo taskList ⇒ hết va tên khi 2 vai KHÁC nhau cùng một lần gọi. J2 badge 🔒PeBudgetSummaryTable :1350-1359, 🔴 className header lặp 2 chỗ (:1350 budget và :1931 winners) ⇒ anchor của Edit phải kèm text nhìn thấy được ("Tổng hợp ngân sách trình ký") mới unique. J3 entry mảng có điều-kiện phải spread TRƯỚC as T[] (né suy-diễn sai); ?? undefined = cách bỏ key chuẩn của axios; Max không cần state riêng (derive từ selectedCandidate) ⇒ 0 chỗ phải reset. J4 🔴 2 dialog POST commit thẳng form.thanhTien (:2247/:2906, không có nút "Lưu") ⇒ nút đổi dấu phải ghi giá CÓ DẤU NGAY, chỉ lật cờ là MẤT DẤU thầm lặng; parseVnd/formatVndInput :51-52 là hàm dùng chung (chia với ngân sách) nên không sửa, bọc Math.abs; gate đổi thành !== 0 (không phải > 0) và 2 gate FE cùng đổi, không phải 1 (missingForApproval:212 + submitChecklist:243 → helper hasRealSelectedQuote). J5 nút "Công bố/Ẩn" gọi POST /suppliers/{id}/publishKHÔNG qua UpdateSupplier (#73); badge dùng full-class-literal (P14).
  • BÀI-HỌC: ba khuôn tái dùng được: slot-prop (J1) để mở rộng cell dùng chung, anchor theo chữ nhìn thấy (J2) khi className lặp, spread trước cast (J3). Và J4 dạy cái đắt nhất: khi form không có nút Lưu thì mọi thay đổi hiển-thị phải commit ngay, nếu không thì UI và payload lệch nhau trong im lặng.
  • BẤT-NGỜ: J4 — báo giá nằm ở details[].quotes chứ không phải suppliers[].quotes (đi nhầm nhánh là đo nhầm), và công thức cũ SUM winnerQuoteTotal chặn oan ca net-zero +100/100=0; J5 — mục "3.d disabled" hoá ra đã đúng sẵn ⇒ NO change, kèm một DEVIATION có khai ("Tải file mẫu" chỉ đặt trong dialog trong khi parent 2.d có nhắc thêm nút ở page).

[cao] FE-M — [CHỈ-MỤC] sợi chỉ "tiền-đề sai ⇒ đo đĩa trước khi gõ" ×5

  • VIỆC: đánh dấu chỗ lặp để lead viết 1 dòng luật thay vì 5 lần kể lại.
  • KẾT-LUẬN: K2 (lead khai mid-wire, thật ra 8/8 đã land — việc thật ≈1/4) · K4a (2 tiền-đề sai trong 1 đề bài: "chưa có map" thật ra có 5 call-site · "sắp gỡ ẩn" thật ra chưa từng bị ẩn) · K4b (lần 3 liên-tiếp, làm đúng nguyên văn sẽ giết 6 chip) · K5 (đề bài đòi đường dữ-liệu không tồn tại, đo 4 nơi đều âm) · K6 (spec tự quên 8 designer trong công thức của chính nó).
  • BÀI-HỌC: luật rút gọn: đo đĩa trước khi gõ, và khi đĩa cãi đề bài thì đĩa thắng — nhưng phải KHAI ra chỗ cãi (cả 5 lượt đều khai, nên lead sửa được spec).
  • BẤT-NGỜ: 5 lần sai đều không phải lỗi cẩu thả, mà là lead đọc transcript dở-dang hoặc spec viết trước khi code land ⇒ nguyên-nhân nằm ở độ trễ giữa lúc viết đề bài và lúc đọc đĩa, không nằm ở người viết đề bài.

Token-list (verbatim, implementer-frontend/2026-H2)

  • gotcha#: #82 ("Display-only #82: route+quyền còn nguyên, deep-link vẫn 200, thu gọn = bớt lối đi menu KHÔNG phải thu hồi quyền") · #68 ("build LÀ phép thử TS6133 thật, không cần tin IDE (#68)") · #73 ("POST /suppliers/{id}/publish (KHÔNG qua UpdateSupplier #73)") · #53 (×4: "Relaunch p2 sau p1 chết #53" · "mọi lượt #53, đĩa cứu trọn" · "T2 return mất schema-fail (#53 +1)" · hàm ý ở S161) · G-009 (mojibake — mã governance, không phải gotcha số) · P14 (badge full-class-literal) · 403 S89 (N9 KHÔNG link)
  • surprise (nhãn 🔴/BÀI/BẪY nguyên-văn): "🔴 BÀI TO NHẤT — HARNESS ĐO NHẦM CÁI RENDER BỊ REACT VỨT, suýt kết luận NGƯỢC" · "control-âm cũng ra 3 ⇒ 2 nhánh giống nhau = phép đo mất khả năng phân biệt, trông y như PASS" · "🔴 Tiền-đề đề-bài sai lần 3 liên tiếp (S164→K4a→K4b)" · "🔴 Khuôn S164 'đo đĩa TRƯỚC khi gõ' ăn tiếp 2 lần trong CÙNG 1 đề bài" · "🔴 BÀI CHÍNH — tiền-đề lead SAI, đo git diff TRƯỚC khi gõ … Việc thật ≈ 1/4 mô tả … Viết lại phần đã đúng = phá" · "🔴 BÀI: forward-provision đúng nghĩa = đổi HÌNH DẠNG, KHÔNG bịa field" · "🔴 Bẫy suýt lẫn: resolvePath:173 CÓ nhánh nhận code 'Contract' — nhưng route-resolver có nhánh ≠ menu-row tồn tại" · "(spec tự quên cộng 8 designer vào công thức của chính nó)" · "🔴 Rỗng-vì-lọc ⟂ rỗng-vì-chưa-có-gì phải khác câu" · "🔴 writeGuard là 2 ĐIỂM không phải 1" · "🔴 2 dialog POST commit form.thanhTien THẲNG … chỉ flip cờ = MẤT DẤU thầm lặng" · "🔴 GOTCHA quote ở details[].quotes KHÔNG suppliers[].quotes" · "🔴 Gotcha: className header LẶP 2 chỗ … Edit anchor PHẢI kèm text visible" · "2 hash đứng yên = đối chứng dương" (K5) · "Panel không sửa = đối chứng dương" (K2) · "types 7a99b865 không đổi = đối chứng dương" (K4b) · "im-vì-đúng ⟂ im-vì-câm" (CA4) · "0-hit trần vô nghĩa" (K4a) · "chỉ disabled = containment GIẢ" (W1) · "DEVIATION flagged" (S113) · "3.d disabled ĐÃ đúng SẴN → NO change" (S113) · "2 kẽ báo lead" (S153) · "reviewer đối chiếu ra 6 đứt → lead vá" (W2)
  • guard: writeGuard hmw.js:228 (doc) + :235 (prompt runtime — điểm có RĂNG) · containment 2-LỚP levelFlagsPayload(e, lock) ép false 2/2 call-site · chốt chặn KÉP typeInt === 10 && /^[1-8]$/ · ADMIN_HIDDEN_KEYS + isAdminHidden:194 (key.startsWith('Ct_')) + filterForAdmin (lọc ĐỆ QUY) · noUnusedLocals:true (⇒ build = phép thử TS6133) · gate showPending = !bs.budgetFrozen && (...) · gate !== 0 (thay > 0) + 2 gate missingForApproval:212 / submitChecklist:243hasRealSelectedQuote · preventDefault + stopPropagation trong <summary> · TRANSIENT_QUERY_KEYS:311 (group/filter/view ⇒ giữ active-state) · MenuGroup chỉ xét children.length>0 · CONTROL-ÂM (7 ca: K4a ×3 + nhãn ×1 · K4c ×2 · K5 CA4 · K6 15→63 · K4b control-âm giữ 3) · SHA-pair ×2 app + npm build ×2 PASS (sàn tự áp, S152) · "REFUSE việc <30 min/trivial" (rào nhận việc) · _SECRET_RES (rào-3 tự-không-khớp-mẫu) · placeholder <k> chống parser false-match · "0 dòng key ts:" (không phạm rào-2) · WIDTH_CAP 0 hit + TIER2_EXPECTED_FULL_ID 0 hit (vùng CẤM đo bằng diff)
  • root-cause: ① K4b — "setState gọi trong lúc render ⇒ React huỷ render đó + render lại ngay với state mới TRƯỚC khi commit; harness bản-1 chỉ chạy 1 pass" ② K5 — "pe.ContractId = contracts[0].Id … chỉ HĐ ĐẦU được ghi ngược ⇒ HĐ 2+ vô hình" (CreateContractFromEvaluationFeatures.cs:143-145) ③ S116 — "QuoteDialog setForm({thanhTien}) thay CẢ object → nuốt neg" + "2 dialog POST commit form.thanhTien THẲNG (:2247/:2906, no nút 'Lưu')" ④ S153 — "chỉ sửa :228prompt runtime KHÔNG đổi ⇒ vai read-only bị hướng-dẫn như write-sub ⇒ Bash-write MD ⇒ mojibake G-009" ⑤ K2 — "lead đọc transcript dở-dang → kết luận theo Ý ĐỊNH đang chạy, không theo đĩa" ⑥ K4b — BE /deleted thiếu group ⇒ "server đếm total trên tập chưa lọc → số trang nói dối" ⑦ S153 — "index i reset mỗi invocation ⇒ đợt-2 suýt đè sub-implementer-frontend-1.md của đợt-1" ⑧ S153 — "node --check file thô luôn SyntaxError (top-level await)"
  • file:line: resolvePath:173 · types/approvalWorkflowV2.ts:41 (+ render :165) · DbInitializer.cs:609 + :623 · PeDetailTabs.tsx:2186 · CreateContractFromEvaluationFeatures.cs:143-145 · PurchaseEvaluation.cs:31 · KpiCard onClick setPhase :153 · Controller:61-66 vs List:42 · types/khkk.ts:129 + comment :125-127 · isAdminHidden:194 · TRANSIENT_QUERY_KEYS:311 · hmw.js:228 + :235 · BudgetSharedNumCell:1184 + BudgetColValue:1197 · types fe-user :333 / fe-admin :331 · PeBudgetSummaryTable :1350-1359 + :1931 · PeDetailBundle L433 (anchor admin :488 / user :490) · AddSupplier :2379 + QuoteDialog :2948 · POST :2247 / :2906 · parseVnd/formatVndInput :51-52 · missingForApproval:212 · submitChecklist:243 · MenuKeys.cs (MenuKeys.All 113 key) · Contract.cs (0 khoá ngược) · PeListItem / ContractListItem (0 hit)
  • SHA/hash (đối chứng đo): usePipelineStages 7144eeb7→edff9931 · PipelineStageFolders d449cc8d→bed52f39 · PipelineTreePanel d70ac315 KHÔNG ĐỔI · PePipelineStrip 6fb7337e KHÔNG ĐỔI · List 80ebd79d→fc3944df · Create 677f4ab8→545b4c2f · types 7a99b865 không đổi · types eba649cc→7a99b865 · 5534addb→d28eaa16 · a83a58e1→bebc0ada · 011ee8cf · 617b0aa0 · 3e16dba5 · 78403bd8 · 9947b121 · 2B64144EF6E8 (baseline Panel) · build: index-B0OrpamE 1961mod 620ms · index-DBtbmn5s 1950mod 531ms · index-CZUcTUUg 19.8s / index-BZWvx0Y8 1961mod 10.9s · index-C4F7kHKT 3.6s / index-kv90BBYQ 0.9s · index-B2v0kn5F 1950mod 20.8s / index-84zDBHE- 1961mod 8.2s
  • định-danh run/artifact: runs/2026-07-31-S164-4gd-khkk-fanout/sub-implfe-k6.md · …/sub-implfe-k4c.md · …/sub-implfe-k5.md · …/sub-implfe-k4b.md · …/sub-implfe-k4a.md · …/sub-implfe-k2.md (§1-§5) · runs/2026-07-29-S161-khkk-*/ · .claude/templates/session-mind-template.md (8.819 B) · wf_58302bd5-b9f INV-2.B (S134, engine opus-worker) · chứng: 21/21 (K4c) · 15/15 (K5) · 42/42 resolve + 3 control-âm (K4a) · VALID_ROLES 20→21→23

Ghi-chú liên-file (FLAG-only cho lead — KHÔNG gộp, giữ nguyên tắc đóng-kín từng file)

Phần này KHÔNG phải bản nén. Đây là các quan-hệ giữa file mà lead cần biết trước khi hợp nhất, để không đếm 2 lần hoặc nén mất một bên.

  1. Bản sao nguyên-văn cùng vai: investigator-codebase/2026-05-q1 (mục "2026-05-11 (setup)") và …/2026-05-q3 (mục "2026-05-11 — Setup baseline") có nội-dung trùng ("44 gotchas + 14 memory entries + 6 skills + 27 mig + 81 test pass cumulative … Awaiting first SendMessage"). Đề-xuất: giữ 1 bản, bản kia thành con-trỏ.
  2. Cùng sự-kiện, 2 độ dài: audit 2 bug changelog S25 có bản DÀI (q2, 5Q đầy đủ) và bản NGẮN (q3, wrap + kết quả CI #215 FAIL → #216-#221 PASS). Nén mất bản dài thì mất file:line; nén mất bản ngắn thì mất kết-cục CI.
  3. Cùng sự-kiện, 2 VAI (rủi-ro đếm đôi khi hợp nhất): ① bug lookup per-NV — investigator-q1 B1-A (chẩn-đoán :765) ↔ reviewer-q1 B5-B (Plan N vá 1/5 site → Plan O cascade 4 site). ② Plan P — investigator-q1 B1-B (phát-hiện body 3 field) ↔ reviewer-q1 B5-B ("root cause 2 ngày prod bug F1+F2"). ③ Plan AA — investigator-q1 B1-E ↔ reviewer-q1 B5-D. Đề-xuất: giữ cả hai nhưng ghi rõ vai, vì góc nhìn khác nhau (chẩn-đoán ⟂ nghiệm-thu).
  4. Một tên, hai nghĩa: trong investigator-q2, "Mig 32" xuất-hiện với 2 nghĩa (Plan AG đề-xuất AddProjectPackageTable · Plan B thực-tế AddApprovalWorkflowToContract). Nén ẩu sẽ tạo mâu-thuẫn giả.
  5. Một gotcha, hai cách chú-giải: #48 được chú "SQLite frozen clock tie-break" ở reviewer-q2 + investigator-q3 + test-specialist (dùng làm lý-do lọc theo ContextNote), nhưng ở batch A (sub-harvest-curator-sleep-batchA-S170.md:43) #48 được chú là "refactor >100 LOC Service chạm changelog/audit". Hai chú-giải nằm cạnh nhau trong cùng dòng từ-khoá gốc của reviewer-q2đề-nghị lead chốt 1 chú-giải canonical khi hợp nhất (đây là quan-sát, KHÔNG phải phán một bên sai).
  6. 3 mục liên-quan trực-tiếp tới sức-khoẻ HARVEST (H2 lưu ý riêng):investigator-q4 header — MEMORY trỏ file vắng đĩa, nội-dung git-only d2f52ba, và lô S29-S37 tới nay vẫn khai là chỉ nằm trong git (nếu lead muốn kéo về đĩa thì đây là đầu mối; tôi chưa kiểm trạng-thái hiện tại). ② test-specialist S133 — 2 lần gọi cùng vai trong 1 run-folder ghi đè sub-file của nhau, nội-dung sống nhờ journal + diary (rủi-ro mất nguyên-liệu harvest tại nguồn). ③ investigator-q3 S27 — vai tự nghi-ngờ lịch-sử spawn của chính mình ("agent type NOT FOUND", "trust prior memory entries but FLAG uncertainty"); đây là token loại Fidelity, giữ nguyên, không tự phán.
  7. Ràng buộc nén đã tuân: test-specialist giữ đủ 16 mốc chuỗi gốc + 12 mốc hậu-chuỗi (xem TS-A) và gắn cờ 4 lỗ + 2 mâu-thuẫn nội-tại; implementer-frontend giữ 14/14 mục moved-not-cut ở khối riêng, kèm nguyên chuỗi tag Ctrl-F, mục auto-refuse (dòng 78) tag cao.

Tự-kiểm (harvest-curator, propose-only)

  • Phạm-vi ghi: DUY NHẤT file này. 0 chạm .claude/agent-memory/**, 0 store_memory, 0 sửa canonical. Viết bằng Write/Edit tool (KHÔNG Bash-append) theo rào chống mojibake của chính vai.
  • Anti-#53: file được bồi 5 lượt ghi tăng dần (header+§1 → §2-3 → §4-5 → §6-7 → §8+kết), mỗi lượt tự đứng được nếu lượt sau chết.
  • Tự bắt 1 lỗi trong lúc làm: lượt ghi §1 lỡ kéo :379-387 (thuộc q2) vào token-list của q1 ⇒ đã gỡ ngay bằng Edit — đúng loại cross-file bleed mà lệnh đóng-kín nhắm chặn.
  • Giới hạn hiệu-lực: mọi con số/verdict ở trên là trích từ verbatim, KHÔNG phải phép đo mới của tôi. Cụ-thể: tôi không chạy dotnet test, không chạy git log/show để xác-minh commit, không mở các artifact runs/…/sub-implfe-*.md được dẫn. Mâu-thuẫn nêu ở TS-A là mâu-thuẫn nội-tại của văn bản.
  • Trạng-thái: Phase 1 (GATHER + DEDUP) — đề-xuất, chờ lead VERIFY rồi mới APPEND (B3). Bản nén 4-field đủ field ở 100% cụm (không cụm nào thiếu VIỆC/KẾT-LUẬN/BÀI-HỌC/BẤT-NGỜ).

== HẾT ==