wal: flush 20260803T1053

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

View File

@ -10,7 +10,7 @@ chain:
- [x] V2 ring2-audit — END VERDICT=13Đ/2T (đĩa; lượt-1 skeleton 1.020B, bản chốt = lượt-2)
- [x] hậu-V2 — signal-write S170 OK: light_at=44, 8 class FIRE (gap-carry-dropped 9→10 khớp ring2 dự-tính), 1 RESET đúng semantics; 🔴 7/11 class ≥ JUMP=3 → nêu @close cân deep-JUMP. + vá 2 số stale hậu-ring: STATUS:474 31→59 · investigator-codebase:81 bỏ neo-số (ENV-DEP, ăn sau restart)
- [x] V3 trio TRỌN — eval MIXED 19-trục/14-signal → refine 1-action/14-BÁC (E1 impl-fe drain 62,5% dưới low-water, A4 0-sàn → CHỜ-ANH) → audit 55Đ/5T re-chạy-177-số/172-byte-exact + CHẶN-A1 (2 mục MISS = trivial<30min + build-gap 0 đích reinject; 11/13 đứng-yên trừ khi anh đổi chữ/matcher CHỜ-ANH) · #53: eval-return + audit-×2 (resume ×2 cứu trọn), refine sạch
- [!] V4 nén-ngủ P0✓(17 shard/238.849B) · P1 2 curator XONG (A: 9f/45c 113.797B resume-1× · B: 8f/48c 123.454B sạch) + 🔐 2 secret THÔ redact (xem dòng CHỜ-ANH) + đề-nghị skip cicd/2026-08 (đã-chưng-cất) CHẤP NHẬN 16 gist · P2 2 drafter sạch (A 8-draft/98KB 38/38-pointer · B 8-draft+note/121KB) · P3(a)✓ coverage-máy 16/16 PASS (77 token-literal bổ sau soi-tay 4-ca: MISS = biến-thể dải/range/bold-split, KHÔNG mất nội-dung) · P3(b) reviewer Fidelity ĐANG CHẠY (4-tên-cờ + 4 cặp rủi-ro) P4 archive/ P4.5 ring4 P5 report+commit(chờ-anh)
- [!] V4 nén-ngủ P0✓(17 shard/238.849B) · P1 2 curator XONG (A: 9f/45c 113.797B resume-1× · B: 8f/48c 123.454B sạch) + 🔐 2 secret THÔ redact (xem dòng CHỜ-ANH) + đề-nghị skip cicd/2026-08 (đã-chưng-cất) CHẤP NHẬN 16 gist · P2 2 drafter sạch (A 8-draft/98KB 38/38-pointer · B 8-draft+note/121KB) · P3(a)✓ coverage-máy 16/16 PASS (77 token-literal bổ sau soi-tay 4-ca: MISS = biến-thể dải/range/bold-split, KHÔNG mất nội-dung) · P3(b) FIDELITY PASS-WITH-FIXES (reviewer 38KB ×2-resume: P1 4-cờ [3-drift+1-MA AllowDelegate pickaxe-0] · P2a-d · P3-lưới) 9 nhóm fix ÁP TRỌN (kể cả bug máy-append cut-byte cắt-ký-tự) · P4 16 gist archive/ (233.858B 100,4% nguồn KHÔNG claim giảm-byte; additive hash-assert 16/16; last_sleep 08-03) · P4.5 ring4 ĐANG CHẠY 3.5 đuôi (refresh _mind + ctx-audit + synthesis) P5 report+commit(chờ-anh)
- [ ] Phase 3.5 đuôi refresh _mind block-1 (E stale) + ctx-audit @open + bookend-open-synthesis.md (dạng-1)
- [ ] monthly drift-audit 2026-08-01 DUE quá hạn (session đầu-tháng = L10) + re-sync measured{} 17/23 xếp thứ-tự vs task anh giao
- [ ] 🔴 CHỜ-ANH slot (65) ZZTEST hạn 01-08 QUÁ 2 ngày (chỉ anh kênh) · (64) user TEST · (66) dọn ZZTEST · thư day-wake-probe nấc adopt (ctx-verifier cờ : món 65 từng ngoài bản-đồ)

View File

