diff --git a/.claude/WAL.md b/.claude/WAL.md index ce7abd2..105da7c 100644 --- a/.claude/WAL.md +++ b/.claude/WAL.md @@ -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 → vá 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 có kênh) · (64) user TEST · (66) dọn HĐ ZZTEST · thư day-wake-probe nấc adopt (ctx-verifier cờ ⑩: món 65 từng ngoài bản-đồ) diff --git a/.claude/agent-memory/cicd-monitor/archive/2026-05-q2.gist.md b/.claude/agent-memory/cicd-monitor/archive/2026-05-q2.gist.md new file mode 100644 index 0000000..40f108d --- /dev/null +++ b/.claude/agent-memory/cicd-monitor/archive/2026-05-q2.gist.md @@ -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… diff --git a/.claude/agent-memory/cicd-monitor/archive/2026-05-q3.gist.md b/.claude/agent-memory/cicd-monitor/archive/2026-05-q3.gist.md new file mode 100644 index 0000000..e5cbcda --- /dev/null +++ b/.claude/agent-memory/cicd-monitor/archive/2026-05-q3.gist.md @@ -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 `; 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** có `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 ` · 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 diff --git a/.claude/agent-memory/cicd-monitor/archive/2026-05-q4.gist.md b/.claude/agent-memory/cicd-monitor/archive/2026-05-q4.gist.md new file mode 100644 index 0000000..5c460e2 --- /dev/null +++ b/.claude/agent-memory/cicd-monitor/archive/2026-05-q4.gist.md @@ -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… diff --git a/.claude/agent-memory/cicd-monitor/archive/2026-05-runs.gist.md b/.claude/agent-memory/cicd-monitor/archive/2026-05-runs.gist.md new file mode 100644 index 0000000..825ff71 --- /dev/null +++ b/.claude/agent-memory/cicd-monitor/archive/2026-05-runs.gist.md @@ -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 == ...)` là **đ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** và **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` có `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" và "**0/13** = 1" đều là kết-quả ĐÚNG cần xác-nhận; số 0 ở đây là bằng-chứng, không phải thiếu-sót. Dọn field cũ 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) đã là 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 — vá 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 vá · "CI test-gate bắt được lỗi mà UAT-skip-test bỏ sót" · FE merge synthetic + userMap · capture pre-call mutation state. +- **BÀI-HỌC:** khi nguồn đã có lớp chưng-cất, việc nén nên **giữ lớp đó gần như nguyên văn** và cắt ở lớp Run bên dưới — nén lại cái đã nén là 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 là **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" mà 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 là 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 10→98" · "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"))` (vá #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 có mà 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… diff --git a/.claude/agent-memory/implementer-backend/archive/2026-05-q1.gist.md b/.claude/agent-memory/implementer-backend/archive/2026-05-q1.gist.md new file mode 100644 index 0000000..31de81d --- /dev/null +++ b/.claude/agent-memory/implementer-backend/archive/2026-05-q1.gist.md @@ -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 `
`/`` 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` là **độ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 có `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 cũ · 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 có `approverFullName` (chỉ `LevelDto` BE có `approverUserName`) ⇒ phải tra từ `usersList` · shadcn fe-user là **subset tối-thiểu** ⇒ thiếu Card/Badge · `Summary` là 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 cũ + 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` +… diff --git a/.claude/agent-memory/implementer-backend/archive/2026-05-q2.gist.md b/.claude/agent-memory/implementer-backend/archive/2026-05-q2.gist.md new file mode 100644 index 0000000..7a060d6 --- /dev/null +++ b/.claude/agent-memory/implementer-backend/archive/2026-05-q2.gist.md @@ -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:** `
/` + 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 **`
/` 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**: `
` 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`** `
`+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 `
` 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]` diff --git a/.claude/agent-memory/implementer-backend/archive/2026-05-q3.gist.md b/.claude/agent-memory/implementer-backend/archive/2026-05-q3.gist.md new file mode 100644 index 0000000..8f5af8a --- /dev/null +++ b/.claude/agent-memory/implementer-backend/archive/2026-05-q3.gist.md @@ -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-, ]`; (3) quy-ước `source_path` = `solution_erp/pattern/-`; (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-, ]` · **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` diff --git a/.claude/agent-memory/implementer-backend/archive/2026-05-q4.gist.md b/.claude/agent-memory/implementer-backend/archive/2026-05-q4.gist.md new file mode 100644 index 0000000..53275da --- /dev/null +++ b/.claude/agent-memory/implementer-backend/archive/2026-05-q4.gist.md @@ -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 `
` 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/… diff --git a/.claude/agent-memory/implementer-frontend/archive/2026-H2.gist.md b/.claude/agent-memory/implementer-frontend/archive/2026-H2.gist.md new file mode 100644 index 0000000..e92c3e6 --- /dev/null +++ b/.claude/agent-memory/implementer-frontend/archive/2026-H2.gist.md @@ -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 dù bị giục (bảo vệ sàn chất-lượng) — cả hai là 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 vì 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 là đốt spawn vô ích. (b) KHÔNG hạ chuẩn kiểu "Smart-Friend nể-nang": dù bị giục "làm nhanh" vẫn giữ sàn mirror-SHA × 2 app + `npm build` × 2 PASS; thiếu 1 trong 2 thì trả lại, không ship. +- BÀI-HỌC: hai ranh bảo vệ hai thứ khác nhau — (a) ngân-sách token, (b) sàn chất-lượng; cả hai là quyền TỰ TỪ CHỐI của vai, không cần xin phép từng lần. +- BẤT-NGỜ: một ghi-chú "không làm gì cả" lại là mục đáng giữ nhất nhóm — nó đị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 đó và 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) — và 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. Vá = lặp tới khi state đứng yên → A ra `phase=2`, control-âm giữ `3`. Giải bài chính: override 3 trạng-thái `chipPhase: T|null|undefined` (`undefined` = "nghe URL" ≠ `null` = "user chọn Tất cả") + reset TRONG render theo `navKey`, KHÔNG dùng `useEffect` (effect vẽ một lượt dữ-liệu cũ rồi mới sửa = nháy). +- BÀI-HỌC: mô-phỏng hành-vi framework 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 CÓ CHỦ ĐÍCH (fe-user 6/6 mang `group=1`, fe-admin 5/6 vì WfView admin trỏ Designer). Bẫy K4b thứ hai *(bổ @S170 P3-gate)*: CreatePage 8 leaf chung MỘT route nên leaf→leaf 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 và ô 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 ĐÃ CÓ `KHKK_APPROVAL_GROUP_LABELS` (`types/khkk.ts:129`, land từ K2, 5 call-site) ⇒ đẻ map thứ 2 chính là phá đúng acceptance-5 (single-source) mà nó phục vụ; đo thêm thấy nhãn BE ≡ `` `${n}. ${short}` `` cho 8/8 ⇒ giải bằng DERIVE `khkkGroupMenuLabel(n)`, 0 call-site cũ bị đụng (hardcode lần 2 sẽ ra "N2 - 2. …" đánh số kép ở select). ② comment vá-8 bảo "phải nới regex trước vì sắp gỡ `KeHoachKyKet` khỏi ẩn-admin", nhưng `isAdminHidden:194` = `key.startsWith('Ct_')` ⇒ KHKK chưa bao giờ bị ẩn ⇒ viết comment theo ĐĨA + gắn cờ "7 nhóm rỗng" (`MenuGroup` chỉ xét `children.length>0`, không xét con có resolve được ⇒ header hiện, thân rỗng). +- BÀI-HỌC: chứng regex bằng `new Function()` chạy CHÍNH khối text đã ship (`indexOf('const khkkMatch')` → slice → eval), key sinh từ `KhkkGroupNumbers`/`KhkkLeafActions` đọc thẳng `MenuKeys.cs` ⇒ 42/42 resolve, 0 drop, +3 control-âm (G9→null · action rác→null · `Khkk_List` nhóm-1-không-infix→null vì 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()` (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 `` 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"` mà `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 `` · `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 `` chống parser false-match · "0 dòng key `ts:`" (không phạm rào-2) · `WIDTH_CAP` 0 hit + `TIER2_EXPECTED_FULL_ID` 0 hit (vùng CẤM đo bằng diff) +- **root-cause:** ① K4b — "`setState` gọi **trong lúc render** ⇒ React huỷ render đó + render lại ngay với state mới TRƯỚC khi commit; harness bản-1 chỉ chạy 1 pass" ② K5 — "`pe.ContractId = contracts[0].Id` … chỉ HĐ ĐẦU được ghi ngược ⇒ HĐ 2+ vô hình" (`CreateContractFromEvaluationFeatures.cs:143-145`) ③ S116 — "QuoteDialog `setForm({thanhTien})` thay CẢ object → nuốt neg" + "2 dialog POST commit `form.thanhTien` THẲNG (:2247/:2906, no nút 'Lưu')" ④ S153 — "chỉ sửa `: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` 20→21→**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]` diff --git a/.claude/agent-memory/investigator-codebase/archive/2026-05-q1.gist.md b/.claude/agent-memory/investigator-codebase/archive/2026-05-q1.gist.md new file mode 100644 index 0000000..c42d511 --- /dev/null +++ b/.claude/agent-memory/investigator-codebase/archive/2026-05-q1.gist.md @@ -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 -… diff --git a/.claude/agent-memory/investigator-codebase/archive/2026-05-q2.gist.md b/.claude/agent-memory/investigator-codebase/archive/2026-05-q2.gist.md new file mode 100644 index 0000000..6d15b61 --- /dev/null +++ b/.claude/agent-memory/investigator-codebase/archive/2026-05-q2.gist.md @@ -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 `
/` 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 `
  • ` `: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 — `
    /` native + badge `
    ` 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:**… diff --git a/.claude/agent-memory/investigator-codebase/archive/2026-05-q3.gist.md b/.claude/agent-memory/investigator-codebase/archive/2026-05-q3.gist.md new file mode 100644 index 0000000..05fcc8a --- /dev/null +++ b/.claude/agent-memory/investigator-codebase/archive/2026-05-q3.gist.md @@ -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 diff --git a/.claude/agent-memory/investigator-codebase/archive/2026-05-q4.gist.md b/.claude/agent-memory/investigator-codebase/archive/2026-05-q4.gist.md new file mode 100644 index 0000000..38c4c65 --- /dev/null +++ b/.claude/agent-memory/investigator-codebase/archive/2026-05-q4.gist.md @@ -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… diff --git a/.claude/agent-memory/memory-budget.json b/.claude/agent-memory/memory-budget.json index c57dc44..d12d0b9 100644 --- a/.claude/agent-memory/memory-budget.json +++ b/.claude/agent-memory/memory-budget.json @@ -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.", diff --git a/.claude/agent-memory/reviewer/MEMORY.md b/.claude/agent-memory/reviewer/MEMORY.md index 0b81ebc..39bc027 100644 --- a/.claude/agent-memory/reviewer/MEMORY.md +++ b/.claude/agent-memory/reviewer/MEMORY.md @@ -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) · **`