121 KiB
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 · accessToken ≠ token · 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ỎNG —
PeDetailTabs.tsx:118itemsReadOnly = readOnly && !approverEditModeoverride được, còn:245truyềnreadOnly={readOnly}xuống BudgetAdjustSection và:973canAdjust = !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ànhisAdmin || (!readOnly && isDrafter && isDrafterPhase) || isApproverChoDuyet(đã land, thấy lại ở:977). (ii) BE lấy nhầm hàng:PurchaseEvaluationFeatures.cs:765Levels.FirstOrDefault(l => l.Order == curLevelOrder)— sau Mig 29 tách 1 Level-row PER ApproverUser thìOrdertrùng nhau trong cùng Cấp ⇒ EF trả row đầu theo PK; 2 token (admin + nv.test) gọi API ra CÙNGcurrentLevelOptions⇒ 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/ F3PurchaseEvaluationDetailFeatures.cs:42-99/ F4PurchaseEvaluationFeatures.cs:272-329đều giữ Phase=ChoDuyet; chỉ F1.Drafter:268-275đặtPhase=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
FirstOrDefaulttheo 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-địnhreadOnly=false, chỉ màn Danh sách?pendingMe=1desktop 3-panel mới hard-codereadOnly={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 khiTraLai=98≠DangSoanThao=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-124api.post(/transitions, body)gửi ĐỦ (TargetPhase + Decision + Comment + ReturnMode + ReturnTargetUserId + SkipToFinal, object literal không typed-service); BEPurchaseEvaluationsController.cs:267recordTransitionPeBodychỉ 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.AllowDrafterSkipToFinal→ApprovalWorkflowLevels.AllowApproverSkipToFinal(đổi cả ngữ-nghĩa: Drafter-Nháp → Approver-ChoDuyet). - KẾT-LUẬN: neo đủ 20+ điểm (
User.cs:38·ApprovalWorkflow.cs:86-1056 cờ · nhánh DrafterPurchaseEvaluationWorkflowService.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:873nhãn slot +:853-933panel 6 checkbox ·UsersPage.tsx:306-318/:365-372/:181-186· PeDetailTabs:287-297admin /:294-304user). Path migration thật =Persistence/Migrations/(KHÔNG phảiMigrations/). - 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.ApprovalWorkflowIdFK Restrict +ApprovalWorkflowkế-thừaBaseEntity(KHÔNG soft-delete) ⇒ buộc hard-DELETE;LevelOpinion → ApprovalWorkflowLevelRestrict 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) đòiSET QUOTED_IDENTIFIER ONmớ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 filterIsUserSelectable; menu seedDbInitializer.cs:1429-1437+ quyền:1541-1547cho 7 role (Accounting KHÔNG có, admin cấp tay); DesignerApprovalWorkflowsV2Page.tsx975 dòng, 13 field DTO / 7 cờ, nhãn VI:892-948; sidebarLayout.tsx:325(user) +:218(admin)w-60 xl:w-72→w-72 xl:w-80an-toàn @1280px,w-80 xl:w-96là ngưỡng rủi-ro; enumApprovalWorkflow.cs:45-50{DuyetNcc=1, DuyetNccPhuongAn=2, Contract=3}. 7 commit:ee776d5BE+Layout ·c667802FE Page ·ac2c859docs · 4 polishda218f1/4d60598/fbbd361/ee0902a. - BÀI-HỌC: Tailwind JIT đòi chuỗi class ĐẦY ĐỦ trong mảng —
bg-${color}-100bị 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ỏtruncateKHÔ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_breakpointkhai "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=3khai 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@12345611 ký-tự FAIL 400,TestUser@2026PASS); đổi tên user là 4 trường nguyên-tử (Email + NormalizedEmail + UserName + NormalizedUserName + FullName — xác nhận gotcha #38), sqlcmd cầnSET QUOTED_IDENTIFIER ON; login trảaccessToken/refreshToken/user— KHÔNG có fieldtoken(ví-dụ Bash trong spec dùng.tokensẽ 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
/inboxroute (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 485ConflictExceptionnon-admin + flag off (F2 skipToFinal) ·ApplyReturnModeAsyncgate per-slot per-modePurchaseEvaluationWorkflowService.cs:252-265(Admin bypass :252) · entry guard:760-762(pointer null khi DaDuyet/TraLai/TuChoi) · BE budget-adjustConflictExceptionnếuAllow=false· FK RestrictPE.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 khiisApproverChoDuyetđược xét (PeDetailTabs.tsx:973, nút ẩn:1030) ② "BE line 765 lookup semantic broken sau Mig 29" —Level.Ordertrùng nhau sau khi tách 1 row/ApproverUser ⇒FirstOrDefault(Order==X)lấy row đầu theo PK (PurchaseEvaluationFeatures.cs:765) ③ Plan P —TransitionPeBodyrecord 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) · mig20260513160703_AddAllowApproverEditBudgetToLevels - định-danh khác: commit
a1a910f..ee0902a(7):ee776d5·c667802·ac2c859·da218f1·4d60598·fbbd361·ee0902a· bug UAT prod409a967· phiếuPE/2026/A/026· WFQT-DN-V2-001 v12· usernv.test@solutions.com.vn·TransitionAsyncparamskipToFinalthứ 8 (position 47:47) ·w-60 xl:w-72→w-72 xl:w-80(ngưỡng rủi-row-80 xl:w-96) · grid[260px_1fr] xl:[320px_1fr]·STEP_PALETTE/LEVEL_PALETTE5 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 ApplyReturnModeAsyncZERO dòng log — 4 nhánh chỉ đổi con-trỏ rồi trả summary, người gọiTransitionAsync:100 LogTransitionAsyncchỉ log chuyển-phase, không log tác-dụng-phụ của mode. Query handlerListPurchaseEvaluationChangelogsQueryHandler:1050-1064không lọc gì ⇒ nếu mất thì mất ở FE. Đường vá: B2 trước (thêmChangelogs.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à
EntityTypequá 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=5tồ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-5825 field, sẵn V1 (WorkflowDefinitionId:22,CurrentWorkflowStepIndex:39,Phase:12,RejectedAtStepIndex:40) thiếuApprovalWorkflowId+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-219220 LOC chỉ V1, sinh mã HĐ:148-155ở bước cuối →DaPhatHanh(terminal KHÁC PEDaDuyet). 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 =ApproveV2AsyncPurchaseEvaluationWorkflowService.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ênApprovalWorkflowLevels:86-114Contract 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)RejectedAtStepIndexbên Contract:40đã chết mà chưa đánh dấu obsolete, lệch với PE:50; (c) 7ContractTypeđối đầu 1ApplicableType=3chung — có thể phải thêmContractTypes 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:1333-panellg:grid-cols-[340px_1fr_360px], thân phẳng<ul><li>:199-252, pageSize 50;Master/Project.cs:5-14thiế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=5enum 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)RejectedAtStepIndexContract 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·ApprovalWorkflowLevelsentity:86-114· PERejectedAtStepIndex: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ấtAddProjectPackageTable(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/dashboardvàorag-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ểuinvestigatorNOT 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 spawngeneral-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 =
ApplyReturnModeAsync4 mode khôngChangelog.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 commite23f51c..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
investigatoragent 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 spawngeneral-purposeagent 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+ URLlocalhost:6333/dashboard) - định-danh khác: commit range
e23f51c..506cada(7 commit) · CI run#215FAIL →#216-#221PASS · 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.mddẫnarchive/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 gitd2f52bachứ 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?+WorkflowAppStatus5-state khớp ProposalStatus); phần code mới là SKELETONApplication/Office/WorkflowAppsFeatures.cs:11-15(chỉ Create+List). Mẫu soi gương HOÀN HẢO =ProposalFeatures.cs:403-486(làm phẳngSteps.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:72khai Level KHÔNG OR-of-N (1 ApproverUserId/Level) — NGƯỢC với trí-nhớ cũ "OR-of-N", đòi verify lại; (b) enumApplicableTypeTHIẾU Travel (có Leave=5/OT=6/Vehicle=7/ItTicket=8) và migrationExtendApplicableTypeForWorkflowAppscóUp/DownRỖ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_codeBM25 +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:72nói Level KHÔNG OR-of-N (1 ApproverUserId/Level) — KHÁC memory cũ 'OR-of-N', verify lại" · "⚠️ enumApplicableTypeTHIẾU Travel (có Leave=5/OT=6/Vehicle=7/ItTicket=8);ExtendApplicableTypeForWorkflowAppsmig 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-onlyd2f52ba)" - 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) · migAddAttendances(mới nhất) · migExtendApplicableTypeForWorkflowApps(Up/Down rỗng) ·WorkflowAppStatus5-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; guardskipToFinalcho V1 đặt ở người gọi (:147-149trong TransitionAsync) nên V1 giữ nguyên chữ ký cũ. sqlcmd Dev xác nhận 7 cộtAllow*+Users.AllowDrafterSkipToFinalđã drop (count=0), snapshot regen sạch,HasDefaultValue(false)có nối, Mig 31Up()đảo tay ADD→DROP đúng khuôn. Major-1: endpoint zombiePATCH /api/users/{id}/allow-skip-finalcò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:78vẫn nói cờ "đã move sang Users…" trong khi:107-113là prop mới. Minor:PurchaseEvaluationFeatures.cs:401còn chữ "Drafter";ApprovalWorkflowConfiguration.cs:22chư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-quyPurchaseEvaluationPerNvLookupRegressionTests.cs; Plan P thêm 3 field vàoTransitionPeBody: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:Disabledtắ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:364→ giữ ChoDuyet"; người gọi:94-100truyền Phase mới vào log ⇒ ContextNote khớp assert của test. Drafter mode:268-275giữ nguyên nghĩa; Assignee:335-360vẫ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ẫn98:'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-33gọi thật (không mock) → routeApp.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 quaSeedPurchaseEvaluationPermissionDefaultsAsync:1573. Ghi chú rủi-ro thấp: người không phải admin có thể truyềnisUserSelectable=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 commita1a910f..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 testmỗ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); helperEnsureCanRejectV2Asyncchặn request giả mạoPATCH /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-ignorebỏ sótagent-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=falsenon-admin leak workflow chưa ghim" · "Non-admin passisUserSelectable=falseBE 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-verifybypass" (×2) · class-level[Authorize]bare:18giữ nguyên ·HasDefaultValue(false)EF config ·MenuItemConfiguration HasKey(Key)PK unique ⇒ ToDictionary an toàn ·PermissionGuard/usePermissionKHÔ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-finalsilent NoOp (Task.CompletedTask) ② "Plan P ControllerTransitionPeBody:267record +3 fields mirrorTransitionPurchaseEvaluationCommandschema — 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 testPurchaseEvaluationPerNvLookupRegressionTests.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ớiApplyReturnMode_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ộtSaveChangesAsyncnhận cùng dấu thời-gian ⇒OrderByDescending(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 testkhi 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
0bf6c7e2 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-deterministicOrderByDescending(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 testrecurring 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 mirror21001E90...· commitcdfd542·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.csthêm changelog ở:126rồiRemove(pe)ở:142;CascadeDeleteTiming.Immediatecascade ngay tạiRemove, thực-thể con đang ở trạng-tháiAddedbị đẩy sangDetached(không phảiDeleted) ⇒ 0 row;AuditingInterceptorchạy ởSavingChanges= SAU điểm mất nên không cứu được. Đo bằng chẩn-đoánChangeTracker(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ó wireAuditingInterceptor— bắt buộc khi đoRemove()nghĩa là xoá-mềm, vìTestApplicationDbContextkhô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-tsMALFORMED vẫn cho exit 0 —inject_Aneo vào## MIND-1sau khirenum(...,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/reprtừ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;
_endgiả_pause=9vs không = chứng nguồn đếm là ĐĨA, dùngdiffchứ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 QUAsvc.TransitionAsync(V2 1-Bước-1-CấpAllowApproverFinalize=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ị quaUpdatePeBudgetPro/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 (:426T1 đủ-11-cột ·:480T2 CCM-threshold ·:524T3 all-steps ·:555T4 V1-legacy ·:638T5 guard CCM+Admin ·:687T6 AdjustBudget ·:730T7 live-vs-frozen ·:783T8 null-safe ·:821T9 overwrite+refresh) → lead chạy full exit-0: Domain 45 + Infra 474 = 519. - BÀI-HỌC: INV-C ghi đè
sub-test-specialist-0.mdcủ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 ghiGetPurchaseEvaluationDetailQuery; 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 seedPeBudgetFreezeTests: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+ComputePendingAsynctrê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ọcContextNote.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
curPeIdtổng-hợp +CreatedAtmuộn, không cần row phiếu đang xét (chỉ lọcId!=/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) FluentAssertionsContainInOrder(params T[])đụng overload với chuỗibecausekhi T=Guid ⇒ bọcnew[]{...}. - 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+ExportContractsnay[Authorize(Policy="Reports.Read")], cònMyDashboardcố ý để 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. S118ProcurementMasterAccessSeedTests.cs4 [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. S92AdminOnlyModulesRevokeTests.cs3 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+HrmProfilePermissionSeedTestsVẪ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ỗiSeedAsync⇒ test 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()<=0sangCount==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,GiaTriper-winner = tổng ThanhTien báo giá của ĐÚNG winner (140/260) ⟂ ca single dùng legacyThanhTienNganSach(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.Invokekhông tự bind optional param ⇒ giữ 4 arg làTargetParameterCountExceptionphá 3 test cũ (tự gây, tự tránh). ② S72 — nhánhisSystemKHÔNG với tới được quaApproveV2Asynccô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.PESupplierIdcó FK 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 khiContract.SupplierId/ProjectIdkhông có FK vật lý — hai quy-ước ngược nhau trong cùng một luồng. ④ S87 —Changelog.UserIdlàGuid?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êntruonggiangvsTRUONGGIANGra Inserted=0/Updated=1; nếu service dùngOrdinalthì 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 = stringgiữ 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:
NewSupplierCode = r.Code!.Trim()→ NRE khirow.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 khaiExpectedHeaderTokenschỉ là PHỎNG ĐOÁN ≠ 30 token thật ở dòng 4 ⇒ Case 9 so hai chiều động, hiệnLayoutValid=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
ApplyDrafterBypassOnSubmitAsyncsang 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ờEndedByLevelFinalizethay vì suy từ config, test cũ thêm param + ca tái-hiện mớipeFinButWentCeo(cùng config nhưng cờ FALSE). S86 chứng RED-trên-code-cũ bằnggit stashrồ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êmnull) — 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ớiisProTp, 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ênGuid?KHÔNG bắtGuid.Empty(FluentValidation 7.2 coidefault(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ứcIsolationLevel.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ọcContextNote.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.mdcủa INV-A … sub-file collision" (S133) · "Bài học mới quan trọng nhất: fixturedraft-tsMALFORMED 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) · "⚠️ApprovalWorkflowIdFK 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 wireAuditingInterceptor) · "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.Immediatecascade ngay tại Remove; dependent đangAddedbị cascade ⇒ Detached ⇒ 0 row;AuditingInterceptorchạy ở SavingChanges = SAU điểm mất" ② S153 — "inject_Aneo## MIND-1sau khirenum(...,2)đã đổi heading ⇒str.find=-1 ⇒ chèn GIỮA heading ⇒ts:không ở đầu dòng" ③ S113 — "NewSupplierCode=r.Code!.Trim()→ NRE khirow.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 quaMethodInfo.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-gate152-157· Quote FKcfg:135· call-sitePeWorkItemBudgetTests 388/407/421·PurchaseEvaluationUrgentFeatures.cs:49-55·Api/AuthorizePolicyRegressionTests.cs(15→19 [Fact]) · csprojline 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
setStatetrong 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 raphase=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 raphase=2, control-âm giữ3. Giải bài chính: override 3 trạng-tháichipPhase: T|null|undefined(undefined= "nghe URL" ≠null= "user chọn Tất cả") + reset trong render theonavKey, KHÔNG dùnguseEffect(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
phasetừ searchParams thayuseState") sẽ giết 6 chip lọc (KpiCard onClick setPhase :153) — hỏng CÂM, build vẫn xanh. - BẤT-NGỜ: BE
/deletedthiếu tham sốgroup(Controller:61-66so vớiList:42) ⇒ không lọc được ở client (server đếmtotaltrê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 manggroup=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_LABELSmớ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 DERIVEkhkkGroupMenuLabel(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ỡKeHoachKyKetkhỏi ẩn-admin", nhưngisAdminHidden:194=key.startsWith('Ct_')⇒ KHKK chưa bao giờ bị ẩn ⇒ viết comment theo ĐĨA + gắn cờ "7 nhóm rỗng" (MenuGroupchỉ xétchildren.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ẳngMenuKeys.cs⇒ 42/42 resolve, 0 drop, +3 control-âm (G9→null · action rác→null ·Khkk_Listnhó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 (PeListItem0 hit chữlines·PurchaseEvaluation.cs:31chỉ cóGuid? ContractIdđơn ·ContractListItemkhông cópurchaseEvaluationId·Contract.cs0 khoá ngược). Bằng-chứng đắt nhất là BE tự khai:CreateContractFromEvaluationFeatures.cs:143-145pe.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.leavesgiữ vai danh sách PHẲNG canonical (nguồn đếm duy nhất),groupschỉ 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 =.maptrê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.warnNỔ — 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 (PipelineTreePaneld70ac315,PePipelineStrip6fb7337e) đượ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ửahmw.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_ROLES20→21 (sau T10/T11 = 23); 🔴writeGuardnằm ở 2 CHỖ::228là comment JS (tài-liệu) và:235là 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_CAP0 hit ·TIER2_EXPECTED_FULL_ID0 hit. - BÀI-HỌC: T2 —
tssống trong HEADING sau em-dash nên 0 dòng keyts:(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ố
ireset mỗi lần gọi ⇒ đợt 2 suýt đèsub-implementer-frontend-1.mdcủa đợt 1) = kẽ CÒN LẠI sau S134; ②node --checktrê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-adminRỖNG. - KẾT-LUẬN: đo trước 4 thứ, không đoán cái nào — ①
AwDefinitionDto.codelà tên field THẬT (types/approvalWorkflowV2.ts:41, đang render:165) ② trang tải trọn bộ theo type rồi đọcsummary.history⇒ lọc CLIENT-side, KHÔNG thêm trụcgroupvào queryKey (thêm = 8 lần gọi mạng cho cùng payload + 8 ô cache trùng) ③DbInitializer.cs:609code = $"KHKK-N{n}"+:623IsUserSelectable = true⇒ 8 workflow thực sự lọt bộ lọc của trang (seedfalsethì lọc kiểu gì cũng trắng trang) ④PageHeaderbả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]$/—grouplà 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"mà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_Contractcó 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ẳngMenuKeys.cs) = 200 node → chạyADMIN_HIDDEN_KEYS+isAdminHidden+filterForAdmincắt từ chính file → 166 lá → 15 lá. CONTROL-ÂM: bỏ'KeHoachKyKet'khỏi Set ⇒ 15→63 (+48 = đúng 8 nhóm × 6 leaf). - BÀI-HỌC:
filterForAdminlọc ĐỆ QUY ⇒ ẩn 1 ROOT là cả cây con chết theo ⇒ tập chỉ cần liệt 12 ROOT (thêmWorkflows/PeWorkflowsvì chúng là CON củaSystem); 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:173CÓ 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 difftrướ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: badgeN{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 đ") ·colSpan7→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 buildvớinoUnusedLocals:true⇒ build LÀ phép thử TS6133 thật, không cần tin IDE (#68). - BẤT-NGỜ: quên
colSpanlà lỗ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 (hash2B64144EF6E8BẰ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ùngdisabledlà containment GIẢ vìcopyFromDefinitiontuồn state ra); 2 tiền-đề đề-bài sai tự bẻ (FE không có bảng nhãn — nhãn đến từ BEapplicableTypeLabel; khuôn type-9 không tồn tại ⇒ soi gươngContract: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 —KhkkWorkflowPanelsoi 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 vsLevel.Orderlà 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
approverUserIdtrong 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?: ReactNodecắm vàoBudgetSharedNumCell:1184+BudgetColValue:1197⇒ chèn sub-dòng amber vào N chỗ render không nhân bản markup; gateshowPending = !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ỗ (:1350budget và:1931winners) ⇒ 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ƯỚCas 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ẳngform.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-52là hàm dùng chung (chia với ngân sách) nên không sửa, bọcMath.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→ helperhasRealSelectedQuote). J5 nút "Công bố/Ẩn" gọiPOST /suppliers/{id}/publish— KHÔ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[].quoteschứ không phảisuppliers[].quotes(đi nhầm nhánh là đo nhầm), và công thức cũ SUMwinnerQuoteTotalchặ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 diffTRƯỚ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:173CÓ 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 commitform.thanhTienTHẲNG … chỉ flip cờ = MẤT DẤU thầm lặng" · "🔴 GOTCHA quote ởdetails[].quotesKHÔNGsuppliers[].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) · "types7a99b865khô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:
writeGuardhmw.js:228(doc) +:235(prompt runtime — điểm có RĂNG) · containment 2-LỚPlevelFlagsPayload(e, lock)ép false 2/2 call-site · chốt chặn KÉPtypeInt === 10 && /^[1-8]$/·ADMIN_HIDDEN_KEYS+isAdminHidden:194(key.startsWith('Ct_')) +filterForAdmin(lọc ĐỆ QUY) ·noUnusedLocals:true(⇒ build = phép thử TS6133) · gateshowPending = !bs.budgetFrozen && (...)· gate!== 0(thay> 0) + 2 gatemissingForApproval:212/submitChecklist:243→hasRealSelectedQuote·preventDefault+stopPropagationtrong<summary>·TRANSIENT_QUERY_KEYS:311(group/filter/view ∉ ⇒ giữ active-state) ·MenuGroupchỉ xétchildren.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 keyts:" (không phạm rào-2) ·WIDTH_CAP0 hit +TIER2_EXPECTED_FULL_ID0 hit (vùng CẤM đo bằng diff) - root-cause: ① K4b — "
setStategọ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 — "QuoteDialogsetForm({thanhTien})thay CẢ object → nuốt neg" + "2 dialog POST commitform.thanhTienTHẲNG (:2247/:2906, no nút 'Lưu')" ④ S153 — "chỉ sửa:228⇒ prompt 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/deletedthiếugroup⇒ "server đếmtotaltrên tập chưa lọc → số trang nói dối" ⑦ S153 — "indexireset mỗi invocation ⇒ đợt-2 suýt đèsub-implementer-frontend-1.mdcủa đợt-1" ⑧ S153 — "node --checkfile 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-66vsList:42·types/khkk.ts:129+ comment:125-127·isAdminHidden:194·TRANSIENT_QUERY_KEYS:311·hmw.js:228+:235·BudgetSharedNumCell:1184+BudgetColValue:1197· typesfe-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.All113 key) ·Contract.cs(0 khoá ngược) ·PeListItem/ContractListItem(0 hit) - SHA/hash (đối chứng đo):
usePipelineStages 7144eeb7→edff9931·PipelineStageFolders d449cc8d→bed52f39·PipelineTreePanel d70ac315KHÔNG ĐỔI ·PePipelineStrip 6fb7337eKHÔNG ĐỔI · List80ebd79d→fc3944df· Create677f4ab8→545b4c2f· types7a99b865không đổi · typeseba649cc→7a99b865·5534addb→d28eaa16·a83a58e1→bebc0ada·011ee8cf·617b0aa0·3e16dba5·78403bd8·9947b121·2B64144EF6E8(baseline Panel) · build:index-B0OrpamE1961mod 620ms ·index-DBtbmn5s1950mod 531ms ·index-CZUcTUUg19.8s /index-BZWvx0Y81961mod 10.9s ·index-C4F7kHKT3.6s /index-kv90BBYQ0.9s ·index-B2v0kn5F1950mod 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_ROLES20→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.
- 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ỏ. - 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
#215FAIL →#216-#221PASS). 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. - Cùng sự-kiện, 2 VAI (rủi-ro đếm đôi khi hợp nhất): ① bug lookup per-NV —
investigator-q1B1-A (chẩn-đoán:765) ↔reviewer-q1B5-B (Plan N vá 1/5 site → Plan O cascade 4 site). ② Plan P —investigator-q1B1-B (phát-hiện body 3 field) ↔reviewer-q1B5-B ("root cause 2 ngày prod bug F1+F2"). ③ Plan AA —investigator-q1B1-E ↔reviewer-q1B5-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). - Một tên, hai nghĩa: trong
investigator-q2, "Mig 32" xuất-hiện với 2 nghĩa (Plan AG đề-xuấtAddProjectPackageTable· Plan B thực-tếAddApprovalWorkflowToContract). Nén ẩu sẽ tạo mâu-thuẫn giả. - 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 theoContextNote), 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ủareviewer-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). - 3 mục liên-quan trực-tiếp tới sức-khoẻ HARVEST (H2 lưu ý riêng): ①
investigator-q4header — MEMORY trỏ file vắng đĩa, nội-dung git-onlyd2f52ba, 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-specialistS133 — 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-q3S27 — 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. - Ràng buộc nén đã tuân:
test-specialistgiữ đủ 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-frontendgiữ 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/**, 0store_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ạygit log/showđể xác-minh commit, không mở các artifactruns/…/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 ==