@ -0,0 +1,42 @@
# cicd-monitor/2026-05-q2 — gist
distill-gen: 1
source-verbatim: .claude/agent-memory/cicd-monitor/archive/2026-05-q2.md (1 entry + header-rule → 4 cụm)
pointer-style: substring
## TỔNG-HỢP (3-5 ý)
1. **Deploy-xanh ≠ feature-dùng-được.** Run #231 PASS đủ mọi lớp máy (CI 3m30s, bundle 2/2 rotate, Mig 32/33 applied prod, smoke 5/5) nhưng seed V2 = 0 row ⇒ VERDICT PARTIAL — lớp verify quyết-định là DỮ-LIỆU SEED (sqlcmd đếm row theo `ApplicableType`), không phải bundle-hash + smoke-200.
2. **Hai chủ-đích đúng va nhau, không phải bug:** cờ `DemoSeed:Disabled=true` (thiết-kế Plan T cho UAT sạch) nuốt seed hạ-tầng của Plan B ⇒ sinh gotcha #51; đóng bằng Run #232 `38f1c4d` carve-out option (b). Monitor từ-chối tự quyết fix-option — escalate em-main.
3. **Refactor ~150 LOC `ApproveV2Async` + 0 test** = đúng khuôn rủi-ro gotcha #48 — UAT-skip-test không áp cho scope refactor.
4. **Log-ERR trùng cửa-sổ deploy ≠ do deploy:** SlaExpiryJob 5 entry ERR là nhiễu V1 có trước — quy trách bằng enum/phase trong message, không bằng thời-điểm.
## Entries
**[cao] A2+A3 — Run #231/#232 Plan B Contract V2 (PARTIAL → đóng)**
- **VIỆC:** Verify Run #231 (id=345) push `6eec8d7..3e92584` 11 commit — Plan B wire Contract V2 (Mig 32/33 + `ApproveV2Async` + 6 FE × 2 app).
- **KẾT-LUẬN:** VERDICT=**PARTIAL** — deploy PASS (CI 3m30s; bundle 2/2 rotate `leEMWFLU→BBADl46y` + `Dgn1iU9E→DA_VI3zO`; Mig 32 `20260522051059_AddApprovalWorkflowToContract` + Mig 33 `20260522052240_AddContractLevelOpinions` applied prod; API 200 + 5/5 smoke; 7 HĐ V1 nguyên-vẹn) nhưng SEED PARTIAL: `QT-HD-V2-001` 0 row (ApplicableType=3) ⇒ dropdown FE rỗng ⇒ V2 KHÔNG test được E2E. Đóng bằng Run #232 `38f1c4d` carve `SeedSampleContractWorkflowV2` ra khỏi DemoSeed-gate (option b) + **gotcha #51**.
- **BÀI-HỌC:** deploy-xanh ≠ feature-dùng-được — phải verify **DỮ-LIỆU SEED** (sqlcmd đếm row đúng `ApplicableType`), không dừng ở bundle-hash + smoke-200. Và: refactor >100 LOC (`ApproveV2Async` ~150 LOC) đi kèm **0 test** = đúng khuôn rủi-ro gotcha #48, UAT-skip-test không áp cho scope refactor.
- **BẤT-NGỜ:** thứ chặn feature lại là **feature-flag CỐ Ý** (`DemoSeed:Disabled=true`, thiết-kế Plan T S23 t10 để UAT sạch), không phải bug — 2 chủ-đích đúng đắn va nhau; và CICD-monitor **từ chối tự quyết** (a) hay (b), escalate em-main.
→ substring:"38f1c4d"
**[thấp] A4 — SlaExpiryJob ERR cluster**
- **VIỆC:** phân-loại 5 entry ERR 12:58:16 nổi lên cùng lúc deploy.
- **KẾT-LUẬN:** pre-existing V1 noise — `ConflictException Transition X→Y không hỗ trợ` trên 4 cặp phase (`DangInKy→DangKiemTraCCM`, `DangSoanThao→DangGopY`, `DangGopY→DangDamPhan`, `DangTrinhKy→DongDau`); 0 dính V2; 0 regression.
- **BÀI-HỌC:** log-ERR trùng cửa-sổ deploy ≠ do deploy — phải soi enum/phase trong message để quy trách.
- **BẤT-NGỜ:** job auto-approve V1 vẫn đang fail âm-thầm trên HĐ legacy, tách hẳn thành việc điều-tra riêng.
→ substring:"SlaExpiryJob"
## Phụ-lục token (coverage)
- **gotcha#:** `#48` (dẫn — refactor >100 LOC Service chạm changelog/audit) · `#51` (SINH MỚI — DemoSeed gate) · "44 gotchas cross-ref" (không ở file này, xem q3)
- **surprise:** `CRITICAL DISCOVERY #6 (NEW gotcha to add)` · "seed gap is feature-flag design choice not deploy failure" · "Decision NOT for CICD Monitor — escalating" · "0 new tests added Plan B (~150 LOC ApproveV2Async NO test cover)" · "bro UAT ... V2 wire E2E NOT testable UAT mode"
- **guard:** `DemoSeed:Disabled=true` feature-flag gate · `Reviewer ApplicableType=Contract guard` (hotfix commit trong range) · `paths-ignore`/test-gate 111 baseline · `feedback_uat_skip_verify` (rule được viện-dẫn) · Rule `§6.5` "KHÔNG cắt narrative"
- **root-cause:** "DemoSeed feature flag gates Plan B Chunk A2 sample V2 workflow seed" ⇒ `QT-HD-V2-001 NOT seeded in prod (0 rows ApplicableType=3)` ⇒ V2 endpoint trả `{"active":null,"history":[]}` ⇒ dropdown Create rỗng
- **file:line / định-danh:** log `2026-05-22 12:57:44 [INF] DemoSeed:Disabled=true → skip workflow + contracts + PE + sample V2 seed (Plan T S23 t10 + Plan B Chunk A2 Contract V2)` · `SeedSampleContractWorkflowV2` · `ApproveV2Async` (~150 LOC) · Run #231 id=345 · Run #232 sha=`38f1c4d` · push range `6eec8d7..3e92584` (11 commit) · Mig 32 `20260522051059_AddApprovalWorkflowToContract` · Mig 33 `20260522052240_AddContractLevelOpinions` · cột `ApprovalWorkflowId` + `CurrentApprovalLevelOrder` · bảng `ContractLevelOpinions` (0 row) · bundle `leEMWFLU→BBADl46y` (admin) / `Dgn1iU9E→DA_VI3zO` (user) · `QT-HD-V2-001` (ApplicableType=3) / `QT-DN-V2-001` v1+v2 (ApplicableType=1) · CI 3m30s 12:54:24→12:57:54 · `SlaExpiryJob` 5 ERR @12:58:16 · 4 transition `DangInKy→DangKiemTraCCM` / `DangSoanThao→DangGopY` / `DangGopY→DangDamPhan` / `DangTrinhKy→DongDau` · archive-header: S32 curate, 27KB→~24KB, ngưỡng 25KB
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `#215` — > **KEEP in MEMORY:** S32 startup, S29 wrap summary (Run #229-#232 cumulative aggregate including #231 + #232 hotfix DemoSeed gate catch), S28 Layer A…
- `#216` — > **KEEP in MEMORY:** S32 startup, S29 wrap summary (Run #229-#232 cumulative aggregate including #231 + #232 hotfix DemoSeed gate catch), S28 Layer A…
- `#229` — > **KEEP in MEMORY:** S32 startup, S29 wrap summary (Run #229-#232 cumulative aggregate including #231 + #232 hotfix DemoSeed gate catch), S28 Layer A…
- `Run #215` — > **KEEP in MEMORY:** S32 startup, S29 wrap summary (Run #229-#232 cumulative aggregate including #231 + #232 hotfix DemoSeed gate catch), S28 Layer A…
- `Run #229` — > **KEEP in MEMORY:** S32 startup, S29 wrap summary (Run #229-#232 cumulative aggregate including #231 + #232 hotfix DemoSeed gate catch), S28 Layer A…

View File

@ -0,0 +1,43 @@
# cicd-monitor/2026-05-q3 — gist
distill-gen: 1
source-verbatim: .claude/agent-memory/cicd-monitor/archive/2026-05-q3.md (3 entry → 3 cụm)
pointer-style: substring
## TỔNG-HỢP (3-5 ý)
1. **Test đỏ mà code sản-phẩm đúng — lỗi ở thước đo.** Cặp Run #215 FAIL → #216 PASS (8 phút turnaround): SQLite + clock đóng-băng ⇒ 2 changelog cùng `CreatedAt` ⇒ tie-break `OrderByDescending` bất-định (gotcha #48); fix = lọc theo NỘI-DUNG (`Summary`), code Chunk A không revert.
2. **Giả-thuyết chết bằng quan-sát, không bằng suy-luận:** Discovery #3 — glob `**/*.md` khớp `.md` ở MỌI độ sâu nên commit `cc8a7d3` (docs + 4 agent MEMORY.md) vẫn bị CI SKIP; gotcha #47 không chết mà đổi vai PREVENTIVE cho file non-`.md` tương-lai.
3. **CI test-gate chặn TRƯỚC deploy = lưới cứu chế-độ UAT-skip-test;** refactor BE >100 LOC nên `dotnet test` local trước push.
4. **Baseline 2026-05-12:** vai khởi-động bằng NỀN tri-thức (44 gotchas cross-ref + checklist 5-stage + 3 skill), không bắt đầu bằng run.
## Entries
**[cao] B1 — Run #215 FAIL → #216 PASS (gotcha #48)**
*(trùng liên-file #215: bản dày = file này; cicd-monitor--2026-05-runs giữ bản mỏng ở phần tóm-tắt)*
- **VIỆC:** Verify Plan AB Chunk A (Changelog visibility: Bug 1 Budget Adjust + Bug 2 Return Mode) — 1 commit 3 file, BE `PurchaseEvaluationWorkflowService.cs` +207/-95 + 2 FE `PeDetailTabs.tsx`.
- **KẾT-LUẬN:** #215 (id=329, `cdfd542`, push `e23f51c..cdfd542`) **FAIL** ở test_infra 51/53 — 2 test ReturnMode báo `Expected changelog.ContextNote not to be <null>`; deploy KHÔNG chạy (bundle giữ nguyên Run #214, Mig 31 TOP 1 y cũ). #216 (id=330, `8c05947`) **PASS** — chỉ sửa TEST (+7/-2) thêm `.Where(c => c.Summary!.Contains("Chuyển phase"))` trước `OrderByDescending(CreatedAt).First()`; code Chunk A **không revert**; 58/58 + 53/53, bundle 2/2 rotate, 10:13→10:21 = 8 phút.
- **BÀI-HỌC:** SQLite + **clock đóng-băng** ⇒ 2 changelog cùng `CreatedAt``OrderByDescending` tie-break trả nhầm bản-ghi ⇒ test "lấy entry mới nhất" là **thước không xác-định**; phải lọc theo NỘI-DUNG (Summary) chứ đừng dựa thứ-tự thời-gian. Refactor BE >100 LOC nên `dotnet test` local TRƯỚC push — UAT skip-test không đủ.
- **BẤT-NGỜ:** test đỏ mà **code sản-phẩm đúng** — lỗi nằm ở thước đo; và CI test-gate chặn đúng lúc nên bro tránh được audit-trail vỡ trên prod (fail lại hoá may).
→ substring:"8c05947"
**[cao] B2 — Verify S22 cumulative + Discovery #3 (glob `**/*.md`)**
*(trùng liên-file "Verify S22 chốt": bản dày hơn ở cicd-monitor--2026-05-runs — file này giữ bản của mình, đóng-kín per-file)*
- **VIỆC:** Verify chốt S22, push `3d725c4..cc8a7d3` 12 commit (Plan C/D/E + Mig 30 F4 per-NV Approver edit Budget).
- **KẾT-LUẬN:** VERDICT=PASS — 33 user active prod, bundle 2/2 rotate, **104/104** test (+1 so baseline S21=103), Mig 30 xác-nhận prod. **Discovery #3:** commit `cc8a7d3` gồm docs + 4 agent `MEMORY.md` vẫn bị CI **SKIP** vì glob `**/*.md` khớp `.md` ở MỌI độ sâu.
- **BÀI-HỌC:** glob `**/*.md` trong `paths-ignore` khớp cả file `.md` nằm sâu trong `.claude/agent-memory/` ⇒ giả-thuyết "thư-mục đó sẽ trigger CI" **bị bác bằng quan-sát**, không bằng suy-luận.
- **BẤT-NGỜ:** gotcha #47 KHÔNG chết mà đổi vai — vẫn đúng như biện-pháp PHÒNG cho file state **non-`.md`** tương-lai dưới `.claude/agent-memory/`.
→ substring:"cc8a7d3"
**[thấp] B3 — Setup baseline**
- **VIỆC:** khởi-tạo CI/CD Monitor 2026-05-12.
- **KẾT-LUẬN:** nạp nền: 44 gotcha cross-ref + checklist 5-stage + 3 skill preload + khuôn verify bundle-hash; chưa monitor run nào.
- **BÀI-HỌC:** vai bắt đầu bằng NỀN tri-thức + checklist, không bắt đầu bằng run.
- **BẤT-NGỜ:** (không có — entry mốc; giữ vì là gốc truy-vết baseline 44 gotcha / 5-stage.)
→ substring:"44 gotchas"
## Phụ-lục token (coverage)
- **gotcha#:** `#48` (SQLite tie-break — cặp catch+fix) · `#47` (đổi vai → PREVENTIVE) · dải nền `#39-#52` · "44 gotchas cross-ref" (baseline 05-12)
- **surprise:** `Discovery #3 first surfaced` + "Spec hypothesis ... **disproven** for this commit" · "Side benefit: CI test gate caught BEFORE prod deploy — bro UAT spared broken Plan M edge case audit trail" · "Plan AB Chunk A code `cdfd542` KHÔNG bị revert" · "Demonstrates test-after UAT mode CAN tolerate edge case bug if next chunk lands within minutes — **but** ... UAT skip-test rule risky for refactor scope" · "Deploy NOT shipped ... bro UAT screenshot **pre-deploy stale**"
- **guard:** test-gate CI chặn trước deploy (test_domain + test_infra) · `.Where(c => c.Summary!.Contains("Chuyển phase"))` (fix-guard chọn đúng entry) · `paths-ignore` glob `**/*.md` · Stage 4.6 sqlcmd seed verify (header) · 5-stage checklist + 10-surface-point per-NV (header)
- **root-cause:** `ApplyReturnModeAsync` thêm Changelog MỚI (`EntityType=Workflow + Action=Update + Summary`, **không**`ContextNote`) ở cuối, cùng `SaveChangesAsync` với `LogTransitionAsync` (có `ContextNote=comment`) ⇒ SQLite + frozen test clock ⇒ **cùng `CreatedAt`**`.OrderByDescending(c => c.CreatedAt).FirstAsync()` tie-break trả nhầm entry Workflow · Discovery #3 root-cause: glob `**/*.md` khớp `.md` ở mọi độ sâu
- **file:line / định-danh:** `PurchaseEvaluationWorkflowService.cs` +207/-95 · `ApplyReturnModeAsync` **line 403-412** · `LogTransitionAsync` **line 100** · `PeDetailTabs.tsx` (2 FE mirror filter) · test `PurchaseEvaluationWorkflowServiceReturnModeTests.ApplyReturnMode_OneStep_AtStep1_ResetsToBuoc1Cap1_KeepsChoDuyet` · test `...ApplyReturnMode_OneLevel_AtStep1Level1_ResetsToBuoc1Cap1_KeepsChoDuyet` · lỗi `Expected changelog.ContextNote not to be <null>` · Run #215 id=329 `cdfd542` (1m06s) / Run #216 id=330 `8c05947` (+7/-2) · push `e23f51c..cdfd542` · Run #214 (baseline bundle) · Mig 31 TOP 1 · Run S22: push `3d725c4..cc8a7d3` 12 commit, Mig 30 F4, 33 user, 104/104 (baseline S21=103) · commit `cc8a7d3` (docs + 4 agent MEMORY.md → CI SKIPPED) · PE `c6e9` · 51/53 → 58/58 + 53/53 · 10:13→10:21 (8 phút) · setup 2026-05-12

View File

@ -0,0 +1,73 @@
# cicd-monitor/2026-05-q4 — gist
distill-gen: 1
source-verbatim: .claude/agent-memory/cicd-monitor/archive/2026-05-q4.md (7 entry → 5 cụm)
pointer-style: substring
## TỔNG-HỢP (3-5 ý)
1. **Chuỗi 3 Run module-mới S33→S34 (#350 → #237 → #238) ALL-PASS theo khuôn Pattern 12-bis cookie-cutter** (PE→Hrm→Office, MediatR auto-discover namespace mới không đăng-ký tay); verify module-mới = **3 lớp**: build/test (120/120) → bundle-rotate → sqlcmd seed + menu-key + permission-row (EmployeeProfiles 33 + `LastSeq=33` khớp nguyên-tử).
2. **Path-filter phải rà theo thư-mục MỚI sinh ra:** DISCOVERY #7`paths-ignore` thiếu `eval/**` ⇒ commit telemetry RAG (Run #234 `b223466`) kích deploy ~3m30s vô-ích; cùng một filter vừa đúng (#41 skip 4 commit docs) vừa thủng trong cùng cửa-sổ — sai-sót là VẮNG-MẶT một dòng.
3. **Kinh-tế spawn (S26, Run #222-#227):** spawn CICD-monitor 1× đầu Phase-wire là đủ, chunk polish tự verify — ranh-giới "được tự verify" = có/không đổi hợp-đồng dữ-liệu, không phải kích-thước diff.
4. **Phiên không-deploy vẫn có việc:** canh cert (`2026-07-23` notAfter), drift Mig TOP 5, MEMORY sát ngưỡng 25KB, RAG rerank 0.906 + ghi baseline bundle-hash; `/healthz` 404 — canonical là `/health/live`.
5. **Ghi cả kết-quả ÂM-TÍNH có chủ-đích** (gotcha #1/#44 KHÔNG dính, #51 áp đúng chiều, bộ test BW1-BW7 caught NOTHING) — nếu chỉ ghi lỗi, lần sau không ai biết bẫy đã được kiểm hay bị bỏ quên.
## Entries
**[cao] C1 — Chuỗi deploy 3 Run module-mới (Hrm → Office), ALL PASS**
- **VIỆC:** Verify 3 Run liên-tiếp: **#350** `48a99e1` (Mig 34 EmployeeProfile 17 entity + Plan C test BW1-BW7) → **#237** `79a8343` (Hrm CQRS endpoint + FE 2 app + menu seed) → **#238** `ea440da` (Office `/api/directory` Danh bạ nội bộ BE+FE 2 app).
- **KẾT-LUẬN:** 3/3 **PASS** (3m38s / 3m50s / ~3m30s). Test 58+62=**120/120** giữ nguyên từ #350 trở đi (62 = 53 baseline + 9 BW). Bundle-chain khớp liền mạch: admin `BUTKoqRP`(#350 giữ)→`CqGMUMOr`(#237)→`ChA9_vP5`(#238); user `CMHv2GS4``C_HKyxBe``DCpX7akt`. Mig 34 `20260526110207_AddEmployeeProfiles` TOP 1 xuyên cả 3. Seed verify sqlcmd: EmployeeProfiles **33 row** (16 demo + 14 Solutions thật + 3 admin/test) + `EmployeeCodeSequences` NV/2026 `LastSeq=33` khớp nguyên-tử; menu key `Hrm`+`Hrm_HoSo` rồi `Off`+`Off_DanhBa` (+2 row Permission auto-grant); `/api/directory` trả 13.6KB/34 dòng phủ đủ 9 phòng ban.
- **BÀI-HỌC:** khuôn **Pattern 12-bis cookie-cutter mirror** (PE→Hrm→Office) chạy end-to-end không cần đăng-ký thủ-công: namespace mới `SolutionErp.Application.Office` được MediatR auto-discover. Verify module-mới phải gồm **3 lớp**: build/test → bundle-rotate → **sqlcmd seed + menu-key + permission-row** (bundle xanh không nói gì về seed).
- **BẤT-NGỜ:** cả 3 Run **không bắt được lỗi nào** — kể cả bộ test BW1-BW7 vừa thêm cũng "caught NOTHING, all PASS first try"; và `LastSeq=33` khớp đúng 33 row là bằng-chứng codegen nguyên-tử không rò.
→ substring:"20260526110207_AddEmployeeProfiles"
**[cao] C3 — DISCOVERY #7: `eval/**` ngoài `paths-ignore`**
- **VIỆC:** rà vì sao Run #234 `b223466` (3m52s) chạy dù commit chỉ là docs + `eval/*.json` của S31 RAG.
- **KẾT-LUẬN:** `paths-ignore` **thiếu `eval/**`** ⇒ mọi commit telemetry RAG kích deploy ~3m30s vô-ích (0 thay-đổi code); đề-xuất em-main cân thêm `'eval/**'` nếu tần-suất tăng. (Đối-chứng: `b832f43..5400983` 4 commit docs-only SKIP đúng theo #41.)
- **BÀI-HỌC:** path-filter phải rà theo **thư-mục MỚI sinh ra** (eval/, telemetry) — filter đúng hôm qua thành thủng hôm nay khi repo mọc thư-mục mới.
- **BẤT-NGỜ:** cùng một filter vừa **đúng** (skip 4 commit docs) vừa **thủng** (chạy `eval/**`) trong cùng cửa-sổ vài ngày — sai-sót là VẮNG-MẶT một dòng, không phải dòng sai.
→ substring:"b223466"
**[cao] C4 — Khuôn "hybrid verify" (S26, Run #222-#227)**
- **VIỆC:** Plan AG series (PE List tree-view) 6 Run liên-tiếp — chọn cách verify tiết-kiệm.
- **KẾT-LUẬN:** spawn CICD-Monitor **1 lần** ở Run #222 `0bf6c7e` (bundle 2/2 rotate `C8TvDy7r→CWHIdoFo` / `BvcWrq2z→Bg2FNeIz`, smoke 5/5 200, PE List API giữ 9 field, test-gate 111, Mig 31 y cũ); #223-#227 (AG2-AG6) em-main **tự verify** (bundle mắt-thường + push OK + Gitea auto-trigger 3-4 phút). AG4 cross-stack (DTO +4 field Drafter*/Department* + 3 projection JOIN) chạy `dotnet test` 111/111 local trước push. 0 regression toàn cụm.
- **BÀI-HỌC:** ROI spawn: **1× đầu Phase-wire** là đủ cho kịch-bản 1 dev solo lặp nhanh; chunk polish (CSS/UX/copy) tự verify — re-spawn `~150K × N` là phí.
- **BẤT-NGỜ:** cách rẻ hơn lại **không giảm an-toàn** — vì chunk polish không đụng shape API; ranh-giới "được tự verify" là **có/không đổi hợp-đồng dữ-liệu**, chứ không phải kích-thước diff.
→ substring:"0bf6c7e"
**[vừa] C2 — Snapshot phi-deploy S32/S33 (hạ-tầng + tự-kiểm)**
- **VIỆC:** 3 lượt spawn read-only không có deploy nào để soi (S32 startup, S32 wrap, S33 health-check).
- **KẾT-LUẬN:** VERDICT=HEALTHY — 0 unpushed (HEAD=`5400983`), 5 Run gần nhất SUCCESS (#235/#234/#233/#232/#231), 3 endpoint smoke 200 (0.23s/0.29s/0.31s), Mig prod TOP 5 khớp repo không drift (33→32→31→30→29), cert `api.solutions.com.vn` notAfter `Jul 23 01:58:16 2026 GMT` (~58 ngày, auto-renew ~2026-06-23 cửa-sổ win-acme 30d). Tự-kiểm: MEMORY 24.9KB/221 dòng sát ngưỡng 25KB → sau curate còn 24.2KB; RAG 2949 chunk, `search_memory` top rerank **0.906**.
- **BÀI-HỌC:** phiên không-deploy vẫn có việc: canh **cert · drift Mig · kích-thước MEMORY · sức-khoẻ RAG** — và ghi lại **baseline bundle-hash** để lượt sau có mốc so.
- **BẤT-NGỜ:** `/healthz` trả **404** — canonical là `/health/live`; và Gitea API gọi **unauth** (token rỗng) vẫn đọc được lịch-sử Run.
→ substring:"Jul 23 01:58:16 2026 GMT"
**[vừa] C5 — Bốn ca "âm-tính" đáng giữ**
- **VIỆC:** ghi nhận các bẫy ĐÃ BIẾT nhưng KHÔNG tái-diễn trong 3 Run.
- **KẾT-LUẬN:** gotcha **#1** không dính (namespace `Application.Office` mới vẫn được MediatR auto-discover); gotcha **#44** silent-403 không thấy (`[Authorize]` mức class trên `DirectoryController` cho mọi user đã đăng-nhập); gotcha **#51** được **áp đúng chiều** (`SeedDemoEmployeeProfilesAsync` cố-ý KHÔNG gate `DemoSeed:Disabled` vì là seed HẠ-TẦNG); EmployeeProfiles 33 không tăng ở #237 = **guard idempotent** chạy.
- **BÀI-HỌC:** ghi cả kết-quả ÂM-TÍNH có chủ-đích — nếu chỉ ghi lỗi, lần sau không ai biết bẫy đã được kiểm hay bị bỏ quên.
- **BẤT-NGỜ:** bài-học từ #51 (seed bị gate nhầm) đã đảo thành **tiêu-chí phân-loại** DEMO-seed ⟂ INFRASTRUCTURE-seed và được viện-dẫn chủ-động ở Run sau.
→ substring:"caught NOTHING"
## Phụ-lục token (coverage)
- **gotcha#:** `#1` (MediatR auto-discovery — **KHÔNG** dính) · `#41` (path filter docs-only skip — dẫn 3 lần) · `#44` (silent 403 — **NOT observed**) · `#48` (SQLite tie-break — foundation preserved ×2) · `#51` (INFRASTRUCTURE vs DEMO seed — dẫn + áp)
- **Discovery#:** `#5` (sqlcmd pattern — "UPDATED from") · `#6` (INFRASTRUCTURE vs DEMO seed Stage 4.6) · `#7` (**NEW** — `paths-ignore` thiếu `eval/**`) · `#8` (chỉ có ở HEADER dòng 3 "NEW collection naming", **0 hit trong thân** — con-trỏ mồ-côi; nội-dung thuộc entry S35/S36 NGOÀI file này — KHÔNG cite như content của file)
- **surprise:** `DISCOVERY #7 NEW: path filter paths-ignore MISSING eval/** → ... triggered ~3m30s deploy wastefully (no code change)` · `Gotcha #44 silent 403 NOT observed` · `Application/Office namespace mới auto-discovered MediatR — KHÔNG gotcha #1` · `Plan C BW1-BW7 ROI: test gate caught NOTHING this run (all PASS first try)` · `SeedDemoEmployeeProfilesAsync correctly NOT gated DemoSeed:Disabled per gotcha #51 lesson` · `EmployeeProfiles total=33 (unchanged Run #350 baseline — idempotent guard skip)` · `/healthz 404 N/A — /health/live canonical` · `Gitea API unauth (token empty OK)` · `Pattern saved: CICD Monitor spawn 1× đầu Phase wire ROI tốt ... re-spawn ~150K × N wasteful` · `bundle hash 2/2 UNCHANGED (... expected NO FE)` · `Pattern 12-bis cross-module mirror reaffirmed strong`
- **guard:** class-level `[Authorize]` trên `DirectoryController` · idempotent guard skip (`EmployeeProfiles` 33 không đổi) · `DemoSeed:Disabled` gate (và ca **cố-ý không** gate) · `paths-ignore` + glob `**/*.md` · Stage 4 + **Stage 4.6 sqlcmd seed verify** · test-gate CI + "Implementer Reviewer pre-commit gate strong" · MEMORY ngưỡng **25KB** · win-acme cửa-sổ renew **30d** · `EmployeeCodeSequences` LastSeq nguyên-tử · BW-guard: BW4 `ForbiddenException` outsider · BW5 wrong `ApplicableType` · BW6a UNIQUE composite · BW6b UPSERT 1 row · BW6c Cascade delete · BW7 V1 fallback `ConflictException`
- **root-cause:** `eval/**` KHÔNG có trong `paths-ignore` ⇒ commit `eval/*.json` (S31 RAG) kích deploy vô-ích · `**/*.md` khớp mọi độ sâu ⇒ commit docs+MEMORY bị SKIP đúng thiết-kế · seed hạ-tầng từng bị nhốt nhầm sau `DemoSeed` (bài #51) ⇒ tiêu-chí tách DEMO ⟂ INFRASTRUCTURE
- **file:line / định-danh:** **Run #238** `ea440da` push `edba4ae..ea440da` 2 commit/23 file (`7b0781b` curate 4 agent MEMORY = 8 file MD) · `DirectoryFeatures.cs` · `DirectoryController.cs` route `/api/directory` · `MenuKeys.cs` (+`Off`, `Off_DanhBa`) · `DbInitializer.cs` · FE ×2 app: `types/directory.ts`, `pages/office/InternalDirectoryPage.tsx`, `App.tsx`, `Layout.tsx`, `menuKeys.ts` · directory **13.6KB / 34 row / 9 phòng ban** · bundle `CqGMUMOr→ChA9_vP5` + `C_HKyxBe→DCpX7akt` · namespace `SolutionErp.Application.Office` · ~25K token
· **Run #237** `79a8343` 3m50s push `48a99e1..79a8343` 3 commit/18 file (`0e191de` Task 4+6 · `9616ae2` Task 5 FE ×2 · `79a8343` MEMORY 3 agent) · `EmployeesController` + `EmployeeDtos` + `EmployeeFeatures` · `/api/employees` GET list + paged `page=1&pageSize=5` · bundle `BUTKoqRP→CqGMUMOr` + `CMHv2GS4→C_HKyxBe` · key `Hrm` + `Hrm_HoSo` · mẫu `NV/2026/0007` (BOD 1 — Director, Ban Giám đốc, hireDate 2021-08-01) · ~30K token
· **Run #350** `48a99e1` 3m38s push `5400983..48a99e1` 4 commit/34 file (`1bc6b70` docs 3 MD · `b3444a3` 3 MEMORY · `0605f19` Plan C +4 test file +9 `[Fact]`/Theory · `48a99e1` Mig 34 + 17 entity/config new + 7 modified) · test_infra **62/62** = 53 + 9 BW (BW1 happy Cấp 1→2 · BW2 terminal `DaPhatHanh` + gen mã · BW3 skipToFinal F2 admin opt-in · BW4-BW7 xem mục guard) · build_be **+6555 LOC** · Mig 34 `20260526110207_AddEmployeeProfiles` · `SeedDemoEmployeeProfilesAsync` · EmployeeProfiles **33** (16+14+3) · `NV/2026/0001-0003` `EmployeeStatus=1` Active `Nationality="Việt Nam"` · ~30K token
· **S33 startup:** HEAD=`5400983` · 5 Run: #235 `1e1c9a2` 3m38s / #234 `b223466` 3m52s / #233 `e199603` / #232 `38f1c4d` / #231 `3e92584` · skip range `b832f43..5400983` · smoke `api/health/live` 0.23s + admin 0.29s + eoffice 0.31s · sqlcmd `ssh vietreport-vps "powershell ... '.\\SQLEXPRESS' -E"` · Mig TOP 5: `AddContractLevelOpinions`(33) → `AddApprovalWorkflowToContract`(32) → `RefactorSkipToFinalToApproverLevel`(31) → `AddAllowApproverEditBudgetToLevels`(30) → `RefactorAdvancedOptionsToPerLevelAndDrafterUser`(29) · cert `api.solutions.com.vn` notAfter `Jul 23 01:58:16 2026 GMT` · bundle baseline `index-BUTKoqRP.js` / `index-CMHv2GS4.js` · ~12K token
· **S32 wrap:** agent id `a505a02d84fc1fabe` · MEMORY 27→24.2KB · 5 commit docs-only · cert `2026-07-23` (openssl s_client) · Plan G 11 module backlog · BW1-BW7 spec ready · 3 trigger chờ S33 (a) G-H1 Hồ sơ NS · (b) BW test bundle · (c) UAT smoke V2 contract · ~3K token · Tag `[wrap, phase-9-to-phase-10, cicd]`
· **S32 startup:** MEMORY **24.9KB / 221 lines** · `search_memory` query "Run 232 Plan B Hotfix CICD SeedSampleContractWorkflowV2" rerank **0.906** / 0.828 / 0.816 · RAG **2949 chunk** · last push `f938bf5` (S31 docs patch `cicd-monitor.md`) · ~10K token
· **S26:** Run #222 `0bf6c7e` ~12K · bundle `C8TvDy7r→CWHIdoFo` + `BvcWrq2z→Bg2FNeIz` · smoke 5/5 200 · PE List API **9 field** · test-gate **111** · Mig 31 unchanged · Run #223-#227 = Plan AG2-AG6 · AG4 DTO +4 field `DrafterUserId`/`DrafterName`/`DepartmentId`/`DepartmentName` + 3 projection JOIN `Users`+`Departments` · `dotnet test` 111/111 local pre-push · `~150K × N wasteful`
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `Mig 29`**Mig prod TOP 5 DESC** sqlcmd Windows-auth via `ssh vietreport-vps "powershell ... '.\\SQLEXPRESS' -E"` pattern (UPDATED from Discovery #5): `AddCont…
- `Mig 30`**Mig prod TOP 5 DESC** sqlcmd Windows-auth via `ssh vietreport-vps "powershell ... '.\\SQLEXPRESS' -E"` pattern (UPDATED from Discovery #5): `AddCont…
- `Mig 32`**Mig prod TOP 5 DESC** sqlcmd Windows-auth via `ssh vietreport-vps "powershell ... '.\\SQLEXPRESS' -E"` pattern (UPDATED from Discovery #5): `AddCont…
- `Mig 33` — Archived 2026-05-28 S36 startup curate (em main proxy) — 6 verbose Run entries from S32-S34 absorbed into baseline summary (test 120 → 130, Mig 33…
- `Run #231` — Session 32 đóng clean. Em chủ trì spawn em 1 lần S32 startup verify (a505a02d84fc1fabe alive, MEMORY 27→24.2KB post curate Run #231 PARTIAL d…
- `Run #232`**Mig prod TOP 5 DESC** sqlcmd Windows-auth via `ssh vietreport-vps "powershell ... '.\\SQLEXPRESS' -E"` pattern (UPDATED from Discovery #5): `AddCont…
- `Run #235`**Cert api.solutions.com.vn notAfter `Jul 23 01:58:16 2026 GMT`** (matches HANDOFF expected ~2026-07-23, ~58 days lead, auto-renew ~2026-06-23 win-acm…

View File

@ -0,0 +1,138 @@
# cicd-monitor/2026-05-runs — gist
distill-gen: 1
source-verbatim: .claude/agent-memory/cicd-monitor/archive/2026-05-runs.md (6 tóm-tắt + 20 Run + 8 key-pattern → 10 cụm)
pointer-style: substring
## TỔNG-HỢP (3-5 ý)
1. **Chuỗi per-NV lookup discrimination (Plan K→P, Mig 29/30/31):** bug `FirstOrDefault` không lọc actor sống 2-3 ngày prod, "em main + Reviewer + Implementer all missed across 3 plan", checklist phình 6→8→9→10 điểm — với schema OR-of-N, mọi `FirstOrDefault(Order==)` là điểm mù (quét TOÀN kho, không chỉ handler lộ bệnh); record body Controller phải soi-gương param với Command (thiếu field ⇒ ASP.NET `default` im-lặng).
2. **Saga Discovery #3 chốt sau 5 lần quan-sát:** Gitea xét `paths-ignore` trên TRỌN push-range — muốn loại một biến phải tìm ca VẮNG biến mà hiện-tượng vẫn còn (#202 then-chốt); kết-luận là "lỗi" nhưng "Anomaly is BENEFICIAL".
3. **Chuỗi audit-trail S25 (#215 FAIL → #221):** phát hiện "field X không set ở N chỗ" thì vá cả N chỗ một lượt (9 site UserName); dữ-liệu cũ hỏng cứu ở FE bằng `userMap` từ dữ-liệu nhúng sẵn; Run #217 `a734bf2` = nguồn duy nhất Discovery #5 + pattern "capture pre-call mutation state".
4. **Plan T tắt DemoSeed:** cờ chống re-seed chỉ tin sau restart THẬT (`Restart-WebAppPool` + sqlcmd đếm 0); `appsettings.Production.json` bị `.gitignore` ⇒ default phải đặt ở `appsettings.json`; chính cờ này về sau nuốt seed hạ-tầng Plan B (gotcha #51 — xem gist q2).
5. **Lệnh đo sai trả về IM-LẶNG** (7 phát-hiện công-cụ: `/actions/tasks`≠runs · `updated_at` trễ ~2 phút · 429 ~5 req/phút → cache token · sqlcmd 4-backslash Discovery #5 · enum cần số Discovery #4 · 503 recycle lành · route `/api/auth/login` + field `accessToken`) — mỗi phép đo cần ca đối-chứng dương. MEMORY tự phình 35→72KB qua 8 nấc cảnh-báo leo thang không tự dừng ⇒ cần cổng ngoài. 🔐 dòng 48 verbatim: password DB prod ĐÃ redact bởi lead — ROTATE REQUIRED.
## Entries
> **Ghi-chú hợp-nhất nội-file:** phần (a) tóm-tắt ∩ phần (b) đầy-đủ của verbatim chồng 4 Run (#202 · #203 · #210 · #219) — cụm dưới đây chọn bản DÀY; riêng **#215 + #217 CHỈ có ở phần (a)** — ĐÃ GIỮ khi hợp-nhất (KHÔNG mất Run #217).
**[cao] D1 — Chuỗi per-NV lookup discrimination (Plan K → P), sinh checklist 10 điểm**
- **VIỆC:** wire cờ opt-in per-NV (Allow* theo từng slot Approver) xuyên Mig 29/30/31, rồi vá 5 lượt liên-tiếp khi lộ ra thiếu điểm nối.
- **KẾT-LUẬN:** #194 `098baa6` **PARTIAL** — schema + FE checkbox thứ 7 đã ship nhưng `AwLevelDto` (`ApprovalWorkflowV2AdminFeatures.cs:23-38`) **thiếu hẳn param** `AllowApproverSkipToFinal` (chỉ tồn-tại trong **comment dòng 60**) + `ToDto:156-158` thiếu ⇒ checkbox đọc/ghi vào hư-vô. #195 `0062fcb` vá 15 LOC ⇒ 13 key round-trip PASS, đúc **checklist 8 điểm**. #201 `fb3c22c` (Plan N) vá `PurchaseEvaluationFeatures.cs:765` thêm `&& l.ApproverUserId == currentUser.UserId` + fallback admin ⇒ **điểm 9**. #202 `a1c8386` (Plan O) quét ra **4 site cùng khuôn** (`...WorkflowService.cs:204` + `:258-260`, `...DetailFeatures.cs:75-76`, `...Features.cs:314-315`) + 3 test hồi-quy (111/111). #203 `1727bd5` (Plan P) vá Controller `TransitionPeBody` từ 3 → **7 param****điểm 10**.
- **BÀI-HỌC:** với schema **OR-of-N** (nhiều NV cùng một Cấp), mọi `FirstOrDefault(... Order == ...)`**điểm mù**: phải quét TOÀN kho (`grep -n "FirstOrDefault.*Order.*=="`) chứ không chỉ sửa handler lộ bệnh. Và **record body của Controller phải soi-gương số param với Command** — thiếu field thì ASP.NET **không báo lỗi**, chỉ lặng-lẽ `default`.
- **BẤT-NGỜ:** bug sống trên prod **2-3 ngày****3 lượt refactor liên-tiếp (Mig 29/30/31) đều trượt** — verbatim tự khai "em main + Reviewer + Implementer **all missed across 3 plan**"; đồng-thời checklist phình **6 → 8 → 9 → 10 điểm**, tức mỗi lần "đã đủ" đều sai.
→ substring:"098baa6"
**[cao] D2 — Saga Discovery #3 (`paths-ignore` xét trọn push-range)**
*(trùng liên-file "Verify S22 chốt": bản dày = file này; cicd-monitor--2026-05-q3 B2 giữ bản mỏng — đóng-kín per-file, không xoá bên nào)*
- **VIỆC:** truy vì sao commit docs-only vẫn kích CI (ngược gotcha #41), qua 5 lần quan-sát.
- **KẾT-LUẬN:** giả-thuyết đầu (Discovery #2, Run #188): "`.claude/agent-memory/**` không nằm trong `paths-ignore` nên kích CI" → **BỊ BÁC** ở S22-chốt vì `**/*.md` khớp mọi độ sâu (`cc8a7d3` SKIP đúng). Chốt sau 3 lần củng-cố (#200 `f4055a1`, #201 `fb3c22c`, #202 `a1c8386` — lần 3 **0 file agent-memory** nên loại hẳn biến này): **Gitea xét paths-ignore trên TRỌN push-range**, chỉ cần 1 commit trung-gian có file không-bị-ignore là cả range chạy. #210 mixed-diff nên **không tính** là bằng-chứng.
- **BÀI-HỌC:** muốn loại một biến thì phải tìm ca **VẮNG biến đó mà hiện-tượng vẫn còn** (#202 là ca then-chốt) — thêm ca có-biến chỉ củng-cố giả-thuyết sai. Ghi rõ ca nào **không** tính là bằng-chứng (#210) cũng quan-trọng ngang ca tính.
- **BẤT-NGỜ:** kết-luận cuối là "lỗi" nhưng verbatim đánh giá **"Anomaly is BENEFICIAL"** — CI chạy dư lại cứu được gate verify trên commit docs; và gotcha #47 tuy bị bác vẫn **giữ giá-trị phòng-ngừa** cho file non-`.md` tương-lai.
→ substring:"f4055a1"
**[cao] D3 — Chuỗi audit-trail S25 (Plan AB → AF): 5 khuôn phục-hồi lịch-sử**
*(trùng liên-file #215: bản dày hơn ở cicd-monitor--2026-05-q3 B1; #215+#217 trong verbatim file này chỉ có ở phần tóm-tắt (a) — Run #217 = nguồn duy nhất Discovery #5 + pattern "capture pre-call mutation state", ĐÃ GIỮ)*
- **VIỆC:** làm "Lịch sử duyệt" hiển-thị đúng + cứu dữ-liệu lịch-sử đã ghi thiếu.
- **KẾT-LUẬN:** #215 **FAIL** (tie-break, xem q3) → #217 `a734bf2` (AC) thêm Approval-row nhánh Reject `Service:75-103` + prefix `[Duyệt vượt cấp tới Cấp cuối]` `:472`, xác-minh 6/6 approval có đủ 8 key → #218 `25837b6` (AC2) FE dựng **synthetic Reject row** từ Changelog `entityType=5` (20 entry, 8 Workflow-entry, khớp keyword "Trả về") → #219 `0aaf2df` (AD) **bỏ badge from/to** + `extractNextTargetHint()` parse `\[Bước (\d+) — Cấp (\d+)\]`#220 `9ea62be` (AE) vá **9 site** `UserName = currentUser.FullName ?? currentUser.Email` (4 Features + 5 DetailFeatures) → #221 `506cada` (AF) FE `userMap` useMemo dựng từ 5 nguồn nhúng sẵn (drafter/approvals/levelOpinions/departmentOpinions/approvalFlow.steps), 3 nấc: tin `entry.userName` → tra map → "Hệ thống". Cumulative 7 commit, 1 catch + 6 PASS, 0 regression.
- **BÀI-HỌC:** khi phát-hiện "field X không set ở N chỗ giống nhau", **vá cả N chỗ một lượt** thay vì chỗ báo lỗi (AE: 1 lộ + 8 phòng-xa). Dữ-liệu cũ đã hỏng thì **cứu ở FE bằng dữ-liệu đã nhúng sẵn trong bundle** — không cần đổi hợp-đồng API, né luôn ràng-buộc `/api/users` chỉ-admin.
- **BẤT-NGỜ:** bằng-chứng chốt Bug 1 là **một dòng dữ-liệu cũ còn sót**: entry Budget Adjust `02:31:39` `entityType=1``userName=""` — tức prod **tự lưu giữ vật-chứng tiền-sửa** để đối-chứng sau deploy; và AD **bỏ bớt** thông-tin (badge from/to) lại làm UI đúng hơn vì phần lớn là `ChoDuyet→ChoDuyet` tự-lặp.
→ substring:"a734bf2"
**[cao] D5 — Plan T tắt DemoSeed + chứng bằng restart THẬT**
- **VIỆC:** chặn seed demo tự mọc lại trên prod (#207 `0b97840`, #208 `7b7b28f`).
- **KẾT-LUẬN:** bọc 5 method demo trong `if (!demoSeedDisabled)` (V1 workflow ×2, demo Contract, demo PE, sample AW V2); **GIỮ** 8 nhóm seed hạ-tầng (Roles/Admin/30 user UAT/master/template/catalog/backfill). T5 xoá 4 PE + 1 V2 + 2 V1 + cascade; **T6 ép `Restart-WebAppPool SolutionErp-Api`** rồi sqlcmd: PE=0, V2=0, V1=0, master còn nguyên (Users 33 · Suppliers 19 · Projects 9 · Contracts 7). Tổng R+S+T ≈ **720 dòng** bị xoá, cờ tồn-tại vĩnh-viễn.
- **BÀI-HỌC:** cờ chống re-seed chỉ được tin sau khi **khởi-động lại thật** rồi đếm lại — không tin vào việc đọc code. Và vì `appsettings.Production.json` bị `.gitignore`, giá-trị mặc-định phải đặt ở `appsettings.json` (thứ đi qua git) mới tới được prod.
- **BẤT-NGỜ:** chính cái cờ chứng-minh-tốt này **về sau nuốt luôn seed hạ-tầng** của Plan B (Run #231/#232, gotcha #51) — biện-pháp đúng ở lớp này thành bẫy ở lớp khác.
→ substring:"Restart-WebAppPool"
**[cao] D6 — 7 phát-hiện về CÔNG-CỤ ĐO (dễ mất nhất khi nén)**
- **VIỆC:** gom các bài học về chính phép đo, rải rác trong 6 Run.
- **KẾT-LUẬN:** (1) API liệt-kê Run của Gitea là `/api/v1/repos/.../actions/**tasks**`, `/actions/runs` trả **404**; đọc **không cần auth**. (2) `updated_at` trong bảng task **trễ ~2 phút** — phải đối-chiếu `LastWriteTime` của `.dll/.html` trên VPS. (3) đăng-nhập **~5 request/phút là 429** ⇒ cache token ra `%TEMP%\*_token.txt`, 2 login + 8 lệnh dùng lại (Run #188 dính 429, S22-chốt hết dính). (4) **Discovery #5:** sqlcmd Windows-auth qua ssh cần escape **4 dấu `\\\\SQLEXPRESS`** — 2 dấu ra **rỗng KHÔNG báo lỗi**. (5) **Discovery #4:** ASP.NET Core 10 record + enum đòi **số**, không có `JsonStringEnumConverter` (grep `Program.cs` = 0 hit) ⇒ payload mẫu `"Drafter"` trong brief là **sai, fail 400**. (6) 503 giữa lúc IIS recycle (~5-15s) là **bình-thường, tự khỏi**. (7) route thật là `/api/auth/login` (không `/api/v1/...`), field token là `accessToken` (không `token`); `/api/me` + `/api/menu-keys` **404 vì không tồn-tại**, không phải regression.
- **BÀI-HỌC:** **lệnh đo sai trả về IM-LẶNG** (2-backslash ra rỗng; enum string ra 400; endpoint sai ra 404) — mỗi phép đo phải có ca đối-chứng dương, và phải phân-biệt "0 kết-quả" với "đo hỏng".
- **BẤT-NGỜ:** brief do người ra lệnh viết lại chính là nguồn sai (payload `"Drafter"`) — và verbatim ghi thẳng "brief example payload was misleading", không đổ cho hệ-thống.
→ substring:"JsonStringEnumConverter"
**[cao] D7 — MEMORY phình 35→72KB: 8 lần cảnh-báo leo thang mà không ai dừng**
- **VIỆC:** vai tự theo-dõi kích-thước diary của chính mình sau mỗi entry.
- **KẾT-LUẬN:** chuỗi tự khai: ~35KB ("approaching") → ~40 → ~43 ("over 25KB trigger, under 50KB hard") → ~50 ("at hard threshold") → ~58 ("**REQUIRED** ARCHIVE ... BEFORE next entry") → ~60 ("**MANDATORY**") → ~62 ("**DEDICATED CURATION SESSION PRIORITY NEXT**") → ~66→69 ("priority MAX") → ~72 ("**priority MAX REINFORCED**" — chữ nguồn viết `priority` thường). Chốt chỉ xảy ra khi có **phiên curate riêng** (chính file archive này), kéo về mục-tiêu ~25-30KB.
- **BÀI-HỌC:** cảnh-báo do **chính kẻ đang phình** phát ra thì không tự dừng được — cần **cổng ngoài** (ngưỡng cứng chặn ghi) hoặc **việc riêng có chủ**; và ngưỡng "mềm 25KB / cứng 50KB" bị vượt **gần 3 lần** vẫn chỉ sinh thêm chữ in-đậm.
- **BẤT-NGỜ:** mức độ khẩn-cấp trong câu chữ leo thang **8 nấc** (approaching → REQUIRED → MANDATORY → priority MAX → REINFORCED) — bản thân nó là dữ-liệu: leo-thang ngôn-từ **không** thay cho cơ-chế.
→ substring:"priority MAX REINFORCED"
**[cao] D10 — Tự khai tác-dụng-phụ và giới-hạn phép đo**
- **VIỆC:** ghi lại thứ phép kiểm-chứng làm hỏng + thứ nó KHÔNG chứng được.
- **KẾT-LUẬN:** kiểm-chứng bằng POST thật đã **đổi state prod**: PE/2026/A/025 phase 10→98 (admin bypass qua `if (isAdmin) return` **dòng 186**), khai rõ "admin bypass là thiết-kế, không phải regression", đề bro tự chọn revert hay giữ; PE/2026/A/021 trả 409 ⇒ **không đổi state (atomic)** — có kiểm lại chứ không đoán. Giới-hạn tự khai: NV Test **không đóng vai approver** (đã quét 20 PE) nên biến-thể actor **không khả-thi**; #194 dùng chữ **"presumed" 4 lần** cho phần không đo được; #219 **bỏ qua sqlcmd** vì thiếu `PROD_DB_PASSWORD`, chấp-nhận verify gián-tiếp và nói rõ là gián-tiếp.
- **BÀI-HỌC:** báo-cáo verify phải tách 3 mức: **đo trực-tiếp** / **suy ra** (`inferred`, `presumed`) / **không đo được** — và ghi luôn phép đo đã làm bẩn dữ-liệu.
- **BẤT-NGỜ:** ca 409 lại là **bằng-chứng dương mạnh nhất** cho Plan O: lỗi 409 "mode mismatch" chứng-tỏ đã qua được cửa actor-check (403 cũ), tức **thất-bại đúng chỗ = thành-công**.
→ substring:"no state change (atomic)"
**[vừa] D4 — Chuỗi Mig 28→31 + backfill có kiểm số**
- **VIỆC:** 5 lượt deploy schema per-NV/per-Level.
- **KẾT-LUẬN:** Mig 28 `AddAdvancedOptionsToApprovalWorkflows` (#186, 6 cờ Allow* mức workflow, `allowReturnToDrafter=true` mặc-định + 5 false) → Mig 29 `RefactorAdvancedOptionsToPerLevelAndDrafterUser` (#187): Levels **+5** Allow*, Users **+1**, ApprovalWorkflows **6**; backfill **48/48** Levels=1 và **0/13** Users=1 (đúng vì nguồn `false`) → Plan D/C/E (#188): `/api/users` +`allowDrafterSkipToFinal`, PATCH admin 204 / nv.test **403**, scope V2 chặt (nv.test 8 PE < admin 17) Mig 30 `AddAllowApproverEditBudgetToLevels` (S22-chốt): `currentLevelOptions` 6 key, `AwLevelDto` 12 key Mig 31 `RefactorSkipToFinalToApproverLevel` (#194): Levels +1, Users 1, endpoint zombie `/allow-skip-final` trả **404** đúng ý.
- **BÀI-HỌC:** backfill phải kiểm **cả hai chiều** "48/48 = 1" "**0/13** = 1" đều kết-quả ĐÚNG cần xác-nhận; số 0 đây bằng-chứng, không phải thiếu-sót. Dọn field thì phải kiểm **cả endpoint lẫn key trong DTO đã biến mất**.
- **BẤT-NGỜ:** cùng một tính-năng bị dời chỗ 3 lần (workflow user level) trong ~2 ngày; test-gate suy-ra từ "deploy chạy được" chứ không đọc trực-tiếp (`test gate inferred PASS`).
substring:"AddAdvancedOptionsToApprovalWorkflows"
**[thấp] D8 Đánh-bóng UX + 1 ca revert trong phiên**
- **VIỆC:** 4 Run FE thuần: #204 banner `mx-5``mb-3`; #209 sidebar `truncate` (`min-w-0 flex-1` + `shrink-0` + `title=` tooltip); #210 widen `w-60 → w-72 xl:w-80` **kèm revert Plan U**; #214 wrap nhãn dài.
- **KẾT-LUẬN:** 4/4 PASS, bundle rotate đúng kỳ vọng mỗi lần; menu `Pe_DuyetNcc_WfView` Order=2 + họ `Pe_DuyetNccPhuongAn` Order 6-10, tổng **1-10 liền mạch không hở/không chồng** (seed idempotent).
- **BÀI-HỌC:** đổi FE-thuần vẫn verify được gián-tiếp bằng **bundle-hash làm proxy**; nhưng khi một chunk bị revert trong cùng phiên thì hash phản-ánh **trạng-thái tích-luỹ**, không phải diff của chunk đừng đọc hash như chữ-ký của một thay-đổi đơn lẻ.
- **BẤT-NGỜ:** phép đo bundle của Plan P **bắt nhầm** thay-đổi của Run #204 đang bay cùng lúc (`QZIPWD-g`/`DaLTMGcx`) vẫn kết-luận đúng nhờ đối-chiếu **diff có 0 file FE**, tức dùng nguồn thứ hai để cứu phép đo bị nhiễu.
substring:"ac2c859"
**[cao] D9 "Key patterns extracted S21-S25" (giữ gần như nguyên)**
- **VIỆC:** phần (c) đã bản chưng-cất 8 mục do chính vai viết.
- **KẾT-LUẬN:** 8 mục = Discovery #3 · Discovery #4 · Discovery #5 · **checklist 10 điểm per-NV** (chép ĐỦ 10, nguyên-văn rút gọn P2a-MAJOR-2 @S170): **(1)** Domain entity field · **(2)** EF config `HasDefaultValue(false)` · **(3)** Migration 3-file rule (Up + Designer + Snapshot) · **(4)** Service handler read field · **(5)** Domain DTO + Application DTO mirror · **(6)** Designer FE checkbox inline · **(7)** Admin overview `AwLevelDto` record + `ToDto` ctor · **(8)** `CreateAwLevelInput` record + Update mutation handler · **(9)** Lookup discrimination in handler (`FirstOrDefault` ADD `ApproverUserId == actorId` filter, fallback admin) · **(10)** Controller body record mirror count check (`[FromBody]` record param count = Command record param count) kèm gotcha #48 + cách · "CI test-gate bắt được lỗi UAT-skip-test bỏ sót" · FE merge synthetic + userMap · capture pre-call mutation state.
- **BÀI-HỌC:** khi nguồn đã lớp chưng-cất, việc nén nên **giữ lớp đó gần như nguyên văn** cắt lớp Run bên dưới nén lại cái đã nén chỗ dễ mất mã-số (#48, điểm 9/10) nhất. *(Chính draft này lượt đầu đã dính đúng bẫy đó: tuyên "bản duy nhất đủ 10" rồi chỉ giữ ~6 — reviewer P2a bắt, vá bằng chép đủ 10 ở trên.)*
- **BẤT-NGỜ:** checklist 10 điểm verbatim **bản duy nhất liệt-kê đủ 10** trong toàn batch A mọi chỗ khác chỉ nhắc số "8"/"9"/"10 surface points" không kèm nội-dung (đủ 10 mục gốc: verbatim vùng "Key patterns extracted" mục 4).
substring:"Key patterns extracted"
## Phụ-lục token (coverage)
- **gotcha#:** `#41` (path filter dẫn 6 lần) · `#44` (silent 403 "fix confirmed" #210) · `#45` (fix Trả lại, Run #186) · `#47` (agent-memory **disproven**, giữ vai PREVENTIVE) · `#48` (**NEW** multi-`Changelog.Add` + SQLite frozen-clock tie-break)
- **Discovery#:** `#1` rate-limit 429 ~5 req/phút · `#2` `.claude/agent-memory/**` không nằm trong paths-ignore (**về sau bị bác**) · `#3` Gitea xét TRỌN push-range (số lần củng-cố: NGUỒN TỰ MÂU-THUẪN tiêu-đề verbatim ghi "RE-CONFIRMED 4th time" nhưng thân ngay dưới ghi #210 "does NOT reinforce" 3 lần củng-cố THẬT #200/#201/#202 + 1 lần #210 không-tính; thân-draft dùng số 3 theo vế thân) · `#4` ASP.NET Core 10 record+enum cần **số** · `#5` sqlcmd cần `\\\\SQLEXPRESS` **4 backslash** · *(không đánh số)* `/actions/tasks` `/actions/runs` (#186) · `updated_at` trễ ~2 phút (#187) · "Discovery 1" 503 giữa deploy + "Discovery 2" `/api/auth/login` + field `accessToken` (#221)
- **surprise:** `CRITICAL FAIL — K3 DTO mirror INCOMPLETE` · "`AllowApproverSkipToFinal` mentioned **only in comment line 60**, NOT as actual record param" · "**em main + Reviewer + Implementer all missed across 3 plan**" · "Bug present **2 days prod**" / "**3-day prod bug latency**" · "Spec hypothesis ... **disproven for this commit**" · "**Anomaly is BENEFICIAL** catches verify gate even on docs-only tip" · "PROVES ACTOR VALIDATION SUCCESS ... 409 mode mismatch NOT 403" · "First attempt with **string enum** `\"Drafter\"` HTTP 400 ... Brief example payload was **misleading**" · "Test 2 HTTP 409 = handler threw exception **no state change (atomic)**" · "NV Test actor variant **not feasible** (scanned 20 PEs…)" *(trích cắt — nguồn còn "— NV Test = Drafter not approver")* · "the Budget Adjust entry ... has `userName=\"\"` empty this is the **EXACT pre-fix Bug 1 evidence**" · "**Side effect logged:** Admin transition on PE/2026/A/025 mutated phase 1098" · "Run #190 ... **CANCELLED** by concurrency ... normal not a fail" · "`/api/me` + `/api/menu-keys` returned 404 (route names different) **Not regression**" · "Memory size ... **priority MAX REINFORCED**" · "8 surface points checklist (**NOT 6**)" · "prophylactic codebase scan is needed"
- **guard:** `if (isAdmin) return` (**line 186**, admin bypass CỐ Ý) · `if (!isAdmin && currentLevel is not null)` (**line 264**) · `&& l.ApproverUserId == actorId` + `throw if null` (4 site Plan O) · fallback `?? curStep?.Levels.FirstOrDefault(...)` cho admin/non-approver (Plan N) · `[Authorize]` class-level + `GetWorkflowsForUserAsync` auth filter (fix #44) · `if (!demoSeedDisabled)` bọc 5 seed demo (Plan T) · `.Where(c => c.Summary!.Contains("Chuyển phase"))` ( #48) · `HasDefaultValue(false)` (điểm 2 checklist) · **Migration 3-file rule** (Up + Designer + Snapshot điểm 3) · `[FromBody]` record param count = Command param count (điểm 10) · seed Order shift **idempotent** (1-10 liền mạch) · token-cache `%TEMP%\*_token.txt` chống 429 · test-gate CI chặn trước deploy
- **root-cause:** `FirstOrDefault(l => l.Order == curLevelOrder)` **không lọc actor** luôn lấy `Levels[0]` theo thứ-tự DB cờ per-NV admin tick bị bỏ qua (4 site cùng bệnh) · Controller `TransitionPeBody` 3 field Command 7 field ASP.NET `default` **im-lặng** 3 field F1/F2 không tới handler suốt 2 ngày · `AwLevelDto` thiếu param + `ToDto` thiếu ctor-arg cột DB API không trả · nhiều `Changelog.Add()` cùng `SaveChangesAsync` + SQLite frozen-clock `OrderByDescending(CreatedAt)` tie-break bất-định · Gitea xét paths-ignore trên trọn push-range tip docs-only vẫn chạy · thiếu `JsonStringEnumConverter` enum dạng chuỗi fail 400 · `\\SQLEXPRESS` (2 backslash) **0 output, im-lặng** · `UserName` không set 9 site `Changelog.Add()` cột người-dùng rỗng
- **file:line / định-danh (BE):** `ApprovalWorkflowV2AdminFeatures.cs` **:23-38** (`AwLevelDto` record), **comment :60**, `ToDto` **:156-158**, vùng **:184-238** · `PurchaseEvaluationFeatures.cs` **:765** (per-NV lookup), **:314-315** (AdjustBudget) · `PurchaseEvaluationWorkflowService.cs` **:204** (`EnsureCanRejectV2Async`), **:258-260** (`ApplyReturnModeAsync`), **:264** (guard), **:186** (admin bypass), **:75-103** (nhánh Reject Plan AC), **:472** (prefix `[Duyệt vượt cấp tới Cấp cuối]`), **:287-333** (F1 edge Bước 1), **:268-275** (F1.Drafter giữ TraLai) · `PurchaseEvaluationDetailFeatures.cs` **:75-76** (`EnsureEditableForDetailsAsync`) · Controller `TransitionPeBody` **:267** (3 field) **:280-286** (7 param), `mediator.Send` **:76-78**, marker Plan P **:71-75** · `TransitionPurchaseEvaluationCommand` **:395** · `Program.cs` (0 hit `JsonStringEnumConverter`/`AddJsonOptions`) · `DbInitializer.cs` · `MenuKeys.cs` · `appsettings.json` / `appsettings.Development.json` / `appsettings.Production.json` (**bị `.gitignore`**)
- **file:line / định-danh (test + FE):** test `ApplyReturnMode_OneStep_AtStep1` **line 350** · `ApplyReturnMode_OneLevel_AtStep1Level1` **line 308** · `GetPurchaseEvaluationCurrentLevelOptionsTests.cs` (2 method) · `PurchaseEvaluationPerNvLookupRegressionTests.cs` (3 test) · `PeDetailTabs.tsx` (fe-user + fe-admin, mirror **§3.9**) · `PeWorkflowPanel.tsx` · `types/purchaseEvaluation.ts` (`WorkflowReturnMode = { OneLevel:1, OneStep:2, Assignee:3, Drafter:4 }`) · `Layout.tsx` (`w-60 → w-72 xl:w-80`; `min-w-0 flex-1` + `shrink-0` + `truncate` + `title=`) · `WorkflowMatrixViewPage.tsx` · `approvalWorkflowV2.ts` · `App.tsx` · helper `extractNextTargetHint(comment, decision)` · regex `\[Bước (\d+) — Cấp (\d+)\]` · `resolveActorName`/`resolveUserName` + `userMap` useMemo
- **Run / sha / bundle:** #186 `eea86fd` (id=300) · #187 `c0af9e0` (301) · #188 `a74e671` (302) · #189 `40f64c6` · #190 `8185070` **CANCELLED** · #191 `0e70789` · #192 `30d51c8` · #193 `b04a11a` (307) · #194 `098baa6` (308) **PARTIAL** · #195 `0062fcb` (309) · #199 `da30e27` · #200 `f4055a1` (314) · #201 `fb3c22c` (315) · #202 `a1c8386` (316) · #203 `1727bd5` (317) · #204 `108268a` · #207 `0b97840` · #208 `7b7b28f` · #209 `86d8806` · #210 `ac2c859` (324) · #214 `ee0902a` (328) · #217 `a734bf2` · #218 `25837b6` (332) · #219 `0aaf2df` (333) · #220 `9ea62be` (334) · #221 `506cada` (335) · commit lẻ: `c2042ef`/`4dd6f9c`/`508b17a` (Plan M) · `0326458` (N1+N2) · `ae01ca5` (O1-O5) · `ee776d5`/`c667802` (AA) · `b079b27` (BE Mig 30) · `cc8a7d3` (SKIP) · `3d725c4..cc8a7d3` · `e23f51c..506cada` · bundle-chain admin `CzesdXLh→D5l49-70→Cclc8Uwu→CpI5OL8n→CRsX6cFo→D_JENTBi→QZIPWD-g→Dmk--X6w→CZdXQ2eo→CrHpBYFM→B5iZMa7g→CDVnRDe6→DR95zKWg→C8TvDy7r`; user `DP-gH4LW→B6N5hq3d→d064StNa→X7qb4Zl4→COJhbRxy→DaLTMGcx→Bd4gh3Tp→DCwhhey2→C3bCWZ90→CHJDH3M2→gAFN3NVx→BAj_Yaj5→BvcWrq2z`
- **Mig / dữ-liệu prod:** Mig 28 `20260513114505_AddAdvancedOptionsToApprovalWorkflows` · Mig 29 `RefactorAdvancedOptionsToPerLevelAndDrafterUser` (backfill **48/48** Levels · **0/13** Users) · Mig 30 `20260513160703_AddAllowApproverEditBudgetToLevels` · Mig 31 `20260514160124_RefactorSkipToFinalToApproverLevel` · test-count chuỗi **84 → 103 → 104 → 106 → 108 → 111** · Users **33** · Suppliers 19 · Projects 9 · Contracts 7 · PE-ID `3248f2f9-c6e9-43ff-a4ca-067ffecf9f36` (PE/2026/A/032) · `98736f06-...` (A/025) · `59176ad5-...` (A/027) · `6148ba7a-...` (A/026) · `f9476dad-...` (A/021) · workflow `bf31f120` · token len admin **468** / nv.test **477** · `~720 rows wiped`
- **🔐 secret (đã xử-lý):** dòng **48** verbatim `appsettings.Production.json` tại `C:\inetpub\solution-erp\api\`, `user=vrapp`, password prod **ĐÃ ĐƯỢC LEAD REDACT** thành nhãn `pwd=[đã che 71b7740a — ROTATE REQUIRED]` gist chép NHÃN CHE này, TUYỆT ĐỐI không tra/chép giá-trị gốc từ git history; **ROTATE REQUIRED** vẫn treo (mật-khẩu từng nằm trong lịch-sử git)
- **meta / size-trend:** `~35KB → ~40 → ~43 → ~50 → ~58 → ~60 → ~62 → ~66 → ~69 → ~72KB` · ngưỡng mềm **25KB** / cứng **50KB** · FIFO `~8 → ~9 → ~10 → ~11 → ~12 → ~13 entry` · token-cost mỗi lượt `~10k-28k` · `feedback_md_compact_narrative.md` 6.5) · `feedback_uat_skip_verify` · `feedback_per_nv_permission_scope.md`
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `#216` > **Target slim MEMORY.md after archive:** ~25-30KB (~5 entries kept: Run #215 fail lesson + Run #216 fix + Run #222-#227 S26 + setup baseline).…
- `#222` — > **Target slim MEMORY.md after archive:** ~25-30KB (~5 entries kept: Run #215 fail lesson + Run #216 fix + Run #222-#227 S26 + setup baseline).…
- `#227` — > **Target slim MEMORY.md after archive:** ~25-30KB (~5 entries kept: Run #215 fail lesson + Run #216 fix + Run #222-#227 S26 + setup baseline).…
- `83c9f7b` — (S23 t3 Plan M push range `83c9f7b..f4055a1` 4 commits: Chunk M1 BE Service F1.OneLevel/OneStep edge case Bước 1 → reset (0,1) giữ ChoDuyet + a…
- `8c05947`**PASS** verdict — fix Bug 3 "Lịch sử duyệt" panel show Trả lại + Duyệt vượt cấp. Push `8c05947..a734bf2` 1 commit Plan AC. Duratio…
- `Mig 26` — (S25 t4 Plan AC2 — FE merge view recover historical Reject events PE cũ — push `a734bf2..25837b6` 1 commit Plan AC2 FE-only). Duration 3m23s base…
- `Mig 27` — (S23 t11 Plan U FE sidebar truncate). Commit 2 file FE × 2 app mirror `Layout.tsx` — MenuNodeRenderer button (accordion toggle) + MenuLeaf NavLink …
- `PurchaseEvaluationDetailFeatures.cs:75` — (S23 t5 Plan O HOTFIX cascade 4 lookup sites — push range `fb3c22c..a1c8386` 2 commits: `ae01ca5` Chunk O1-O5 atomic BE fix 4 lookup sites cùng pat…
- `PurchaseEvaluationFeatures.cs:314` — (S23 t5 Plan O HOTFIX cascade 4 lookup sites — push range `fb3c22c..a1c8386` 2 commits: `ae01ca5` Chunk O1-O5 atomic BE fix 4 lookup sites cùng pat…
- `PurchaseEvaluationWorkflowService.cs:204` — - `PurchaseEvaluationWorkflowService.cs:204` `step.Levels.FirstOrDefault(l => l.Order == curLvl && l.ApproverUserId == actorId)` + throw if null…
- `PurchaseEvaluationWorkflowService.cs:258` — - `PurchaseEvaluationWorkflowService.cs:258-260` `step.Levels.FirstOrDefault(l => l.Order == evaluation.CurrentApprovalLevelOrder && l.ApproverUserId …
- `Run #187` — ### 2026-05-13 20:12-20:15 — Run #187 id=301 sha=c0af9e0 VERDICT=PASS…
- `Run #193` — (S22 chốt — em main spawn cumulative verify cuối S22). Latest CI run #193 id=307 sha=`b04a11a` (S22+5 Chunk B FE) success at 23:16. **Tip commit…
- `Run #194` — ### 2026-05-14 ~16:30 — Run #194 id=308 sha=`098baa6` VERDICT=PARTIAL…
- `Run #195` — ### 2026-05-14/15 — Run #195 id=309 sha=`0062fcb` VERDICT=PASS…
- `Run #199` — (S23 t3 Plan M push range `83c9f7b..f4055a1` 4 commits: Chunk M1 BE Service F1.OneLevel/OneStep edge case Bước 1 → reset (0,1) giữ ChoDuyet + a…
- `Run #200` — ### 2026-05-15 ~11:21-11:25 — Run #200 id=314 sha=`f4055a1` VERDICT=PASS…
- `Run #201` — ### 2026-05-15 12:42-12:45 — Run #201 id=315 sha=`fb3c22c` VERDICT=PASS…
- `Run #202` — ### 2026-05-15 13:18 — added S23 t5 Plan O HOTFIX Run #202 entry…
- `Run #203` — ### 2026-05-15 13:45 — added S23 t6 Plan P HOTFIX Run #203 entry…
- `Run #207` — ### 2026-05-15 ~14:15 — Run #207 sha=`0b97840` VERDICT=PASS…
- `Run #208` — ### 2026-05-15 ~14:30 — Run #208 sha=`7b7b28f` VERDICT=PASS…
- `Run #209` — ### 2026-05-15 ~14:50 — Run #209 sha=`86d8806` VERDICT=PASS…
- `Run #210` — ### 2026-05-15 15:25 — added S24 t1 Plan AA Run #210 entry…
- `Run #214` — id=328 sha=`ee0902a` **VERDICT=PASS** (bundle baseline admin `CZdXQ2eo` + user `DCwhhey2`, Mig 31 TOP 1 unchanged, smoke 5/5 200) — *dòng này @P3-mức-3 thay đoạn trích đầu-dòng cũ vốn dính thân entry Run #215 FAIL ⇒ gán verdict ngược (reviewer P3-B1)*
- `Run #215` — > **Target slim MEMORY.md after archive:** ~25-30KB (~5 entries kept: Run #215 fail lesson + Run #216 fix + Run #222-#227 S26 + setup baseline).…
- `Run #216` — > **Target slim MEMORY.md after archive:** ~25-30KB (~5 entries kept: Run #215 fail lesson + Run #216 fix + Run #222-#227 S26 + setup baseline).…
- `Run #218` — ### 2026-05-19 11:00-11:03 — Run #218 id=332 sha=`25837b6` VERDICT=PASS…
- `Run #219` — ### 2026-05-19 11:25 — added S25 t5 Plan AD Run #219 entry…
- `Run #220` — ### 2026-05-19 12:33-12:36 — Run #220 id=334 sha=`9ea62be` VERDICT=PASS…
- `Run #221` — ### 2026-05-19 13:05-13:08 — Run #221 id=335 sha=`506cada` VERDICT=PASS…
- `Run #222` — > **Target slim MEMORY.md after archive:** ~25-30KB (~5 entries kept: Run #215 fail lesson + Run #216 fix + Run #222-#227 S26 + setup baseline).…
- `a1a910f` — (S24 t1 Plan AA — IsUserSelectable filter + WfView menu + sidebar widen). Push range `a1a910f..ac2c859` 3 commits: `ee776d5` Chunk A BE+Layout (6 fi…
- `eb106f2` — (Plan K 8 commits S23 t1 Mig 31 F2 refactor sang per-Approver-slot — push range `eb106f2..098baa6`). Run completed `status=success`. **Mig 31 prod T…

View File

@ -0,0 +1,66 @@
# implementer-backend/2026-05-q1 — gist
distill-gen: 1
source-verbatim: .claude/agent-memory/implementer-backend/archive/2026-05-q1.md (12 entry → 5 cụm)
pointer-style: substring
## TỔNG-HỢP (3-5 ý)
1. **Tỉ-lệ TỪ-CHỐI gần tuyệt-đối cả quý** (S21 t3-t5: 3/3 · S22: 100% · S23 t4→t11: 8 plan liên-tiếp 0 spawn · S24: 4/4) theo 7 tiêu-chí phạm-vi — verbatim tự đối-chiếu khớp cảnh-báo Anthropic "tightly interdependent coding"; ranh-giới đo bằng độ ràng-buộc chéo lớp (tiêu-chí #3/#4), không bằng kích-thước diff, và mỗi lần từ-chối ghi kèm tiêu-chí thì tỉ-lệ mới đọc được về sau.
2. **Chuỗi K Mig 31 (pre-A → K1 → K3 → K5 → K7):** refactor đổi chỗ lưu cờ Users→Levels có chunk dọn-rác riêng (K5) + grep-về-ZERO 5 biến-thể tên + test 3 xoá/3 thêm triệt-tiêu giữ baseline 104; mất dữ-liệu prod CÓ CHỦ-ĐÍCH (Option A không backfill, khai đích danh 4 tài-khoản); nợ kỹ-thuật được ĐẶT LỊCH (test vỡ để lại cho K7), không bỏ quên.
3. **Mục "Mismatches discovered" tự khai 4 chỗ đoán sai** (per-NV mặc-định vs cờ admin bật riêng → Mig 30 · `PE.changelogs` vắng ⇒ TS2339 · Dialog `size="xl"` không tồn-tại · `accessToken``token` ⇒ 401) — giả-định hình-dạng API/thư-viện phải kiểm bằng đọc, không bằng trí nhớ; bẫy `accessToken` cắn 2 vai khác nhau (tái-xuất ở cicd-monitor Run #221).
4. **3 pattern FE mới 13/14/15 từ Plan AA** (read-only mirror Designer · Tailwind JIT full-class-string · rowSpan flat-row builder); shadcn fe-user là subset thiếu hẳn `Card`/`Badge` — 2 app tưởng song sinh nhưng kho UI lệch nhau.
## Entries
**[cao] F1 — Nhật-ký TỪ-CHỐI: vai này ghi cả việc mình KHÔNG làm**
- **VIỆC:** phân-loại việc được giao theo 7 tiêu-chí phạm-vi, trả lại việc không hợp.
- **KẾT-LUẬN:** S21 t3-t5 **từ-chối 3/3**; S22 **từ-chối 100%**; S23 t4→t11 **8 plan liên-tiếp em-main tự làm, 0 lần spawn** (N+O 5 site lookup · P Controller ~6 LOC · Q CSS 8 LOC · R+S+T5 xoá dữ-liệu prod ~720 dòng qua 4 file `.sql` · T cờ DemoSeed ~25 LOC · U sidebar 25 LOC); S24 **4/4 từ-chối đúng**, mỗi ca ghi rõ commit + tiêu-chí (`da218f1` #6 vặt <30 phút · `4d60598` #2 luồng UX · `fbbd361` #2 · `ee0902a` #6). Lý-do chính: tiêu-chí **#3** (xuyên >2 lớp) + **#4** (chuỗi suy-luận sửa lỗi).
- **BÀI-HỌC:** ranh-giới "được giao" phải quyết theo **độ ràng-buộc chéo lớp**, không theo kích-thước diff — và mỗi lần từ-chối phải **ghi lại kèm tiêu-chí** thì tỉ-lệ mới đọc được về sau.
- **BẤT-NGỜ:** cả một quý mà tỉ-lệ từ-chối gần như tuyệt-đối; verbatim tự đối-chiếu: "REFUSE rate 100% **match Anthropic warning 'tightly interdependent coding'**" — tức vai tự xác-nhận giới-hạn của chính mô-hình chia việc, chứ không cố nhận cho có.
→ substring:"tightly interdependent coding"
**[cao] F2 — Chuỗi K Mig 31: đổi chỗ lưu cờ F2 từ `Users` sang `ApprovalWorkflowLevels`**
- **VIỆC:** 5 chunk nối tiếp trong S23 t1 (pre-A → K1 → K3 → K5 → K7).
- **KẾT-LUẬN:** **pre-A** đổi nhãn slot Designer (`ApprovalWorkflowsV2Page.tsx:873`, tra `fullName` từ `usersList` đã có sẵn ~`:500`, khuôn `.find` đã dùng ở `:535`). **K1** Mig 31 ADD-DROP **không BACKFILL** (Option A) — 6 file BE, cắt **-37 LOC** nhánh Drafter SUBMIT (`PurchaseEvaluationWorkflowService.cs:121-157`), 4 chỗ vỡ biên-dịch vá bằng sentinel `false` + marker K2, giữ tham-số thứ 8 `bool skipToFinal` để K2 dùng lại. **K3** checkbox thứ 7 = 7 tiểu-mục nguyên-tử (type → default → propagate `?? false` → vị-trí cuối nhóm → viết lại banner `~:623` → body mutation). **K5** dọn zombie: xoá endpoint `PATCH /allow-skip-final`, xoá field DTO/Command/Handler, viết lại 6 chỗ chú-thích lỗi-thời, xoá cột "Skip cuối" FE — rồi **grep 5 từ-khoá ra ZERO** toàn `src/Backend` + `fe-admin/src` (+42/-94 LOC / 9 file). **K7** xoá 3 test Drafter đã hết nghĩa + thêm 3 test Approver, helper mới `SeedApproverF2WorkflowAsync` (2 Bước × 2 Cấp để chứng skip **không** rơi sang Bước kế) ⇒ **104 PASS, baseline không đổi vì 3 xoá + 3 thêm triệt tiêu nhau**.
- **BÀI-HỌC:** refactor đổi chỗ lưu-trữ phải có **chunk dọn rác riêng** (K5) và kết bằng **grep-về-0 trên nhiều biến-thể tên** (PascalCase / kebab / camel / nhãn tiếng Việt / tên icon) — không thì cờ chết vẫn còn dấu vết khắp nơi. Test cũng phải **xoá thứ đã hết nghĩa**, đừng chỉ thêm.
- **BẤT-NGỜ:** (i) chấp-nhận **mất dữ-liệu prod có chủ-đích** — Option A không backfill, khai đích danh 4 tài-khoản mất giá-trị (`fin.pp`, `pm.nv`, `nv.test`, `truong.nguyen`); (ii) `UserConfiguration.cs` **không tồn-tại**`User` được cấu-hình inline trong `ApplicationDbContext.OnModelCreating` ~`:86` nên không có `HasDefaultValue`, EF vẫn tự nhận đổi prop; (iii) test hỏng do K1 được **cố-ý để lại** cho K7 (`...ReturnModeTests.cs:253`), tức nợ kỹ-thuật được **đặt lịch** chứ không bỏ quên.
→ substring:"SeedApproverF2WorkflowAsync"
**[cao] F3 — Bốn "Mismatch" tự khai ở S22**
- **VIỆC:** ghi lại 4 chỗ hiểu sai giữa ý bro và cách vai định làm.
- **KẾT-LUẬN:** (1) "người điều chỉnh cũng là người duyệt" — vai định cho **mặc-định** Approver luôn được phép, bro chỉnh thành **cờ admin bật riêng** (thành Mig 30); (2) `PE.changelogs` **không có** trong `PeDetailBundle` ⇒ dựng lịch-sử trong `BudgetAdjustSection` **fail TS2339**, phải bỏ và hoãn sang S23 bằng endpoint fetch riêng; (3) `Dialog size="xl"` **không tồn-tại** (chỉ `sm|md|lg`) ⇒ dùng `lg` cho iframe xem trước; (4) field đăng-nhập là **`accessToken`**, không phải `token``seed-test-users-prod.ps1` **401** ở lần đầu, sửa thành `$authResp.accessToken`.
- **BÀI-HỌC:** phải **chốt "mặc-định vs admin bật riêng" TRƯỚC** khi mở rộng phạm-vi quyền — đây là loại nhầm không lộ ra ở compile mà lộ ở nghiệm-thu. Ba cái còn lại chung một gốc: **giả-định về hình-dạng API/thư-viện phải kiểm bằng đọc, không bằng trí nhớ**.
- **BẤT-NGỜ:** vai chủ-động mở mục "Mismatches discovered" để ghi **cái mình đoán sai** — và mục này về sau là phần giá-trị nhất của entry; nhầm về `accessToken` còn tái-xuất ở cicd-monitor (Run #221) ⇒ cùng một bẫy cắn **hai vai khác nhau**.
→ substring:"Mismatches discovered"
**[vừa] F4 — Plan AA: trang matrix chỉ-đọc cho fe-user**
- **VIỆC:** dựng bản xem quy-trình V2 đã ghim cho phía user (3 file: types ~55 LOC + page ~215 LOC + route).
- **KẾT-LUẬN:** PASS — lọc **ở BE** bằng `?isUserSelectable=true` thay vì lọc ở FE; bảng 10 cột (3 meta + 7 cờ Allow*), rowSpan theo Bước, đủ 3 trạng-thái Loading/Error/Empty, route đặt **trước** `/workspace`. Đúc 3 pattern mới: **13** (mirror trang Designer ở dạng chỉ-đọc = bỏ mutation + tái-dùng DTO dạng subset), **14** (Tailwind JIT phải viết **chuỗi class đầy đủ** trong mảng), **15** (helper dựng hàng phẳng cho rowSpan).
- **BÀI-HỌC:** làm bản chỉ-đọc thì **đẩy điều-kiện lọc về BE** — vừa gọn FE vừa tránh lộ dữ-liệu không được xem. Và phải **kiểm kho `components/ui/` của đúng app TRƯỚC khi import**.
- **BẤT-NGỜ:** `shadcn` của **fe-user thiếu hẳn `Card`/`Badge`** (chỉ có 6 thành-phần) — 2 app tưởng song sinh nhưng kho UI lệch nhau, phải nội-suy bằng `<div>`/`<span>` thuần cho khớp mắt với Designer bên admin.
→ substring:"WorkflowMatrixViewPage"
**[vừa] F5 — Plan M: mở-rộng helper + đổi nhãn có kỷ-luật**
- **VIỆC:** M2 thêm 2 test ca-biên "Bước 1 không lùi được"; M3 đổi nhãn hiển-thị Phase 98 "Trả lại" → "Cần chỉnh sửa lại".
- **KẾT-LUẬN:** M2 **mở-rộng** `SeedWorkflowAsync` thêm 2 tham-số tuỳ-chọn mặc-định `false` (**không clone helper**, 4 test cũ không vỡ) ⇒ **106/106**; khẳng-định assert đúng chỗ: `Summary` là chuỗi cố-định `"Chuyển phase {from} → {to}"` nên câu "không lùi được" phải tìm ở **`ContextNote`**. M3 đổi đúng **8 dòng / 4 file × 2 app**, và liệt-kê rõ **4 nhóm KHÔNG đụng** (nút hành-động `← Trả lại`**động-từ**, nhãn 4 mode, chú-thích lý-do, `types/contracts.ts` + `types/budget.ts` ngoài phạm-vi).
- **BÀI-HỌC:** phân-biệt **"nhãn trạng-thái" ⟂ "động-từ hành-động"** khi đổi chữ — cùng một chuỗi "Trả lại" mà một chỗ phải đổi, một chỗ không. Danh-sách **KHÔNG-đụng** có giá-trị ngang danh-sách đã-đụng.
- **BẤT-NGỜ:** vai tự đặt **ngưỡng chống-nghịch: "anti-fiddle <20% LOC"** và tự biện-hộ vì sao 2 chuỗi inline ngoài spec vẫn được sửa (cùng ngữ-nghĩa "tham-chiếu trạng-thái") — tự ràng mình bằng con-số trước khi bị hỏi.
→ substring:"Cần chỉnh sửa lại"
## Phụ-lục token (coverage)
- **gotcha#:** `#45` (bug Trả lại — lý-do REFUSE) · `#44` (Authorize policy — Pattern 10, 5 test ~50 LOC) · "45 gotcha" (mốc S21) / "46 gotchas unchanged" (mốc S22)
- **criteria# (7 tiêu-chí phạm-vi):** `#2` luồng UX · `#3` xuyên >2 lớp · `#4` chuỗi suy-luận sửa lỗi · `#6` vặt <30 phút
- **Pattern#:** `2` EF migration 3-file rule · `3` audit-reuse **EXTEND không clone helper** · `5` mirror 2 app strict · `7` per-NV admin opt-in · `8` tách endpoint narrow scope · `9` defense-in-depth FE+BE guard pair · `10` Reflection-based Authorize regression · `11` test-infra helper cookie-cutter · `12` `InternalsVisibleTo` · **`13`** read-only mirror Designer (MỚI) · **`14`** Tailwind JIT full class string (MỚI) · **`15`** rowSpan flat row builder (MỚI) · `16-18` (S25 wrap, chỉ nhắc header) · `19` (S26 Plan AG, chỉ nhắc header)
- **surprise:** "**Surprise:** shadcn fe-user KHÔNG `Card`/`Badge`" · "`UserConfiguration.cs` **file không tồn tại** User entity configured inline `ApplicationDbContext.OnModelCreating` ~line 86" · "Dialog `size=\"xl\"` **NOT supported**" · "API auth field `accessToken` **không phải** `token` ... FAIL 401" · "`PE.changelogs` **KHÔNG có** trong PeDetailBundle ... build **FAIL TS2339**" · "accept **lose 4 prod user value** `fin.pp` + `pm.nv` + `nv.test` + `truong.nguyen`" · "3 deleted + 3 added **cancel out**, baseline preserved" · "REFUSE rate 100% **match Anthropic warning 'tightly interdependent coding'**" · "Grep ... **ZERO results**" · "Test compile error ... **left for K7 chunk** (spec exclude test scope)" · "**Anti-fiddle threshold <20% LOC respected**" · "**Cookie-cutter 0 (lần đầu pattern)**" · "2 pre-existing **DocxRenderer** warn" (lặp 3 lần)
- **guard:** `HasDefaultValue(false)` (`ApprovalWorkflowConfiguration.cs`) · sentinel `false` + marker comment K2 tại 4 site vỡ biên-dịch · tham-số tuỳ-chọn mặc-định `false` giữ tương-thích 4 test · grep-về-ZERO 5 biến-thể (`AllowDrafterSkipToFinal` · `allow-skip-final` · `allowDrafterSkipToFinal` · `Skip cuối` · `FastForward`) · `InternalsVisibleTo` (Pattern 12) · guard đôi FE+BE (Pattern 9) · `dotnet build` 0 err + `npm run build` 0 TS err trước khi khai PASS · ngưỡng **anti-fiddle <20% LOC** · route đặt **trước** `/workspace`
- **root-cause:** `PeDetailBundle` không phơi `changelogs` TS2339 khi dựng lịch-sử tại chỗ · Mig 31 chuyển chỗ lưu 4 site Application vỡ biên-dịch + 1 test tham-chiếu prop đã xoá (`:253`) · `EditLevelEntry` không `approverFullName` (chỉ `LevelDto` BE `approverUserName`) phải tra từ `usersList` · shadcn fe-user **subset tối-thiểu** thiếu Card/Badge · `Summary` chuỗi cố-định nội-dung động phải nằm `ContextNote` (`service:96-99` `LogTransition`)
- **file:line / định-danh (BE):** `ApprovalWorkflow.cs` (+`AllowApproverSkipToFinal`; narrative **:78-80**; docstring **:108**) · `User.cs` (1 prop) · `ApprovalWorkflowConfiguration.cs` (**:22-24** narrative, **:87** storage-move comment) · `PurchaseEvaluationWorkflowService.cs` **:121-157** (37 LOC), **:287-333** (F1 edge), **:96-99** (comment ContextNote) · `UserFeatures.cs` · `SetUserAllowDrafterSkipToFinalCommand(+Handler)` · `UsersController.cs` + `SetAllowDrafterSkipToFinalBody` · `PurchaseEvaluationFeatures.cs` **:401** · `ApprovalWorkflowV2AdminFeatures.cs` **:58** · `IPurchaseEvaluationWorkflowService.cs` · `PurchaseEvaluationDtos.cs` · `ApplicationDbContext.OnModelCreating` **~:86** · `DbInitializer.cs` (cờ DemoSeed ~25 LOC)
- **file:line / định-danh (test + FE):** `PurchaseEvaluationWorkflowServiceReturnModeTests.cs` **:253** (vỡ, để cho K7), **:241** (chèn 2 `[Fact]` mới) · helper `SeedWorkflowAsync` / `SeedApproversAsync` / **`SeedApproverF2WorkflowAsync`** (2 Bước × 2 Cấp) / `BuildPeAtLevel2` · test xoá: `SkipToFinal_DrafterAllowed_SetsPointerToFinalLevel` · `SkipToFinal_DrafterDenied_NonAdmin_Throws` · `SkipToFinal_AdminBypass_Succeeds` · test thêm: `ApproveV2_SkipToFinal_AdminTickFlag_SetsPhaseDaDuyet` · `ApproveV2_SkipToFinal_FlagOff_NonAdmin_ThrowsConflictException` · `ApproveV2_SkipToFinal_FlagOff_Admin_BypassesFlagCheck` · `ApplyReturnMode_OneLevel_AtStep1Level1_ResetsToBuoc1Cap1_KeepsChoDuyet` · `ApplyReturnMode_OneStep_AtStep1_ResetsToBuoc1Cap1_KeepsChoDuyet` · `ApprovalWorkflowsV2Page.tsx` **:873**, **~:500**, **:535**, banner **~:623**, stale **:75** + **:504** (hoãn K6) · `UsersPage.tsx` + `types/users.ts` · `types/purchaseEvaluation.ts` (`PurchaseEvaluationPhaseLabel[98]`, `PeDisplayStatusLabel.TraLai`) · `PeWorkflowPanel.tsx` · `fe-user/src/types/approvalWorkflowV2.ts` (~55 LOC, 5 record type `AwLevelDto`/`AwStepDto`/`AwDefinitionDto`/`AwTypeSummaryDto`/`AwAdminOverviewDto`) · `fe-user/src/pages/pe/WorkflowMatrixViewPage.tsx` (~215 LOC) · `fe-user/src/App.tsx` route `/purchase-evaluations/workflow-matrix` · `seed-test-users-prod.ps1` `$authResp.accessToken` · `scripts/plan-r-*.sql` + `plan-s-*.sql` + `plan-t5-*.sql` + `plan-t-backup.sql`
- **số-đo:** test **84 → 104 → 106** · mig **29 → 30 → 31** · endpoint ~**146** (+3) · user prod **33** (13 + 20 mới) · diff K5 **+42/-94** (9 file) · K7 **+175/-92** (1 file) · K3 **+26/-7** · M2 **+94** · M3 **+4/-4 ×2 = 8 LOC** · bundle fe-admin **1395 / 1395.74 KB** · fe-user **1275 / 1282 KB** (1907 module, 2.61s) · commit S24 **7** (`a1a910f..ee0902a`, 1 spawn + 6 solo) · token/chunk ~5k-14k · MEMORY nguồn **38.8 KB** · `~720 rows wiped`
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `#1` Cleanup zombie F2 endpoint + UsersPage column + DTO field + stale narrative comments (Reviewer Major #1 + Major #2 + Minor #3 + Minor #4). Pattern pos
- `Mig 25` FE user read-only matrix view workflow V2 ghim (Mig 25 IsUserSelectable). 3 file: (1) CREATE `fe-user/src/types/approvalWorkflowV2.ts` (~55 LOC) s
- `Mig 29` Em main classified ALL S22 work as cross-stack reasoning chain (BE Mig + Service guard + DTO + FE Designer + FE Section + FE types + tests) REFUSE
- `PurchaseEvaluationWorkflowServiceReturnModeTests.cs:253` Mig 31 schema swap F2 storage Users ApprovalWorkflowLevels. Pattern Mig 29 ADD-DROP no-BACKFILL Option A (accept lose 4 prod user value `fin.pp` +…

View File

@ -0,0 +1,59 @@
# implementer-backend/2026-05-q2 — gist
distill-gen: 1
source-verbatim: .claude/agent-memory/implementer-backend/archive/2026-05-q2.md (5 entry → 5 cụm)
pointer-style: substring
## TỔNG-HỢP (3-5 ý)
1. **Mirror phải chứng bằng máy, không bằng mắt:** SHA256 sau khi ghi (AG `21001E90…`; Plan CA 4 hash) hoặc `git diff --stat` byte-similar; Pattern 5 (mirror 2 app §3.9) tự đếm tích-luỹ **7th → 8th → 9th** — kinh-nghiệm biến thành con-số kiểm được.
2. **Gộp N nhánh log về MỘT đường chung** (Plan AB `cdfd542`: gỡ `return` sớm nhánh Drafter → 1 `Changelog.Add()` duy nhất cuối phủ cả 4 mode); FE lọc bằng **substring của summary** (động-từ tiếng Việt) thay enum cứng ⇒ thêm luật lọc mới không cần migrate BE.
3. **Pattern 19 — thiếu thư-viện thì dùng thẻ HTML gốc:** `<details>/<summary>` + Tailwind named-group + `localStorage` (fe-user không có Accordion); anti-pattern nested `group` không đặt tên ⇒ con kế-thừa trạng-thái cha, cả hai chevron xoay cùng lúc.
4. **VA-CHẠM số-hiệu Pattern 16** (2 nghĩa khác nhau trong cùng file — CẤM gộp khi nén) + **bẫy công-cụ**: PowerShell `$_` trong `ForEach-Object` bị Bash tool nuốt khi escape ⇒ đổi sang `Get-FileHash` dạng liệt-kê — chính cách gọi shell làm hỏng phép kiểm, không phải lệnh sai.
## Entries
> ⚠️ **VA-CHẠM Pattern 16 (fidelity — giữ nguyên từ curator, lead định-đoạt):** trong verbatim, nhãn "Pattern 16 NEW" được gán cho **HAI thứ khác nhau**: (S25 wrap) *"Preventive systemic batch fix khi audit phát hiện 9 sites cùng bug pattern"* ⟂ (S27 Plan CA) *"byte-identical mirror admin → user khi parity confirmed"*. Verbatim q4 tự khai chỗ mờ: "Pattern 16 baseline **implied** trong recent activity S27 **chưa numbered explicit**". ⇒ **CẤM gộp 2 mục này thành một chỉ vì trùng số.**
**[cao] G1 — Plan AB Chunk A: gộp 4 nhánh về một chỗ ghi log**
- **VIỆC:** sửa 2 lỗi UAT về nhật-ký duyệt (`cdfd542`, 3 file, +146/-95).
- **KẾT-LUẬN:** BE viết lại `ApplyReturnModeAsync` (`:215-378`): bỏ `return` sớm của nhánh Drafter (`~:282-287`) → chuyển thành `if/else` **một đường chung**, để **một `Changelog.Add()` duy nhất ở cuối** phủ cả 4 mode (`Summary=$"Trả lại ({modeName}): {summary}"`, `modeName` switch 4 giá-trị tiếng Việt); `actorName` lấy qua `userManager.FindByIdAsync` **theo đúng khuôn `LogTransition` `~:660-667`**; **không** gọi `SaveChangesAsync` mới vì caller `TransitionAsync:100` đã lưu. FE 2 app nới bộ lọc: theo `summary` chứa "Trả lại" (Workflow) và `entityType === PE_ENTITY_HEADER(=1)` + chứa "ngân sách". Test **bỏ qua** theo chế-độ UAT (giữ mốc 111).
- **BÀI-HỌC:** khi N nhánh cùng phải ghi một loại log, **gỡ return sớm để có đường chung** rồi ghi **một lần** — thay vì rải N lời gọi. Và lọc ở FE bằng **chuỗi con của summary** (động-từ tiếng Việt) thay vì enum cứng: FE thêm luật lọc mới **không cần migrate BE**.
- **BẤT-NGỜ:** cùng một đoạn được tả 2 lần trong cùng file với **số dòng lệch nhau** (`282-287` ở entry dày vs `280-287` ở entry wrap) ⇒ khi hợp-nhất phải chọn bản dày, đừng trộn số.
→ substring:"cdfd542"
**[cao] G2 — Plan AG: cây 2 tầng bằng HTML thuần (Pattern 19)**
- **VIỆC:** bro phản-hồi danh-sách PE phẳng là "đám rừng" → dựng cây Dự-án ▸ Gói-thầu kiểu thư-mục Outlook (`0bf6c7e`, 2 file, +346/-116).
- **KẾT-LUẬN:** gom bằng `useMemo` 2 tầng (`ProjectGroup` / `GoiThauGroup`, khoá chuẩn-hoá `trim`+`toLowerCase`, hiển-thị bản gốc đầu tiên), có nhánh dự-phòng "(Dự án đã xoá)" / "(Chưa phân loại)", sắp theo locale `vi`, **lọc trước khi gom** để trạng-thái rỗng đúng. UI dùng **`<details>/<summary>` thuần trình-duyệt** (fe-user không có Accordion của shadcn) + Tailwind **named group** `group/proj` · `group/gt` + `[&::-webkit-details-marker]:hidden`. Ghi nhớ trạng-thái mở bằng `localStorage` (`pe_list_expanded_groups`, khoá `${projectId}::${normalizedGoiThau}`, mặc-định đóng hết) có `try/catch` phòng chế-độ ẩn-danh. 2 app **giống nhau tới SHA256** (`21001E90…`).
- **BÀI-HỌC:** thiếu thư-viện thì **dùng thẻ HTML gốc**: `<details>` cho sẵn trạng-thái đóng/mở, bàn-phím Space/Enter, **0 dòng JS quản trạng-thái từng nút**. Đổi thước đếm cũng phải đổi đồng-bộ (`rows.length``projectGroups.length`), nếu không trạng-thái rỗng sẽ nói dối.
- **BẤT-NGỜ:** anti-pattern được ghi rõ: nếu **hai group lồng nhau trùng tên** `group` (không đặt tên) thì con **kế thừa trạng-thái cha ⇒ cả hai xoay chevron cùng lúc** — một lỗi UI chỉ lộ khi lồng, và named-group là cách chữa.
→ substring:"pe_list_expanded_groups"
**[cao] G3 — Plan CA Chunk B: mirror nguyên-si + bẫy PowerShell**
- **VIỆC:** dời 4 trang danh-mục từ fe-admin sang fe-user (`06a441c`, 6 file, **+962 LOC**).
- **KẾT-LUẬN:** vì Investigator đã xác-nhận 2 app **ngang bằng** (DataTable/PageHeader/PermissionGuard/usePermission/6 shadcn/types), nên **chép nguyên file không sửa**, rồi **verify SHA256 cả 4** (`C1760788…`/`BDF0529E…`/`68213D62…`/`6F482614…`) ⇒ an-toàn vì mã bên admin đã qua UAT. Đúc `pattern_master_page_mirror.md`.
- **BÀI-HỌC:** "chép y nguyên" chỉ hợp-lệ khi **đã chứng ngang-bằng trước**; và bằng-chứng phải là **hash sau khi ghi**, không phải cảm-giác đã-copy-đúng.
- **BẤT-NGỜ:** **bẫy công-cụ:** biến `$_` của PowerShell trong khối `ForEach-Object` **bị Bash tool nuốt khi escape** ⇒ phải đổi sang dạng liệt-kê `Get-FileHash file1, file2, … | Format-Table` thay vì lặp qua pipeline. Tức chính **cách gọi shell** làm hỏng phép kiểm, không phải lệnh sai.
→ substring:"06a441c"
**[vừa] G4 — Plan B Chunk D: dropdown V2 cho ContractCreatePage**
- **VIỆC:** mirror ô chọn quy-trình V2 từ PE sang HĐ (`62b50d1`, 2 file, +44 LOC mỗi app).
- **KẾT-LUẬN:** dựa nguyên bản `PeWorkspaceCreateView.tsx` (`:80-89` useQuery + `:152-172` UI Select), đổi **duy nhất** trường phân-biệt `ApplicableType=3`; kiểm **tiền-đề BE trước khi wire**: `CreateContractCommand` (`ContractFeatures.cs:17-36`) **đã có** `Guid? ApprovalWorkflowId = null` kèm marker của em-main ⇒ không lệch DTO. Áp **Pattern 16-bis 4 chỗ**: trang ×2 **DONE**, còn Routes / menuKeys / Layout staticMap đều **N/A có lý-do**.
- **BÀI-HỌC:** mirror xuyên module thì **liệt-kê đủ 4 chỗ rồi ghi N/A kèm lý-do**, đừng im lặng bỏ qua — "N/A" có lý-do mới chứng-minh là đã xét.
- **BẤT-NGỜ:** vai **kiểm tiền-đề phía BE trước khi sửa FE** (đọc record của commit em-main làm trước) — nhờ vậy không đẻ ra lỗi lệch hợp-đồng dữ-liệu; và tự khai phần **cố-ý không làm** (chỉ `ContractHeaderForm`, không đụng `ContractEditForm`).
→ substring:"62b50d1"
**[cao] G5 — META: mirror phải chứng bằng hash**
- **VIỆC:** trục lặp ở 3/5 entry.
- **KẾT-LUẬN:** Pattern 5 (mirror 2 app §3.9) được đếm tích-luỹ **lần 7 → 8 → 9**; mỗi lần đều kèm bằng-chứng máy: SHA256 khớp (AG, CA) hoặc `git diff --stat` byte-similar (Chunk D). Verbatim còn đề-xuất biến thành công-cụ: *"recommend tooling `git diff fe-admin/X fe-user/X` after every multi-file edit batch"*.
- **BÀI-HỌC:** với công-việc nhân-bản, **đối-chứng máy rẻ hơn review mắt** và phát-hiện được thứ mắt không thấy (khác 1 ký-tự).
- **BẤT-NGỜ:** vai tự **đếm số lần áp-dụng** một pattern (7/8/9) — biến "kinh-nghiệm" thành con-số kiểm được, thay vì nói "đã dùng nhiều lần".
→ substring:"git diff fe-admin"
## Phụ-lục token (coverage)
- **Pattern#:** `4` `feedback_service_hook_vs_endpoint` (log trong service hook, KHÔNG endpoint `/return-changelog` rời) · `5` mirror 2 app §3.9 (**7th/8th/9th cumulative**) · **`16` (VA-CHẠM — 2 nghĩa, xem cảnh-báo trên)** · **`16-bis`** kiểm 4 chỗ cross-app · **`17`** FE merge synthetic row · **`18`** FE userMap fallback từ dữ-liệu nhúng · **`19`** `<details>`+named group+localStorage · **`12-bis`** cookie-cutter xuyên module · Pattern 7 (per-NV opt-in, nhắc lại)
- **criteria#:** `#4` (tight coupling BE+FE ⇒ vai sẽ REFUSE Plan AC trở đi)
- **surprise:** "**Gotcha S27**: PowerShell `$_` variable in `ForEach-Object` block **bị Bash tool shell-escape eaten**" · "fe-user **no shadcn Accordion** → native browser disclosure widget **free**" · "**Anti-pattern:** nested same-name `group` would inherit parent state → **both rotate sync**" · "Mirror 2 app §3.9 **IDENTICAL post-edit (SHA256 verify match `21001E90...`)**" · "**stash em main WIP** ... race condition trick" (nhắc chéo) · "KHÔNG ops git push (em main verify Reviewer rồi mới push)" (lặp 3 entry) · "FE filter qua **substring summary keyword chứ KHÔNG enum field strict**" · "recommend tooling `git diff fe-admin/X fe-user/X` after every multi-file edit batch" · số dòng lệch `282-287` vs `280-287` cho cùng đoạn
- **guard:** `try/catch` quanh `localStorage` (quota / ẩn-danh) · lọc `pendingMe` **TRƯỚC** khi gom nhóm · `projectGroups.length === 0` thay `rows.length === 0` · SHA256 sau khi ghi (4 file) · `git diff --stat` byte-similar · kiểm **tiền-đề BE** (`Guid? ApprovalWorkflowId = null`) trước khi wire FE · `[&::-webkit-details-marker]:hidden` · named group `group/proj``group` mặc-định · **không** gọi `SaveChangesAsync` mới (caller đã lưu)
- **root-cause:** `return` sớm ở nhánh Drafter ⇒ không có đường chung ⇒ phải rải nhiều lời-gọi log ⇒ gom về `if/else` + 1 `Changelog.Add()` · fe-user thiếu Accordion ⇒ chuyển sang `<details>` gốc · `group` không đặt tên khi lồng ⇒ con kế-thừa trạng-thái cha · Bash tool escape nuốt `$_``ForEach-Object` gãy
- **file:line / định-danh:** `PurchaseEvaluationWorkflowService.cs` `ApplyReturnModeAsync` **:215-378**, Drafter early-return **:282-287** (wrap ghi `:280-287`), `userManager.FindByIdAsync` mirror **:660-667**, caller `TransitionAsync` **:100** · `PeDetailTabs.tsx` ×2 (filter + comment 3-nguồn) · `PE_ENTITY_HEADER` **= 1** · `Summary=$"Trả lại ({modeName}): {summary}"` (`"Người soạn thảo"`/`"1 Cấp"`/`"1 Bước"`/`"Người chỉ định"`) · `ProjectGroup{projectId, projectName, goiThauList[], totalCount}` · `GoiThauGroup{displayName, normalizedKey, items[]}` · `pe_list_expanded_groups` · `__no_project__` · `${projectId}::${normalizedGoiThau}` · `group/proj` · `group/gt` · `group-open/proj:rotate-90` · `PeWorkspaceCreateView.tsx` **:80-89** + **:152-172** · `ContractCreatePage.tsx` ×2 · `CreateContractCommand` @ `ContractFeatures.cs` **:17-36** (`Guid? ApprovalWorkflowId = null`, marker `[Plan B S29 2026-05-22 Chunk E1]`) · `ApplicableType=3` · Mig 25 `IsUserSelectable` · `pattern_master_page_mirror.md` · `feedback_uat_skip_verify`
- **sha / số-đo:** `cdfd542` (3 file +146/-95; BE +83/-49; FE +14/-6 mỗi app) · `0bf6c7e` (2 file +346/-116 = +115 mỗi app) · `06a441c` (6 file, **+962**) · `62b50d1` (2 file, +44 ×2 = **+88**) · SHA256 `21001E90…` · `C1760788…` / `BDF0529E…` / `68213D62…` / `6F482614…` · build: BE 20.27s · fe-user 1907 module 16.62s / 16.05s / 14.14s / 8.84s · fe-admin 1926 module 6.98s / 6.86s / 16.07s · bundle fe-user **1291.33 KB** (gzip 337.00) / 1.32MB (gzip 343KB) · fe-admin **1402.68 KB** (gzip 357.51) / 1.40MB (gzip 358KB) · test mốc **111** (bỏ qua theo UAT) · token ~10k-16k · warn nền: CSS `@import` + `INEFFECTIVE_DYNAMIC_IMPORT realtime.ts` (lặp 3 entry) · tag `[pattern, phase-9, frontend]`

View File

@ -0,0 +1,58 @@
# implementer-backend/2026-05-q3 — gist
distill-gen: 1
source-verbatim: .claude/agent-memory/implementer-backend/archive/2026-05-q3.md (6 entry → 4 cụm)
pointer-style: substring
## TỔNG-HỢP (3-5 ý)
1. **Nhận/từ-chối việc là bài học 2 chiều đối-xứng:** H1 ca nhận-rồi-vỡ (E3 tự dừng giữa chừng đúng chỗ judgment call; phải `stash` WIP em-main vì song-song cùng lớp BE) ⟂ H2 ca lẽ-ra-nhận-mà-không-được (2/8 task hợp-lệ nhưng registry rỗng ⇒ em-main forced solo, thiệt ~30 phút) — ngưỡng giao việc = **độ mơ-hồ spec >20%**, không phải số file.
2. **Song-song trên cùng một lớp code đẻ chi-phí ẩn** ⇒ luật: chunk **tuần-tự A→B→C** khi cùng chạm BE, KHÔNG parallel.
3. **Governance S28:** cấm tự-cho-phép đặt luật xuyên dự-án (bro bắt tại trận); luật cũ phải **khai tử công-khai (ABANDONED)** thay vì để trôi — luật zombie hại hơn không có luật; pattern proven ≥2× mới đề-cử Layer B.
4. ⚠️ **FIDELITY-FLAG (reviewer ĐÃ chấm @S170): 4 tên cờ trong claim Pattern 7 đều 0-hit src/Backend, VÀ con số "4×" cũng CHƯA KIỂM** — "4×" suy từ đếm đúng 4 tên vừa bị bác (3 tên đổ về ~3 migration), KHÔNG có chứng độc-lập; đừng cite cả tên lẫn số như fact. Ý giữ được ở dạng không mang số: "Pattern 7 (per-NV admin opt-in) lặp qua chuỗi Mig 28→31 (số lần chưa kiểm)".
## Entries
> ⚠️ **FIDELITY-FLAG — reviewer CHẤM XONG @S170 (sub-reviewer-sleep-fidelity-S170.md §P1), bắt buộc mang theo mọi trích-dẫn Pattern 7 từ file này:** verbatim (S29 wrap) khai *"Pattern 7 per-NV admin opt-in flag (Mig 29 AllowDrafterEdit + Mig 30 AllowApproverEditSection1 + Mig 31 AllowEarlyApprove + AllowDelegate) đã proven 4×"*. **Phán từng tên (2 dụng-cụ độc-lập: grep src + git pickaxe toàn lịch-sử):**
> - `AllowDrafterEdit` = **tên viết sai, lai-ghép 2 cờ thật**: `AllowDrafterSkipToFinal` (Mig 29 → bảng `Users`) + `AllowApproverEditDetails` (Levels — và là *approver*, không phải drafter);
> - `AllowApproverEditSection1` = **trôi từ-vựng UI** của `AllowApproverEditBudget` (Mig 30 — đúng 1 cột duy nhất);
> - `AllowEarlyApprove` = tên sai nhưng **CÓ đối-ứng ngữ-nghĩa 1-1** = `AllowApproverSkipToFinal` (Mig 31);
> - `AllowDelegate` = **MA HOÀN TOÀN — 0 đối-ứng, pickaxe 0 commit toàn lịch-sử** (chưa từng được viết ra dòng code nào; `src/Backend` không có khái-niệm uỷ-quyền duyệt).
> **Con số "4×" của claim CŨNG CHƯA KIỂM** (suy từ đếm 4 tên trên). Số đo control dương `AllowApproverSkipToFinal` = **54 file với bộ lọc trừ `/obj/`+`/bin/`** (raw = 86; còn 12 khi trừ thêm `*.Designer.cs`+ModelSnapshot — 0-hit của 4 tên vững dưới cả 3 bộ lọc); control âm tên bịa = 0 — thước còn răng. Tên Mig thật: `20260513114505_AddAdvancedOptionsToApprovalWorkflows` (28) · `20260513130144_RefactorAdvancedOptionsToPerLevelAndDrafterUser` (29) · `20260513160703_AddAllowApproverEditBudgetToLevels` (30) · `20260514160124_RefactorSkipToFinalToApproverLevel` (31).
**[cao] H1 — S29 wrap: 5 lần được gọi, và bài học về chạy song-song**
- **VIỆC:** phiên bận nhất — Plan CA Chunk B (~10K) + Plan B 4 chunk (A2 Mig 32 ~25K · C Mig 33 ~25K · D FE ~12K · **E3 dừng giữa chừng**).
- **KẾT-LUẬN:** A2 phải **`stash` phần đang làm dở của em-main** (`ContractWorkflowService.cs`) mới build sạch được — vì em-main và vai **cùng sửa BE song song**. E3 (FE Section 5 V2) **tự dừng** đúng chỗ phải phán-đoán ("check `ContractDetail` type") và trả lại cho em-main. Đúc 3 luật: (1) khi cùng chạm BE thì chunk phải **tuần-tự A→B→C, không song-song**; (2) tính-năng FE phức-tạp cần mở rộng type + thêm component thì **em-main tự làm đáng tin hơn** khi độ mơ-hồ của spec **>20%**; (3) Pattern 16-bis được củng-cố lần 2.
- **BÀI-HỌC:** song-song trên cùng một lớp code **đẻ ra chi-phí ẩn** (mẹo `stash`) — nhanh trên giấy, chậm khi chạy. Và ngưỡng giao việc nên tính bằng **độ mơ-hồ của spec**, không bằng số file.
- **BẤT-NGỜ:** vai **tự dừng giữa nhiệm-vụ** thay vì đoán bừa — biến "không đủ dữ-kiện" thành hành-động hợp-lệ, và ghi lại đúng chỗ dừng để người sau nối tiếp.
→ substring:"stash em main WIP"
**[cao] H2 — S27 hồi-cứu: tự chấm điểm việc mình KHÔNG được gọi**
- **VIỆC:** dựng bảng 8 task của S27, đối chiếu từng dòng với tiêu-chí nhận/từ-chối.
- **KẾT-LUẬN:** 6/8 xác-nhận "em-main tự làm là ĐÚNG" (curate MEMORY = phán-đoán §6.5 giữ-hay-cắt → #1 · dashboard = #7 chưa có tiền-lệ + #2 · viết docs 421 dòng = #2 · sửa lỗi Qdrant = #4). **2/8 lẽ ra giao được**: A3.1 viết 5 script PS (Case 2) và F2 sửa `model:inherit` ở 4 file agent (Case 1) — nhưng **registry không nạp được** nên em-main buộc phải tự làm; thiệt-hại ước ~30 phút + mất kỷ-luật mirror. Đúc **Pattern 20** (bộ 5 script PS start/stop/status/dashboard/boot: cùng kỷ-luật ASCII, cùng cấu-trúc `Write-Host`, cùng khuôn `try-catch`).
- **BÀI-HỌC:** hồi-cứu phải chấm **từng dòng**, không chấm tổng — nhờ vậy mới tách được "quyết đúng" khỏi "quyết đúng nhưng vì lý do sai" (2 ca này em-main đúng kết-quả nhưng nguyên-nhân là **hạ-tầng hỏng**, không phải phán-đoán).
- **BẤT-NGỜ:** cơ-hội bị mất **không do ai quyết sai** mà do **registry rỗng** — tức lỗi hạ-tầng lặng-lẽ biến thành lỗi phân-công, và chỉ lộ ra khi có bảng hồi-cứu.
→ substring:"Net loss"
**[vừa] H3 — S28: 5 điều tự áp sau khi bị bắt lỗi**
- **VIỆC:** ghi lại governance Layer A dưới góc nhìn của vai.
- **KẾT-LUẬN:** (1) pattern đã chứng **≥2 lần** thì đủ tư-cách đề-cử lên Layer B; (2) tag bắt buộc `[pattern, phase-<N>, <bc>]`; (3) quy-ước `source_path` = `solution_erp/pattern/<topic>-<date>`; (4) **cấm tự cho phép mình đặt luật xuyên dự-án** — đúc từ ca t4 bro bắt tại trận; (5) luật cũ "mọi tương-tác đều phải lưu" **BỊ BỎ**.
- **BÀI-HỌC:** phạm-vi quyền của một vai phải **thu về chính dự-án của nó**; và luật cũ nên **khai tử công-khai** ("ABANDONED") thay vì để trôi — luật zombie còn hại hơn không có luật.
- **BẤT-NGỜ:** chính vai này ghi nhận mình **quên mất luật cũ** rồi mới phát-hiện luật đó đã bị bỏ — quên đúng thứ đáng quên, nhưng chỉ biết là "đúng" sau khi tra lại.
→ substring:"ABANDONED"
**[thấp] H4 — Log curate + baseline**
- **VIỆC:** ghi mốc: dồn 12 entry S21→S24 sang `archive/2026-05-q1.md`; và mốc khởi-tạo 2026-05-11.
- **KẾT-LUẬN:** MEMORY **38.8 KB → mục-tiêu ~22-24 KB**, giữ nguyên khối Pattern 1-19; lúc khởi-tạo vai đã nạp sẵn 5 (thực tế liệt-kê 6) pattern nền tích-luỹ S1-S20, chưa làm gì, **đã bật sẵn cơ-chế tự từ-chối theo phạm-vi**.
- **BÀI-HỌC:** mỗi lần dồn kho phải ghi **nguồn → đích + kích-thước trước/sau + thứ giữ lại** thì lần sau mới truy được.
- **BẤT-NGỜ:** vai **bật quy-tắc từ-chối ngay từ lúc chưa làm việc nào** — ranh-giới được cài trước, không phải rút ra sau khi vỡ.
→ substring:"2026-05-q1"
## Phụ-lục token (coverage)
- **Pattern#:** `7` per-NV admin opt-in (**tên cờ SAI — xem FIDELITY-FLAG**) · `12-bis` cross-module cookie-cutter · `16-bis` 4-place mirror (**reinforced 2×**) · **`20` MỚI** (bộ 5 script PS mirror) · nền "5 patterns S1-S20" (liệt-kê **6** mục)
- **criteria#:** `#1` phán-đoán giữ-hay-cắt (§6.5) · `#2` thiết-kế UX / viết docs · `#4` chuỗi suy-luận sửa lỗi · `#7` lần đầu, chưa tiền-lệ · Case 1 (n file sửa cơ-học giống nhau) · Case 2 (cookie-cutter mirror)
- **surprise:** "**E3 stopped mid-task** ... STOPPED at 'check ContractDetail type' judgment call" · "**stash em main WIP `ContractWorkflowService.cs`** để build verify clean (em main + Implementer **parallel touch BE → race condition trick**)" · "Forward **SEQUENTIAL** chunks A→B→C khi cùng touch BE, **NOT parallel**" · "em main solo **more reliable** than Implementer khi **spec ambiguity > 20%**" · "**2/8 task lẽ ra delegate được** ... nhưng **registry empty → em main forced solo**" · "Net loss **~30 phút**" · "registry KHÔNG load (per pitfall **VIPIX #1+#2**)" · "bro **caught self-authorize cross-project rule mistake**" · "Quên rule cũ 'mọi tương tác mandatory store' — **ABANDONED**" · "Implementer = **busiest agent S29** với 5 spawn"
- **guard:** `git stash` phần WIP trước khi build verify · chunk **tuần-tự** khi cùng chạm BE · ngưỡng **spec ambiguity 20%** để chuyển về em-main · tự-dừng khi gặp judgment call · tag schema bắt buộc `[pattern, phase-<N>, <bc>]` · **cấm tự-cho-phép luật liên-dự-án** · pattern phải proven **≥2×** mới đề-cử Layer B
- **root-cause:** em-main + vai **cùng sửa BE song song** ⇒ cây làm việc bẩn ⇒ build verify không sạch ⇒ phải `stash` · **registry không nạp** ⇒ không spawn được ⇒ 2 việc hợp-lệ bị dồn cho em-main · từ-vựng **UI-section** dùng thay tên cờ trong code ⇒ 3/4 tên = drift của cờ thật, 1/4 (`AllowDelegate`) = ma 0-đối-ứng (xem FIDELITY-FLAG)
- **file:line / định-danh:** `ContractWorkflowService.cs` (WIP bị stash) · Mig 32 (+column FK **Restrict** + IX + Configuration + `DbInitializer` `SeedSampleContractWorkflowV2`) · Mig 33 `ContractLevelOpinions` (entity + Mig + Config + DbSet + `Contract.cs` +`LevelOpinions` nav) · `ContractCreatePage.tsx` ×2 (88 LOC) · 4 master page + `App.tsx` (+5 route) + `menuKeys.ts` (+5 key `Catalogs*`) · `pattern_master_page_mirror.md` · `archive/2026-05-q1.md` · script PS `start/stop/status/dashboard/boot`
- **số-đo:** 5 spawn S29 · ~10K + ~25K + ~25K + ~12K token · **948 LOC** mirror / 6 file · MEMORY **38.8 KB → ~22-24 KB** · 12 entry dồn kho · setup **2026-05-11**
- **🔬 disk-check hôm nay (S170, có 2 control):** `AllowApproverSkipToFinal` = **54 file (bộ lọc: trừ `/obj/`+`/bin/` — raw 86; 12 khi trừ thêm Designer/Snapshot)** (control dương; pickaxe 5 commit) · `AllowXyzKhongTonTai` = **0** (control âm) · `AllowDrafterEdit` / `AllowApproverEditSection1` / `AllowEarlyApprove` / `AllowDelegate` = **0 / 0 / 0 / 0** (grep) + **0 commit pickaxe toàn lịch-sử** · cột thật: `AllowApproverEditDetails` · `AllowDrafterSkipToFinal``Users` · `AllowApproverEditBudget` · `AllowApproverSkipToFinal` · Mig thật: `20260513114505_AddAdvancedOptionsToApprovalWorkflows` · `20260513130144_RefactorAdvancedOptionsToPerLevelAndDrafterUser` · `20260513160703_AddAllowApproverEditBudgetToLevels` · `20260514160124_RefactorSkipToFinalToApproverLevel`

View File

@ -0,0 +1,53 @@
# implementer-backend/2026-05-q4 — gist
distill-gen: 1
source-verbatim: .claude/agent-memory/implementer-backend/archive/2026-05-q4.md (5 khối → 4 cụm)
pointer-style: substring
## TỔNG-HỢP (3-5 ý)
1. **Scaffold ULTRA-MINIMAL S33 Task 5** (14 file, 3 file mới SHA256 IDENTICAL ×2 app, test 120/120, "Ambiguities encountered: 0"): phạm-vi hẹp phải kèm danh-sách **"cố-ý không làm"** khai minh-bạch — không thì người đọc sau tưởng là thiếu sót. Entry này bị CHẺ ĐÔI trong verbatim (2 digest S65 FIFO trim chèn giữa) — gist đã NỐI lại.
2. **Số đếm không khớp danh-sách:** cả 2 pattern ghi "4th time" nhưng chỉ liệt-kê 3 ca (×2 chỗ), cùng lớp lệch với "5 patterns/6 mục" ở q3 ⇒ khi nén **giữ danh-sách, bỏ số thứ-tự**.
3. **LOC thô không đo công sức:** +4265 LOC nhưng Designer autogen 95%, tay chỉ ~232; HRM không có `HasQueryFilter` ⇒ phải `.Where(!IsDeleted)` tay; MaxLength lấy **EF làm nguồn chân-lý** (`Code=50` not spec 20).
4. **Phát-hiện ⟂ xử-lý:** tự đo MEMORY 36.2KB vượt ngưỡng 25KB ~45% → gắn cờ cho em-main nhưng **cố-ý KHÔNG tự dọn** (giữ-hay-cắt = phán-đoán §6.5 của em-main); và **REFUSE forward** trước cả khi được giao (nhận scaffold theo spec, KHÔNG nhận schema design Mig 34).
## Entries
**[cao] I1 — S33 Task 5: scaffold HRM cho 2 app, phạm-vi cực hẹp** *(nối từ 2 mảnh verbatim — đuôi "Verify all clean / Files touched / Out-of-scope respected" nằm sau 2 digest S65 FIFO trim chèn giữa; đối-chiếu verbatim xác nhận đuôi thuộc Task 5: toàn file HRM + tag `[mirror-page, phase-10.1, frontend]`, KHÔNG thuộc S29 Chunk C)*
- **VIỆC:** dựng 3 trang/type HRM (Phase 1 chỉ-đọc) mirror sang cả 2 app — tổng **14 file** (3 file MỚI ×2 app + 4 file sửa ×2).
- **KẾT-LUẬN:** ACCEPT vì spec **xác-định 100%**, kiểu **ULTRA-MINIMAL** (không tách component DetailTabs, 6 mục dùng `<details>` nội-tuyến, không CRUD vệ-tinh). 3 file mới **giống hệt tới SHA256**: `CCFC70666568` · `DC859C897C5C` · `C796F25D01AC`. Kiểm sạch toàn bộ: fe-admin 21.4s / fe-user 9.2s / `dotnet build` 1.59s **0 warn 0 err** / `dotnet test` **120/120** (58 Domain + 62 Infra) — và **"Ambiguities encountered: 0"**. Pattern 16-bis + 12-bis đều được ghi là **lần thứ 4**.
- **BÀI-HỌC:** phạm-vi hẹp phải ghi kèm **danh-sách "cố-ý không làm"** (không form vệ-tinh · không sửa nội-tuyến Header · không wire `PermissionGuard` vì em-main giữ Task 6 · không thêm `Bg_*`/`Catalog*`) — nếu không, người đọc sau sẽ tưởng là thiếu sót.
- **BẤT-NGỜ:** cả hai "lần thứ 4" đều **chỉ liệt-kê 3 lần trước** (16-bis: CA HF1 + Chunk D + Task 5; 12-bis: Chunk C + Task 4 + Task 5) ⇒ **số đếm không khớp danh-sách**; cùng loại lệch với "5 patterns/6 mục" ở q3 ⇒ khi nén nên **giữ danh-sách, bỏ số thứ-tự**.
→ substring:"CCFC70666568"
**[cao] I4 — S32 startup: tự đo kho rồi TỪ CHỐI tự dọn**
- **VIỆC:** lượt spawn chỉ để kiểm bối-cảnh phiên 32.
- **KẾT-LUẬN:** đo `Get-Item Length` = **36.207 byte** (36.2KB) ⇒ **vượt ngưỡng 25KB ~45%****gắn cờ cho em-main**, và **cố-ý KHÔNG tự dọn** vì việc giữ-hay-cắt là phán-đoán §6.5 thuộc em-main (viện dẫn chính bài học C1-C4 của S27). Xác-nhận còn đủ **17 pattern đánh số** (1-12 + 12-bis + 13-15 + 16-bis + 17-19). RAG sống: 2 tool MCP hiện diện, truy-vấn thử trả 3 kết-quả rerank **0.824 / 0.801 / 0.793** — đều **> ngưỡng 0.7**.
- **BÀI-HỌC:** đo bằng **byte thật của hệ-điều-hành** rồi mới phán "quá ngưỡng"; và **phát-hiện ⟂ xử-lý** — vai được quyền gắn cờ nhưng không được quyền cắt, đúng ranh-giới đã chốt từ ca trước.
- **BẤT-NGỜ:** tự-kiềm-chế được ghi kèm **lý-do có dẫn nguồn** (bài học S27), không phải "ngại làm" — và chính cái cờ này về sau thành đợt curate S32 (36.2 → 27.5KB).
→ substring:"Length=36207"
**[vừa] I2 — Hai digest "archived S65 FIFO trim"**
- **VIỆC:** 2 việc BE cũ được nén còn vài dòng ở đợt dồn kho S65.
- **KẾT-LUẬN:** **S35 G-H2**: CRUD 4 danh-mục HRM (`HrmConfigFeatures.cs` 372 LOC + Controller 134 LOC, **16 endpoint** = 4 tài-nguyên × 4 động-từ), giữ mốc **130 test**, spec rõ 95%. **S29 Chunk C**: mirror Contract V2 (Mig 33), **8 file +4265 LOC** nhưng **Designer tự sinh 95%**, tay chỉ **~232 LOC**.
- **BÀI-HỌC:** 2 điểm kỹ-thuật đắt nhất nằm ở S35: HRM **không có `HasQueryFilter`** ⇒ phải tự viết `.Where(!IsDeleted)`; và **độ dài MaxLength lấy EF làm chuẩn**, không lấy spec (`Code=50`, spec ghi 20). *(Đây là lớp chưng-cất sẵn — giữ gần nguyên, nén tiếp sẽ mất mã-số kỹ-thuật.)*
- **BẤT-NGỜ:** "+4265 LOC" nghe khổng-lồ nhưng **95% là mã tự sinh** — con-số LOC thô **không đo được công sức**, phải tách phần tay viết (~232) mới so được.
→ substring:"HrmConfigFeatures.cs 372 LOC"
**[vừa] I3 — S32 wrap: dựng hàng chờ cho phiên sau**
- **VIỆC:** đóng phiên 32, lập backlog.
- **KẾT-LUẬN:** ghi **11 module Plan G** thành **10 sprint nguyên-tử** trong `migration-todos`; nêu 4 việc chờ phiên 33 kèm **phân loại sẵn** (a) G-H1 scaffold hồ-sơ NS · (b) bộ test BW1-BW7 mirror khuôn test PE · (c) BookingCalendar (FullCalendar = **thư-viện mới**) · (d) G-O3..G-O6 mirror 4 module Workflow Apps. Kèm **REFUSE trước**: sẽ nhận scaffold theo spec xác-định, **không nhận thiết-kế schema** (Mig 34 để em-main).
- **BÀI-HỌC:** backlog nên **kèm sẵn nhãn nhận/từ-chối** cho từng mục — quyết định phân-công xong từ lúc lập kế-hoạch, không phải lúc bị gọi.
- **BẤT-NGỜ:** vai **từ-chối trước cả khi được giao** ("REFUSE forward") — ranh-giới được tuyên trước, giúp em-main khỏi phí một lượt spawn để nhận về lời từ-chối.
→ substring:"FullCalendar lib new dep"
## Phụ-lục token (coverage)
- **Pattern#:** `16-bis` 4-place mirror (**"4th time"**, liệt-kê 3 ca: S29 Plan CA **HF1** + S29 Plan B Chunk D + S33 Task 5) · `12-bis` cross-module (**"4th time"**, liệt-kê 3 ca: Chunk C Mig 33 + G-H1 Task 4 BE + Task 5 FE; riêng S35 ghi **"3rd application catalog-mega"**) · "**17 numbered patterns**" (1-12 + 12-bis + 13-15 + 16-bis + 17-19) · ghi chú `Pattern 16 baseline **implied** ... **chưa numbered explicit**`
- **surprise:** "**Ambiguities encountered: 0** — spec deterministic 100%" · "**HRM no `HasQueryFilter`** → `.Where(!IsDeleted)` manual" · "**Validator MaxLength = EF source-of-truth** (`Code=50` **not spec 20**)" · "8 file **+4265 LOC** (Designer **autogen 95%**, handcraft **~232 LOC**)" · "MEMORY size **36.2KB** ... **OVER 25KB threshold ~45% bigger**" · "**KHÔNG self-curate** vì ... reserve cho em main solo judgment call §6.5" · "**KHÔNG commit** (em main commits)" · "**REFUSE forward:** ... **KHÔNG schema design**" · "FullCalendar lib **new dep**" · "3 NEW file **SHA256 verified IDENTICAL**" · số "4th time" nhưng chỉ liệt-kê **3** ca (×2 chỗ)
- **guard:** SHA256 xác-nhận sau khi ghi (3 file) · `.Where(!IsDeleted)` thủ-công (không có query filter toàn-cục) · MaxLength lấy **EF làm nguồn chân-lý** · ngưỡng MEMORY **25KB** + đo bằng `Get-Item Length` · ngưỡng rerank **> 0.7** · ranh-giới **phát-hiện ⟂ tự-dọn** (§6.5 KEEP vs CUT thuộc em-main) · danh-sách **out-of-scope** khai minh-bạch
- **root-cause:** đợt dồn kho **S65 FIFO trim** chèn 2 digest **vào giữa** entry S33 Task 5 ⇒ thân và đuôi bị tách rời (gist này đã NỐI lại — xem ghi chú I1) · HRM thiếu `HasQueryFilter` ⇒ mọi truy-vấn phải tự lọc soft-delete · spec ghi MaxLength lệch EF ⇒ validator sai nếu tin spec
- **file:line / định-danh:** `types/employee.ts` (**NEW ~283 LOC**, SHA256 `CCFC70666568`) · `pages/hrm/EmployeesListPage.tsx` (**~417 LOC**, `DC859C897C5C`) · `pages/hrm/EmployeeCreatePage.tsx` (**~178 LOC**, `C796F25D01AC`) · `menuKeys.ts` (+3 LOC `Hrm`+`HrmHoSo`) · `Layout.tsx` (+4 LOC staticMap **`Hrm_HoSo`**) · `App.tsx` (+5 LOC) · 6 file fe-user mirror · `HrmConfigFeatures.cs` **372 LOC** + Controller **134 LOC** (**16 endpoint**) · Mig 33 `ContractLevelOpinions` · Mig 34 (em-main giữ) · `migration-todos` (11 module / 10 sprint) · `PurchaseEvaluationWorkflowServiceReturnModeTests.cs``ContractWorkflowServiceApproveV2Tests.cs` (7 kịch-bản, spec D-Bis) · `mcp__rag-unified__search_memory` + `mcp__rag-unified__cross_project_search` · "Pattern curate trigger rule **line 364**" · agent id `ab23cc322b5d495c0`
- **số-đo:** 14 file (S33 Task 5) · build fe-admin **21.4s / 1429KB** · fe-user **9.2s / 1345KB** · `dotnet build` **1.59s 0 warn 0 err** · `dotnet test` **120/120** (58 + 62) · test mốc S35 **130** · MEMORY **36.2KB** (`Length=36207`) → curate **27.5KB** · rerank **0.824 / 0.801 / 0.793** (ngưỡng 0.7) · token ~25k (Task 5) / ~5K (wrap) / ~5k (startup) · tag `[mirror-page, phase-10.1, frontend]` · `[s35, be-crud, hrm, 12-bis-3x]` · `[s29, plan-b, 12-bis]` · `[wrap, phase-9-to-phase-10, frontend+backend]` · `[verify, phase-9, infra]`
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `Mig 22` — (d) **Plan G-O3..G-O6 Workflow Apps** — cookie-cutter mirror PE Mig 22-26 Plan B pattern 12-bis cross-module entity scaffold cho 4 module (Proposal/…

View File

@ -0,0 +1,109 @@
# implementer-frontend/2026-H2 — gist
distill-gen: 1
source-verbatim: .claude/agent-memory/implementer-frontend/archive/2026-H2.md (14 entry moved-not-cut → 11 cụm [9 đơn + 1 nhóm J1..J5 giữ 5 danh-tính + 1 chỉ-mục FE-M])
pointer-style: substring
## TỔNG-HỢP (3-5 ý)
- Sợi chỉ lặp 5 lần trong 3 phiên (FE-M): tiền-đề đề-bài/lead SAI — K2 việc thật ≈ 1/4 mô tả · K4a 2 tiền-đề sai trong 1 đề bài · K4b lần 3 liên-tiếp · K5 đường dữ-liệu không tồn-tại · K6 spec tự quên 8 designer ⇒ luật rút gọn: đo đĩa TRƯỚC khi gõ, đĩa cãi đề bài thì đĩa thắng — nhưng PHẢI KHAI chỗ cãi.
- Phép đo phải có control-âm mới gọi là đo: harness đo nhầm cái render bị React vứt — control-âm cũng ra 3 ⇒ phép đo mất khả-năng phân-biệt, trông y như PASS (FE-B); im-vì-đúng ⟂ im-vì-câm (FE-D CA4); 0-hit trần vô nghĩa (FE-C); 2 hash đứng yên = đối chứng dương (FE-D).
- Điểm-có-RĂNG phải xác định trước khi sửa: `writeGuard` nằm 2 CHỖ, chỉ `:235` (template-literal vào prompt runtime) có răng, `:228` là comment (FE-E); containment phải 2 LỚP render + payload — chỉ `disabled` là containment GIẢ (FE-I); gate FE đổi phải đổi CẢ 2 chỗ, không phải 1 (FE-J4).
- Hai ranh auto-refuse tự áp của vai (FE-A, {cao}): việc <30 phút TỪ CHỐI nhận (bảo vệ ngân-sách token) + không hạ sàn mirror-SHA × 2 app + `npm build` × 2 PASS bị giục (bảo vệ sàn chất-lượng) cả hai quyền tự từ chối, không cần xin phép từng lần.
- Sai-thầm-lặng phải đổi thành thiếu-nhìn-thấy-được: rỗng-vì-lọc rỗng-vì-chưa-có phải khác câu (FE-F); BE `/deleted` thiếu param `group` không cho UI tuyên nhóm màn đã-xoá thay hiện số trang nói dối (FE-B).
## Entries
> 🔴 Ràng buộc kế-thừa từ lệnh lead (giữ nguyên khi chốt): TOÀN BỘ file verbatim là nội-dung moved-not-cut đợt drain 08-01 — con-trỏ 1 dòng ở `MEMORY.md` nhảy bằng tag Ctrl-F ⇒ 14/14 mục giữ danh-tính riêng + NGUYÊN chuỗi tag (mất tag = con-trỏ MEMORY trỏ vào hư-không); mục auto-refuse (FE-A) tag {cao}.
**[cao] FE-A S152 R1: hai ranh giới auto-refuse** `[s152, auto-refuse-trivial-30min, no-lower-bar-smart-friend]`
- 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 đốt spawn ích. (b) KHÔNG hạ chuẩn kiểu "Smart-Friend nể-nang": 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 bảo vệ hai thứ khác nhau (a) ngân-sách token, (b) sàn chất-lượng; cả hai 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 cả" lại mục đáng giữ nhất nhóm định-nghĩa cái vai này KHÔNG nhận, thứ mọi mục còn lại (toàn việc đã làm) không nói được.
substring:"auto-refuse-trivial-30min"
**[cao] FE-B S166 K4b: phép đo bắt đúng cái render đã bị React vứt** `[s166, khkk-k4b-group-wire, harness-do-nham-render-bi-vut]`
- 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: `setState` gọi TRONG LÚC render khiến React huỷ render đó render lại với state mới TRƯỚC khi commit; harness bản 1 chạy 1 lượt nên đọc ra `phase=3` (giá-trị của render đã bị vứt) control-âm cũng ra 3 hai nhánh giống hệt phép đo mất khả-năng phân-biệt, trông y như PASS. = lặp tới khi state đứng yên A ra `phase=2`, control-âm giữ `3`. Giải bài chính: override 3 trạng-thái `chipPhase: T|null|undefined` (`undefined` = "nghe URL" `null` = "user chọn Tất cả") + reset TRONG render theo `navKey`, KHÔNG dùng `useEffect` (effect vẽ một lượt dữ-liệu rồi mới sửa = nháy).
- BÀI-HỌC: mô-phỏng hành-vi framework phải mô-phỏng CẢ vòng đời, không chỉ một lượt gọi hàm. Tiền-đề đề-bài sai lần 3 liên-tiếp: làm đúng nguyên văn ("derive `phase` từ searchParams thay `useState`") sẽ giết 6 chip lọc (`KpiCard onClick setPhase :153`) hỏng CÂM, build vẫn xanh.
- BẤT-NGỜ: BE `/deleted` thiếu tham số `group` (`Controller:61-66` so `List:42`) không lọc được client (server đếm `total` trên tập chưa lọc số trang nói dối) xử bằng 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ờ lane BE; staticMap lệch CHỦ ĐÍCH (fe-user 6/6 mang `group=1`, fe-admin 5/6 WfView admin trỏ Designer). Bẫy K4b thứ hai *(bổ @S170 P3-gate)*: CreatePage 8 leaf chung MỘT route nên leafleaf KHÔNG remount preset phải so `lastPresetGroup` chứ KHÔNG so `approvalGroup` so nhầm thì người dùng bấm đổi nhóm bị kéo ngược ô chọn thành nút chết. K4b bàn giao lead 4 FLAG.
substring:"harness-do-nham-render-bi-vut"
**[cao] FE-C S166 K4a: hai tiền-đề lead sai trong CÙNG một đề bài** `[s166, khkk-k4a-regex-42-leaf, tien-de-lead-sai-x2]`
- VIỆC: nới regex cho 42 leaf KHKK + gom nhãn nhóm về một nguồn. Artifact `…/sub-implfe-k4a.md`.
- KẾT-LUẬN: đề bài bảo export `KHKK_GROUP_LABELS` MỚI, nhưng đĩa ĐÃ `KHKK_APPROVAL_GROUP_LABELS` (`types/khkk.ts:129`, land từ K2, 5 call-site) đẻ map thứ 2 chính phá đúng acceptance-5 (single-source) phục vụ; đo thêm thấy nhãn BE `` `${n}. ${short}` `` cho 8/8 ⇒ giải bằng DERIVE `khkkGroupMenuLabel(n)`, 0 call-site cũ bị đụng (hardcode lần 2 sẽ ra "N2 - 2. …" đánh số kép ở select). ② comment vá-8 bảo "phải nới regex trước vì sắp gỡ `KeHoachKyKet` khỏi ẩn-admin", nhưng `isAdminHidden:194` = `key.startsWith('Ct_')` ⇒ KHKK chưa bao giờ bị ẩn ⇒ viết comment theo ĐĨA + gắn cờ "7 nhóm rỗng" (`MenuGroup` chỉ xét `children.length>0`, không xét con có resolve được ⇒ header hiện, thân rỗng).
- BÀI-HỌC: chứng regex bằng `new Function()` chạy CHÍNH khối text đã ship (`indexOf('const khkkMatch')` → slice → eval), key sinh từ `KhkkGroupNumbers`/`KhkkLeafActions` đọc thẳng `MenuKeys.cs` ⇒ 42/42 resolve, 0 drop, +3 control-âm (G9→null · action rác→null · `Khkk_List` nhóm-1-không-infix→null vì 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Ố Ý; Layout 2 app cố ý KHÔNG mirror (user +34 dòng regex · admin +13 dòng comment).
→ substring:"khkk-k4a-regex-42-leaf"
**[cao] FE-D — S166 K5: "chuẩn bị sẵn cho sau" = đổi HÌNH DẠNG, không bịa field** `[s166, khkk-k5-8-sub-folder]`
- VIỆC: mở 8 ngăn nhóm ở GĐ2 của cây toàn-trình. Artifact `…/sub-implfe-k5.md`.
- KẾT-LUẬN: đề bài đòi GĐ3/4 đi qua `lines[].contractId pe.contractId`; đo 4 nơi đều ÂM (`PeListItem` 0 hit chữ `lines` · `PurchaseEvaluation.cs:31` chỉ có `Guid? ContractId` đơn · `ContractListItem` không có `purchaseEvaluationId` · `Contract.cs` 0 khoá ngược). Bằng-chứng đắt nhất là BE TỰ KHAI: `CreateContractFromEvaluationFeatures.cs:143-145` `pe.ContractId = contracts[0].Id` + comment "N HĐ liên-danh truy qua changelog" — endpoint TRẢ `contractIds[]` (`PeDetailTabs.tsx:2186`) nhưng chỉ HĐ đầu được ghi ngược ⇒ HĐ thứ 2 trở đi VÔ HÌNH. ⇒ chỉ wire vế làm được: `contractIdsOfPackage = new Set<string>()` (hợp) thay vì đọc 1 ô, đánh dấu sẵn chỗ cắm nguồn-2 + trích nguyên văn BE; FLAG 2 đường cho lead.
- BÀI-HỌC: `stage.leaves` giữ vai danh sách PHẲNG canonical (nguồn đếm duy nhất), `groups` chỉ chia lại chính nó ⇒ bất-biến tổng là chuyện CẤU TRÚC, không phải kỷ-luật nhớ-cộng-cho-khớp. Hiện đủ 8 ngăn kể cả rỗng = `.map` trên hằng, KHÔNG `.filter`; ngăn "(chưa phân nhóm)" chỉ mọc khi thật có phiếu lạc (8 nhóm là khung nghiệp-vụ cố định ⟂ ngăn kia là chỗ chứa bất thường).
- BẤT-NGỜ: CA4 control-âm tiêm lỗi nhân-đôi ⇒ `console.warn` NỔ — không có CA4 thì "0 cảnh báo" ở CA1-3 vô nghĩa (im-vì-đúng ⟂ im-vì-câm); 2 hash ĐỨNG YÊN (`PipelineTreePanel` `d70ac315`, `PePipelineStrip` `6fb7337e`) dùng làm đối chứng dương cho phép đo SHA.
→ substring:"khkk-k5-8-sub-folder"
**[cao] FE-E — S153 T5: `writeGuard` là 2 điểm, chỉ một điểm có răng** `[s153, t5-writeguard-2-diem-235-co-rang]`
- VIỆC: T2 dựng template `session-mind-template.md` + T5 sửa `hmw.js`. Lead seed on-behalf @closeout M9 (T2 return mất schema-fail — #53 +1, phải derive-from-body; template VẪN LAND 8.819 B).
- KẾT-LUẬN: `VALID_ROLES` 20→21 (sau T10/T11 = 23); 🔴 `writeGuard` nằm 2 CHỖ: `:228` là comment JS (tài-liệu) VÀ `:235` là template-literal ĐI VÀO PROMPT của sub. Chỉ sửa `:235` ⇒ tài-liệu lệch trong chính file đó; chỉ sửa `:228` ⇒ prompt runtime KHÔNG đổi ⇒ vai read-only bị hướng-dẫn như vai được ghi ⇒ Bash-write MD ⇒ mojibake G-009. `:235` = điểm có RĂNG. Vùng cấm đo bằng diff: `WIDTH_CAP` 0 hit · `TIER2_EXPECTED_FULL_ID` 0 hit.
- BÀI-HỌC: T2 — `ts` sống trong HEADING sau em-dash nên 0 dòng key `ts:` (không phạm rào-2); placeholder `<k>` trong schema-comment để parser không khớp nhầm; kiểm `_SECRET_RES` để ví-dụ của rào-3 không tự khớp mẫu của chính nó.
- BẤT-NGỜ: 2 kẽ báo lead — ① va tên cùng-vai 2 lần gọi (chỉ số `i` reset mỗi lần gọi ⇒ đợt 2 suýt đè `sub-implementer-frontend-1.md` của đợt 1) = kẽ CÒN LẠI sau S134; ② `node --check` trên file thô LUÔN SyntaxError (top-level await) ⇒ acceptance phải là parse-sau-khi-bọc + đối chứng HEAD.
→ substring:"t5-writeguard-2-diem-235-co-rang"
**[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ì** `[s166, khkk-k4c-wfview-loc-nhom, rong-vi-loc-khac-rong-that]`
- VIỆC: lọc trang WfView theo nhóm (đóng FLAG-3 của K4b: `?group=` chưa ai tiêu-thụ ⇒ 8 leaf mở 8 trang y hệt). fe-user ONLY, 1 file (+68/5), `git diff -- fe-admin` RỖNG.
- KẾT-LUẬN: đo trước 4 thứ, không đoán — ① `AwDefinitionDto.code` là tên field THẬT (`types/approvalWorkflowV2.ts:41`, đang render `:165`) ② trang tải trọn bộ theo type rồi đọc `summary.history` ⇒ lọc CLIENT-side, KHÔNG thêm trục `group` vào queryKey (thêm = 8 lần gọi mạng cùng payload + 8 ô cache trùng) ③ `DbInitializer.cs:609` `code = $"KHKK-N{n}"` + `:623` `IsUserSelectable = true` ⇒ 8 workflow thực sự lọt bộ lọc của trang (seed `false` thì lọc kiểu gì cũng trắng trang) ④ `PageHeader` bản đang import CÓ `actions` (repo có ≥2 bản khác chữ ký — lặp bài S37).
- BÀI-HỌC: chốt chặn KÉP `typeInt === 10 && /^[1-8]$/``group` là khái-niệm riêng của KHKK, type khác hoặc rác thì bỏ qua hoàn toàn (thà hiện đủ còn hơn lọc theo số vô nghĩa ra trang trắng); regex trên chuỗi bắt được `"03"``Number()` sẽ nuốt; lọc giữ đủ VERSION cùng code ("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).
→ substring:"khkk-k4c-wfview-loc-nhom"
**[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** `[s166, k6-admin-thu-gon, dung-lai-cay-seed-de-dem, route-resolver-khac-menu-row]`
- VIỆC: thu gọn menu admin về đúng vai "nơi cấu-hình". fe-admin ONLY, 1 file (+52/19), fe-user 0-diff.
- KẾT-LUẬN: spec treo ẩn-số "N = 7 hay 8 tuỳ `AwV2_Contract` có tồn tại"; đo ra 0 HIT toàn repo ⇒ 2 seed leaf + 8 designer ⇒ chốt N = 1 Dashboard + 4 System-leaf + 2 AwV2 + 8 designer = 15 (spec TỰ QUÊN cộng 8 designer vào công thức của chính nó). Phép đếm: parse 72 tuple literal từ `DbInitializer` + sinh 128 key từ vòng lặp (hằng đọc thẳng `MenuKeys.cs`) = 200 node → chạy `ADMIN_HIDDEN_KEYS` + `isAdminHidden` + `filterForAdmin` cắt từ chính 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: `filterForAdmin` lọc ĐỆ QUY ⇒ ẩn 1 ROOT là cả cây con chết theo ⇒ tập chỉ cần liệt 12 ROOT (thêm `Workflows`/`PeWorkflows` vì là CON của `System`); liệt từng leaf = thừa + 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ũ nói NGƯỢC luật đang chạy, 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ố vỡ ngay; bẫy suýt lẫn: `resolvePath:173` CÓ nhánh nhận code `'Contract'` nhưng route-resolver có nhánh ≠ menu-row tồn tại.
→ substring:"k6-admin-thu-gon"
**[vừa] FE-H — S164/S165 K2: tiền-đề lead sai, việc thật chỉ ≈ 1/4 mô tả** `[s164, khkk-nhom-duyet, do-dia-truoc-khi-go, colspan-loi-cam]`
- VIỆC: relaunch p2 sau khi p1 chết vì #53 — nhóm duyệt N1-N8 cho KHKK.
- KẾT-LUẬN: lead khai CreatePage "mid-wire, 8 khai báo chưa vào JSX" (nghĩa là TS6133 sẽ đỏ); đo `git diff` TRƯỚC khi gõ thì 8/8 ĐÃ có call-site, Detail cũng đã land ⇒ việc thật ≈ 1/4 mô tả, chỉ ListPage còn dở. p2 sửa đúng 3 điểm: badge `N{g}` nhét vào ô "Mã kế hoạch" (không đẻ cột — bảng đã cuộn ngang @1366 do cây GĐ 19rem) · thêm cột "Tổng duyệt" (BE trả 0 cho phiếu chưa duyệt ⇒ ép hiển thị `—` kẻo đọc nhầm "đã duyệt 0 đ") · `colSpan` 7→8 ở HAI chỗ (loading + empty row).
- BÀI-HỌC: khuôn "WRITE-lane garble ≠ mất code" (S114) đúng thêm một lần: lead đọc transcript dở-dang nên kết-luận theo Ý ĐỊNH đang chạy, không theo đĩa ⇒ viết lại phần đã đúng chính là PHÁ. `npm run build` = `tsc -b && vite build` với `noUnusedLocals:true` ⇒ build LÀ phép thử TS6133 thật, không cần tin IDE (#68).
- BẤT-NGỜ: quên `colSpan` là lỗi CÂM cho tới lúc bảng rỗng — chỉ lộ ở trạng-thái ít ai test; SHA-pair 5/5 IDENTICAL với Panel không sửa (hash `2B64144EF6E8` BẰNG baseline) làm đối-chứng dương.
→ substring:"khkk-nhom-duyet"
**[vừa] FE-I — S161 ARC W1+W2+W3đ1: containment 2 lớp và hợp-đồng types** `[s161, khkk-fe-3-wave, containment-2-lop, types-theo-entity]`
- VIỆC: 3 lượt FE cho KHKK (Designer type-10 · 3 page + types · panel workflow). Mọi lượt đều #53, đĩa cứu trọn.
- KẾT-LUẬN: W1 — containment phải 2 LỚP render + payload (`levelFlagsPayload(e, lock)` ép false ở 2/2 call-site; chỉ dùng `disabled` là containment GIẢ vì `copyFromDefinition` tuồn state ra); 2 tiền-đề đề-bài sai tự bẻ (FE không có bảng nhãn — nhãn đến từ BE `applicableTypeLabel`; khuôn type-9 KHÔNG tồn tại ⇒ soi gương `Contract:3`). W2 — types theo ENTITY khi BE chạy song song; header types = HỢP-ĐỒNG 12 endpoint (reviewer đối chiếu ra 6 chỗ đứt → lead vá). W3đ1 — `KhkkWorkflowPanel` soi gương PE-panel 1063 dòng theo bảng GIỮ/BỎ tường minh; cây workflow THÔ nên FE tự suy ✓/●/○ từ con-trỏ đôi (StepIndex 0-based vs `Level.Order` là giá-trị).
- BÀI-HỌC: hợp-đồng giữa 2 lane phải do LEAD định-nghĩa literal — để mỗi lane tự suy thì đứt ở chỗ không ai nhìn; "place-3 menuKeys SKIP-có-khai (0 consumer)" cho thấy bỏ-qua-có-khai là hành-vi hợp lệ, khác hẳn bỏ sót.
- BẤT-NGỜ: W2 khai thiếu `approverUserId` trong opinion, tới W3đ1 mới bổ — lỗ hổng hợp-đồng chỉ lộ ở lượt TIÊU-THỤ, không lộ ở lượt định-nghĩa.
→ substring:"khkk-fe-3-wave"
**[vừa; J4 cao] FE-J1..J5 — 5 mục micro-lesson (khối riêng từng mục, KHÔNG hợp nhất nội-dung)**
- VIỆC: 5 lượt FE 2-app rời nhau, xếp chung để tiện đọc — danh-tính + tag từng mục giữ nguyên:
- **J1 — S134 lũy-kế tạm tính** `[s134, pe-luyke-tam-tinh, sub-slot-prop]`: slot-prop `sub?: ReactNode` cắm `BudgetSharedNumCell:1184` + `BudgetColValue:1197` ⇒ chèn sub-dòng amber vào N chỗ render không nhân bản markup; gate `showPending = !bs.budgetFrozen && (...)`; hmw đánh chỉ-số sub-file GLOBAL theo taskList ⇒ hết va tên khi 2 vai KHÁC nhau cùng lần gọi.
- **J2 — S133 badge budget-frozen** `[s133, budgetfrozen-badge, anchor-by-visible-text]`: badge 🔒 ở `PeBudgetSummaryTable :1350-1359`; 🔴 className header LẶP 2 chỗ (`:1350` budget và `:1931` winners) ⇒ anchor của Edit PHẢI kèm text nhìn thấy được ("Tổng hợp ngân sách trình ký") mới unique.
- **J3 — S117 radio thứ 5 ProMinMax** `[s117, prominmax-5th-radio, spread-conditional-before-as-cast]`: entry mảng có điều-kiện phải spread TRƯỚC `as T[]` (né suy-diễn sai); `?? undefined` = cách bỏ key chuẩn của axios; Max không cần state riêng (derive từ selectedCandidate) ⇒ 0 chỗ phải reset.
- **J4 {cao} — S116 nhập số ÂM báo giá** `[s116, sign-toggle-immediate-commit, hasRealSelectedQuote]`: 🔴 2 dialog POST commit thẳng `form.thanhTien` (`:2247`/`:2906`, không có nút "Lưu") ⇒ nút đổi dấu phải ghi giá CÓ DẤU NGAY, chỉ lật cờ là MẤT DẤU thầm lặng; `parseVnd/formatVndInput :51-52` là hàm dùng chung (chia với ngân sách) nên KHÔNG sửa, bọc `Math.abs`; gate đổi thành `!== 0` (không phải `> 0`) và 2 gate FE cùng đổi, không phải 1 (`missingForApproval:212` + `submitChecklist:243` → helper `hasRealSelectedQuote`).
- **J5 — S113 import v2 + publish** `[s113, supplier-publish-draft, publish-not-update]`: nút "Công bố/Ẩn" gọi `POST /suppliers/{id}/publish` — KHÔNG qua UpdateSupplier (#73); badge dùng full-class-literal (P14).
- KẾT-LUẬN: 5 mục = 5 lượt việc rời, mỗi mục 1 tag Ctrl-F sống làm con-trỏ MEMORY.
- BÀI-HỌC: ba khuôn tái dùng — 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). J4 dạy cái đắt nhất: form KHÔNG có nút Lưu thì mọi thay đổi hiển-thị phải commit ngay, không thì UI và payload lệch nhau trong im lặng.
- BẤT-NGỜ: J4 — báo giá nằm ở `details[].quotes` chứ KHÔNG phải `suppliers[].quotes` (đi nhầm nhánh là đo nhầm), công thức cũ SUM `winnerQuoteTotal` chặn oan ca net-zero `+100/100=0`; J5 — mục "3.d disabled" hoá ra ĐÃ đúng sẵn ⇒ NO change, kèm 1 DEVIATION có khai ("Tải file mẫu" chỉ đặt trong dialog trong khi parent 2.d nhắc thêm nút ở page).
→ substring J1:"sub-slot-prop" · J2:"anchor-by-visible-text" · J3:"spread-conditional-before-as-cast" · J4:"sign-toggle-immediate-commit" · J5:"publish-not-update"
**[cao] FE-M — [CHỈ-MỤC, không thay thế mục nào] 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 ở L1 thay vì 5 lần kể lại (đề-xuất curator — giá-trị dedup thật của file này).
- 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ả — lead đọc transcript dở-dang hoặc spec viết trước khi code land ⇒ nguyên-nhân ở ĐỘ TRỄ giữa lúc viết đề bài và lúc đọc đĩa, không ở người viết đề bài.
→ substring:"do-dia-truoc-khi-go"
## Phụ-lục token (coverage)
*(chép TRỌN token-list section implementer-frontend/2026-H2 của proposal curator — lưới coverage-diff)*
- **gotcha#:** `#82` ("**Display-only #82**: route+quyền còn nguyên, deep-link vẫn 200, thu gọn = bớt lối đi menu KHÔNG phải thu hồi quyền") · `#68` ("build LÀ phép thử TS6133 thật, không cần tin IDE (#68)") · `#73` ("`POST /suppliers/{id}/publish` (**KHÔNG qua UpdateSupplier** #73)") · `#53` (×4: "Relaunch p2 sau p1 chết #53" · "mọi lượt #53, đĩa cứu trọn" · "T2 return mất schema-fail (#53 +1)" · hàm ý ở S161) · `G-009` (mojibake — mã governance, không phải gotcha số) · `P14` (badge full-class-literal) · `403 S89` (N9 KHÔNG link)
- **surprise (nhãn 🔴/BÀI/BẪY nguyên-văn):** "🔴 **BÀI TO NHẤT — HARNESS ĐO NHẦM CÁI RENDER BỊ REACT VỨT, suýt kết luận NGƯỢC**" · "**control-âm cũng ra 3 ⇒ 2 nhánh giống nhau = phép đo mất khả năng phân biệt, trông y như PASS**" · "🔴 **Tiền-đề đề-bài sai lần 3 liên tiếp** (S164→K4a→K4b)" · "🔴 **Khuôn S164 'đo đĩa TRƯỚC khi gõ' ăn tiếp 2 lần trong CÙNG 1 đề bài**" · "🔴 **BÀI CHÍNH — tiền-đề lead SAI, đo `git diff` TRƯỚC khi gõ** … Việc thật ≈ **1/4** mô tả … **Viết lại phần đã đúng = phá**" · "🔴 **BÀI: forward-provision đúng nghĩa = đổi HÌNH DẠNG, KHÔNG bịa field**" · "🔴 **Bẫy suýt lẫn: `resolvePath:173` CÓ nhánh nhận code `'Contract'` — nhưng route-resolver có nhánh ≠ menu-row tồn tại**" · "(spec tự quên cộng 8 designer vào công thức của chính nó)" · "🔴 **Rỗng-vì-lọc ⟂ rỗng-vì-chưa-có-gì phải khác câu**" · "🔴 **writeGuard là 2 ĐIỂM không phải 1**" · "🔴 2 dialog POST commit `form.thanhTien` THẲNG … chỉ flip cờ = **MẤT DẤU thầm lặng**" · "🔴 GOTCHA quote ở `details[].quotes` KHÔNG `suppliers[].quotes`" · "🔴 Gotcha: className header LẶP 2 chỗ … Edit anchor PHẢI kèm text visible" · "**2 hash đứng yên = đối chứng dương**" (K5) · "Panel không sửa = đối chứng dương" (K2) · "types `7a99b865` không đổi = đối chứng dương" (K4b) · "**im-vì-đúng ⟂ im-vì-câm**" (CA4) · "**0-hit trần vô nghĩa**" (K4a) · "chỉ `disabled` = containment GIẢ" (W1) · "DEVIATION flagged" (S113) · "3.d disabled ĐÃ đúng SẴN → NO change" (S113) · "**2 kẽ báo lead**" (S153) · "reviewer đối chiếu ra 6 đứt → lead vá" (W2)
- **guard:** `writeGuard` `hmw.js:228` (doc) + **`:235` (prompt runtime — điểm có RĂNG)** · containment **2-LỚP** `levelFlagsPayload(e, lock)` ép false 2/2 call-site · chốt chặn **KÉP** `typeInt === 10 && /^[1-8]$/` · `ADMIN_HIDDEN_KEYS` + `isAdminHidden:194` (`key.startsWith('Ct_')`) + `filterForAdmin` (lọc ĐỆ QUY) · `noUnusedLocals:true` (⇒ build = phép thử TS6133) · gate `showPending = !bs.budgetFrozen && (...)` · gate `!== 0` (thay `> 0`) + 2 gate `missingForApproval:212` / `submitChecklist:243``hasRealSelectedQuote` · `preventDefault` + `stopPropagation` trong `<summary>` · `TRANSIENT_QUERY_KEYS:311` (group/filter/view **∉** ⇒ giữ active-state) · `MenuGroup` chỉ xét `children.length>0` · **CONTROL-ÂM** (7 ca: K4a ×3 + nhãn ×1 · K4c ×2 · K5 CA4 · K6 15→63 · K4b control-âm giữ 3) · **SHA-pair ×2 app** + `npm build ×2 PASS` (sàn tự áp, S152) · "REFUSE việc <30 min/trivial" (rào nhận việc) · `_SECRET_RES` (rào-3 tự-không-khớp-mẫu) · placeholder `<k>` chống parser false-match · "0 dòng key `ts:`" (không phạm rào-2) · `WIDTH_CAP` 0 hit + `TIER2_EXPECTED_FULL_ID` 0 hit (vùng CẤM đo bằng diff)
- **root-cause:** K4b "`setState` gọi **trong lúc render** React huỷ render đó + render lại ngay với state mới TRƯỚC khi commit; harness bản-1 chỉ chạy 1 pass" K5 "`pe.ContractId = contracts[0].Id` chỉ ĐẦU được ghi ngược 2+ hình" (`CreateContractFromEvaluationFeatures.cs:143-145`) S116 "QuoteDialog `setForm({thanhTien})` thay CẢ object nuốt neg" + "2 dialog POST commit `form.thanhTien` THẲNG (:2247/:2906, no nút 'Lưu')" S153 "chỉ sửa `: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 `/deleted` thiếu `group` "server đếm `total` trên tập chưa lọc số trang nói dối" S153 "index `i` reset mỗi invocation đợt-2 suýt đè `sub-implementer-frontend-1.md` của đợt-1" S153 "`node --check` file thô **luôn** SyntaxError (top-level await)"
- **file:line:** `resolvePath:173` · `types/approvalWorkflowV2.ts:41` (+ render `:165`) · `DbInitializer.cs:609` + `:623` · `PeDetailTabs.tsx:2186` · `CreateContractFromEvaluationFeatures.cs:143-145` · `PurchaseEvaluation.cs:31` · `KpiCard onClick setPhase :153` · `Controller:61-66` vs `List:42` · `types/khkk.ts:129` + comment `:125-127` · `isAdminHidden:194` · `TRANSIENT_QUERY_KEYS:311` · `hmw.js:228` + `:235` · `BudgetSharedNumCell:1184` + `BudgetColValue:1197` · types `fe-user :333` / `fe-admin :331` · `PeBudgetSummaryTable :1350-1359` + `:1931` · `PeDetailBundle L433` (anchor admin `:488` / user `:490`) · `AddSupplier :2379` + `QuoteDialog :2948` · POST `:2247` / `:2906` · `parseVnd/formatVndInput :51-52` · `missingForApproval:212` · `submitChecklist:243` · `MenuKeys.cs` (`MenuKeys.All` 113 key) · `Contract.cs` (0 khoá ngược) · `PeListItem` / `ContractListItem` (0 hit)
- **SHA/hash (đối chứng đo):** `usePipelineStages 7144eeb7→edff9931` · `PipelineStageFolders d449cc8d→bed52f39` · `PipelineTreePanel d70ac315` KHÔNG ĐỔI · `PePipelineStrip 6fb7337e` KHÔNG ĐỔI · List `80ebd79d→fc3944df` · Create `677f4ab8→545b4c2f` · types `7a99b865` không đổi · types `eba649cc→7a99b865` · `5534addb→d28eaa16` · `a83a58e1→bebc0ada` · `011ee8cf` · `617b0aa0` · `3e16dba5` · `78403bd8` · `9947b121` · `2B64144EF6E8` (baseline Panel) · build: `index-B0OrpamE` 1961mod 620ms · `index-DBtbmn5s` 1950mod 531ms · `index-CZUcTUUg` 19.8s / `index-BZWvx0Y8` 1961mod 10.9s · `index-C4F7kHKT` 3.6s / `index-kv90BBYQ` 0.9s · `index-B2v0kn5F` 1950mod 20.8s / `index-84zDBHE-` 1961mod 8.2s
- **định-danh run/artifact:** `runs/2026-07-31-S164-4gd-khkk-fanout/sub-implfe-k6.md` · `…/sub-implfe-k4c.md` · `…/sub-implfe-k5.md` · `…/sub-implfe-k4b.md` · `…/sub-implfe-k4a.md` · `…/sub-implfe-k2.md` 1-§5) · `runs/2026-07-29-S161-khkk-*/` · `.claude/templates/session-mind-template.md` (8.819 B) · `wf_58302bd5-b9f INV-2.B` (S134, engine opus-worker) · chứng: **21/21** (K4c) · **15/15** (K5) · **42/42 resolve + 3 control-âm** (K4a) · `VALID_ROLES` 2021→**23**
- **chuỗi tag Ctrl-F 14/14 (con-trỏ MEMORY PHẢI sống nguyên vẹn):** `[s166, k6-admin-thu-gon, dung-lai-cay-seed-de-dem, route-resolver-khac-menu-row]` · `[s166, khkk-k4c-wfview-loc-nhom, rong-vi-loc-khac-rong-that]` · `[s166, khkk-k5-8-sub-folder]` · `[s166, khkk-k4b-group-wire, harness-do-nham-render-bi-vut]` · `[s166, khkk-k4a-regex-42-leaf, tien-de-lead-sai-x2]` · `[s164, khkk-nhom-duyet, do-dia-truoc-khi-go, colspan-loi-cam]` · `[s161, khkk-fe-3-wave, containment-2-lop, types-theo-entity]` · `[s153, t5-writeguard-2-diem-235-co-rang]` · `[s134, pe-luyke-tam-tinh, sub-slot-prop]` · `[s133, budgetfrozen-badge, anchor-by-visible-text]` · `[s117, prominmax-5th-radio, spread-conditional-before-as-cast]` · `[s116, sign-toggle-immediate-commit, hasRealSelectedQuote]` · `[s113, supplier-publish-draft, publish-not-update]` · `[s152, auto-refuse-trivial-30min, no-lower-bar-smart-friend]`

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
{
"_note": "Harness-9 (S70, 2026-06-17) memory budget. Caps SEEDED BY MEASUREMENT (scripts/measure-agent-memory.ps1), NOT imagined headroom. Budget-audit @session-start (session-start.md §2.1.2): if curate-to-fit is dropping important markers, BUMP the relevant cap rather than cut markers. Re-measure with the script; never hand-edit measured_bytes.",
"seeded_date": "2026-06-18 (S71 re-measure after G1 curate — reviewer 36.7KB + investigator 29.8KB over-cap from S71 same-role race, curated L1->L2 back under auto-inject cap)",
"last_sleep_at": "2026-07-26",
"last_sleep_at": "2026-08-03",
"_last_sleep_at_note": "Harness-10b sleep-recovery (S72): timestamp lan cuoi chay /sleep-recovery-memory-l2. null = chua tung. session-start §2.1.2 + session-end §L.b(c) doc field nay -> INFORM goi-y nen L2 neu null hoac today-last_sleep_at>=7 ngay. Lead=single-writer (chi command sleep set field nay).",
"session_ctx_kb": 64,
"_session_ctx_kb_owner_set": "🔴 OWNER-SET TUONG-MINH @S146 2026-07-22 — anh chot '1 -> tuong minh' sau khi lead surface DAY DU 2 ly-do phan-bien (K6.2 muon-so-chua-do + H18 ghost-wire). Quyet-dinh cua owner co concern trong tam nhin => THUC THI, khong hoi lai. Don vi = KB; code doc PHAI nhan 1024 (khop manifest hub 101e69d6 'session_ctx_kb: 64'). Nguon so 64 = manifest hub, KHONG phai SE tu do.",

View File

@ -11,6 +11,8 @@
- **[→ archive/2026-07.md — verbatim @S168 K8] S168 gate K8-blocker Lines-editor KHKK — PASS-WITH-FLAGS 8, 0 blocker (digest):** 642 = 639 baseline + 3 Fact ⇒ **1 con số đóng 2 câu hỏi** · **quy-ước-của-chính-file là thước rẻ nhất** (mọi changelog sub-entity điền `EntityId`, chỗ mới là chỗ DUY NHẤT trống ⇒ MAJOR bằng 1 grep) · **`<select>` controlled NUỐT giá-trị đã lưu** khi option vắng (đang tải · query lỗi không toast · entry `IsActive=false` sau gán) ⇒ regression hiển thị do ĐỔI KIỂU Ô, tsc+build+test 0 bắt · **bình-luận tự-hẹn mắt-xích tương lai** chưa trả ⇒ nhưng ĐO khả-chạm trước khi hô (UNIQUE tầng trên khoá ⇒ hạ MINOR) · khe RỖNG khai rỗng ×2 · test phủ NỬA vế `if` (`!IsActive` chưa chạy lần nào) · thước hỏng `$?` sau `| tail` ⇒ đo lại `tsc -b --force`.
- **S170 GATE Fidelity /sleep-recovery-memory-l2 (16 gist-draft) — PASS-WITH-FIXES (digest):** **tên-cờ-bịa sống 4 hiện-vật** — grep 0-hit CẦN dụng-cụ THỨ HAI (`git log --all -S` + control-dương) mới nâng 'nghi'→'chốt: chưa từng tồn tại'; 3/4 là drift của cờ THẬT, 1/4 (`AllowDelegate`) 0 đối-ứng ⇒ **đừng gộp 2 lớp**. 🔴 **Nhãn phủ TÊN mà không phủ CON-SỐ** — claim mang tải là 'proven 4×' (kích-hoạt luật ≥2× promote), và đơn-thuốc 'giữ 4 lần, bỏ tên cờ' = **giữ khẳng-định + vứt CHỨNG-NHÂN** ⇒ bất-khả-bác (Goodhart). **Census tự-xưng đủ** ('4 LỖ') thiếu 4 lỗ nữa gồm 1 lỗ +23 — số-đóng-vai-đã-phủ-hết (meta-count blind-spot), dù draft TỰ chép đủ số để trừ ra. **Chính-sách số BẤT-ĐỐI-XỨNG trong cùng đợt**: file A bị buộc 'KHÔNG tự sửa số', file B lén sửa 7→6 field không khai ⇒ đọc-sau không biết số nào là chữ nguồn. **`pointer-style: substring` phải CHẠY THỬ**: 1/10 con-trỏ CHẾT do lệch hoa-thường (`PRIORITY` vs `priority`). **Lưới token máy-append in ĐẦU-DÒNG không in lân-cận-token** ⇒ mega-line cho trích-dẫn LẠC HẲN (gán FAIL cho Run PASS) — tái-lập ở 2 file độc-lập. Thước tốt giữ lại: control-ÂM cạnh control-dương · claim liên-file phải HAI BỜ CÙNG KHAI · tag-chain 14/14 đo bằng grep -F từng chuỗi. Tag `[s170, nhan-phu-ten-khong-phu-so, census-tu-xung-du, con-tro-chet-hoa-thuong, luoi-dau-dong]`
- **[→ archive/2026-07.md §@S167-K7-curate-2 — verbatim] S167 gate K6 admin thu-gọn — PASS-WITH-FLAGS 5 (digest):** **cây menu PROD SỐNG là thước, seed chỉ là bản vẽ** (`curl /api/menus/me` + control-âm bỏ 1 key). **`filterForAdmin` mới là bước MỘT** — còn `resolvePath→null` drop im lặng (#50) + group rỗng-hoá render như LEAF. **typo-noop**: key ẩn sai chính tả = `Set.has` false, module vẫn hiện, tsc vẫn xanh ⇒ assert TỪNG key TỒN TẠI trong cây prod. MAJOR: CẤM số-đếm trong comment.
- **[→ archive/2026-07.md §@S166-curate — verbatim] S164/S165 K3 · S164 K1 · S162 cây-4-folder · S161-W2 (digest):** tham-số-chết-vì-thiếu-tầng-UI (đo TẦNG-CUỐI ai GỬI) · sweep 2-cờ bất-đối-xứng · flake ⇒ 'N/0 tươi' phải khai SỐ LƯỢT · **ghost-policy** (grep policy, đừng suy từ 'key đã vào All') · **#82 ngược chiều** (FE OR nhiều key LỎNG HƠN policy khớp-CHÍNH-XÁC-1-key ⇒ tự chuốc 403) · hợp-đồng FE↔BE VỠ 6 điểm mà tsc+build+574-test XANH ⇒ so ROUTE+FIELD trên ĐĨA.

View File

@ -0,0 +1,66 @@
# reviewer/2026-05-q1 — gist
distill-gen: 1
source-verbatim: .claude/agent-memory/reviewer/archive/2026-05-q1.md (7 entry [4 review thật + 3 self-review no-spawn] → 5 cụm)
pointer-style: substring
## TỔNG-HỢP (3-5 ý)
- Rủi-ro không tập trung ở phần phức-tạp mà ở phần bị coi là ĐÃ XONG: cùng lượt review Plan K vừa xác nhận wire đúng chỗ khó (UPSERT-trước-skip) vừa bắt endpoint zombie thân rỗng `Task.CompletedTask`; sentinel tạm chỉ được dùng khi có chunk dọn ĐẶT TÊN TRƯỚC.
- Vá theo báo-cáo lỗi = vá 1/5: Plan N sửa 1 site, Plan O phải cascade 4 site còn lại cùng khuôn + 3 test hồi-quy; record Controller phải soi gương record Command — lệch field là gốc của 2 ngày lỗi prod F1+F2.
- ROI spawn đo được: 1 lượt ~25K cho chunk nặng xuyên tầng thì lãi; 4 lượt polish ≈ 100K token đổi gần như 0 phát-hiện ⇒ luật: spawn cho wire BE/migration/xuyên-tầng, bỏ qua polish <30 phút. Chuỗi test giữ xanh 84 104 106/106 111.
- Dọn prod xong IIS recycle làm seeder chạy lại thành vòng lặp gốc không lệnh xoá seeder không công-tắc; Plan T cờ `DemoSeed:Disabled` mới thật sự đóng.
- PASS hết việc: "Smart Friend guard" bật cả trong lượt PASS-0-issue, rủi-ro thấp treo lại KHAI; gotcha #47 phát-hiện nhưng treo chờ anh quyết không tự thêm vào docs.
## Entries
**[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), 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 UPSERT opinion `:441-468`, TRƯỚC đẩy con-trỏ `:502` chữ người duyệt ghi trước khi nhảy terminal; guard `skipToFinal` V1 đặt NGƯỜI GỌI (`:147-149` TransitionAsync) nên V1 giữ chữ . sqlcmd Dev xác nhận 7 cột `Allow*` + `Users.AllowDrafterSkipToFinal` drop (count=0), snapshot regen sạch, `HasDefaultValue(false)` nối, Mig 31 `Up()` đảo tay ADDDROP đúng khuôn. Major-1: endpoint zombie `PATCH /api/users/{id}/allow-skip-final` còn sống, thân rỗng (`Task.CompletedTask`) admin tick, BE nuốt im đòi chunk K5 dọn. Major-2: comment `ApprovalWorkflow.cs:78` vẫn nói cờ "đã move sang Users…" trong khi `:107-113` prop mới. Minor: `PurchaseEvaluationFeatures.cs:401` còn chữ "Drafter"; `ApprovalWorkflowConfiguration.cs:22` chưa ghi Mig 31.
- BÀI-HỌC: sentinel tạm kèm lời hứa "chunk sau dọn" bẫy chunk sau đổi phạm-vi thì sentinel hoá xác sống. Muốn dùng phải chunk dọn ĐẶT TÊN TRƯỚC, đứng trước cả chunk sửa test.
- BẤT-NGỜ: cùng lượt review vừa xác nhận wire ĐÚNG chỗ khó (thứ tự UPSERT-trước-skip) vừa bắt lỗi chỗ dễ (endpoint quên xoá) rủi-ro không nằm phần phức-tạp phần bị coi đã xong.
substring:"Task.CompletedTask"
**[cao] B5-B Plan NU: 8 kế-hoạch self-review, 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 `PurchaseEvaluationFeatures.cs:765`; Plan O phát-hiện 4 site còn lại cùng khuôn (`EnsureCanRejectV2Async:201` · `ApplyReturnModeAsync:248` · `EnsureEditableForDetailsAsync:72` · `AdjustBudgetCommandHandler:311`) + 3 test hồi-quy `PurchaseEvaluationPerNvLookupRegressionTests.cs`; Plan P thêm 3 field vào `TransitionPeBody:267` gốc của 2 ngày lỗi prod F1+F2; Plan R+S+T5 xoá ~720 row prod BE vẫn khoẻ nhờ giữ V1 active; Plan T thêm cờ `DemoSeed:Disabled` tắt 5 hàm seed demo. Tổng: 111 test (+7 so 104), 31 mig, 47 gotcha.
- BÀI-HỌC: theo báo-cáo lỗi = 1/5; refactor đổi hình-dạng khoá phải LIỆT TOÀN BỘ site tra-cứu cùng khuôn rồi một lượt. Record Controller phải soi gương record Command lệch field 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 lệnh xoá seeder không công-tắc Plan T mới thật sự đóng.
substring:"PerNvLookupRegressionTests"
**[vừa] B5-C Plan M: PASS, hẹp 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 xuống reset SLA `:364` GIỮ ChoDuyet"; người gọi `:94-100` truyền Phase mới vào log ContextNote khớp assert test. Drafter mode `:268-275` giữ nghĩa; Assignee `:335-360` vẫn ném nếu không khớp; F2/F3/F4 KHÔNG đụng. Build 0 err 2 warn, 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, 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 + 2 module anh em (`contracts.ts:29`, `budget.ts:20`) vẫn `98:'Trả lại'` deploy chung thì 3 module gọi khác nhau cho cùng một trạng-thái.
substring:"c2afef2"
**[vừa] B5-D Plan AA: PASS sạch + bài toán ROI của việc spawn** *(gộp 2 entry cùng Plan: pre-commit + post-wrap)*
- VIỆC: review trước commit chunk A (BE+Layout) + chunk B (FE trang ma-trận, implementer làm), rồi ghi nhận 4 vòng polish sau đó KHÔNG spawn.
- KẾT-LUẬN: PASS 0 critical / 0 major / 0 minor; wire xuyên suốt Controller `:21-26` Handler `:114-117` (`request.IsUserSelectable is bool ius → Where(...)`) FE `:29-33` gọi thật (không mock) route `App.tsx:38` + `Layout.tsx resolvePath:81-86`; 0 migration (cột từ Mig 25); seed menu idempotent (PK trên Key ToDictionary an toàn); `[Authorize]` trần `:18` được GIỮ (gotcha #44); quyền 7 role qua `SeedPurchaseEvaluationPermissionDefaultsAsync:1573`. Rủi-ro thấp ghi nhận: non-admin truyền `isUserSelectable=false` xem được workflow chưa ghim dữ-liệu không nhạy-cảm, không chặn commit. Plan AA tổng ~620 LOC / 7 commit `a1a910f..ee0902a`, spawn 1 lần ~25K.
- BÀI-HỌC: gom review một lượt cho chunk nặng (wire BE / migration / xuyên tầng), bỏ qua với polish <30 phút (CSS/màu/layout) 4 lần spawn polish 100K token đổi gần như không phát-hiện.
- BẤT-NGỜ: "Smart Friend guard" khai VẪN BẬT ngay trong lượt PASS-0-issue, kèm 3 ghi-chú rủi-ro treo PASS nghĩa "không đáng chặn", không phải "không còn "; bất đối-xứng cấu-trúc 2 app (fe-user StaticLeaf "Hộp thư", fe-admin không) được CHẤP-NHẬN thay ép mirror.
substring:"a1a910f"
**[vừa] B5-E chế-độ UAT không spawn (S21/S22): 3 phản-mẫu + 1 gotcha treo**
- VIỆC: 2 phiên em-main tự review theo `feedback_uat_skip_verify` (bỏ `dotnet test` mỗi chunk nhưng vẫn build + npm build 2 app); 11-12 commit cộng dồn.
- KẾT-LUẬN: 4 điểm xác nhận: phân quyền V2 siết theo `actor.UserId` (bỏ mệnh-đề lỏng `|| ApprovalWorkflowId != null`); helper `EnsureCanRejectV2Async` chặn request giả mạo `PATCH /transitions decision=Reject` (phòng-thủ 2 lớp FE+BE); handler chỉnh ngân-sách 3 tầng (Drafter Nháp/Trả-lại · Approver ChoDuyet · Admin) siết 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, xem kiểu trước; (c) script PS 5.1 dấu tiếng Việt parser lỗi (gotcha #30).
- BẤT-NGỜ: vai reviewer tự khai KHÔNG spawn 2 phiên liên-tiếp vẫn viết entry đầy-đủ; ghi nhận CICD Monitor đứng thay vai reviewer sau deploy; phát-hiện gotcha #47 (`paths-ignore` bỏ sót `agent-memory`) nhưng TREO chờ anh quyết, không tự thêm docs.
substring:"paths-ignore"
## Phụ-lục token (coverage)
*(chép TRỌN token-list section reviewer/2026-05-q1 của proposal curator lưới coverage-diff)*
- **gotcha#:** `#45` (fix + 3 test hồi-quy, test-before §7) · `#44` (class-level `[Authorize]` bare "protect", nhắc 2 lần) · `#47` ("paths-ignore agent-memory gap pending bro decide" **New gotcha discovered**) · `#30` (PS 5.1 Vietnamese diacritics)
- **surprise (nhãn nguyên-văn dạng Anti-pattern/Pattern caught/Discovery):** "**Pattern caught: 'Transient sentinel pattern'** đặt sentinel + comment commit chunk khác cleanup nhưng chunk đó scope SHIFT zombie state" · "**Anti-patterns observed:** (a) Default scope expansion mistake S22+4 S22+5 fix (b) History display field assumption FAIL TS2339 (c) PS 5.1 Vietnamese diacritics gotcha #30 reinforced" · "**Plan N point 9 chỉ catch 1/5 sites**" · "**Plan O caveat #1 surfaced Plan P pre-existing bug**" · "**DbInitializer auto re-seed** loop Plan R+S clean IIS recycle re-seed loop" · "**Anti-pattern observed S24 polish chunks**: UI/UX iteration thuần CSS/layout KHÔNG cần Reviewer spawn mỗi chunk" · "**Discovery #3 anomaly re-tested S24** KHÔNG reinforce docs-only anomaly hypothesis" · "**Low note S24**: BE filter `IsUserSelectable=false` non-admin leak workflow chưa ghim" · "Non-admin pass `isUserSelectable=false` BE không block workflow chưa ghim leak risk Low severity"
- **guard:** **"Smart Friend guard"** (bật/nhắc 6/7 entry; "KHÔNG lower bar") · `EnsureCanRejectV2Async` (BE helper chặn forge Reject, defense-in-depth FE+BE) · entry guard `:75-81` (đòi ChoDuyet mới Reject) · Reject guard `:81` · Admin bypass `:252-265` · anti-fiddle audit (scope drift 0%) · "No `--no-verify` bypass" (×2) · class-level `[Authorize]` bare `:18` giữ nguyên · `HasDefaultValue(false)` EF config · `MenuItemConfiguration HasKey(Key)` PK unique ToDictionary an toàn · `PermissionGuard`/`usePermission` KHÔNG (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 ADDDROP)
- **root-cause:** "Transient sentinel pattern" orphan endpoint `PATCH /api/users/{id}/allow-skip-final` silent NoOp (`Task.CompletedTask`) "Plan P Controller `TransitionPeBody:267` record +3 fields mirror `TransitionPurchaseEvaluationCommand` schema **root cause 2 ngày prod bug F1+F2 wire fail**" "DbInitializer auto re-seed loop Plan T flag fix root cause" "Plan N point 9 chỉ catch 1/5 sites Plan O cascade fix"
- **file:line:** `ApprovalWorkflow.cs:78` + `:107-113` · `PurchaseEvaluationFeatures.cs:401` · `:765` · `ApprovalWorkflowConfiguration.cs:22` · K2 F2 branch `:477` / UPSERT `:441-468` / advance `:502` / caller guard `:147-149` · test ref `:253` (K7-pending) · `Service.cs:287-333` · `:364` · `:268-275` · `:335-360` · caller `:94-100` · entry guard `:75-81` · `:81` · `:252-265` · `:105-132` · `EnsureCanRejectV2Async:201` · `ApplyReturnModeAsync:248` · `EnsureEditableForDetailsAsync:72` · `AdjustBudgetCommandHandler:311` · `TransitionPeBody:267` · Controller `:21-26` · Handler `:114-117` · `WorkflowMatrixViewPage :29-33` · `App.tsx:38` · `Layout.tsx resolvePath:81-86` · `SeedPurchaseEvaluationPermissionDefaultsAsync:1573` · `[Authorize]` bare `:18` · `PeListPanel.tsx:112` · `PeWorkflowPanel.tsx:270` / `:272` / `:315` / `:342` · `PeDetailTabs.tsx:152` / `:287` · `ApprovalWorkflowsV2Page.tsx:639` · `contracts.ts:29` · `budget.ts:20` · file test `PurchaseEvaluationPerNvLookupRegressionTests.cs`
- **định-danh khác:** commit `eb106f2..56868bf..db66253..364aef6` · `c2afef2..508b17a..4dd6f9c` · `a1a910f..ee0902a` (`da218f1`/`4d60598`/`fbbd361`/`ee0902a`) · Run #210 sha=`ac2c859` (4/4 wire + bundle 2 app rotate) · diff 11 file BE +4093/83 (Designer 3938 dòng) · test 84 104 **106/106** (58 Domain + 48 Infra) **111** · mig 29 30 31 · gotcha 45 46 47 · ~146 endpoint · 33 user · 2 test mới `ApplyReturnMode_OneLevel_AtStep1Level1_ResetsToBuoc1Cap1_KeepsChoDuyet` + `ApplyReturnMode_OneStep_AtStep1_ResetsToBuoc1Cap1_KeepsChoDuyet` · fe-admin npm build 1926 modules / 740ms / 0 TS err · ~720 row prod bị xoá (Plan R+S+T5) · spawn-cost ~25K/lượt vs ~100K nếu spawn 4 polish
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `Mig 27` 8 plan consecutive em main self-review per UAT mode (`feedback_uat_skip_verify` rule em main verify build + test + npm build mỗi chunk). Key val
- `Mig 28` 2. **Stale Mig 28 comment ApprovalWorkflow.cs:78** comment "F2 (Drafter skip) đã move sang Users.AllowDrafterSkipToFinal" còn nguyên l
- `Mig 29` **Anti-patterns observed:** (a) Default scope expansion mistake S22+4 S22+5 fix KHÔNG default expand permission scope without admin tick (per
- `Mig 30` S22+4 attachment view endpoint + S22+4 budget-adjust endpoint chưa qua live curl verify (defer post-deploy bro UAT direct). S22+5 Mig 30 applied
- `Service.cs:288` 1. **Stale narrative comment Service.cs:288-289 + 327-328** UPDATED "Plan M S23 t3 KHÔNG fallback Drafter, phiếu giữ đang duyệt" k

View File

@ -0,0 +1,42 @@
# reviewer/2026-05-q2 — gist
distill-gen: 1
source-verbatim: .claude/agent-memory/reviewer/archive/2026-05-q2.md (3 entry → 3 cụm)
pointer-style: substring
## TỔNG-HỢP (3-5 ý)
- Bề mặt lỗi là PHÉP ĐỌC NGÔN-NGỮ: "chú ý X" (anh gợi ý) bị đọc thành "BẮT BUỘC X" (chính sách) — em-main t2 tự ban luật xuyên dự-án không consent, anh bắt ở t4, luật ABANDONED; sinh hạng-mục kiểm MỚI Cat 6 authority-boundary (phân-biệt gợi-ý ⟂ mệnh-lệnh, gắn cờ mọi "BẮT BUỘC xuyên dự-án" nguồn tự-quyết).
- Gotcha #48: SQLite frozen clock — nhiều `Changelog.Add()` cùng một `SaveChangesAsync` nhận CÙNG timestamp ⇒ `OrderByDescending(CreatedAt).FirstAsync()` không tất-định; bổ Cat 5: bộ lọc test phải phân-biệt bằng thứ khác ngoài mốc thời-gian (EntityType + Summary keyword). Bài về test bắt nguồn từ đặc-tính đồng-hồ của engine DB test — không xuất-hiện ở prod.
- Phép chứng mirror 2 app nâng lên so SHA256 nguyên file (`21001E90...`) — rẻ, không cãi được; 6 commit S26 giữ nguyên baseline 111 test = UI thuần không mua thêm rủi-ro hồi-quy; củng-cố luật ROI: spawn cho việc nặng xuyên tầng, em-main tự lo polish 50-100 LOC.
## Entries
**[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>]`, phase ∈ {phase-9, phase-9plus, phase-10}, 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à reviewer phát-hiện theo lối HỒI-CỐ, tức lúc xảy ra không có ai chặn.
→ substring:"authority boundary"
**[vừa] B6-B — S25 Plan AB: đồng-hồ đóng băng của SQLite**
- VIỆC: ghi từ-khoá đúc kết sau đợt vá changelog (`cdfd542` — PE chỉnh ngân-sách + trả-lại Người-chỉ-định có log).
- KẾT-LUẬN: gotcha #48 — nhiều `Changelog.Add()` trong CÙNG một `SaveChangesAsync` nhận cùng dấu thời-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 test` khi refactor BE > 100 LOC.
- BÀI-HỌC: bổ vào Cat 5: bộ lọc trong test phải phân-biệt bằng thứ khác ngoài mốc thời-gian (EntityType + từ khoá trong Summary).
- BẤT-NGỜ: một bài về test lại bắt nguồn từ đặc-tính đồng-hồ của engine DB dùng trong test — thứ không xuất-hiện ở prod (SQL Server), chỉ lộ khi test đỏ ngẫu-nhiên.
→ substring:"cdfd542"
**[vừa] B6-C — S26 Plan AG: PASS 0 issue, và bài ROI lặp lại**
- VIỆC: review trước commit 3 chunk A+B+C (~25K token, 12 phép kiểm đối-kháng).
- KẾT-LUẬN: PASS 0 issue; commit `0bf6c7e` 2 file +346/116 LOC, hai bản mirror SHA256 IDENTICAL (`21001E90...`); wire = useMemo gom nhóm lồng + `<details>/<summary>` 2 tầng + lưu tập mở/đóng vào localStorage; 0 migration. 5 vòng polish AG2-AG6 sau đó em-main tự verify (SHA256 giống nhau + npm build 2 app + `dotnet test 111/111`), không spawn lại.
- BÀI-HỌC: củng-cố luật phân-bổ: spawn reviewer cho việc nặng xuyên tầng (~370 LOC + 4 sub-agent phối hợp), em-main tự lo vòng polish 50-100 LOC.
- BẤT-NGỜ: phép chứng "2 app giống nhau" nâng lên so SHA256 nguyên file thay vì đọc diff — rẻ và không cãi được; cả 6 commit S26 giữ nguyên baseline 111 test = thay đổi UI thuần không mua thêm rủi-ro hồi-quy.
→ substring:"21001E90"
## Phụ-lục token (coverage)
*(chép TRỌN token-list section reviewer/2026-05-q2 của proposal curator — lưới coverage-diff)*
- **gotcha#:** `#48` (SQLite **frozen clock** tie-break — mô-tả đầy đủ: "Multi-Changelog.Add same SaveChangesAsync transaction non-deterministic `OrderByDescending(CreatedAt).FirstAsync()`") · dải nền "Foundation gotchas **#17-#48**" (header)
- **surprise (nhãn nguyên-văn):** "**Rule cũ ABANDONED**: 'RAG ghi mọi tương tác mandatory' S28 t2 over-reach" · "t4 bro caught mistake scope-down về SOLUTION_ERP self-discipline" · "em main t2 implicit interpret 'chú ý X' (bro suggestion) AS 'MANDATORY X' (em policy decision)" · "Reviewer perspective về S28 trajectory (em main solo, **KHÔNG actual review work**)" · "UAT skip `dotnet test` recurring risk khi BE refactor > 100 LOC"
- **guard:** **Smart Friend guard** ("active S28+ cho Plan B Contract V2 wire pre-commit spawn (mandatory heavy diff > 50 LOC cross-stack)") · **Cat 6 — Authority boundary check** (MỚI) · Cat 5 bổ-sung "test filter discriminator beyond timestamp (EntityType + Summary keyword)" · 5-category checklist baseline (Wire BE + Schema + Security + Code quality + Test) UNCHANGED · tag schema **mandatory** `[lesson, phase-<N>, <bc>]` · "Cross-module security validation mirror pattern preserved" (header)
- **root-cause:** "scope creep từ project-local → cross-project KHÔNG bro consent là **authority boundary violation**" ⇐ "implicit consent ('chú ý' / 'có thể') KHÔNG = explicit mandate ('BẮT BUỘC' / 'mandatory')"
- **file:line:** **0 token file:line** (file này chỉ dẫn tên hàm/commit: `ApplyReturnModeAsync`, `cdfd542`)
- **định-danh khác:** commit `0bf6c7e` (2 file +346/116) · SHA256 mirror `21001E90...` · commit `cdfd542` · `dotnet test 111/111` (baseline 111 preserved, 6 commit S26, 0 prod regression) · spawn ~25K · phase enum {phase-9, phase-9plus, phase-10} · BC enum {contract, pe, budget, workflow, identity, form, infra} · gotcha `#48` chú-giải KHÁC batch A — xem `_lienfile-notes-batchB.md` #5

View File

@ -0,0 +1,106 @@
# test-specialist/baseline-history — gist
distill-gen: 1
source-verbatim: .claude/agent-memory/test-specialist/archive/baseline-history.md (29 entry-heading + 1 dòng chuỗi → 10 cụm; curator khai 25 entry — chênh ghi ở phụ-lục "nguồn-đếm")
pointer-style: substring
## TỔNG-HỢP (3-5 ý)
- Chuỗi baseline 254 (S60) → 561 (S155) là RUỘT file: chỉ có giá-trị khi mỗi mốc kèm delta + lý-do; giữ NGUYÊN 16 mốc chuỗi gốc + 12 mốc hậu-chuỗi + **8 LỖ** (4 curator: 419→431 · 434→440 · 477→486 · 500→501 + 4 bổ @S170 P3-gate reviewer: 254→263 chênh+1 · 402→419 chênh+11 · 519→528 chênh+1 · **528→561 chênh+23 — LỚN NHẤT, gấp đôi 4 lỗ đầu cộng lại**; cùng một định-nghĩa delta-không-nối) + **2 MÂU-THUẪN nội-tại** — ⚠️ mâu-thuẫn nội-tại văn-bản — KHÔNG tự sửa số @S170.
- Bẫy vacuous là chủ-đề lặp xuyên file: fixture không wire `AuditingInterceptor` ⇒ assert đúng ở CẢ HAI thế-giới (TS-B); fixture-hỏng-cho-xanh trông Y HỆT máy-đúng-cho-xanh (TS-C); phép chống mạnh nhất = số CHÍNH XÁC, 2 hàm, 1 seed (C6, TS-E) + mỗi assert "cho qua" của phần tiền kèm 1 seed "chặn lại" (TS-G).
- Test tìm được cả bug prod lẫn lỗi tài-liệu: T26 GIỮ ĐỎ = bug prod thật (cascade Immediate → dependent `Added``Detached` ⇒ 0 row changelog, TS-B); spec ghi sai tên query `GetPurchaseEvaluationDetailQuery` — test bắt, lead sửa spec (TS-D). Cả hai đều REPORT, KHÔNG tự sửa.
- Test xanh KHÔNG chứng đường chạy thật còn sống: S92 reflection gọi method cô-lập vẫn xanh dù prod đã TẮT lời gọi seed (TS-F); dedup collation nằm ở SERVICE `OrdinalIgnoreCase` — fixture SQLite BINARY không bắt được ca prod SQL Server bắt (TS-H).
- Hai ghi-chú "API trông như có kiểm mà không kiểm" sống sót mọi vòng curate: `NotEmpty()` trên `Guid?` không bắt `Guid.Empty`; `ExecuteUpdateAsync` đi vòng change tracker (TS-J).
## Entries
**[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 PE 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 urgent drafter-unset) → 374 (S85b +3 UpdateDraft WF-preserve) → 377-395 (S86-S86d) → 402 (S87 +7 CCM-budget-period) → 419 (D2 +6 PE Mig58 multi-winner + translation-smoke)**. Hậu-chuỗi 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 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ì 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 — ⚠️ mâu-thuẫn nội-tại văn-bản — KHÔNG tự sửa số @S170: **8 LỖ (4 curator + 4 bổ @S170 P3-gate):** ① 419 → 431 (+12 không có mốc) · ② 434 → 440 (+6) · ③ 477 → 486 (+9) · ④ 500 → 501 (+1) · **⑤ 254→263** khai +22/14=+8 nhưng thật +9, chênh +1 (S61) · **⑥ 402→419** khai +6 nhưng thật +17, chênh +11 (D2) · **⑦ 519→528** khai +8 (Infra 474→483) nhưng thật +9, chênh +1 (S134) · **⑧ 528→561** khai +10 và tự khai "Infra cũ 506 nguyên vẹn" trong khi S134 để lại 483 — thật +33, **chênh +23, lỗ LỚN NHẤT** (4 lỗ bổ dùng CHÍNH định-nghĩa delta-không-nối của curator, reviewer P2b áp lên toàn chuỗi; số gốc GIỮ NGUYÊN — chỉ danh-sách lỗ được bổ, con số "4 LỖ" cũ là claim "đã-soi-hết" sai). **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ó. Curator 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.
→ substring:"Count progression"
**[cao] TS-B — S155: giữ nguyên 1 test ĐỎ vì đó là bug prod**
- VIỆC: đợt 2 tính-năng "xoá phiếu khi đang duyệt" + màn "Đã xoá" — +10 test `PeDeleteByApproverTests.cs` (T21 lũy-kế 4 hướng · T22/b/c màn Đã xoá · T23/T24/b cờ+lượt · T25 authz reflection · T25b chiều thuận · T26 changelog).
- KẾT-LUẬN: 561 = 560 PASS / 1 FAIL (45 Domain + 516 Infra), 0 hồi-quy (506 Infra cũ nguyên vẹn). T26 GIỮ ĐỎ vì là BUG PROD thật: `PeSoftDeleteFeatures.cs` thêm changelog `:126` rồi `Remove(pe)` `:142`; `CascadeDeleteTiming.Immediate` cascade NGAY TẠI `Remove`, thực-thể con đang `Added` bị đẩy sang `Detached` (không phải `Deleted`) ⇒ 0 row; `AuditingInterceptor` chạy ở `SavingChanges` = SAU điểm mất nên không cứu được. Đo bằng chẩn-đoán `ChangeTracker` (`Added→Detached`, `rowsInDb=0`), KHÔNG suy đoán. Grep cùng lớp: 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: `SoftDeleteFixture` (~90 LOC, ngay trong file test) là fixture ĐẦU TIÊN của repo wire `AuditingInterceptor` — bắt buộc khi đo `Remove()` nghĩa là xoá-mềm, vì `TestApplicationDbContext` không wire ⇒ mặc-định XOÁ CỨNG ⇒ màn "Đã xoá" rỗng ⇒ assert đúng ở CẢ HAI thế-giới = đúng bẫy vacuous.
- BẤT-NGỜ: số học T21 phải seed thêm phiếu hàng-xóm (DaDuyet + TraLai) để hai đại-lượng "không đổi" (PrevSelected, PendingSubmitted) khác 0 ở cả hai phía — để 0 thì "bất động" và "rỗng" nhìn giống hệt nhau.
→ substring:"PeDeleteByApproverTests"
**[cao] TS-C — S153: tiêm lỗi vào máy `mind-check`, 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 (con số "509" khai kèm — xem mâu-thuẫn (b) ở TS-A). Bắt 1 GAP THẬT: ở chế-độ closed, xoá con-trỏ → cờ/exit 0 trong khi giữ con-trỏ đang-chờ trung thực → trượt/exit 1 ⇒ đường rẻ nhất để xanh là đường SAI (chữ luật cấm, gradient thưởng). Báo em-main, KHÔNG tự sửa.
- BÀI-HỌC: fixture `draft-ts` MALFORMED vẫn cho exit 0 — `inject_A` neo `## MIND-1` sau khi `renum(...,2)` đã đổi heading ⇒ `str.find` = 1 ⇒ chèn GIỮA heading ⇒ `ts:` không đầu dòng ⇒ máy ĐÚNG mà vẫn xanh. Fixture-hỏng-cho-xanh trông Y HỆT máy-đúng-cho-xanh ⇒ soi RUỘT fixture (`cat -A` / `repr` từng dòng), không đọc mỗi exit code.
- BẤT-NGỜ: 2 kỹ-thuật tái dùng — (i) cặp CÙNG-RUỘT khác đúng 1 biến (cap=32 vs cap=1 cùng tệp = chứng đọc config LIVE; `_end` giả `_pause=9` vs không = chứng nguồn đếm là ĐĨA, `diff` chứng 2 output BYTE-IDENTICAL); (ii) đo wall-clock phải đi nhánh ĐẮT — lệnh literal rơi nhánh thoát-sớm là tự đo nhánh rẻ; đo 55-64ms/lượt vs ước 140-300ms ⇒ lệch 2.2-5.4×, trả lead điền lại.
→ substring:"inject_A"
**[cao] TS-D — S133 budget-freeze: dựng ĐỎ rồi đẩy XANH + 2 lượt cùng vai đè file nhau** *(gộp 2 entry INV-A + INV-C — hai nửa một chu-trình test-before, giữ đủ 2 verdict)*
- VIỆC: INV-A dựng test tái-hiện lỗi (test-before) → INV-C viết T1-T9 phủ tính-năng đóng băng ngân-sách phiếu đã duyệt.
- KẾT-LUẬN: INV-A RED thật `Failed 1/1` — kỳ vọng ProInitial 100tr, thấy 999tr @`PeBudgetFreezeTests.cs:184`; luồng chuẩn = đưa phiếu tới DaDuyet QUA `svc.TransitionAsync` (V2 1-Bước-1-Cấp `AllowApproverFinalize=true` + giá chốt), KHÔNG seed thẳng Phase (sau vá thì snapshot chỉ đặt qua transition); đổi giá qua `UpdatePeBudgetPro/Ccm` (2 handler không chặn theo phase = đúng cơ-chế lỗi); 2 sanity-assert chống vacuous (Phase==DaDuyet + bản ghi live đổi thật) đặt TRƯỚC assert chính. INV-C +9 test (`:426` T1 đủ-11-cột · `:480` T2 CCM-threshold · `:524` T3 all-steps · `:555` T4 V1-legacy · `:638` T5 guard CCM+Admin · `:687` T6 AdjustBudget · `:730` T7 live-vs-frozen · `:783` T8 null-safe · `:821` T9 overwrite+refresh) → lead chạy full exit-0: Domain 45 + Infra 474 = 519.
- BÀI-HỌC: INV-C ghi đè `sub-test-specialist-0.md` của INV-A — cùng vai + cùng chỉ-số task qua 2 lần gọi trong cùng run-folder = va-chạm tên sub-file; nội-dung INV-A sống 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 — thật là `GetPurchaseEvaluationQuery` (bundle `.BudgetSummary`), spec ghi `GetPurchaseEvaluationDetailQuery`; test-specialist bắt được và lead sửa spec (test soi ra lỗi tài-liệu, không phải lỗi mã).
→ substring:"wf_d9639faa"
**[cao] TS-E — S134: hai hàm, một seed, khớp số CHÍNH XÁC**
- VIỆC: +8 test `PePendingAccumulationTests.cs` (C1-C8) cho lũy-kế TẠM TÍNH (mirror seed `PeBudgetFreezeTests:277-336`).
- KẾT-LUẬN: C1 ChoDuyet+winner → delta2+PriorPes · C2 TuChoi/DaDuyet loại sạch · C3 nháp chỉ-liệt-kê / TraLai delta1 · C4 cửa-sổ `CreatedAt` · C5 delta âm có dấu · C6 bổ-sung-không-đếm-hai-lần: gọi CẢ `ComputeAsync` + `ComputePendingAsync` trên CÙNG seed, assert SỐ CHÍNH XÁC hai phía (PrevSelected 90tr/1 bất động ⟂ PendingSelected 50tr/1) · C7 frozen→0/null qua query handler · C8 changelog D4 dương + đối-chứng (lọc `ContextNote.Contains("[S134 pending-prior]")`, gotcha #48). Full 528/0 (45D+483I).
- BÀI-HỌC: (1) gọi thẳng accumulator chỉ cần `curPeId` tổng-hợp + `CreatedAt` muộn, không cần row phiếu đang xét (chỉ lọc `Id!=` / `CreatedAt<`); (2) phép chống hồi-quy mạnh nhất cho đại-lượng "bất động" = kiểu C6 — số chính xác, 2 hàm, 1 seed; (3) FluentAssertions `ContainInOrder(params T[])` đụng overload với `because` khi T=Guid ⇒ bọc `new[]{...}`.
- BẤT-NGỜ: helper D4 không gọi SaveChanges ⇒ row chỉ thực sự nằm DB SAU `TransitionAsync` — assert sớm sẽ thấy "không có row" và kết luận sai về tính-năng.
→ substring:"PePendingAccumulationTests"
**[vừa] TS-F — cụm SECURITY/authz: khuôn reflection + đối-chứng chống vacuous** *(gộp 5 entry theo loại rủi-ro: S118b +4→509 · S118 +4→505 · S92 +3→434 · S69 Office +6 · S54 ItTicket +13)*
- VIỆC: 5 đợt khoá quyền bằng test hồi-quy security.
- KẾT-LUẬN: S118b mở rộng `Api/AuthorizePolicyRegressionTests.cs` (Pattern 10 reflection, file 15→19 [Fact]): `Dashboard` + `ExportContracts` nay `[Authorize(Policy="Reports.Read")]`, `MyDashboard` CỐ Ý để trần — T4 assert `(attr?.Policy).Should().BeNull()` để CẤM mọi policy (thêm policy = khoá dashboard cá-nhân role không-Admin); csproj đã ref Api từ S45. S118 `ProcurementMasterAccessSeedTests.cs` 4 [Fact], T2 ⭐ScopeIsolation là lõi: DeptManager pre-grant → revoke → seed ⇒ 9 key VẪN all-false (row còn giữ vết) = cấp quyền role này không rò sang role khác, kèm đối-chứng Procurement thật sự được nâng. S92 `AdminOnlyModulesRevokeTests.cs` 3 test giữ bất-biến "PE KHÔNG bị revoke". S69 Office khác HRM: 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"; authz theo dữ-liệu (handler) khác hẳn authz tĩnh (`[Authorize]` đọc reflection) — hai điểm thực-thi, test khác nhau.
- BẤT-NGỜ: S92 — `OfficeModulePermissionSeedTests` + `HrmProfilePermissionSeedTests` VẪN xanh dù prod đã TẮT 2 lời gọi cấp quyền, vì test gọi thẳng method cô-lập qua reflection, không chạy chuỗi `SeedAsync` ⇒ test xanh không chứng minh đường chạy thật còn sống; đã thêm dòng ghi chú đầu mỗi file.
→ substring:"ProcurementMasterAccessSeedTests"
**[cao] TS-G — cụm FINANCIAL / critical-algo: giá chốt, số âm, ngưỡng, đa người thắng** *(gộp 6 entry: S117 +5→500 · S116 +9→495 · S72 +28 · S69b +14 · D2 +6→419 · S87 +7)*
- VIỆC: 6 đợt test cho phần dính tiền.
- KẾT-LUẬN: S117 khoá bất-biến `Max != null ⟺ Source=="ProMinMax"` + ca biên min==max PHẢI OK (revert `>` thành `>=` là đỏ). S116 đổi guard nộp phiếu từ `Sum()<=0` sang `Count==0` / `All(x==0)` cho báo giá ÂM đi qua; ranh-giới = ca net==0 nhưng từng dòng ≠ 0 `[+500k, 500k] → ChoDuyet`; ngưỡng CCM tính trên NET (`[+1.2tỷ, 0.3tỷ]=0.9tỷ` không chạm ⟂ đối-chứng `[+1.2tỷ]` một mình thì chạm). S72 chuyển CCM-finalize từ TỰ-ĐỘNG sang CHỌN-THAM-GIA + thứ tự fail-closed (threshold-null → Conflict / sai role → Forbidden / vượt ngưỡng → Conflict). D2 chuyển 1→N hợp-đồng cho N đơn-vị thắng, `GiaTri` per-winner = tổng ThanhTien báo giá của ĐÚNG winner (140/260) ⟂ ca single dùng legacy `ThanhTienNganSach` (800). S87 CCM budget-period setter authz (+7).
- 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 — method prod đổi sang 5 tham số: `MethodInfo.Invoke` không tự bind optional param ⇒ giữ 4 arg là `TargetParameterCountException` phá 3 test cũ (tự gây, tự tránh). ② S72 — nhánh `isSystem` KHÔNG với tới được qua `ApproveV2Async` công-khai (PE không có job SLA) ⇒ phòng-thủ/chết, test bằng unit + reflection, ghi rõ OBSERVATION. ③ D2 — `PurchaseEvaluationQuote.PESupplierId` 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 khi `Contract.SupplierId/ProjectId` KHÔNG có FK vật lý — hai quy-ước ngược nhau cùng một luồng. ④ S87 — `Changelog.UserId``Guid?` không FK nên Guid ngẫu-nhiên vô-hại.
→ substring:"TargetParameterCountException"
**[vừa] TS-H — cụm Supplier import: lỗi so-sánh hoa/thường + 1 NRE báo-không-sửa** *(gộp 2 entry: D3 +10→450 test-before · S113 +19→477 test-after)*
- VIỆC: D3 test-before cho import Excel; S113 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, prod SQL Server (CI) thì bắt — `truonggiang` vs `TRUONGGIANG` ra Inserted=0/Updated=1; nếu service dùng `Ordinal` thì prod nổ 500 cả lô. Thứ tự ưu-tiên dedup: existing-MST → existing-Code → batch-MST → batch-Code → New; MST thắng, không âm thầm ghi đè (FillNulls bỏ qua Code). Guard công-bố: `PublishSupplierCommand` (KHÔNG đi qua UpdateSupplier — #73), chặn Code rỗng, chặn trùng chính-xác.
- BÀI-HỌC: khuôn dựng header xlsx qua reflection từ `ExpectedHeaderTokens` ⇒ test tự đồng-bộ khi hằng đổi; ClosedXML in-memory gán `.Value = string` GIỮ NGUYÊN chữ (0.105 không bị ép về số ⇒ mã có số 0 đứng đầu an toàn).
- BẤT-NGỜ: S113 tìm ra BUG thật và KHÔNG sửa: `NewSupplier` `Code = r.Code!.Trim()` → NRE khi `row.Code == NULL` (đường R4 nháp: parser sinh null cho ô rỗng, preview phân loại New không báo lỗi ⇒ tới confirm mới nổ ⇒ 500 cả lô, dbCount=0), 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. D3 khai `ExpectedHeaderTokens` chỉ là PHỎNG ĐOÁN ≠ 30 token thật dòng 4 ⇒ Case 9 so hai chiều động, hiện `LayoutValid=false`, tự lật true khi hằng được sửa.
→ substring:"truonggiang"
**[vừa] TS-I — cụm bug-fix / spec-change / compile-fix arity** *(gộp 9 entry: D4 +8→458 · S97 +0 · S86 +3 · S85b +3 · S85 +5 · S77c rewrite 12→11 · S77 +7 · S76 +5 · S74 +5)*
- VIỆC: 9 đợt nhỏ quanh bug-fix + spec-change + đổi arity record command.
- KẾT-LUẬN: D4 đổi `ApplyDrafterBypassOnSubmitAsync` sang A1 — bypass CHỈ các cấp dưới thuần (Order < minOwn), con-trỏ DỪNG tại minOwn để người soạn TAY; 4 test ĐỔI TÊN tên gây hiểu nhầm. S97 = spec-change: đọc thẳng cờ `EndedByLevelFinalize` thay suy từ config, test thêm param + ca tái-hiện mới `peFinButWentCeo` (cùng config nhưng cờ FALSE). S86 chứng RED-trên-code-cũ bằng `git stash` rồi khôi phục nguyên trạng. S77c = đả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 MỘT PHẦN của việc test (S76 5 call-site + đổi assert `.ProEstimateAmount` `.ProInitialAmount`; S74 3 call-site thêm `null`) bỏ qua thì không phải "test đỏ" "không build được".
- BẤT-NGỜ: S86 phép loại-trừ theo actor KHÔNG che được bug (actor Cấp 1, người tạo Cấp 2) nên test mới thật sự răng; S85 nhánh `isDrafter` ưu tiên độc lập với `isProTp`, người vừa Drafter vừa Procurement-không-DM vẫn gỡ được CẢ 2 cờ; S77c ghi thẳng "test theo CODE" (không đụng prod).
substring:"ApplyDrafterBypassOnSubmitAsync"
**[thấp] TS-J 2 ghi-chú kỹ-thuật lẻ (S57bis / S56)** *(rút narrative — token sống ở phụ-lục)*
- VIỆC: giữ 2 quan-sát không gắn với đợt test nào.
- KẾT-LUẬN: S57bis `NotEmpty()` trên `Guid?` KHÔNG bắt `Guid.Empty` (FluentValidation 7.2 coi `default(Guid?)` == null) dựa FK-guard create-handler (phòng-thủ nhiều lớp, không phải bug prod). S56 `ExecuteUpdateAsync` đi vòng change tracker bản đọc đang tracked bị , phải `.AsNoTracking()` đọc lại; transaction đã ship `IsolationLevel.Serializable`.
- BÀI-HỌC: hai ca đều loại "API trông như kiểm không kiểm" validator im lặng lệnh update im lặng; phải biết trước mới viết được assert đúng.
- BẤT-NGỜ: cả hai sống sót qua mọi vòng curate không kèm test nào giá-trị nằm chỗ CHẶN một giả-định sai, không số lượng test.
substring:"ExecuteUpdateAsync"
## Phụ-lục token (coverage)
*(chép TRỌN token-list section test-specialist/baseline-history của proposal curator lưới coverage-diff)*
- **gotcha#:** `#48` (S134 lọc `ContextNote.Contains("[S134 pending-prior]")`) · `#73` (S87 "null=clear (pre-seed 500tr, gotcha #73)"; S113 "`PublishSupplierCommand` (KHÔNG UpdateSupplier #73)") · **các ⚠️/"Gotcha" không đánh số:** S117 "⚠ Gotcha (self-inflicted-avoided)" · S92 "**Gotcha:**" (test xanh prod tắt) · S56 "gotcha:" (`ExecuteUpdateAsync`)
- **surprise (nhãn nguyên-văn):** "Surprise: D4 no-SaveChanges-in-helper row chỉ persist sau TransitionAsync" (S134) · "⚠ Lesson mới: INV-C **ghi đè** `sub-test-specialist-0.md` của INV-A sub-file collision" (S133) · "**Bài học mới quan trọng nhất:** fixture `draft-ts` MALFORMED ra exit 0 **Fixture-hỏng cho-xanh trông Y HỆT máy-đúng cho-xanh**" (S153) · "đo 55-64ms/lượt vs ước 140-300ms lệch 2.2-5.4×" (S153) · "**1 FAIL = T26 GIỮ ĐỎ = BUG PROD**" (S155) · "🔴 1 BUG-FOUND (REPORTED em-main, NOT fixed)" (S113) · "**Spec-drift FLAGGED (không phải bug)**: `ExpectedHeaderTokens` = BEST-GUESS 30 real row-4 token" (D3) · "⚠**SEED-FK: PurchaseEvaluationQuote HARD FK PESupplierIdSupplier Restrict (cfg:135)**" (D2) · "⚠OBSERVATION: isSystem KHÔNG reachable qua public ApproveV2Async defensive/dead" (S72) · "⚠`ApprovalWorkflowId` FK Restrict PE seed PHẢI ref WF thật" (S85b) · "⚠**actor-exclusion KHÔNG che bug**" (S86) · "⚠Changelog.UserId Guid? no FK User random-Guid an toàn" (S87) · "⚠GUARD-FIRST: bypass-test PHẢI dựng PE đủ 4 ĐK" (S60) · "Bắt được lỗi spec: query THẬT = `GetPurchaseEvaluationQuery` spec ghi nhầm" (S133 INV-A) · "[superseded by S77c for urgent SET/UNSET asymmetry]" (S69b) · "(1 FAIL GIU DO = bug prod)" (tiêu-đề S155) · "**baseline 509 KHÔNG đổi**" (S153 xem mâu-thuẫn TS-A)
- **guard:** `SoftDeleteFixture` (fixture ĐẦU TIÊN wire `AuditingInterceptor`) · "2 sanity-assert chống vacuous (Phase==DaDuyet + liveRec đổi thật) TRƯỚC assert chính" (S133) · "+CONTRAST assert (chống vacuous-pass)" (S118) · "CONTROL/đối-chứng" S116 (`[+1.2tỷ]` alone) · "Forbidden red-able BY-CONTRAST (non-IT vs IT-staff identical-setup)" (S54) · submit-guard `:202-208` (S116) · publish-guard (S113) · "guard R2-revised" (S116) · "wire-bug-guard" (S117) · "fail-closed order (832-851)" (S72) · "role-gate 152-157 TRƯỚC side-effect" (S74) · "GUARD-FIRST" (S60) · `PurchaseEvaluationDraftGuard`-tương-đương: "T5 guard CCM+Admin Conflict + ChoDuyet-allow" (S133 `:638`) · "authz-capability CanReassign flag + Forbidden/Conflict guard + empty-staff 0-leak + no-mutation" (S54) · "Pattern 10 reflection static `[Authorize]`" (S118b) · "T4 `(attr?.Policy).Should().BeNull()`" (S118b rào NGƯỢC: cấm thêm policy) · "invariant T6"-tương-đương: "Pe-module-NOT-revoked invariant" (S92) · "REPORT lead, KHÔNG tự sửa" (S155, S153) · "repo read-only mọi mutation trong cây tạm qua seam `--root`" (S153) · "No prod touch verify qua stash, restore exact" (S86) · "KHÔNG touch prod" (S113/S116/S117/S118/S118b)
- **root-cause:** S155 "`Remove(pe)` (:142) `CascadeDeleteTiming.Immediate` cascade **ngay tại Remove**; dependent đang `Added` bị cascade **Detached** 0 row; `AuditingInterceptor` chạy SavingChanges = SAU điểm mất" S153 "`inject_A` neo `## MIND-1` sau khi `renum(...,2)` đã đổi heading `str.find`=-1 chèn GIỮA heading `ts:` không đầu dòng" S113 "`NewSupplier` `Code=r.Code!.Trim()` NRE khi `row.Code==NULL`" D3/S113 "dedup SERVICE OrdinalIgnoreCase, KHÔNG dựa DB SQLite fixture BINARY collation KHÔNG bắt CI-dup, prod SQL Server CI thì " S117 "optional param KHÔNG auto-bind qua `MethodInfo.Invoke`" S92 "test invoke method-CÔ-LẬP qua reflection KHÔNG chạy SeedAsync chain"
- **file:line:** `PeSoftDeleteFeatures.cs:126` + `:142` · `PeBudgetFreezeTests.cs:184` · `:426` `:480` `:524` `:555` `:638` `:687` `:730` `:783` `:821` + seed `:277-336` · `CreateContractFromEvaluationFeatures.cs:196` · `PurchaseEvaluationFeatures.cs:285` (UpdateDraft) + `:849-864` (FullAmount) · `PurchaseEvaluationWorkflowService.LogTransitionAsync (~:1083-1135)` · submit-guard `:202-208` · `ApproveV2Async 816-854` (strict-`<` @838) + `832-851` · role-gate `152-157` · Quote FK `cfg:135` · call-site `PeWorkItemBudgetTests 388/407/421` · `PurchaseEvaluationUrgentFeatures.cs:49-55` · `Api/AuthorizePolicyRegressionTests.cs` (1519 [Fact]) · csproj `line 25` · `scripts/session_ctx.py mind-check`
- **tên file test (định-danh):** `PeDeleteByApproverTests.cs` · `PePendingAccumulationTests.cs` · `PeBudgetFreezeTests.cs` · `SupplierImportV2DedupTests.cs` · `SupplierPublishAndListTests.cs` · `Services/SupplierExcelImportServiceTests.cs` · `ProcurementMasterAccessSeedTests.cs` · `AdminOnlyModulesRevokeTests.cs` · `OfficeModulePermissionSeedTests.cs` · `HrmProfilePermissionSeedTests.cs` · `PeSubmitGuardAndBypassTests.cs` · `PeCcmThresholdFinalizeTests.cs` · `PeApprovedPriceFinalizeTests.cs` · `PeSuggestedPriceSetterAuthzTests.cs` · `PeUrgentToggleAuthzTests.cs` · `PeCcmBudgetPeriodSetterAuthzTests.cs` · `PeApproverNotifyExcludesDrafterTests.cs` · `PeUpdateDraftWorkflowPreserveTests.cs` · `PeMultiWinnerTests` · `PeWorkItemBudgetTests` · `PeFinalizeProjectionTests` · `PeApproverFinalizeTests` · `CreateContractFromEvaluationMultiWinnerTests.cs` · `PeListWinnerNamesProjectionTests.cs` · `ItTicketReassignAuthzTests.cs` · `SqliteDbFixture` / `TestApplicationDbContext` / `IdentityFixture` / `CapturingNotificationService`
- **định-danh run/engine:** `run 2026-07-27-S155-pe-delete-approver · sub-d2-test-4` (diary 13.9KB) · `run 2026-07-26-S153-ctx-adopt` `[engine: opus-5]` · `wf_97d6a582-b7e` `[engine: opus-worker]` (S134) · `wf_75c6bfd3-754` (S133 INV-C, **#53 return-fail lần 3**) · `wf_d9639faa-97a` (S133 INV-A, return sạch) · Mig 54/55/56/57/58/59/60/61/65/66/67/68 được dẫn rải rác
- **nguồn-đếm (ghi-chú drafter @S170):** đĩa đo 30 heading `##` = 1 dòng "Count progression" + 29 entry-heading (S155 S54, gồm cả S60 heading gộp "S57bis / S56 (notes kept)"); proposal curator khai "25 entry". Entry S60 KHÔNG thuộc cụm narrative nào của curator sống mốc mở chuỗi "254 (S60)" (TS-A) + token GUARD-FIRST phụ-lục này. Chi tiết `_lienfile-notes-batchB.md` §bổ-sung.
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `#6` **S97 (2026-07-01) PE EndsBeforeCeo CONFIG→RUNTIME (bug anh Kiệt FDC, spec-change test) — 440 PASS no-red, NET +0 [Fact] (extend in-place):** em
- `Mig 55` ## S74 PE Mig 55 CcmNote (compile-fix arity + test-after), +5
- `Mig 56` ## S76 PE Mig 56 PRO-column-split (spec-change + compile-fix arity), +5
- `Mig 57` ## S77 PE Mig 57 suggested-price NOTE (test-after, mirror CcmNote), +7
- `Mig 58` ## D2 PE Mig 58 multi-winner (test-after + EF translation-smoke), +6 419
- `Mig 59` ## S87 PE Mig 59 CCM-budget-period setter (test-after authz), +7
- `Mig 60` **S97 (2026-07-01) PE EndsBeforeCeo CONFIG→RUNTIME (bug anh Kiệt FDC, spec-change test) — 440 PASS no-red, NET +0 [Fact] (extend in-place):** em
- `PurchaseEvaluationFeatures.cs:849` `UpdatePeBudgetProCommand` record 34 param `(PeId, ProInitialAmount, ProAdjustmentAmount, ProNote)`; handler set ProInitial+ProAdjust absolute-set

View File

@ -33,7 +33,7 @@ pointer-style: substring
- **root-cause:** "DemoSeed feature flag gates Plan B Chunk A2 sample V2 workflow seed" ⇒ `QT-HD-V2-001 NOT seeded in prod (0 rows ApplicableType=3)` ⇒ V2 endpoint trả `{"active":null,"history":[]}` ⇒ dropdown Create rỗng
- **file:line / định-danh:** log `2026-05-22 12:57:44 [INF] DemoSeed:Disabled=true → skip workflow + contracts + PE + sample V2 seed (Plan T S23 t10 + Plan B Chunk A2 Contract V2)` · `SeedSampleContractWorkflowV2` · `ApproveV2Async` (~150 LOC) · Run #231 id=345 · Run #232 sha=`38f1c4d` · push range `6eec8d7..3e92584` (11 commit) · Mig 32 `20260522051059_AddApprovalWorkflowToContract` · Mig 33 `20260522052240_AddContractLevelOpinions` · cột `ApprovalWorkflowId` + `CurrentApprovalLevelOrder` · bảng `ContractLevelOpinions` (0 row) · bundle `leEMWFLU→BBADl46y` (admin) / `Dgn1iU9E→DA_VI3zO` (user) · `QT-HD-V2-001` (ApplicableType=3) / `QT-DN-V2-001` v1+v2 (ApplicableType=1) · CI 3m30s 12:54:24→12:57:54 · `SlaExpiryJob` 5 ERR @12:58:16 · 4 transition `DangInKy→DangKiemTraCCM` / `DangSoanThao→DangGopY` / `DangGopY→DangDamPhan` / `DangTrinhKy→DongDau` · archive-header: S32 curate, 27KB→~24KB, ngưỡng 25KB
### Bổ-sung coverage @P3-gate S170 (token literal — nội-dung đã sống dạng dải/range ở trên; dòng dưới chép NGUYÊN-VĂN context verbatim làm lưới grep)
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `#215` — > **KEEP in MEMORY:** S32 startup, S29 wrap summary (Run #229-#232 cumulative aggregate including #231 + #232 hotfix DemoSeed gate catch), S28 Layer A…
- `#216` — > **KEEP in MEMORY:** S32 startup, S29 wrap summary (Run #229-#232 cumulative aggregate including #231 + #232 hotfix DemoSeed gate catch), S28 Layer A…

View File

@ -62,7 +62,7 @@ pointer-style: substring
· **S32 startup:** MEMORY **24.9KB / 221 lines** · `search_memory` query "Run 232 Plan B Hotfix CICD SeedSampleContractWorkflowV2" rerank **0.906** / 0.828 / 0.816 · RAG **2949 chunk** · last push `f938bf5` (S31 docs patch `cicd-monitor.md`) · ~10K token
· **S26:** Run #222 `0bf6c7e` ~12K · bundle `C8TvDy7r→CWHIdoFo` + `BvcWrq2z→Bg2FNeIz` · smoke 5/5 200 · PE List API **9 field** · test-gate **111** · Mig 31 unchanged · Run #223-#227 = Plan AG2-AG6 · AG4 DTO +4 field `DrafterUserId`/`DrafterName`/`DepartmentId`/`DepartmentName` + 3 projection JOIN `Users`+`Departments` · `dotnet test` 111/111 local pre-push · `~150K × N wasteful`
### Bổ-sung coverage @P3-gate S170 (token literal — nội-dung đã sống dạng dải/range ở trên; dòng dưới chép NGUYÊN-VĂN context verbatim làm lưới grep)
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `Mig 29`**Mig prod TOP 5 DESC** sqlcmd Windows-auth via `ssh vietreport-vps "powershell ... '.\\SQLEXPRESS' -E"` pattern (UPDATED from Discovery #5): `AddCont…
- `Mig 30`**Mig prod TOP 5 DESC** sqlcmd Windows-auth via `ssh vietreport-vps "powershell ... '.\\SQLEXPRESS' -E"` pattern (UPDATED from Discovery #5): `AddCont…

View File

@ -53,10 +53,10 @@ pointer-style: substring
**[cao] D7 — MEMORY phình 35→72KB: 8 lần cảnh-báo leo thang mà không ai dừng**
- **VIỆC:** vai tự theo-dõi kích-thước diary của chính mình sau mỗi entry.
- **KẾT-LUẬN:** chuỗi tự khai: ~35KB ("approaching") → ~40 → ~43 ("over 25KB trigger, under 50KB hard") → ~50 ("at hard threshold") → ~58 ("**REQUIRED** ARCHIVE ... BEFORE next entry") → ~60 ("**MANDATORY**") → ~62 ("**DEDICATED CURATION SESSION PRIORITY NEXT**") → ~66→69 ("priority MAX") → ~72 ("**PRIORITY MAX REINFORCED**"). Chốt chỉ xảy ra khi có **phiên curate riêng** (chính file archive này), kéo về mục-tiêu ~25-30KB.
- **KẾT-LUẬN:** chuỗi tự khai: ~35KB ("approaching") → ~40 → ~43 ("over 25KB trigger, under 50KB hard") → ~50 ("at hard threshold") → ~58 ("**REQUIRED** ARCHIVE ... BEFORE next entry") → ~60 ("**MANDATORY**") → ~62 ("**DEDICATED CURATION SESSION PRIORITY NEXT**") → ~66→69 ("priority MAX") → ~72 ("**priority MAX REINFORCED**" — chữ nguồn viết `priority` thường). Chốt chỉ xảy ra khi có **phiên curate riêng** (chính file archive này), kéo về mục-tiêu ~25-30KB.
- **BÀI-HỌC:** cảnh-báo do **chính kẻ đang phình** phát ra thì không tự dừng được — cần **cổng ngoài** (ngưỡng cứng chặn ghi) hoặc **việc riêng có chủ**; và ngưỡng "mềm 25KB / cứng 50KB" bị vượt **gần 3 lần** vẫn chỉ sinh thêm chữ in-đậm.
- **BẤT-NGỜ:** mức độ khẩn-cấp trong câu chữ leo thang **8 nấc** (approaching → REQUIRED → MANDATORY → PRIORITY MAX → REINFORCED) — bản thân nó là dữ-liệu: leo-thang ngôn-từ **không** thay cho cơ-chế.
→ substring:"PRIORITY MAX REINFORCED"
- **BẤT-NGỜ:** mức độ khẩn-cấp trong câu chữ leo thang **8 nấc** (approaching → REQUIRED → MANDATORY → priority MAX → REINFORCED) — bản thân nó là dữ-liệu: leo-thang ngôn-từ **không** thay cho cơ-chế.
→ substring:"priority MAX REINFORCED"
**[cao] D10 — Tự khai tác-dụng-phụ và giới-hạn phép đo**
- **VIỆC:** ghi lại thứ phép kiểm-chứng làm hỏng + thứ nó KHÔNG chứng được.
@ -81,16 +81,16 @@ pointer-style: substring
**[cao] D9 "Key patterns extracted S21-S25" (giữ gần như nguyên)**
- **VIỆC:** phần (c) đã bản chưng-cất 8 mục do chính vai viết.
- **KẾT-LUẬN:** 8 mục = Discovery #3 · Discovery #4 · Discovery #5 · **checklist 10 điểm per-NV** (liệt-kê đủ 10) · gotcha #48 + cách · "CI test-gate bắt được lỗi UAT-skip-test bỏ sót" · FE merge synthetic + userMap · capture pre-call mutation state.
- **BÀI-HỌC:** khi nguồn đã lớp chưng-cất, việc nén nên **giữ lớp đó gần như nguyên văn** cắt lớp Run bên dưới nén lại cái đã nén chỗ dễ mất mã-số (#48, điểm 9/10) nhất.
- **BẤT-NGỜ:** checklist 10 điểm đây **bản duy nhất liệt-kê đủ 10** trong toàn batch A mọi chỗ khác chỉ nhắc số "8"/"9"/"10 surface points" không kèm nội-dung.
- **KẾT-LUẬN:** 8 mục = Discovery #3 · Discovery #4 · Discovery #5 · **checklist 10 điểm per-NV** (chép ĐỦ 10, nguyên-văn rút gọn P2a-MAJOR-2 @S170): **(1)** Domain entity field · **(2)** EF config `HasDefaultValue(false)` · **(3)** Migration 3-file rule (Up + Designer + Snapshot) · **(4)** Service handler read field · **(5)** Domain DTO + Application DTO mirror · **(6)** Designer FE checkbox inline · **(7)** Admin overview `AwLevelDto` record + `ToDto` ctor · **(8)** `CreateAwLevelInput` record + Update mutation handler · **(9)** Lookup discrimination in handler (`FirstOrDefault` ADD `ApproverUserId == actorId` filter, fallback admin) · **(10)** Controller body record mirror count check (`[FromBody]` record param count = Command record param count) kèm gotcha #48 + cách · "CI test-gate bắt được lỗi UAT-skip-test bỏ sót" · FE merge synthetic + userMap · capture pre-call mutation state.
- **BÀI-HỌC:** khi nguồn đã lớp chưng-cất, việc nén nên **giữ lớp đó gần như nguyên văn** cắt lớp Run bên dưới nén lại cái đã nén chỗ dễ mất mã-số (#48, điểm 9/10) nhất. *(Chính draft này lượt đầu đã dính đúng bẫy đó: tuyên "bản duy nhất đủ 10" rồi chỉ giữ ~6 — reviewer P2a bắt, vá bằng chép đủ 10 ở trên.)*
- **BẤT-NGỜ:** checklist 10 điểm verbatim **bản duy nhất liệt-kê đủ 10** trong toàn batch A mọi chỗ khác chỉ nhắc số "8"/"9"/"10 surface points" không kèm nội-dung (đủ 10 mục gốc: verbatim vùng "Key patterns extracted" mục 4).
substring:"Key patterns extracted"
## Phụ-lục token (coverage)
- **gotcha#:** `#41` (path filter dẫn 6 lần) · `#44` (silent 403 "fix confirmed" #210) · `#45` (fix Trả lại, Run #186) · `#47` (agent-memory **disproven**, giữ vai PREVENTIVE) · `#48` (**NEW** multi-`Changelog.Add` + SQLite frozen-clock tie-break)
- **Discovery#:** `#1` rate-limit 429 ~5 req/phút · `#2` `.claude/agent-memory/**` không nằm trong paths-ignore (**về sau bị bác**) · `#3` Gitea xét TRỌN push-range (củng-cố 4 lần: #200/#201/#202 + #210 "NOT applicable") · `#4` ASP.NET Core 10 record+enum cần **số** · `#5` sqlcmd cần `\\\\SQLEXPRESS` **4 backslash** · *(không đánh số)* `/actions/tasks` `/actions/runs` (#186) · `updated_at` trễ ~2 phút (#187) · "Discovery 1" 503 giữa deploy + "Discovery 2" `/api/auth/login` + field `accessToken` (#221)
- **surprise:** `CRITICAL FAIL — K3 DTO mirror INCOMPLETE` · "`AllowApproverSkipToFinal` mentioned **only in comment line 60**, NOT as actual record param" · "**em main + Reviewer + Implementer all missed across 3 plan**" · "Bug present **2 days prod**" / "**3-day prod bug latency**" · "Spec hypothesis ... **disproven for this commit**" · "**Anomaly is BENEFICIAL** catches verify gate even on docs-only tip" · "PROVES ACTOR VALIDATION SUCCESS ... 409 mode mismatch NOT 403" · "First attempt with **string enum** `\"Drafter\"` HTTP 400 ... Brief example payload was **misleading**" · "Test 2 HTTP 409 = handler threw exception **no state change (atomic)**" · "NV Test actor variant **not feasible** (scanned 20 PEs)" · "the Budget Adjust entry ... has `userName=\"\"` empty this is the **EXACT pre-fix Bug 1 evidence**" · "**Side effect logged:** Admin transition on PE/2026/A/025 mutated phase 1098" · "Run #190 ... **CANCELLED** by concurrency ... normal not a fail" · "`/api/me` + `/api/menu-keys` returned 404 (route names different) **Not regression**" · "Memory size ... **PRIORITY MAX REINFORCED**" · "8 surface points checklist (**NOT 6**)" · "prophylactic codebase scan is needed"
- **Discovery#:** `#1` rate-limit 429 ~5 req/phút · `#2` `.claude/agent-memory/**` không nằm trong paths-ignore (**về sau bị bác**) · `#3` Gitea xét TRỌN push-range (số lần củng-cố: NGUỒN TỰ MÂU-THUẪN tiêu-đề verbatim ghi "RE-CONFIRMED 4th time" nhưng thân ngay dưới ghi #210 "does NOT reinforce" 3 lần củng-cố THẬT #200/#201/#202 + 1 lần #210 không-tính; thân-draft dùng số 3 theo vế thân) · `#4` ASP.NET Core 10 record+enum cần **số** · `#5` sqlcmd cần `\\\\SQLEXPRESS` **4 backslash** · *(không đánh số)* `/actions/tasks` `/actions/runs` (#186) · `updated_at` trễ ~2 phút (#187) · "Discovery 1" 503 giữa deploy + "Discovery 2" `/api/auth/login` + field `accessToken` (#221)
- **surprise:** `CRITICAL FAIL — K3 DTO mirror INCOMPLETE` · "`AllowApproverSkipToFinal` mentioned **only in comment line 60**, NOT as actual record param" · "**em main + Reviewer + Implementer all missed across 3 plan**" · "Bug present **2 days prod**" / "**3-day prod bug latency**" · "Spec hypothesis ... **disproven for this commit**" · "**Anomaly is BENEFICIAL** catches verify gate even on docs-only tip" · "PROVES ACTOR VALIDATION SUCCESS ... 409 mode mismatch NOT 403" · "First attempt with **string enum** `\"Drafter\"` HTTP 400 ... Brief example payload was **misleading**" · "Test 2 HTTP 409 = handler threw exception **no state change (atomic)**" · "NV Test actor variant **not feasible** (scanned 20 PEs)" *(trích cắt — nguồn còn "— NV Test = Drafter not approver")* · "the Budget Adjust entry ... has `userName=\"\"` empty this is the **EXACT pre-fix Bug 1 evidence**" · "**Side effect logged:** Admin transition on PE/2026/A/025 mutated phase 1098" · "Run #190 ... **CANCELLED** by concurrency ... normal not a fail" · "`/api/me` + `/api/menu-keys` returned 404 (route names different) **Not regression**" · "Memory size ... **priority MAX REINFORCED**" · "8 surface points checklist (**NOT 6**)" · "prophylactic codebase scan is needed"
- **guard:** `if (isAdmin) return` (**line 186**, admin bypass CỐ Ý) · `if (!isAdmin && currentLevel is not null)` (**line 264**) · `&& l.ApproverUserId == actorId` + `throw if null` (4 site Plan O) · fallback `?? curStep?.Levels.FirstOrDefault(...)` cho admin/non-approver (Plan N) · `[Authorize]` class-level + `GetWorkflowsForUserAsync` auth filter (fix #44) · `if (!demoSeedDisabled)` bọc 5 seed demo (Plan T) · `.Where(c => c.Summary!.Contains("Chuyển phase"))` ( #48) · `HasDefaultValue(false)` (điểm 2 checklist) · **Migration 3-file rule** (Up + Designer + Snapshot điểm 3) · `[FromBody]` record param count = Command param count (điểm 10) · seed Order shift **idempotent** (1-10 liền mạch) · token-cache `%TEMP%\*_token.txt` chống 429 · test-gate CI chặn trước deploy
- **root-cause:** `FirstOrDefault(l => l.Order == curLevelOrder)` **không lọc actor** luôn lấy `Levels[0]` theo thứ-tự DB cờ per-NV admin tick bị bỏ qua (4 site cùng bệnh) · Controller `TransitionPeBody` 3 field Command 7 field ASP.NET `default` **im-lặng** 3 field F1/F2 không tới handler suốt 2 ngày · `AwLevelDto` thiếu param + `ToDto` thiếu ctor-arg cột DB API không trả · nhiều `Changelog.Add()` cùng `SaveChangesAsync` + SQLite frozen-clock `OrderByDescending(CreatedAt)` tie-break bất-định · Gitea xét paths-ignore trên trọn push-range tip docs-only vẫn chạy · thiếu `JsonStringEnumConverter` enum dạng chuỗi fail 400 · `\\SQLEXPRESS` (2 backslash) **0 output, im-lặng** · `UserName` không set 9 site `Changelog.Add()` cột người-dùng rỗng
- **file:line / định-danh (BE):** `ApprovalWorkflowV2AdminFeatures.cs` **:23-38** (`AwLevelDto` record), **comment :60**, `ToDto` **:156-158**, vùng **:184-238** · `PurchaseEvaluationFeatures.cs` **:765** (per-NV lookup), **:314-315** (AdjustBudget) · `PurchaseEvaluationWorkflowService.cs` **:204** (`EnsureCanRejectV2Async`), **:258-260** (`ApplyReturnModeAsync`), **:264** (guard), **:186** (admin bypass), **:75-103** (nhánh Reject Plan AC), **:472** (prefix `[Duyệt vượt cấp tới Cấp cuối]`), **:287-333** (F1 edge Bước 1), **:268-275** (F1.Drafter giữ TraLai) · `PurchaseEvaluationDetailFeatures.cs` **:75-76** (`EnsureEditableForDetailsAsync`) · Controller `TransitionPeBody` **:267** (3 field) **:280-286** (7 param), `mediator.Send` **:76-78**, marker Plan P **:71-75** · `TransitionPurchaseEvaluationCommand` **:395** · `Program.cs` (0 hit `JsonStringEnumConverter`/`AddJsonOptions`) · `DbInitializer.cs` · `MenuKeys.cs` · `appsettings.json` / `appsettings.Development.json` / `appsettings.Production.json` (**bị `.gitignore`**)
@ -100,7 +100,7 @@ pointer-style: substring
- **🔐 secret (đã xử-lý):** dòng **48** verbatim `appsettings.Production.json` tại `C:\inetpub\solution-erp\api\`, `user=vrapp`, password prod **ĐÃ ĐƯỢC LEAD REDACT** thành nhãn `pwd=[đã che 71b7740a — ROTATE REQUIRED]` gist chép NHÃN CHE này, TUYỆT ĐỐI không tra/chép giá-trị gốc từ git history; **ROTATE REQUIRED** vẫn treo (mật-khẩu từng nằm trong lịch-sử git)
- **meta / size-trend:** `~35KB → ~40 → ~43 → ~50 → ~58 → ~60 → ~62 → ~66 → ~69 → ~72KB` · ngưỡng mềm **25KB** / cứng **50KB** · FIFO `~8 → ~9 → ~10 → ~11 → ~12 → ~13 entry` · token-cost mỗi lượt `~10k-28k` · `feedback_md_compact_narrative.md` 6.5) · `feedback_uat_skip_verify` · `feedback_per_nv_permission_scope.md`
### Bổ-sung coverage @P3-gate S170 (token literal — nội-dung đã sống dạng dải/range ở trên; dòng dưới chép NGUYÊN-VĂN context verbatim làm lưới grep)
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `#216` > **Target slim MEMORY.md after archive:** ~25-30KB (~5 entries kept: Run #215 fail lesson + Run #216 fix + Run #222-#227 S26 + setup baseline).…
- `#222` — > **Target slim MEMORY.md after archive:** ~25-30KB (~5 entries kept: Run #215 fail lesson + Run #216 fix + Run #222-#227 S26 + setup baseline).…
@ -126,7 +126,7 @@ pointer-style: substring
- `Run #208` — ### 2026-05-15 ~14:30 — Run #208 sha=`7b7b28f` VERDICT=PASS…
- `Run #209` — ### 2026-05-15 ~14:50 — Run #209 sha=`86d8806` VERDICT=PASS…
- `Run #210` — ### 2026-05-15 15:25 — added S24 t1 Plan AA Run #210 entry…
- `Run #214`**FAIL** verdict at test_infra gate — 2 Plan M edge case tests broken `ApplyReturnMode_OneStep_AtStep1` line 350 + `ApplyReturnMode_OneLevel_AtStep1…
- `Run #214`id=328 sha=`ee0902a` **VERDICT=PASS** (bundle baseline admin `CZdXQ2eo` + user `DCwhhey2`, Mig 31 TOP 1 unchanged, smoke 5/5 200) — *dòng này @P3-mức-3 thay đoạn trích đầu-dòng cũ vốn dính thân entry Run #215 FAIL ⇒ gán verdict ngược (reviewer P3-B1)*
- `Run #215` — > **Target slim MEMORY.md after archive:** ~25-30KB (~5 entries kept: Run #215 fail lesson + Run #216 fix + Run #222-#227 S26 + setup baseline).…
- `Run #216` — > **Target slim MEMORY.md after archive:** ~25-30KB (~5 entries kept: Run #215 fail lesson + Run #216 fix + Run #222-#227 S26 + setup baseline).…
- `Run #218` — ### 2026-05-19 11:00-11:03 — Run #218 id=332 sha=`25837b6` VERDICT=PASS…

View File

@ -58,7 +58,7 @@ pointer-style: substring
- **file:line / định-danh (test + FE):** `PurchaseEvaluationWorkflowServiceReturnModeTests.cs` **:253** (vỡ, để cho K7), **:241** (chèn 2 `[Fact]` mới) · helper `SeedWorkflowAsync` / `SeedApproversAsync` / **`SeedApproverF2WorkflowAsync`** (2 Bước × 2 Cấp) / `BuildPeAtLevel2` · test xoá: `SkipToFinal_DrafterAllowed_SetsPointerToFinalLevel` · `SkipToFinal_DrafterDenied_NonAdmin_Throws` · `SkipToFinal_AdminBypass_Succeeds` · test thêm: `ApproveV2_SkipToFinal_AdminTickFlag_SetsPhaseDaDuyet` · `ApproveV2_SkipToFinal_FlagOff_NonAdmin_ThrowsConflictException` · `ApproveV2_SkipToFinal_FlagOff_Admin_BypassesFlagCheck` · `ApplyReturnMode_OneLevel_AtStep1Level1_ResetsToBuoc1Cap1_KeepsChoDuyet` · `ApplyReturnMode_OneStep_AtStep1_ResetsToBuoc1Cap1_KeepsChoDuyet` · `ApprovalWorkflowsV2Page.tsx` **:873**, **~:500**, **:535**, banner **~:623**, stale **:75** + **:504** (hoãn K6) · `UsersPage.tsx` + `types/users.ts` · `types/purchaseEvaluation.ts` (`PurchaseEvaluationPhaseLabel[98]`, `PeDisplayStatusLabel.TraLai`) · `PeWorkflowPanel.tsx` · `fe-user/src/types/approvalWorkflowV2.ts` (~55 LOC, 5 record type `AwLevelDto`/`AwStepDto`/`AwDefinitionDto`/`AwTypeSummaryDto`/`AwAdminOverviewDto`) · `fe-user/src/pages/pe/WorkflowMatrixViewPage.tsx` (~215 LOC) · `fe-user/src/App.tsx` route `/purchase-evaluations/workflow-matrix` · `seed-test-users-prod.ps1` `$authResp.accessToken` · `scripts/plan-r-*.sql` + `plan-s-*.sql` + `plan-t5-*.sql` + `plan-t-backup.sql`
- **số-đo:** test **84 → 104 → 106** · mig **29 → 30 → 31** · endpoint ~**146** (+3) · user prod **33** (13 + 20 mới) · diff K5 **+42/-94** (9 file) · K7 **+175/-92** (1 file) · K3 **+26/-7** · M2 **+94** · M3 **+4/-4 ×2 = 8 LOC** · bundle fe-admin **1395 / 1395.74 KB** · fe-user **1275 / 1282 KB** (1907 module, 2.61s) · commit S24 **7** (`a1a910f..ee0902a`, 1 spawn + 6 solo) · token/chunk ~5k-14k · MEMORY nguồn **38.8 KB** · `~720 rows wiped`
### Bổ-sung coverage @P3-gate S170 (token literal — nội-dung đã sống dạng dải/range ở trên; dòng dưới chép NGUYÊN-VĂN context verbatim làm lưới grep)
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `#1` Cleanup zombie F2 endpoint + UsersPage column + DTO field + stale narrative comments (Reviewer Major #1 + Major #2 + Minor #3 + Minor #4). Pattern pos
- `Mig 25` FE user read-only matrix view workflow V2 ghim (Mig 25 IsUserSelectable). 3 file: (1) CREATE `fe-user/src/types/approvalWorkflowV2.ts` (~55 LOC) s

View File

@ -48,6 +48,6 @@ pointer-style: substring
- **file:line / định-danh:** `types/employee.ts` (**NEW ~283 LOC**, SHA256 `CCFC70666568`) · `pages/hrm/EmployeesListPage.tsx` (**~417 LOC**, `DC859C897C5C`) · `pages/hrm/EmployeeCreatePage.tsx` (**~178 LOC**, `C796F25D01AC`) · `menuKeys.ts` (+3 LOC `Hrm`+`HrmHoSo`) · `Layout.tsx` (+4 LOC staticMap **`Hrm_HoSo`**) · `App.tsx` (+5 LOC) · 6 file fe-user mirror · `HrmConfigFeatures.cs` **372 LOC** + Controller **134 LOC** (**16 endpoint**) · Mig 33 `ContractLevelOpinions` · Mig 34 (em-main giữ) · `migration-todos` (11 module / 10 sprint) · `PurchaseEvaluationWorkflowServiceReturnModeTests.cs``ContractWorkflowServiceApproveV2Tests.cs` (7 kịch-bản, spec D-Bis) · `mcp__rag-unified__search_memory` + `mcp__rag-unified__cross_project_search` · "Pattern curate trigger rule **line 364**" · agent id `ab23cc322b5d495c0`
- **số-đo:** 14 file (S33 Task 5) · build fe-admin **21.4s / 1429KB** · fe-user **9.2s / 1345KB** · `dotnet build` **1.59s 0 warn 0 err** · `dotnet test` **120/120** (58 + 62) · test mốc S35 **130** · MEMORY **36.2KB** (`Length=36207`) → curate **27.5KB** · rerank **0.824 / 0.801 / 0.793** (ngưỡng 0.7) · token ~25k (Task 5) / ~5K (wrap) / ~5k (startup) · tag `[mirror-page, phase-10.1, frontend]` · `[s35, be-crud, hrm, 12-bis-3x]` · `[s29, plan-b, 12-bis]` · `[wrap, phase-9-to-phase-10, frontend+backend]` · `[verify, phase-9, infra]`
### Bổ-sung coverage @P3-gate S170 (token literal — nội-dung đã sống dạng dải/range ở trên; dòng dưới chép NGUYÊN-VĂN context verbatim làm lưới grep)
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `Mig 22` — (d) **Plan G-O3..G-O6 Workflow Apps** — cookie-cutter mirror PE Mig 22-26 Plan B pattern 12-bis cross-module entity scaffold cho 4 module (Proposal/…

View File

@ -25,7 +25,7 @@ pointer-style: substring
- 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: `setState` gọi TRONG LÚC render khiến React huỷ render đó render lại với state mới TRƯỚC khi commit; harness bản 1 chạy 1 lượt nên đọc ra `phase=3` (giá-trị của render đã bị vứt) control-âm cũng ra 3 hai nhánh giống hệt phép đo mất khả-năng phân-biệt, trông y như PASS. = lặp tới khi state đứng yên A ra `phase=2`, control-âm giữ `3`. Giải bài chính: override 3 trạng-thái `chipPhase: T|null|undefined` (`undefined` = "nghe URL" `null` = "user chọn Tất cả") + reset TRONG render theo `navKey`, KHÔNG dùng `useEffect` (effect vẽ một lượt dữ-liệu rồi mới sửa = nháy).
- BÀI-HỌC: mô-phỏng hành-vi framework phải mô-phỏng CẢ vòng đời, không chỉ một lượt gọi hàm. Tiền-đề đề-bài sai lần 3 liên-tiếp: làm đúng nguyên văn ("derive `phase` từ searchParams thay `useState`") sẽ giết 6 chip lọc (`KpiCard onClick setPhase :153`) hỏng CÂM, build vẫn xanh.
- BẤT-NGỜ: BE `/deleted` thiếu tham số `group` (`Controller:61-66` so `List:42`) không lọc được client (server đếm `total` trên tập chưa lọc số trang nói dối) xử bằng 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ờ lane BE; staticMap lệch CHỦ ĐÍCH (fe-user 6/6 mang `group=1`, fe-admin 5/6 WfView admin trỏ Designer).
- BẤT-NGỜ: BE `/deleted` thiếu tham số `group` (`Controller:61-66` so `List:42`) không lọc được client (server đếm `total` trên tập chưa lọc số trang nói dối) xử bằng 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ờ lane BE; staticMap lệch CHỦ ĐÍCH (fe-user 6/6 mang `group=1`, fe-admin 5/6 WfView admin trỏ Designer). Bẫy K4b thứ hai *(bổ @S170 P3-gate)*: CreatePage 8 leaf chung MỘT route nên leafleaf KHÔNG remount preset phải so `lastPresetGroup` chứ KHÔNG so `approvalGroup` so nhầm thì người dùng bấm đổi nhóm bị kéo ngược ô chọn thành nút chết. K4b bàn giao lead 4 FLAG.
substring:"harness-do-nham-render-bi-vut"
**[cao] FE-C S166 K4a: hai tiền-đề lead sai trong CÙNG một đề bài** `[s166, khkk-k4a-regex-42-leaf, tien-de-lead-sai-x2]`

View File

@ -21,7 +21,7 @@ pointer-style: substring
**[cao] B1-B — Plan P: Controller record nuốt 3 field, ASP.NET im lặng**
- VIỆC: em-main giả-thiết Plan P chỉ đụng BE; investigator đối-chiếu FE × 2 app xác nhận trước khi sửa.
- KẾT-LUẬN: FE `PeWorkflowPanel.tsx:113-124` gửi ĐỦ 6 field (TargetPhase + Decision + Comment + ReturnMode + ReturnTargetUserId + SkipToFinal, object literal không typed-service); BE `PurchaseEvaluationsController.cs:267` record `TransitionPeBody` chỉ 3 field ⇒ model-binder BỎ IM LẶNG 3 field còn lại. Verdict: Plan P = BE Controller ONLY ~6 LOC, không cần test mới (Mig 28/31 Domain test đã phủ handler).
- KẾT-LUẬN: FE `PeWorkflowPanel.tsx:113-124` — nguồn ghi "SEND ĐÚNG **7 fields**" nhưng LIỆT KÊ 6 (TargetPhase + Decision + Comment + ReturnMode + ReturnTargetUserId + SkipToFinal, object literal không typed-service)*lấy 6 theo danh-sách; số "7" của nguồn chưa xác-minh, tự nó là bằng-chứng sống cho bài "đếm field body so đếm field command là phép thử rẻ" (chính người viết verbatim cũng đếm nhầm — nhãn @S170 P2d, KHÔNG lặng-lẽ chọn một phía)*; BE `PurchaseEvaluationsController.cs:267` record `TransitionPeBody` chỉ 3 field ⇒ model-binder BỎ IM LẶNG 3 field còn lại. Verdict: Plan P = BE Controller ONLY ~6 LOC, không cần test mới (Mig 28/31 Domain test đã phủ handler).
- BÀI-HỌC: "FE gửi rồi" và "BE nhận được" là hai mệnh-đề khác nhau — điểm gãy ở ranh giới kiểu dữ-liệu (record thiếu field = mất dữ-liệu không lỗi, không log). Đếm field body ↔ đếm field command là phép thử rẻ.
- BẤT-NGỜ: một lượt audit FE thuần chặn được em-main sửa mù xuyên tầng — giá trị của spawn nằm ở chỗ nó BÁC BỎ giả-thiết, không phải xác nhận.
→ substring:"TransitionPeBody"
@ -71,7 +71,7 @@ pointer-style: substring
- **file:line:** `User.cs:38` · `ApprovalWorkflow.cs:86-105` · `ApprovalWorkflow.cs:45-50` · `PurchaseEvaluationWorkflowService.cs:119-161` (:125) · `~:393-525` · `:268-275` · `:285-312` · `:303-310` · `:314-333` · `:335-360` · `:364` · `:441-468` · `:483-524` (:485) · `:252-265` (:252) · `PurchaseEvaluationFeatures.cs:393-402` · `:272-329` (:283-290 / :291-323) · `:735-770` · `:765` · `:281-329` · `PurchaseEvaluationDtos.cs:86-92` · `PurchaseEvaluationDetailBundleDto` `:214` + `:217` · `UsersController` `:91-98` + `:105` · `UserFeatures.cs:332` · `PurchaseEvaluationDetailFeatures.cs:42` + `:42-99` (:49-51 / :54-94) · `PurchaseEvaluationsController.cs:267` · `ApprovalWorkflowsV2Controller.cs:16-19` · `DbInitializer.cs:1429-1437` + `:1541-1547` · `system/ApprovalWorkflowsV2Page.tsx:873` + `:853-933` + `:889-946` + `:892-948` (975 dòng) · `system/UsersPage.tsx:306-318` + `:365-372` + `:181-186` · `PeDetailTabs.tsx:113-118` + `:118` + `:224` + `:245` + `:957-973` + `:973` + `:977` + `:1030` + `:287-297`(admin) / `:294-304`(user) + `:109-115` + `:967-979` · `PeWorkflowPanel.tsx:51` + `:60-68` + `:113-124` + `:331-422` (:343-396, :343/:357/:371/:397) + `:425-442` (:425) · `fe-user/src/pages/pe/PurchaseEvaluationsListPage.tsx:256-261` · `fe-user Layout.tsx:325` + `fe-admin Layout.tsx:218` · `Persistence/Migrations/` (path thật) · mig `20260513160703_AddAllowApproverEditBudgetToLevels`
- **định-danh khác:** commit `a1a910f..ee0902a` (7): `ee776d5` · `c667802` · `ac2c859` · `da218f1` · `4d60598` · `fbbd361` · `ee0902a` · bug UAT prod `409a967` · phiếu `PE/2026/A/026` · WF `QT-DN-V2-001 v12` · user `nv.test@solutions.com.vn` · `TransitionAsync` param `skipToFinal` thứ 8 (position 47:47) · `w-60 xl:w-72``w-72 xl:w-80` (ngưỡng rủi-ro `w-80 xl:w-96`) · grid `[260px_1fr] xl:[320px_1fr]` · `STEP_PALETTE`/`LEVEL_PALETTE` 5 màu · token-cost 32k (Pre-A)
### Bổ-sung coverage @P3-gate S170 (token literal — nội-dung đã sống dạng dải/range ở trên; dòng dưới chép NGUYÊN-VĂN context verbatim làm lưới grep)
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `ApprovalWorkflowsV2Page.tsx:889` — Bro UAT login `nv.test@solutions.com.vn` vào menu eoffice "Duyệt NCC → Duyệt" phiếu PE/2026/A/026 (Phase=ChoDuyet, WF=QT-DN-V2-001 v12, ở B…
- `Mig 27` — 7 commits total `a1a910f..ee0902a`: BE+Layout (`ee776d5`) + FE Page (`c667802`) + Docs (`ac2c859`) + 4 polish iter UAT (`da218f1` px-2 + `4d60598` v1 …

View File

@ -50,7 +50,7 @@ pointer-style: substring
- **file:line:** `PurchaseEvaluationFeatures.cs:379-387` · `PurchaseEvaluationWorkflowService.cs:215-378` (ApplyReturnModeAsync) · `TransitionAsync:100` (LogTransitionAsync) · `PurchaseEvaluationChangelog.cs` (EntityType=Workflow(5)) · `ListPurchaseEvaluationChangelogsQueryHandler:1050-1064` · `Master/Project.cs:5-14` · `PurchaseEvaluationsListPage.tsx:133` + `:199-252` · `Contract.cs:8-58` (:12 Phase, :22 WorkflowDefinitionId, :39 CurrentWorkflowStepIndex, :40 RejectedAtStepIndex) · `ContractPhase.cs:14-28` · `ContractWorkflowService.cs:23-219` (:113-126 match Dept+PositionLevel, :148-155 gen mã HĐ) · `20260508053749_AddApprovalWorkflowsV2.cs:14-127` · `20260508072821_AddApprovalWorkflowIdToPurchaseEvaluation.cs:14-31` · `20260508074937_AddCurrentApprovalLevelOrderToPe.cs:14-17` · `PurchaseEvaluationWorkflowService.cs:446-634` (:484-495, :522-546, :561-602, :605-633) + `:167-171` · `ApprovalWorkflowLevels` entity `:86-114` · PE `RejectedAtStepIndex:50`
- **định-danh khác:** tag schema `[audit, phase-9, contract]` · `source_path: solution_erp/audit/investigator-plan-b-contract-v2-preflight-2026-05-22` · token-cost 28k/30k/40k/25K · prod 35 PE·17 V2·4 V1 (entry q1) vs "7 Contracts 100% V1Pinned" (entry này) · Mig 32 tên đề-xuất `AddProjectPackageTable` (Plan AG) vs Mig 32 thực-dụng cho Contract (Plan B) — **2 nghĩa của "Mig 32" trong CÙNG file** (xem `_lienfile-notes-batchB.md` #4)
### Bổ-sung coverage @P3-gate S170 (token literal — nội-dung đã sống dạng dải/range ở trên; dòng dưới chép NGUYÊN-VĂN context verbatim làm lưới grep)
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `Mig 21` — Em main spawn Investigator pre-flight Plan B priority HIGH. 5Q audit + Bonus chunk refinement. Tag schema: `[audit, phase-9, contract]`. **Findings:**
- `Mig 23` — Em main spawn Investigator pre-flight Plan B priority HIGH. 5Q audit + Bonus chunk refinement. Tag schema: `[audit, phase-9, contract]`. **Findings:**

View File

@ -49,6 +49,6 @@ pointer-style: substring
- **file:line:** `Application/Office/WorkflowAppsFeatures.cs:11-15` · `ProposalFeatures.cs:403-486` · `ApprovalWorkflow.cs:72` · `Office/{Module}.cs` (không số dòng) · `.../Persistence/Migrations/*.cs` · `.claude/agents/` (8 file ~22K)
- **định-danh khác:** commit `d2f52ba` (nguồn git-only) · Mig 39 · Mig 41+ (dự-kiến) · mig `AddAttendances` (mới nhất) · mig `ExtendApplicableTypeForWorkflowApps` (Up/Down rỗng) · `WorkflowAppStatus` 5-state ↔ `ProposalStatus` · tag `[cross-project, bvaau, port]` · `[state-grounding, docs-drift, s40]` · `[pre-flight, p11-a, workflowapps]`
### Bổ-sung coverage @P3-gate S170 (token literal — nội-dung đã sống dạng dải/range ở trên; dòng dưới chép NGUYÊN-VĂN context verbatim làm lưới grep)
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `Mig 38` — - **2026-05-30 (P11-A WorkflowApps wire pre-flight):** 4 module Leave/OT/Travel/Vehicle. Schema pin ĐÃ CÓ SẴN (Mig 39): `Office/{Module}.cs` đ<EFBFBD><EFBFBD>
- `Mig 38` — - **2026-05-30 (P11-A WorkflowApps wire pre-flight):** 4 module Leave/OT/Travel/Vehicle. Schema pin ĐÃ CÓ SẴN (Mig 39): `Office/{Module}.cs` đều có `A

View File

@ -57,7 +57,7 @@ pointer-style: substring
- **file:line:** `ApprovalWorkflow.cs:78` + `:107-113` · `PurchaseEvaluationFeatures.cs:401` · `:765` · `ApprovalWorkflowConfiguration.cs:22` · K2 F2 branch `:477` / UPSERT `:441-468` / advance `:502` / caller guard `:147-149` · test ref `:253` (K7-pending) · `Service.cs:287-333` · `:364` · `:268-275` · `:335-360` · caller `:94-100` · entry guard `:75-81` · `:81` · `:252-265` · `:105-132` · `EnsureCanRejectV2Async:201` · `ApplyReturnModeAsync:248` · `EnsureEditableForDetailsAsync:72` · `AdjustBudgetCommandHandler:311` · `TransitionPeBody:267` · Controller `:21-26` · Handler `:114-117` · `WorkflowMatrixViewPage :29-33` · `App.tsx:38` · `Layout.tsx resolvePath:81-86` · `SeedPurchaseEvaluationPermissionDefaultsAsync:1573` · `[Authorize]` bare `:18` · `PeListPanel.tsx:112` · `PeWorkflowPanel.tsx:270` / `:272` / `:315` / `:342` · `PeDetailTabs.tsx:152` / `:287` · `ApprovalWorkflowsV2Page.tsx:639` · `contracts.ts:29` · `budget.ts:20` · file test `PurchaseEvaluationPerNvLookupRegressionTests.cs`
- **định-danh khác:** commit `eb106f2..56868bf..db66253..364aef6` · `c2afef2..508b17a..4dd6f9c` · `a1a910f..ee0902a` (`da218f1`/`4d60598`/`fbbd361`/`ee0902a`) · Run #210 sha=`ac2c859` (4/4 wire + bundle 2 app rotate) · diff 11 file BE +4093/83 (Designer 3938 dòng) · test 84 104 **106/106** (58 Domain + 48 Infra) **111** · mig 29 30 31 · gotcha 45 46 47 · ~146 endpoint · 33 user · 2 test mới `ApplyReturnMode_OneLevel_AtStep1Level1_ResetsToBuoc1Cap1_KeepsChoDuyet` + `ApplyReturnMode_OneStep_AtStep1_ResetsToBuoc1Cap1_KeepsChoDuyet` · fe-admin npm build 1926 modules / 740ms / 0 TS err · ~720 row prod bị xoá (Plan R+S+T5) · spawn-cost ~25K/lượt vs ~100K nếu spawn 4 polish
### Bổ-sung coverage @P3-gate S170 (token literal — nội-dung đã sống dạng dải/range ở trên; dòng dưới chép NGUYÊN-VĂN context verbatim làm lưới grep)
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `Mig 27` 8 plan consecutive em main self-review per UAT mode (`feedback_uat_skip_verify` rule em main verify build + test + npm build mỗi chunk). Key val
- `Mig 28` 2. **Stale Mig 28 comment ApprovalWorkflow.cs:78** comment "F2 (Drafter skip) đã move sang Users.AllowDrafterSkipToFinal" còn nguyên l

View File

@ -4,7 +4,7 @@ source-verbatim: .claude/agent-memory/test-specialist/archive/baseline-history.m
pointer-style: substring
## TỔNG-HỢP (3-5 ý)
- Chuỗi baseline 254 (S60) → 561 (S155) là RUỘT file: chỉ có giá-trị khi mỗi mốc kèm delta + lý-do; giữ NGUYÊN 16 mốc chuỗi gốc + 12 mốc hậu-chuỗi + **4 LỖ** (419→431 · 434→440 · 477→486 · 500→501) + **2 MÂU-THUẪN nội-tại** — ⚠️ mâu-thuẫn nội-tại văn-bản — KHÔNG tự sửa số @S170.
- Chuỗi baseline 254 (S60) → 561 (S155) là RUỘT file: chỉ có giá-trị khi mỗi mốc kèm delta + lý-do; giữ NGUYÊN 16 mốc chuỗi gốc + 12 mốc hậu-chuỗi + **8 LỖ** (4 curator: 419→431 · 434→440 · 477→486 · 500→501 + 4 bổ @S170 P3-gate reviewer: 254→263 chênh+1 · 402→419 chênh+11 · 519→528 chênh+1 · **528→561 chênh+23 — LỚN NHẤT, gấp đôi 4 lỗ đầu cộng lại**; cùng một định-nghĩa delta-không-nối) + **2 MÂU-THUẪN nội-tại** — ⚠️ mâu-thuẫn nội-tại văn-bản — KHÔNG tự sửa số @S170.
- Bẫy vacuous là chủ-đề lặp xuyên file: fixture không wire `AuditingInterceptor` ⇒ assert đúng ở CẢ HAI thế-giới (TS-B); fixture-hỏng-cho-xanh trông Y HỆT máy-đúng-cho-xanh (TS-C); phép chống mạnh nhất = số CHÍNH XÁC, 2 hàm, 1 seed (C6, TS-E) + mỗi assert "cho qua" của phần tiền kèm 1 seed "chặn lại" (TS-G).
- Test tìm được cả bug prod lẫn lỗi tài-liệu: T26 GIỮ ĐỎ = bug prod thật (cascade Immediate → dependent `Added``Detached` ⇒ 0 row changelog, TS-B); spec ghi sai tên query `GetPurchaseEvaluationDetailQuery` — test bắt, lead sửa spec (TS-D). Cả hai đều REPORT, KHÔNG tự sửa.
- Test xanh KHÔNG chứng đường chạy thật còn sống: S92 reflection gọi method cô-lập vẫn xanh dù prod đã TẮT lời gọi seed (TS-F); dedup collation nằm ở SERVICE `OrdinalIgnoreCase` — fixture SQLite BINARY không bắt được ca prod SQL Server bắt (TS-H).
@ -16,7 +16,7 @@ pointer-style: substring
- 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 PE 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 urgent drafter-unset) → 374 (S85b +3 UpdateDraft WF-preserve) → 377-395 (S86-S86d) → 402 (S87 +7 CCM-budget-period) → 419 (D2 +6 PE Mig58 multi-winner + translation-smoke)**. Hậu-chuỗi 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 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ì 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 — ⚠️ mâu-thuẫn nội-tại văn-bản — KHÔNG tự sửa số @S170: **4 LỖ:** ① 419 → 431 (+12 không có mốc) · ② 434 → 440 (+6) · ③ 477 → 486 (+9) · ④ 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ó. Curator 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.
- BẤT-NGỜ / CỜ CHO LEAD — ⚠️ mâu-thuẫn nội-tại văn-bản — KHÔNG tự sửa số @S170: **8 LỖ (4 curator + 4 bổ @S170 P3-gate):** ① 419 → 431 (+12 không có mốc) · ② 434 → 440 (+6) · ③ 477 → 486 (+9) · ④ 500 → 501 (+1) · **⑤ 254→263** khai +22/14=+8 nhưng thật +9, chênh +1 (S61) · **⑥ 402→419** khai +6 nhưng thật +17, chênh +11 (D2) · **⑦ 519→528** khai +8 (Infra 474→483) nhưng thật +9, chênh +1 (S134) · **⑧ 528→561** khai +10 và tự khai "Infra cũ 506 nguyên vẹn" trong khi S134 để lại 483 — thật +33, **chênh +23, lỗ LỚN NHẤT** (4 lỗ bổ dùng CHÍNH định-nghĩa delta-không-nối của curator, reviewer P2b áp lên toàn chuỗi; số gốc GIỮ NGUYÊN — chỉ danh-sách lỗ được bổ, con số "4 LỖ" cũ là claim "đã-soi-hết" sai). **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ó. Curator 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.
→ substring:"Count progression"
**[cao] TS-B — S155: giữ nguyên 1 test ĐỎ vì đó là bug prod**
@ -94,7 +94,7 @@ pointer-style: substring
- **đị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
- **nguồn-đếm (ghi-chú drafter @S170):** đĩa đo 30 heading `##` = 1 dòng "Count progression" + 29 entry-heading (S155 S54, gồm cả S60 heading gộp "S57bis / S56 (notes kept)"); proposal curator khai "25 entry". Entry S60 KHÔNG thuộc cụm narrative nào của curator sống mốc mở chuỗi "254 (S60)" (TS-A) + token GUARD-FIRST phụ-lục này. Chi tiết `_lienfile-notes-batchB.md` §bổ-sung.
### Bổ-sung coverage @P3-gate S170 (token literal — nội-dung đã sống dạng dải/range ở trên; dòng dưới chép NGUYÊN-VĂN context verbatim làm lưới grep)
### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)
- `#6` **S97 (2026-07-01) PE EndsBeforeCeo CONFIG→RUNTIME (bug anh Kiệt FDC, spec-change test) — 440 PASS no-red, NET +0 [Fact] (extend in-place):** em
- `Mig 55` ## S74 PE Mig 55 CcmNote (compile-fix arity + test-after), +5

View File

@ -8,10 +8,10 @@
- [x] P1 — FIDELITY-FLAG 4 cờ (`implementer-backend--2026-05-q3`) → **FLAG ĐÚNG nhưng CHƯA ĐỦ (2 MAJOR + 2 MINOR)**
- [x] P2a — cicd-monitor--2026-05-runs → **PASS-WITH-FIXES** (3 câu hỏi lead đều ĐẠT; 1 con-trỏ CHẾT + 1 keep-vs-drop MAJOR)
- [ ] P2b — test-specialist--baseline-history
- [ ] P2c — implementer-frontend--2026-H2
- [ ] P2d — investigator-codebase--2026-05-q1
- [ ] P3 — chấm subsection `### Bổ-sung coverage @P3-gate S170`
- [x] P2b — test-specialist--baseline-history**PASS-WITH-FIXES** (nhãn ĐỦ · 0 số bị sửa đẹp · nhưng CENSUS 4-lỗ THIẾU ít nhất 4, có lỗ +23)
- [x] P2c — implementer-frontend--2026-H2**PASS** (14/14 sống, tag NGUYÊN 14/14; auto-refuse = FE-A [cao] đặt ĐẦU; 1 MINOR nén-âm + 1 ghi-chú L1)
- [x] P2d — investigator-codebase--2026-05-q1**PASS-WITH-FIXES** (5/5 claim khớp nguồn; 1 MAJOR: lén sửa 7→6 field không khai)
- [x] P3 — subsection `### Bổ-sung coverage @P3-gate S170`**HỢP LỆ VỀ MỤC ĐÍCH, GÂY HIỂU NHẦM Ở DẠNG HIỆN TẠI** (1 dòng gán verdict SAI)
---
@ -80,3 +80,196 @@ Từ vựng bịa đã sống sẵn trong 4 hiện vật trí nhớ: `implemente
🔴 **Cảnh báo khi dọn:** `_INDEX.md:15` dùng tên sai **bên trong con trỏ** `substring:"30 migrations** (+1 Mig 30 AllowApproverEditSection1"`. Con trỏ đó trỏ vào văn bản nguồn, nên **sửa chính tả ở đây sẽ giết con trỏ**. Luật dọn: vá chỗ **khẳng định**, không đụng chỗ **trích để định vị** (use ⟂ mention).
---
## P2a — `cicd-monitor--2026-05-runs.gist.draft.md` ↔ `archive/2026-05-runs.md` (59.861 B → 30.188 B)
**Verdict: PASS-WITH-FIXES.** Cả ba câu hỏi lead đặt ra đều ĐẠT. Nhưng có 1 con trỏ chết (cơ học, đo được) và 1 chỗ bỏ sót đáng kể.
### A. Ba câu hỏi lead — trả lời có bằng chứng
**A1. Run #217 còn sống? → CÓ.** Sống ở 4 chỗ: cụm D3 (thân bài, mang `a734bf2` + `Service:75-103` + prefix `:472` + "6/6 approval đủ 8 key"), ghi chú hợp nhất đầu §Entries, ghi chú cạnh D3, và §Phụ-lục dòng Run/sha. Con trỏ `→ substring:"a734bf2"` giải được (3 hit trong verbatim). Nội dung độc nhất của #217 — Discovery #5 (`\\SQLEXPRESS` 4 dấu) và khuôn "capture pre-call mutation state" — đều được giữ, lần lượt ở cụm D6 và D9.
**A2. Nhãn che mật khẩu chép đúng, không có mật khẩu thô? → ĐÚNG, và không có.** Verbatim dòng 48 ghi `pwd=[đã che 71b7740a — ROTATE REQUIRED]`; draft dòng 100 chép lại **đúng từng ký tự** nhãn đó. Tôi quét toàn draft các token `pwd|password|passwd|mật khẩu`: chỉ ra 4 chỗ, tất cả vô hại — tên biến môi trường `PROD_DB_PASSWORD`, hai mảnh hash bundle chứa chuỗi `PWD-g` (`QZIPWD-g`), và chính dòng nhãn che. **Không có giá trị mật khẩu thô nào.** Draft còn chủ động thêm câu răn "TUYỆT ĐỐI không tra/chép giá trị gốc từ git history" và giữ trạng thái "ROTATE REQUIRED vẫn treo" — đây là xử lý đúng và nên giữ nguyên.
**A3. Hợp nhất phần (a) tóm tắt với phần (b) đầy đủ — có chọn bản dày? → CÓ, chọn đúng bản dày.** Cấu trúc verbatim: phần (a) là "Last curate logs" (dòng 12-39, 6 Run), phần (b) là "Run entries archived" (dòng 40-181, 20 Run), phần (c) là "Key patterns extracted" (dòng 182-201). Bốn Run nằm ở cả hai phần là #202 · #203 · #210 · #219 — đúng như draft khai. Đo kích thước từng vùng: (a) 944 B / (b) 5.520 B cho #202; 1.224 B / 5.407 B cho #203; 881 B / 3.854 B cho #210; 1.410 B / 3.509 B cho #219 — phần (b) dày hơn ở cả bốn.
Ba dấu vân tay chứng minh draft lấy từ (b) chứ không phải (a):
| Dấu vân tay trong draft | Chỉ có ở | Không có ở |
|---|---|---|
| biểu thức chính quy `\[Bước (\d+) — Cấp (\d+)\]` | (b) #219, dòng 170 | (a) #219, dòng 36 |
| `Pe_DuyetNccPhuongAn` Order **6-10** | (b) #210, dòng 150 | (a) #210 chỉ ghi `Order=7` |
| 4 vị trí `...WorkflowService.cs:204` · `:258-260` · `...DetailFeatures.cs:75-76` · `...Features.cs:314-315` | (b) #202, dòng 90-96 | (a) #202, dòng 16 |
Và hai Run chỉ có ở phần (a) — #215, #217 — đều được giữ, đúng như ghi chú tự khai.
**Điểm cộng vượt chuẩn:** claim liên file "#215 bản dày nằm ở `cicd-monitor--2026-05-q3` B1" là **thật** — tôi mở q3 draft, cụm B1 có `id=329` · `cdfd542` · `test_infra 51/53` · `#216 id=330 8c05947`, dày hơn hẳn bản tóm tắt ở đây. Hơn nữa q3 B1 ghi ngược lại "bản dày = file này; `cicd-monitor--2026-05-runs` giữ bản mỏng" ⇒ **hai bờ cùng khai**, không phải một bên tự nhận. Đây là cách xử lý trùng lặp liên file đúng chuẩn, nên nhân rộng.
### B. MAJOR-1 — một con trỏ substring CHẾT (lỗi cơ học, đo được)
Draft khai `pointer-style: substring` ở đầu file, tức mỗi dòng `→ substring:"..."` phải tìm lại được trong verbatim. Tôi chạy toàn bộ 10 con trỏ của file này qua `grep -F` (khớp chuỗi, phân biệt hoa thường):
- 9/10 giải được (`098baa6` 3 hit · `f4055a1` 3 · `a734bf2` 3 · `Restart-WebAppPool` 1 · `JsonStringEnumConverter` 4 · `no state change (atomic)` 1 · `AddAdvancedOptionsToApprovalWorkflows` 1 · `ac2c859` 3 · `Key patterns extracted` 1).
- **1/10 CHẾT: `→ substring:"PRIORITY MAX REINFORCED"` (cụm D7) = 0 hit.** Verbatim dòng 178 viết `priority MAX REINFORCED` — chữ `priority` thường. Draft viết hoa toàn bộ, nên con trỏ không về được nguồn.
Cùng lỗi này lặp ở §Phụ-lục "surprise": trích dẫn `"Memory size ... **PRIORITY MAX REINFORCED**"` cũng sai kiểu chữ so với nguồn.
**Tiêu chí sửa:** đổi con trỏ về đúng chữ nguồn `priority MAX REINFORCED`, hoặc đổi sang chuỗi khác chắc chắn tồn tại; sau đó chạy lại toàn bộ con trỏ của cả 16 draft — đây là phép kiểm rẻ, chỉ một vòng `grep -F`. (Tôi đã chạy sẵn cho 5 file: 4 file kia 0 con trỏ chết, xem từng mục bên dưới.)
### C. MAJOR-2 — bỏ mất phần lớn "checklist 10 điểm", đúng thứ draft tự nhận là quý nhất
Cụm D9 tự viết: *"checklist 10 điểm ở đây là bản duy nhất liệt kê đủ 10 trong toàn batch A"* và bài học *"nén lại cái đã nén là chỗ dễ mất mã số nhất"*. Nhưng chính draft **không chép 10 mục đó**, chỉ ghi "(liệt kê đủ 10)". Đối chiếu 10 mục ở verbatim dòng 188-197 với draft:
- Còn, có ghi rõ số điểm: mục 2 (`HasDefaultValue(false)`), mục 3 (quy tắc migration 3 file), mục 10 (`[FromBody]` record đếm param).
- Còn, dạng gián tiếp: mục 7 (`AwLevelDto` + `ToDto`), mục 9 (lọc `ApproverUserId == actorId`), một phần mục 6.
- **Mất hẳn:** mục 1 "Domain entity field", mục 4 "Service handler read field", mục 8 `CreateAwLevelInput` + Update mutation handler. Đo: `CreateAwLevelInput` xuất hiện 3 lần trong verbatim, **0 lần** trong draft; `Domain entity field``Service handler read` cũng 1-0.
Tức bản chưng cất tự tuyên bố mình là bản duy nhất giữ đủ 10, rồi giữ khoảng 6. Nếu người sau tin câu đó và không mở verbatim, họ mất 3-4 điểm kiểm.
**Tiêu chí sửa:** hoặc chép đủ 10 mục vào cụm D9 (chúng ngắn, tổng ~10 dòng), hoặc bỏ mệnh đề "bản duy nhất liệt kê đủ 10" và ghi thẳng "đủ 10 mục chỉ có ở verbatim dòng 188-197".
### D. MINOR
- **D-1. Trích dẫn bị cắt mà không có dấu lược.** Draft ghi `"NV Test actor variant **not feasible** (scanned 20 PEs)"`; nguồn là `... (scanned 20 PEs — NV Test = Drafter not approver)`. Draft tự đóng ngoặc, biến một câu bị cắt thành câu trọn. Không sai dữ kiện, nhưng đã đặt trong ngoặc kép thì nên có dấu lược.
- **D-2. Số lần củng cố Discovery #3 vênh nhau trong chính draft.** Thân bài cụm D2 ghi "chốt sau **3 lần** củng cố"; §Phụ-lục ghi "củng cố **4 lần**: #200/#201/#202 + #210 'NOT applicable'". Nguồn tự mâu thuẫn thật (verbatim dòng 157 tiêu đề ghi "RE-CONFIRMED 4th time" rồi thân ngay dưới ghi "does NOT reinforce the anomaly hypothesis"), nên đây là **trung thành với nguồn**, không phải bịa. Nhưng nên nói thẳng "nguồn tự mâu thuẫn" thay vì để hai con số khác nhau nằm hai chỗ trong cùng một file — người đọc sẽ tưởng bản nén cẩu thả.
### E. Không phát hiện bịa hay lệch nghĩa
Mọi mã số tôi lấy mẫu đều truy được về nguồn: 4 vị trí `file:line` của Plan O (verbatim 90-96), `111/111 test baseline +3` (dòng 16, 90), 8 mục "Key patterns" khớp đúng thứ tự và đúng nội dung (dòng 184-201), backfill `48/48``0/13`, chuỗi test-count `84 → 103 → 104 → 106 → 108 → 111`. Bốn trích dẫn tiếng Anh trong mục "surprise" mà tôi kiểm khớp chữ đúng nguyên văn (`Anomaly is BENEFICIAL`, `all missed across 3 plan`, `no state change (atomic)`, `8 surface points checklist`). Riêng phần "🔐 secret" là xử lý mẫu mực.
---
## P2b — `test-specialist--baseline-history.gist.draft.md` ↔ `archive/baseline-history.md` (32.069 B → 27.359 B)
**Verdict: PASS-WITH-FIXES.** Hai câu hỏi lead đặt ra đều ĐẠT, và đạt tốt. Nhưng tôi tìm thêm một lỗi mà draft chưa thấy, thuộc đúng loại nó đang canh.
### A. Hai câu hỏi lead — trả lời có bằng chứng
**A1. 4 lỗ + 2 mâu thuẫn có được giữ nguyên kèm nhãn? → CÓ, nhãn đủ và đặt đúng chỗ.** Cụm TS-A dòng 19 mang nguyên câu `⚠️ mâu-thuẫn nội-tại văn-bản — KHÔNG tự sửa số @S170`, và nhãn được nhắc lại ở §TỔNG-HỢP dòng 7 — tức người đọc gặp cảnh báo trước cả khi vào thân bài. Tôi kiểm cả hai mâu thuẫn trên nguồn:
- **Mâu thuẫn (a) — có thật.** Tiêu đề verbatim dòng 24 ghi `+10 -> 519`, thân dòng 25 ghi `+9 test vào PeBudgetFreezeTests` rồi lại ghi `(464+10 khớp)`. Cách hoà giải draft đề xuất — T0 của INV-A (+1) cộng T1-T9 của INV-C (+9) — khớp với dòng 28 (INV-A dựng T0, sau INV-B thì T0 xanh). Draft ghi rõ "hoà giải KHẢ DĨ, **chưa xác minh**" ⇒ đúng mức, không tự nâng giả thuyết thành kết luận.
- **Mâu thuẫn (b) — có thật.** Verbatim dòng 18-19 (S153, 2026-07-26) ghi `baseline 509 KHÔNG đổi`, trong khi S134 (2026-07-17, trước đó 9 ngày) đã ghi 528 và S155 (2026-07-27, ngay sau) ghi 561. Con số 509 thuộc mốc S118b, tức trễ hai nấc. Draft nói rõ đây là mâu thuẫn **trong văn bản**, không phải phán quyết về baseline thật, và nêu lý do không thể phán (curator chỉ đọc file, không chạy `dotnet test`). Đây là cách khai giới hạn đúng chuẩn.
**A2. Có lén "sửa đẹp" số nào không? → KHÔNG. Tôi đối chiếu từng mắt xích.** Chuỗi gốc ở draft dòng 17 so với verbatim dòng 13: 16 mốc `254 · 263 · 286 · 292 · 306 · 334 · 339 · 344 · 351 · 354 · 366 · 371 · 374 · 377-395 · 402 · 419`**trùng khít, kể cả các delta** (`+22/14`, `+23`, `+6`, `+14`, `+28`, `+5`, `+5`, `+7`, `symmetric superseded`, `S78-S84 chưa ghi delta`, `+5`, `+3`, `S86-S86d`, `+7`, `+6`). Khác biệt duy nhất là một dấu hai chấm thêm vào sau `S61` — thuần hình thức.
12 mốc hậu chuỗi cũng khớp từng cái với tiêu đề entry trên đĩa: `431→434 (S92 +3)` · `440 (S97 +0)` · `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 (45D+474I)` · `528 (45D+483I)` · `561 (+10; 560/1; 45D+516I)`.
**Điểm tinh tế đáng khen:** ở mốc S133 draft **cố ý không viết "+10"** mà viết thành phần `Domain 45 + Infra 474` — tức nó tránh khẳng định lại chính con số đang tranh chấp, thay vì im lặng chọn một phía. Đó là xử lý đúng.
Số đếm meta cũng đúng: draft khai "16 mốc chuỗi gốc + 12 mốc hậu chuỗi" — tôi đếm lại: 16 và 12. Ghi chú `nguồn-đếm` khai "đĩa đo 30 heading `##`" — tôi chạy `grep -c "^## "` = **30**. Khớp.
### B. MAJOR — bản kiểm kê "4 LỖ" tự nhận là đủ, nhưng thiếu ít nhất 4 lỗ nữa, trong đó có lỗ lớn gấp đôi cả bốn cộng lại
Draft dùng đúng một định nghĩa "lỗ": số đầu của một mốc không nối được với số cuối của mốc liền trước. Áp lại chính định nghĩa đó lên toàn chuỗi (số liệu lấy từ chính draft, không cần mở thêm nguồn):
| Đoạn | Delta khai | Delta thật | Chênh | Draft có gắn cờ? |
|---|---|---|---|---|
| 254 → 263 (S61) | +22 rồi 14 ⇒ +8 | +9 | **+1** | ✗ |
| 402 → 419 (D2) | +6 | +17 | **+11** | ✗ |
| 419 → 431 | — | +12 | +12 | ✓ lỗ ① |
| 434 → 440 | — | +6 | +6 | ✓ lỗ ② |
| 477 → 486 | — | +9 | +9 | ✓ lỗ ③ |
| 500 → 501 | — | +1 | +1 | ✓ lỗ ④ |
| 519 → 528 (S134) | +8 (Infra 474→483) | +9 | **+1** | ✗ |
| 528 → 561 (S155) | +10, và tự khai "Infra cũ **506** nguyên vẹn" trong khi S134 để lại 483 | +33 | **+23** | ✗ |
Bốn lỗ được gắn cờ cộng lại là 28 test. Bốn lỗ **không** được gắn cờ cộng lại là 36 test, riêng đoạn S134→S155 đã là 23. Nghịch lý là draft **có chép đủ số để tự phát hiện** — nó viết `528 (45D+483I)``561 (45D+516I)` cạnh nhau, chỉ thiếu bước trừ.
Vì sao đây là MAJOR chứ không phải chuyện nhỏ: câu "**4 LỖ**" là một con số đóng vai "đã soi hết". Người đọc sau sẽ hiểu là ngoài bốn chỗ đó thì chuỗi liền mạch, và sẽ dùng chuỗi làm bằng chứng "không hồi quy" — đúng công dụng mà TS-A nói là ruột của file. Một lỗ 23 test nằm ngoài danh sách phá hỏng chính công dụng ấy.
**Tiêu chí sửa:** hoặc bổ sung 4 lỗ trên vào danh sách và đổi tổng, hoặc bỏ con số "4" và viết "các lỗ đã soi được (danh sách chưa chắc đủ)". Tuyệt đối không sửa số gốc — nhãn "KHÔNG tự sửa số @S170" vẫn đúng và phải giữ.
### C. Không phát hiện bịa hay lệch nghĩa
Các cụm còn lại khớp nguồn khi lấy mẫu: TS-B (T26 giữ đỏ — `PeSoftDeleteFeatures.cs:126` thêm changelog, `:142` `Remove(pe)`, `CascadeDeleteTiming.Immediate`, `Added→Detached`, `rowsInDb=0`, `SoftDeleteFixture` là fixture đầu tiên wire `AuditingInterceptor`) khớp verbatim dòng 16 gần như nguyên văn. TS-C (18/18, `inject_A` neo `## MIND-1` sau `renum`, `str.find` = 1, "fixture hỏng cho xanh trông y hệt máy đúng cho xanh") khớp dòng 19. TS-D (`GetPurchaseEvaluationQuery` mới là tên thật, spec ghi nhầm `GetPurchaseEvaluationDetailQuery`, RED thật `Failed 1/1` tại `PeBudgetFreezeTests.cs:184`) khớp dòng 28. Mười con trỏ `substring` của file này **giải được cả 10** (`Count progression` · `PeDeleteByApproverTests` · `inject_A` · `wf_d9639faa` · `PePendingAccumulationTests` · `ProcurementMasterAccessSeedTests` · `TargetParameterCountException` · `truonggiang` · `ApplyDrafterBypassOnSubmitAsync` · `ExecuteUpdateAsync`).
Tinh thần "REPORT, KHÔNG tự sửa" được giữ nhất quán ở cả bốn ca (T26 bug prod · gap `mind-check` · NRE `Code = r.Code!.Trim()` · spec sai tên query) — đúng vai.
---
## P2c — `implementer-frontend--2026-H2.gist.draft.md` ↔ `archive/2026-H2.md` (22.215 B → 29.828 B)
**Verdict: PASS.** Đây là bản chưng cất tốt nhất trong bốn cặp tôi soi. Hai câu hỏi lead đều ĐẠT, có đo.
### A. Hai câu hỏi lead
**A1. 14/14 mục moved-not-cut còn sống? → CÓ, và giữ được thứ quan trọng hơn cả nội dung: chuỗi tag.**
Verbatim có đúng **14** tiêu đề `##`. Draft gom thành 11 cụm nhưng **không mục nào bị nuốt**: 9 cụm đơn (FE-A → FE-I) + 1 khối FE-J1..J5 giữ 5 danh tính riêng, mỗi mục con có tag riêng và **con trỏ riêng** (`→ substring J1:"sub-slot-prop" · J2:"anchor-by-visible-text" · J3:"spread-conditional-before-as-cast" · J4:"sign-toggle-immediate-commit" · J5:"publish-not-update"`) + 1 cụm FE-M được ghi rõ là `[CHỈ-MỤC, không thay thế mục nào]`. Cộng lại 9 + 5 = 14.
Phép đo quyết định: tôi rút **toàn bộ chuỗi tag** từ verbatim rồi tìm từng chuỗi trong draft — **14/14 chuỗi có mặt, mỗi chuỗi 2 lần** (một ở tiêu đề cụm, một ở phụ lục coverage). Không chuỗi nào bị rút gọn hay viết lại. Đây đúng là ràng buộc mà chính draft tự đặt ở dòng 15 ("mất tag = con trỏ MEMORY trỏ vào hư không") — và nó tự giữ được lời.
**A2. Mục auto-refuse có tag cao? → CÓ, và còn hơn thế.** FE-A mang `[cao]`, đứng **vị trí đầu tiên** trong §Entries, được nhắc lại ở §TỔNG-HỢP dòng 10, và ràng buộc "mục auto-refuse (FE-A) tag {cao}" được chép thẳng vào dòng 15 để đời sau không hạ nó xuống. Phần BẤT-NGỜ của FE-A còn nói đúng lý do nên đặt cao: *"một ghi chú 'không làm gì cả' lại là mục đáng giữ nhất nhóm — nó định nghĩa cái vai này KHÔNG nhận"*. Nội dung khớp verbatim dòng 78-81 cả hai vế (việc dưới 30 phút; không hạ sàn mirror-SHA ×2 app + `npm build` ×2).
### B. Kiểm bịa / lệch nghĩa — lấy mẫu cụm nặng nhất (FE-B ↔ verbatim dòng 29-33)
Đối chiếu từng chi tiết: `setState` gọi trong lúc render ⇒ React huỷ render và render lại trước khi commit ✓ · harness bản 1 đọc `phase=3`**control âm cũng ra 3** ⇒ phép đo mất khả năng phân biệt ✓ · vá bằng lặp tới khi state đứng yên ⇒ A ra `phase=2`, control âm giữ `3` ✓ · `KpiCard onClick setPhase :153` ✓ · `chipPhase: T|null|undefined` với `undefined` = "nghe URL" ≠ `null` = "chọn Tất cả" ✓ · reset trong render theo `navKey`, không dùng `useEffect` ✓ · BE `/deleted` thiếu `group` (`Controller:61-66` so `List:42`) ✓ · staticMap fe-user 6/6 so fe-admin 5/6 lệch có chủ đích ✓. **Không có chi tiết nào bịa, không có chi tiết nào đổi nghĩa.**
Bốn cặp SHA của K4b/K5 cũng còn: `fc3944df` · `545b4c2f` · `7a99b865` (đứng yên = đối chứng dương, xuất hiện 2 lần) · `edff9931` · `bed52f39` · hash build `index-C4F7kHKT`. Mười con trỏ `substring` **giải được cả 10**.
### C. MINOR — hai chi tiết của K4b rơi khỏi cả thân bài lẫn phụ lục
- `lastPresetGroup` (bài học CreatePage: 8 leaf chung một route nên leaf→leaf không remount ⇒ phải so `lastPresetGroup` chứ **không** so `approvalGroup`, so nhầm thì người dùng bấm đổi nhóm bị kéo ngược và ô chọn thành nút chết) — **0 hit trong draft**. Đây là một cái bẫy cụ thể, có thể tái diễn, và là mục duy nhất trong K4b không được giữ.
- `4 FLAG` (số cờ K4b báo lên lead) — 0 hit. Nhẹ hơn, nhưng là con số bàn giao.
**Tiêu chí sửa:** thêm một câu cho `lastPresetGroup` vào FE-B (mục BẤT-NGỜ hoặc BÀI-HỌC). Không bắt buộc với "4 FLAG".
### D. MINOR — bản "gist" này KHÔNG nén: nó phình 134% so với nguồn
Đo: nguồn 22.215 B; draft 29.828 B, trong đó thân bài (dòng 1-98) **21.826 B** và phụ lục token 8.002 B. Nghĩa là **riêng thân bài đã bằng 98% nguồn**, phụ lục cộng thêm 36%. So với ba file cùng đợt: `cicd-monitor--2026-05-runs` còn 50%, `investigator-codebase--2026-05-q1` còn 80%, `test-specialist--baseline-history` còn 85% — file này là ngoại lệ duy nhất đi ngược chiều (`implementer-backend--2026-05-q3` phình 214% nhưng do gánh khối FIDELITY-FLAG, có lý do riêng).
Đây **không phải lỗi trung thực** — nguồn viết dạng điện tín cực nén, draft viết lại thành câu tiếng Việt đầy đủ nên byte tăng là tất yếu, và đó chính là thứ tầng L2 cần. Nhưng nếu mục đích của tầng gist còn gồm **giảm chi phí ngữ cảnh** thì file này không đạt mục đích đó, và lead nên biết bằng số trước khi chốt. Không đề xuất cắt nội dung.
### E. Ghi chú ngoài phạm vi draft — con trỏ L1 dùng tag NGẮN HƠN tag verbatim
`implementer-frontend/MEMORY.md:46` trỏ mục S152 bằng `[s152, no-lower-bar]`, trong khi verbatim mang `[s152, auto-refuse-trivial-30min, no-lower-bar-smart-friend]`. Tìm bằng mảnh `no-lower-bar` thì vẫn nhảy được (khớp một phần chuỗi dài), nhưng tìm nguyên cụm `[s152, no-lower-bar]` thì trượt. Draft **không có lỗi** ở đây — nó giữ đúng tag nguồn; lệch nằm ở dòng móc L1. Việc của em-main: đồng bộ tag ở `MEMORY.md` với tag verbatim, hoặc chốt luật "móc L1 chỉ dùng một token, không dùng cả cụm ngoặc".
---
## P2d — `investigator-codebase--2026-05-q1.gist.draft.md` ↔ `archive/2026-05-q1.md` (20.699 B → 16.610 B)
**Verdict: PASS-WITH-FIXES.** Năm mẫu tôi bốc đều truy được về nguồn, không mẫu nào bịa. Nhưng có một chỗ draft **tự sửa số của nguồn mà không khai** — đúng loại hành vi mà chính đợt này đang cấm ở file khác.
### A. Năm claim lấy mẫu — đối chiếu từng cái
| # | Claim trong draft | Nguồn | Kết quả |
|---|---|---|---|
| 1 | `PurchaseEvaluationFeatures.cs:765` `FirstOrDefault(l => l.Order == curLevelOrder)` lấy nhầm hàng sau Mig 29 vì `Order` trùng trong cùng Cấp; vá bằng `&& l.ApproverUserId == currentUser.UserId` + fallback cho admin | verbatim dòng 39 + mục root-cause | **KHỚP** |
| 2 | `canAdjust = !readOnly && (...)` đoản mạch trước khi `isApproverChoDuyet` được xét (`:973`), nút bút chì ẩn ở `:1030`, bản vá đảo vế thành `isAdmin \|\| (!readOnly && isDrafter && isDrafterPhase) \|\| isApproverChoDuyet` và land ở `:977` | verbatim dòng 39 (`line 973`, `line 1030`) và dòng 43 (`line 977`) | **KHỚP từng số dòng** |
| 3 | Plan R: 35 PE (28 active + 7 soft) · 17 V2 (15 chưa ghim + 2 ghim) · 4 V1 (2 active + 2 inactive); ước ~620 row con = 446 + ~140 + ~37 | verbatim dòng 55, nguyên văn "446 PE children + ~140 V2 + ~37 V1 = ~620 rows" | **KHỚP** — và "~620" là chữ của nguồn, draft không tự làm tròn |
| 4 | Mốc trạng thái: khởi tạo 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) | verbatim dòng 15, 19, 23 | **KHỚP cả ba mốc** |
| 5 | Mật khẩu Identity đòi từ 12 ký tự (`User@123456` 11 ký tự trả 400, `TestUser@2026` đạt); sidebar `w-72 xl:w-80` an toàn còn `w-80 xl:w-96` là ngưỡng rủi ro; Designer 975 dòng; trí nhớ khai "3-panel" nhưng đo lại là 2-panel `[260px_1fr] xl:[320px_1fr]` | verbatim: mỗi token đều có mặt (`User@123456` 1 · `TestUser@2026` 1 · `w-80 xl:w-96` 1 · `975` 1 · `2-panel` 2 · `260px_1fr` 1) | **KHỚP** |
Bảy con trỏ `substring` của file này **giải được cả bảy**. Không tìm thấy tên file, số dòng hay mã băm nào không có trong nguồn.
### B. MAJOR — draft lặng lẽ sửa "7 field" thành "6 field"
Verbatim dòng 51 viết: `PeWorkflowPanel.tsx:113-124` ... **SEND ĐÚNG 7 fields** (TargetPhase + Decision + Comment + ReturnMode + ReturnTargetUserId + SkipToFinal). Nguồn nói **7** nhưng liệt kê **6**.
Draft dòng 24 viết: FE ... **gửi ĐỦ 6 field** (TargetPhase + Decision + Comment + ReturnMode + ReturnTargetUserId + SkipToFinal, ...).
Về số học thì 6 nhiều khả năng đúng: BE record giữ 3 field, "bỏ im lặng 3 field còn lại" ⇒ tổng 6. Nhưng đây vẫn là **sửa số của nguồn mà không để lại dấu**, và nó va thẳng vào chuẩn mà chính đợt này đang áp: file `test-specialist--baseline-history` được yêu cầu giữ nguyên số sai kèm nhãn `KHÔNG tự sửa số @S170`. Hai file cùng một đợt, hai chính sách ngược nhau — người đọc sau không biết con số nào trong batch là chữ của nguồn, con số nào đã bị curator chỉnh.
Cái mất đi cũng thật: nguồn tự mâu thuẫn ở chỗ này, và mâu thuẫn đó là bằng chứng cho đúng bài học của cụm B1-B ("đếm field body so với đếm field command là phép thử rẻ") — chính người viết verbatim cũng đếm nhầm.
**Tiêu chí sửa:** giữ chữ nguồn và gắn nhãn, ví dụ *"nguồn ghi '7 fields' nhưng liệt kê 6 — lấy 6 theo danh sách; số 7 chưa xác minh"*. Không được im lặng chọn một phía.
### C. Không phát hiện lỗi khác
Ba verdict phân biệt của cụm B1-A (F3 đúng · F4 hỏng ở FE · lượt thứ ba là báo động giả vì `TraLai=98` không phải `DangSoanThao`) đều giữ đúng ranh giới của nguồn, không bị gộp thành một kết luận. Cụm B1-G tự khai "vai KHÔNG spawn mà vẫn ghi entry ⇒ nguồn nhiễu khi đếm coverage" — đó là tự phê bình đúng chỗ, nên giữ.
---
## P3 — chấm subsection `### Bổ-sung coverage @P3-gate S170` (lưới token máy tự chèn)
**Verdict: HỢP LỆ VỀ MỤC ĐÍCH, NHƯNG GÂY HIỂU NHẦM Ở DẠNG HIỆN TẠI — phải sửa trước khi chốt.** Có mặt ở 9 trong 16 draft.
### A. Vì sao hợp lệ
Mục đích khai ngay ở tiêu đề: giữ **token literal** để lưới `grep` của cổng coverage-diff không báo mất. Nó nằm ở cuối, sau phụ lục, tách khỏi thân bài, và tự nhận là "lưới grep" chứ không nhận là nội dung. Chừng nào chỉ đọc nó như một danh sách token thì không sai.
### B. Vì sao gây hiểu nhầm — cơ chế, đo được, tái lập được ở 2 file độc lập
Lưới in **phần đầu của DÒNG chứa token**, không phải vùng lân cận token. Các file archive này toàn dòng khổng lồ (một entry một dòng), nên đoạn trích in ra thường **không liên quan gì đến token đứng đầu dòng**.
1. **Sai nghiêm trọng nhất — gán verdict ngược.** Trong `cicd-monitor--2026-05-runs` dòng 129: token `Run #214` được kèm đoạn trích *"**FAIL** verdict at test_infra gate — 2 Plan M edge case tests broken..."*. Đoạn đó là thân của entry **Run #215**; nó khớp chữ `Run #214` chỉ vì trong đó có câu "bundles unchanged **Run #214** baseline". Sự thật: verbatim dòng 160 ghi `Run #214 ... VERDICT=PASS`. Người đọc lướt lưới sẽ mang đi một mệnh đề sai hẳn.
2. **Xác nhận độc lập lần hai.** Trong `investigator-codebase--2026-05-q1` dòng 77: token `Mig 27` kèm đoạn trích *"7 commits total `a1a910f..ee0902a`: BE+Layout..."*. Tôi kiểm: `Mig 27` chỉ xuất hiện một lần, ở giữa dòng 63, trong ngữ cảnh nhãn sidebar dài 44 ký tự — còn đoạn in ra chính là 190 ký tự **đầu dòng 63**. Hai file khác nhau, cùng một cơ chế.
3. **Tạo cảm giác có nội dung ở chỗ trống rỗng.** Cũng ở file cicd, các token `#216` · `#222` · `#227` · `Run #215` · `Run #216` · `Run #222` đều trỏ về **cùng một dòng 7** — một ghi chú kế hoạch curate (*"~5 entries kept: Run #215 fail lesson + Run #216 fix + Run #222-#227 S26..."*). Ba Run #216/#222/#227 **không có entry nào** trong verbatim này; lưới làm chúng trông như đã được phủ.
4. **Lưới mâu thuẫn với thân bài.** Các token `Run #202` · `#203` · `#210` · `#219` đều trỏ về phần (a) tóm tắt (dòng 14/18/22/34), trong khi thân bài đã cố ý chọn phần (b) dày. Ai tra theo lưới sẽ bị dẫn về đúng bản mỏng mà bản gist vừa loại.
### C. Tiêu chí sửa (ba mức, chọn mức nào cũng đủ)
1. **Rẻ nhất:** đổi câu mở của subsection thành lời cảnh báo đúng bản chất — đại ý *"đoạn trích dưới đây là phần ĐẦU DÒNG chứa token, KHÔNG phải ngữ cảnh của token; chỉ dùng làm lưới grep, không đọc như dữ kiện"*. Câu hiện tại nói "chép NGUYÊN-VĂN context verbatim" là mô tả sai chính nó.
2. **Đúng hơn:** đổi cách sinh — in vùng lân cận token (kiểu `.{0,120}<token>.{0,120}`) kèm số dòng, thay vì đầu dòng.
3. **Bắt buộc trong mọi trường hợp:** sửa riêng dòng `Run #214` của `cicd-monitor--2026-05-runs`, vì nó không chỉ vô nghĩa mà còn khẳng định sai (FAIL cho một Run PASS).
---
FIDELITY: PASS-WITH-FIXES — flag-4-cờ=nhãn ĐÚNG nhưng CHƯA ĐỦ (3/4 tên là drift của cờ thật, `AllowDelegate` 0 đối-ứng · git pickaxe 0 commit cả 4 ⇒ chưa từng tồn tại; phải phủ cờ lên CON SỐ "4×" + bỏ đơn "giữ 4 lần bỏ tên cờ" + thêm tên cột THẬT) · 4-cặp=runs P-fixes · baseline P-fixes · frontend P · investigator P-fixes
== HẾT ==