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

> **Vai:** harvest-curator (H2) · **PROPOSE-ONLY** — file này là kênh run-trace B4/B6, KHÔNG phải ghi trí-nhớ.
> **Batch A** = `cicd-monitor` (5 verbatim) + `implementer-backend` (4 verbatim) = 9 file gen-0.
> **Phương-pháp:** đóng-kín TỪNG file (chống cross-file bleed) — cụm-dedup + importance-tag + bản-nén-nháp 4-field SE + danh-sách token.
> **Khung 4-field SE:** VIỆC · KẾT-LUẬN(+commit/file:line) · BÀI-HỌC · BẤT-NGỜ — rút gọn câu chữ, KHÔNG bỏ field.
> **Token-list** = nguyên-liệu coverage-diff của lead: {surprise · guard · file:line · root-cause · gotcha#}. Thiếu = gate FAIL.
> **Lưu ý đọc:** mọi token dưới đây trích NGUYÊN VĂN từ verbatim gen-0; bản-nén là ĐỀ-XUẤT, chưa phải bản chốt.

---

## cicd-monitor/2026-05-q2 (3.877 B — "Archive Recent Runs Q2, Plan B detail")

**Đặc-điểm file:** 1 entry DUY NHẤT (Run #231) + header-rule. Archive S32 em-main proxy curate, lý-do = MEMORY.md 27KB → target ~24KB.

### Cụm-dedup

| # | Cụm | Thành-phần gộp | Tag |
|---|---|---|---|
| A1 | **Run #231 Plan B Contract V2 wire — VERDICT=PARTIAL** | deploy PASS (3m30s, bundle 2/2 rotate, Mig 32+33 applied, 5/5 smoke, V1 compat) + seed PARTIAL | **vừa** |
| A2 | **DISCOVERY #6 — DemoSeed flag nuốt seed V2 → gotcha #51** | phát-hiện + log-evidence + 2 fix-option (a) admin-tạo-tay / (b) carve-out + escalate + **Resolution Run #232 `38f1c4d` chọn (b)** | **cao** |
| A3 | **Test-gap: ~150 LOC `ApproveV2Async` 0 test** (đúng khuôn gotcha #48 high-risk) | test baseline 111 unchanged + khuyến-nghị Plan B+1 | **cao** |
| A4 | **SlaExpiryJob ERR cluster 5 entry — nhiễu prod CÓ TRƯỚC, KHÔNG liên-quan Plan B** | 4 transition V1 fail + phán-định unrelated + escalate riêng | **thấp** |

> Ghi-chú dedup: A2 vốn nằm rải 2 đoạn (phát-hiện ở thân + Resolution ở dòng cuối) — gộp 1 cụm vì cùng một sự-kiện đóng-mở.

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

**[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.

**[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.

### Token-list (verbatim, cicd-monitor/2026-05-q2)

- **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

---

## cicd-monitor/2026-05-q3 (3.949 B — "Archive Q3, S34 curate")

**Đặc-điểm file:** 3 entry — 1 cặp FAIL→PASS (dày), 1 verify cumulative, 1 setup-baseline.

### Cụm-dedup

| # | Cụm | Thành-phần gộp | Tag |
|---|---|---|---|
| B1 | **Cặp Run #215 FAIL → #216 PASS = gotcha #48 SQLite tie-break** | 2 test fail cùng root-cause + phân-tích tie-break + fix `.Where(Summary Contains)` + 8 phút turnaround + nhận-định "UAT skip-test rủi-ro cho refactor" | **cao** |
| B2 | **Verify S22 chốt cuối + Discovery #3 `**/*.md` glob nuốt CI** | 12 commit PASS/104 test/Mig 30 + giả-thuyết spec BỊ BÁC + gotcha #47 hạ xuống vai-trò PREVENTIVE | **cao** |
| B3 | **Setup baseline 2026-05-12** | khởi-tạo agent, 44 gotcha + 5-stage + 3 skill, 0 run | **thấp** |

> Ghi-chú dedup: B1 vốn là 2 mục Run riêng nhưng verbatim đã tự gộp title "catch+fix pair" — giữ 1 cụm, KHÔNG tách (tách sẽ mất mạch nhân-quả).

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

**[cao] B1 — Run #215 FAIL → #216 PASS (gotcha #48)**
- **VIỆC:** Verify Plan AB Chunk A (Changelog visibility: Bug 1 Budget Adjust + Bug 2 Return Mode) — 1 commit 3 file, BE `PurchaseEvaluationWorkflowService.cs` +207/-95 + 2 FE `PeDetailTabs.tsx`.
- **KẾT-LUẬN:** #215 (id=329, `cdfd542`, push `e23f51c..cdfd542`) **FAIL** ở test_infra 51/53 — 2 test ReturnMode báo `Expected changelog.ContextNote not to be <null>`; deploy KHÔNG chạy (bundle giữ nguyên Run #214, Mig 31 TOP 1 y cũ). #216 (id=330, `8c05947`) **PASS** — chỉ sửa TEST (+7/-2) thêm `.Where(c => c.Summary!.Contains("Chuyển phase"))` trước `OrderByDescending(CreatedAt).First()`; code Chunk A **không revert**; 58/58 + 53/53, bundle 2/2 rotate, 10:13→10:21 = 8 phút.
- **BÀI-HỌC:** SQLite + **clock đóng-băng** ⇒ 2 changelog cùng `CreatedAt` ⇒ `OrderByDescending` tie-break trả nhầm bản-ghi ⇒ test "lấy entry mới nhất" là **thước không xác-định**; phải lọc theo NỘI-DUNG (Summary) chứ đừng dựa thứ-tự thời-gian. Refactor BE >100 LOC nên `dotnet test` local TRƯỚC push — UAT skip-test không đủ.
- **BẤT-NGỜ:** test đỏ mà **code sản-phẩm đúng** — lỗi nằm ở thước đo; và CI test-gate chặn đúng lúc nên bro tránh được audit-trail vỡ trên prod (fail lại hoá may).

**[cao] B2 — Verify S22 cumulative + Discovery #3 (glob `**/*.md`)**
- **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/`.

**[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.)

### Token-list (verbatim, cicd-monitor/2026-05-q3)

- **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 <null>` · Run #215 id=329 `cdfd542` (1m06s) / Run #216 id=330 `8c05947` (+7/-2) · push `e23f51c..cdfd542` · Run #214 (baseline bundle) · Mig 31 TOP 1 · Run S22: push `3d725c4..cc8a7d3` 12 commit, Mig 30 F4, 33 user, 104/104 (baseline S21=103) · commit `cc8a7d3` (docs + 4 agent MEMORY.md → CI SKIPPED) · PE `c6e9` · 51/53 → 58/58 + 53/53 · 10:13→10:21 (8 phút) · setup 2026-05-12

---

## cicd-monitor/2026-05-q4 (10.576 B — "late May, S32-S34 verbose Run entries")

**Đặc-điểm file:** 7 entry — 3 Run deploy (#238, #237, #350) + 3 snapshot phi-deploy (S33 startup, S32 wrap, S32 startup) + 1 cumulative-Run S26. Archive S36 startup curate.

> 🔴 **Coverage-note cho lead:** header (dòng 3) khai archive gồm "**Discovery #8** NEW collection naming" nhưng **THÂN FILE 0 hit `Discovery #8`** — nội-dung nằm ở entry S35/S36 KHÔNG thuộc file này. Đây là **con-trỏ mồ-côi trong header**, không phải mất dữ-liệu; khi nén đừng chép nhãn #8 vào bản-nén của file này (sẽ thành cite-bịa).

### Cụm-dedup

| # | Cụm | Thành-phần gộp | Tag |
|---|---|---|---|
| C1 | **Chuỗi 3 Run module-mới S33→S34 (#350 → #237 → #238)** — Hrm schema → Hrm wire → Office directory | 3 entry Run, cùng khuôn: ALL-PASS 5 stage + Stage 4/4.6 sqlcmd + menu-seed verify + Pattern 12-bis cookie-cutter; bundle-chain nối liền 3 mốc | **cao** |
| C2 | **Snapshot phi-deploy S32×2 + S33×1** (startup/wrap/health-check) | trùng nặng: cert notAfter · 3-endpoint smoke 200 · bundle baseline · "foundation entries CONFIRMED preserved" · docs-only skip #41 · MEMORY size-watch | **vừa** |
| C3 | **DISCOVERY #7 — `paths-ignore` thiếu `eval/**` ⇒ deploy lãng-phí** | tách khỏi C2 vì là phát-hiện có hành-động, không phải snapshot | **cao** |
| C4 | **S26 Run #222-#227 — khuôn "hybrid verify" (spawn 1× đầu Phase, self-verify polish)** | 1 entry, nhưng là bài-học KINH-TẾ spawn (ROI), giá-trị dài-hạn | **cao** |
| C5 | **3 ca "gotcha KHÔNG xảy ra" + 1 ca "test bắt được 0"** (rải trong C1) | #1 MediatR / #44 silent-403 / #51-áp-đúng / BW1-7 caught NOTHING | **vừa** |

> Ghi-chú dedup: C2 gộp 3 entry vì **cùng nội-dung tái-khẳng-định** (cert 2026-07-23, smoke 200, foundation preserved) — chỉ 3 dữ-kiện là RIÊNG: MEMORY 24.9KB/221 dòng (S32-start), MEMORY 27→24.2KB + agent-id (S32-wrap), Mig TOP 5 + last-5-Run (S33-start). C5 cố-ý tách vì "âm-tính" dễ bị nén mất mà lại là bằng-chứng khuôn đã ổn-định.

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

**[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ò.

**[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.

**[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.

**[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.

**[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.

### Token-list (verbatim, cicd-monitor/2026-05-q4)

- **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)
- **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`

---

## cicd-monitor/2026-05-runs (59.848 B — "Archive Runs Q2, Run #186 → #221 / S21 t3 → S25 Plan AF")

**Đặc-điểm file:** file LỚN NHẤT batch A. 3 phần: (a) **"Last curate logs"** 6 mục tóm-tắt · (b) **"Run entries archived"** 20 mục Run đầy-đủ · (c) **"Key patterns extracted (cumulative S21-S25)"** 8 mục = **lớp chưng-cất có sẵn**. Header khai nguồn MEMORY.md ~72KB/~211 dòng/~14 entry → mục-tiêu ~25-30KB.

> 🔴 **3 cảnh-báo cho lead TRƯỚC khi nén:**
> **(1) TRÙNG NỘI-FILE:** phần (a) và phần (b) **chồng nhau 4 lần** — #202 (dòng 16 vs 88) · #203 (20 vs 109) · #210 (24 vs 145) · #219 (36 vs 168). Nhưng **#215 (dòng 28) và #217 (dòng 32) CHỈ có ở phần (a)** — nếu nén bằng cách "bỏ phần tóm-tắt, giữ phần đầy-đủ" thì **mất trắng Run #217** (Plan AC, nguồn duy nhất của pattern "capture pre-call mutation state" + Discovery #5).
> **(2) TRÙNG LIÊN-FILE (chỉ báo, KHÔNG gộp — giữ đóng-kín):** mục "Verify S22 chốt cuối cumulative" có ở **cả** file này (dòng 54, bản DÀY) **lẫn** `2026-05-q3.md` (dòng 22, bản MỎNG); Run #215 có ở cả hai (dòng 28 mỏng ở đây / q3 dòng 10 dày). ⇒ khi lead hợp-nhất phải chọn **bản DÀY hơn**, đừng chọn theo thứ-tự file.
> **(3) 🔐 BÍ-MẬT LỘ TRONG FILE (ưu-tiên cao, ngoài phạm-vi nén):** **dòng 48** chứa **mật-khẩu DB prod dạng thô** — `appsettings.Production.json` ở `C:\inetpub\solution-erp\api\`, `user=vrapp` + `pwd=<24 ký-tự, CỐ Ý KHÔNG chép lại ở đây>`. File này **git-tracked**. Đề-xuất em-main: (i) khi nén **redact** chuỗi đó, (ii) cân nhắc xoay mật-khẩu vì đã nằm trong lịch-sử git. Vai này KHÔNG tự sửa (propose-only).

### Cụm-dedup

| # | Cụm | Thành-phần gộp (Run) | Tag |
|---|---|---|---|
| D1 | **Chuỗi hotfix per-NV lookup discrimination → checklist 10 điểm** | #194 PARTIAL (K3 DTO thiếu field) → #195 (K10, chốt 8 điểm) → #201 (Plan N, điểm 9) → #202 (Plan O, 4 site cascade) → #203 (Plan P, điểm 10) | **cao** |
| D2 | **Saga Discovery #3 — `paths-ignore` xét TRỌN push-range** | #188 (Discovery #2) → S22-chốt (**bác** giả-thuyết) → #200 (bất-thường) → #201 (lần 2) → #202 (lần 3, chốt) → #210 (lần 4, không áp-dụng) → Key-pattern 1 | **cao** |
| D3 | **Chuỗi audit-trail S25 (Plan AB→AF)** | #215 FAIL → #217 (AC) → #218 (AC2) → #219 (AD) → #220 (AE) → #221 (AF) | **cao** |
| D4 | **Chuỗi schema Mig 28→31 + backfill** | #186 (Mig 28) · #187 (Mig 29 + backfill 48/48 & 0/13) · #188 (Plan C/D/E) · S22-chốt (Mig 30) · #194 (Mig 31) | **vừa** |
| D5 | **Plan T — tắt DemoSeed + CHỨNG bằng restart thật** | #207 (flag) · #208 (T5 xoá + T6 restart chứng-minh) | **cao** |
| D6 | **Đo-đạc / vận-hành (7 phát-hiện công-cụ)** | #186 endpoint Gitea · #187 `updated_at` trễ · #188 rate-limit 429 + cache token · #217 Discovery #5 · #203 Discovery #4 · #221 503 + route/`accessToken` · #187 credential prod | **cao** |
| D7 | **Phình MEMORY 35→72KB, 8 lần cảnh-báo leo thang** | rải ở cuối mọi mục phần (a) + #218/#219/#220/#221 | **cao** |
| D8 | **Đánh-bóng UX FE + 1 ca revert-trong-phiên** | #204 (banner `mx-5`) · #209 (Plan U truncate) · #210 (Plan AA widen + **revert Plan U**) · #214 (wrap fix) | **thấp** |
| D9 | **"Key patterns extracted" 8 mục — lớp chưng-cất SẴN CÓ** | phần (c) dòng 182-201 | **cao** |
| D10 | **Tác-dụng-phụ UAT tự khai + giới-hạn phép đo tự khai** | #202 caveat #2 (PE/025 10→98) · #203 side-effect + "Test 2 409 = không đổi state" · #203 "NV Test không khả-thi (quét 20 PE)" · #194 "presumed ×4" · #219 sqlcmd bỏ qua (thiếu `PROD_DB_PASSWORD`) | **cao** |

> Ghi-chú dedup: D1 và D2 **đan nhau về mặt Run** (#201/#202 nằm ở cả hai) nhưng **khác trục** (một bên là lỗi CODE, một bên là hành-vi CI) ⇒ giữ 2 cụm, khi nén phải viết 2 dòng riêng cho cùng Run. D8 gộp 4 Run vì nội-dung là CSS, giá-trị dài-hạn thấp — **trừ** chi-tiết "Plan U bị Plan AA revert cùng phiên" (giữ, vì đó là bài về đọc bundle-hash tích-luỹ).

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

**[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.

**[cao] D2 — Saga Discovery #3 (`paths-ignore` xét trọn push-range)**
- **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.

**[cao] D3 — Chuỗi audit-trail S25 (Plan AB → AF): 5 khuôn phục-hồi lịch-sử**
- **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.

**[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.

**[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.

**[cao] D7 — MEMORY phình 35→72KB: 8 lần cảnh-báo leo thang mà không ai dừng**
- **VIỆC:** vai tự theo-dõi kích-thước diary của chính mình sau mỗi entry.
- **KẾT-LUẬN:** chuỗi tự khai: ~35KB ("approaching") → ~40 → ~43 ("over 25KB trigger, under 50KB hard") → ~50 ("at hard threshold") → ~58 ("**REQUIRED** ARCHIVE ... BEFORE next entry") → ~60 ("**MANDATORY**") → ~62 ("**DEDICATED CURATION SESSION PRIORITY NEXT**") → ~66→69 ("priority MAX") → ~72 ("**PRIORITY MAX REINFORCED**"). Chốt chỉ xảy ra khi có **phiên curate riêng** (chính file 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ế.

**[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**.

**[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`).

**[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.

**[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** (liệt-kê đủ 10) · 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.
- **BẤT-NGỜ:** checklist 10 điểm ở đây 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.

### Token-list (verbatim, cicd-monitor/2026-05-runs)

- **gotcha#:** `#41` (path filter — dẫn ≥6 lần) · `#44` (silent 403 — "fix confirmed" ở #210) · `#45` (fix Trả lại, Run #186) · `#47` (agent-memory — **disproven**, giữ vai PREVENTIVE) · `#48` (**NEW** multi-`Changelog.Add` + SQLite frozen-clock tie-break)
- **Discovery#:** `#1` rate-limit 429 ~5 req/phút · `#2` `.claude/agent-memory/**` không nằm trong paths-ignore (**về sau bị bác**) · `#3` Gitea xét TRỌN push-range (củng-cố 4 lần: #200/#201/#202 + #210 "NOT applicable") · `#4` ASP.NET Core 10 record+enum cần **số** · `#5` sqlcmd cần `\\\\SQLEXPRESS` **4 backslash** · *(không đánh số)* `/actions/tasks` ≠ `/actions/runs` (#186) · `updated_at` trễ ~2 phút (#187) · "Discovery 1" 503 giữa deploy + "Discovery 2" `/api/auth/login` + field `accessToken` (#221)
- **surprise:** `CRITICAL FAIL — K3 DTO mirror INCOMPLETE` · "`AllowApproverSkipToFinal` mentioned **only in comment line 60**, NOT as actual record param" · "**em main + Reviewer + Implementer all missed across 3 plan**" · "Bug present **2 days prod**" / "**3-day prod bug latency**" · "Spec hypothesis ... **disproven for this commit**" · "**Anomaly is BENEFICIAL** — catches verify gate even on docs-only tip" · "PROVES ACTOR VALIDATION SUCCESS ... 409 mode mismatch NOT 403" · "First attempt with **string enum** `\"Drafter\"` → HTTP 400 ... Brief example payload was **misleading**" · "Test 2 HTTP 409 = handler threw exception → **no state change (atomic)**" · "NV Test actor variant **not feasible** (scanned 20 PEs)" · "the Budget Adjust entry ... has `userName=\"\"` empty — this is the **EXACT pre-fix Bug 1 evidence**" · "**Side effect logged:** Admin transition on PE/2026/A/025 mutated phase 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 (KHÔNG chép giá-trị):** dòng **48** — `appsettings.Production.json` tại `C:\inetpub\solution-erp\api\`, `user=vrapp`, mật-khẩu 24 ký-tự dạng thô → **đề-xuất redact + xoay khoá**
- **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`

---

## cicd-monitor/2026-08 (5.912 B — "L2 archive, moved byte-exact từ L1 khi vượt soft-cap")

**Đặc-điểm file:** 4 entry (S166-S167, 08-01) — **KHÔNG phải văn-xuôi verbose như 4 file trên**. Đây là **L2 đã chưng-cất sẵn**: 2 entry dạng đầy-đủ + 2 entry dạng `_( … )_` in-nghiêng cực-nén, cả 4 đều **trỏ verbatim ra `runs/…/sub-cicd-verify-*.md`**.

> 🔴 **KHUYẾN-NGHỊ NGƯỢC cho lead — ĐỀ NGHỊ KHÔNG NÉN FILE NÀY:**
> (a) mật-độ đã ở mức chưng-cất (5.9KB chứa ~20 bài-học phân-biệt được); nén tiếp là **mất lớp chưng-cất**, đúng khuôn hỏng đã ghi ở S162/F-02.
> (b) file này **mới nhất trong batch A** (mtime 08-01, cùng phiên đang chạy) — giá-trị truy-hồi còn nóng, khác hẳn 8 file 2026-05.
> (c) 🟢 **Đã VERIFY con-trỏ, cả 4 đều SỐNG** (đo trên đĩa hôm nay): `sub-cicd-verify-k7.md` 23.745 B · `k6.md` 12.886 B · `k5-k4c.md` 10.869 B · `k4ab.md` 24.451 B — đều ở `.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/`. ⇒ Rút gọn thêm **được phép về mặt an-toàn** (nguyên-liệu còn), nhưng **không nên** vì (a)+(b).
> Nếu buộc phải chạm: chỉ đề-xuất **gỡ 2 entry `_( … )_`** (K5/K4c + K4a/K4b) vì đã có verbatim sống, **GIỮ NGUYÊN** 2 entry K7 + K6.

### Cụm-dedup

| # | Cụm | Thành-phần | Tag |
|---|---|---|---|
| E1 | **S167 K7 `aaed699` — PUSH-ĐÈ NUỐT DEPLOY (đề-xuất gotcha #86)** | run #441 CANCELLED + ship nhờ MAY + luật ancestry + 4 kỹ-thuật phụ (tiền-đề marker · control-dương tiền-commit · admin-idempotent · appcmd map PID) + nv.test 401 | **cao** |
| E2 | **S167 K6 `1c6d77a` — 0-hit GIẢ 2 lần liên-tiếp do cơ-chế regex** | backtick-minify + shell nuốt backtick → luật `grep -F` + trích byte · COUNT-DELTA · log-CI nhân-chứng-4 · frozen-lành-tính · 2-tầng #82 đo bằng số | **cao** |
| E3 | **S166 K5+K4c `e202393` — marker differential + 2 bẫy đo** | 4/4 element-wise · recipe đếm Mig **false-negative** · bar "FE route 200" **không có răng** (SPA-fallback) | **cao** |
| E4 | **S166 K4a+K4b `88e7ecf` — phủ 2 run 1 lượt + mốc PRE lão-hoá** | delta khép kín · đề-bài SAI bắt bằng đo (143⇒192 vs LIVE 151) · perm-row ≠ policy · soi điều-kiện log trước khi đọc im-lặng · marker lead **bị bác** | **cao** |
| E5 | **META xuyên 4/4 entry: "vắng-mặt / 0-hit KHÔNG được đọc thành kết-luận"** | E1 tiền-đề-marker · E2 0-hit-giả ×2 · E3 false-negative + POS≡NEG · E4 marker-lead-bị-bác | **cao** |

> Ghi-chú dedup: E5 **không phải entry riêng** mà là trục chung rút từ cả 4 — nếu lead nén, E5 là thứ **phải sống sót nguyên vẹn**, vì nó là bài lặp 4/4 lần (bằng-chứng mạnh nhất trong file).

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

**[cao] E1 — K7: push-đè nuốt deploy**
- **VIỆC:** verify `aaed699` (bridge KHKK→HĐ, 13 file cross-stack 4 BE + 6 FE×2 + 3 test, 0-mig).
- **KẾT-LUẬN:** **PARTIAL 4/6+1** (gate **639 = 45+594**, Δ+17; Mig giữ **71**; bảng **97**; bundle `Ig1R5tAh→RgvHZkPH` + `DFOCQ2ks→D65FJPGd`). Run **#441 bị CANCELLED ở 3m02s** vì lead push `628132c` đè lên (Gitea cancel-in-progress); code lên được prod **chỉ nhờ may** — `628132c` tình cờ chạm `fe-*/src/**` nên đẻ run #442.
- **BÀI-HỌC:** sau **2 push liên tiếp**, bắt buộc verify hai điều: run cuối có `head_sha == TIP` **và** TIP chứa commit code (`git merge-base --is-ancestor`). **Cấm** đọc "bảng Actions có run xanh" là xong. Kèm 3 kỹ-thuật rẻ: kiểm **tiền-đề của marker** trước khi tin 0-hit (nghi `\u`-escape và nghi code-split — **cả hai đều sai, nhưng phải ĐO mới có quyền kết luận**: `grep -c -F 'Hợp đồng'`=4 trên bundle PRE; index.html chỉ 2 asset); **control dương = chuỗi bản tiền-commit của chính file đang sửa** (4/4); **admin-seeder = control idempotent miễn phí** (3 role báo **2 upgraded**, không phải 3 ⇒ nhánh no-op đã chạy thật).
- **BẤT-NGỜ:** kịch-bản chết-người là kịch-bản **KHÔNG xảy ra lần này**: nếu commit-2 rơi trọn vào `paths-ignore` (docs/memory-only) thì run-1 bị huỷ + run-2 bị skip ⇒ **code không bao giờ lên prod mà mọi bảng đều trông lành**. Và `appcmd list wp` cứu một hiểu-nhầm: PID 7680 khởi cùng cửa-sổ nhưng thuộc `vietreport-admin` — **dự án khác**.

**[cao] E2 — K6: 0-hit giả hai lần, cùng một cơ-chế regex**
- **VIỆC:** verify `1c6d77a` (`ADMIN_HIDDEN_KEYS` 12 key, FE-admin 1 file, 0-mig).
- **KẾT-LUẬN:** **PASS 5/5** (#440 6m29s; gate **622 = 45+577** delta 0; Mig 71; bảng 97; menu 200 / perm 2221 đứng im). Nhưng marker báo 0-hit **sai 2 lần liên-tiếp**: (a) rolldown/Vite 8 minify chuỗi thành **backtick** (`new Set([\`PurchaseEvaluations\`,…])`) nên regex neo `["']` **không đời nào khớp**; (b) vá xong lại truyền pattern-chứa-backtick qua biến trong `"…"`, shell nuốt ⇒ `grep -cE` gãy (`Unmatched (`) và trả **0 gần như im lặng** trong vòng lặp.
- **BÀI-HỌC:** marker chuỗi trên bundle phải dùng **`grep -F` fixed-string** và **luôn trích byte thật `.{90}X.{90}`** trước khi kết-luận vắng-mặt. Thêm **COUNT-DELTA**: khi literal đã tồn-tại từ trước thì *có/không* = **0 bit**, nhưng *số lần xuất hiện* PRE→POST vẫn đo được (**12/12 key đều +1**).
- **BẤT-NGỜ:** ngoại-lệ duy nhất `Workflows` **+2** lại **giải-thích được** (là substring của `PeWorkflows`) ⇒ chỗ lệch biến thành **kiểm-tra nhất-quán miễn phí**. Và log CI **in thẳng `dist/assets/index-<hash>.js <size>`** = nhân-chứng **thứ 4** độc-lập với curl, khớp LM prod tới từng giây ⇒ phân-biệt được **FROZEN lành-tính** (eoffice LM 11:14→**12:00:41**, log in **cùng hash `DFOCQ2ks`** = rebuild tất-định) với bundle kẹt thật.

**[cao] E3 — K5+K4c: marker differential + 2 bẫy đo**
- **VIỆC:** verify `e202393` (FE-only).
- **KẾT-LUẬN:** **PASS 5/5** (#439 6m16s; gate **622 = 45+577**, +1 đúng bằng 1 `[Fact]` mới). Marker differential **4/4 element-wise** (K5 vào **cả 2** app · K4c **chỉ** eoffice) + Δsize cùng chiều = nhân-chứng vuông-góc với grep.
- **BÀI-HỌC:** recipe đếm Mig đang nằm trong MEMORY có **false-negative**: `grep -v Snapshot` ăn nhầm `AddPeApprovedBudgetSnapshot` (Mig 67) ⇒ phải neo đuôi `Designer\.cs$|ModelSnapshot\.cs$`, và ưu-tiên **set-diff repo↔prod 2 chiều** thay vì khớp tổng.
- **BẤT-NGỜ:** thước "FE route trả 200" **không có răng** với SPA-fallback — ca dương và ca âm **giống nhau tới từng byte (876 B)**; tức một thước đã dùng lâu nay chứng-minh được **0 bit**.

**[cao] E4 — K4a+K4b: phủ 2 run một lượt, mốc PRE lão-hoá**
- **VIỆC:** verify `88e7ecf`.
- **KẾT-LUẬN:** **PASS 7/7** (#438; gate 621 = 45+576; menu **151→200**; perm **1584→2221**). Phủ **2 run trong 1 lượt** ⇒ delta khép kín, không hở khoảng giữa. Phân rã **49+588** khớp cơ-chế 2-site skip-existing (**588 = 49 × 12 role**).
- **BÀI-HỌC:** **mốc PRE lão-hoá sau MỖI deploy** — đề-bài của lead ghi 143⇒192, đo LIVE ra **151**; xử đúng là **truy phạm-vi**, không hô "drift". Và **perm-row ≠ policy** (đếm row không nói được quyền). Trước khi đọc log im-lặng thành hồi-quy, phải **soi điều-kiện in log trong source** (`if (added>0)`).
- **BẤT-NGỜ:** marker do chính lead gợi ý **bị bác bằng đo** — chữ `Kết cấu` vốn đã có sẵn nên 1/1 = **0 bit**; người ra đề cũng là nguồn sai, và cách chữa là đo chứ không phải cãi.

**[cao] E5 — META (xuyên 4/4 entry): vắng-mặt không phải kết-luận**
- **VIỆC:** trục lặp lại ở cả 4 entry của file.
- **KẾT-LUẬN:** 4 dạng dương-giả/âm-giả khác nhau, cùng một bệnh: 0-hit do **regex sai cơ-chế** (E2 ×2) · 0-hit do **chưa kiểm tiền-đề** (E1) · thước **0 bit** vì literal có sẵn (E4) hoặc vì POS≡NEG (E3) · recipe đếm có **false-negative** (E3).
- **BÀI-HỌC:** mỗi phép đo phải kèm **control dương** (thứ chắc-chắn phải khớp) và **trích byte thật**; "không thấy" chỉ có nghĩa khi đã chứng được thước còn răng.
- **BẤT-NGỜ:** trong cùng một file, **suýt FAIL-OAN 3 lần** (K6 ×2 + K7 ×1) — nghĩa là kỷ-luật này không phải lý-thuyết mà là thứ vừa cứu 3 bàn thua.

### Token-list (verbatim, cicd-monitor/2026-08)

- **gotcha#:** **`#86` (ĐỀ-XUẤT MỚI — push-đè nuốt deploy)** · `#82` (2 tầng display ⟂ API — đo bằng số ở K6, **chưa đo được tầng-2 Drafter** ở K7) · Mig `67 AddPeApprovedBudgetSnapshot` (nạn-nhân của recipe false-negative) · K6 nhắc `K4a`
- **surprise:** `PUSH-ĐÈ NUỐT DEPLOY` · "K7 ship được **CHỈ NHỜ MAY**" · "code KHÔNG BAO GIỜ lên prod, mà bảng Actions trông vẫn lành + `git log` đã push" · "**0-hit marker SAI 2 LẦN LIÊN TIẾP do CƠ-CHẾ REGEX** ... suýt FAIL-OAN" · "suýt FAIL-OAN **lần 3**" · "Cả 2 nghi đều SAI nhưng **phải đo mới được quyền kết luận**" · "ngoại lệ duy nhất `Workflows` **+2** giải-thích-được (substring ⊂ `PeWorkflows`)" · "**bar 'FE route 200' KHÔNG CÓ RĂNG**" (POS ≡ NEG byte-identical **876B**) · "**recipe đếm mig trong MEMORY có FALSE-NEGATIVE**" · "**MỐC PRE ĐỀ BÀI SAI, bắt bằng ĐO**" · "**marker lead gợi ý BỊ BÁC BẰNG ĐO** (`Kết cấu` có sẵn ⇒ 1/1 = 0 bit)" · "**PERM row ≠ policy**" · "log lần-2 sẽ **IM LẶNG** — đừng đọc thành hồi-quy" · "PID 7680 ... là `vietreport-admin` (**dự án KHÁC**)" · "**CẤM lấy Admin-qua-được để tuyên grant đã ăn**"
- **guard:** `git merge-base --is-ancestor` + `head_sha == TIP` (luật 2-push) · `grep -F` fixed-string + trích byte `.{90}X.{90}` · **control dương** = chuỗi bản tiền-commit của chính file đang sửa (4/4) · `if(changed) upgraded++` · `if(added>0||upgraded>0)` (guard làm log im) · `if (added>0)` · `appcmd list wp` map PID→pool · neo đuôi `Designer\.cs$|ModelSnapshot\.cs$` + **set-diff repo↔prod 2 chiều** · marker-ÂM display-only (route `/purchase-evaluations` **68→68**) · COUNT-DELTA PRE→POST
- **root-cause:** Gitea **cancel-in-progress** khi push mới đè ⇒ run-1 huỷ; nếu commit-2 trọn `paths-ignore` thì run-2 skip ⇒ **cửa sổ mất deploy im-lặng** · rolldown/Vite 8 minify chuỗi thành **backtick** ⇒ regex neo `["']` không khớp · pattern-chứa-backtick truyền qua biến trong `"…"` ⇒ **shell nuốt**, `grep -cE` gãy `Unmatched (` trả 0 im-lặng · `grep -v Snapshot` khớp nhầm tên Mig chứa chữ *Snapshot* · **SPA-fallback** trả 200 cho mọi route ⇒ thước route mất răng · literal đã tồn-tại từ commit trước ⇒ marker *có/không* = 0 bit
- **file:line / định-danh:** sha `aaed699` (K7) · `628132c` (commit đè) · `1c6d77a` (K6) · `e202393` (K5+K4c) · `88e7ecf` (K4a+K4b) · run **#438 / #439 6m16s / #440 6m29s / #441 3m02s CANCELLED / #442** · gate `639=45+594` (Δ+17) · `622=45+577` (delta 0) · `622=45+577` (+1) · `621=45+576` · Mig **71** · tables **97** · bundle `Ig1R5tAh→RgvHZkPH` + `DFOCQ2ks→D65FJPGd` · hash lặp `DFOCQ2ks` (rebuild tất-định) · `ADMIN_HIDDEN_KEYS` **12-key** · `new Set([\`PurchaseEvaluations\`,…])` · `grep -c -F 'Hợp đồng'` = **4** · `dist/assets/index-<hash>.js <size>` (log CI) · LM `11:14 → 12:00:41` · menu **151→200** · perm **1584→2221** · phân rã **49+588 = 49×12 role** · mốc PRE đề-bài `143⇒192` vs LIVE **151** · `nv.test` login **401** · PID **7680** / pool `vietreport-admin` · seeder **3 role → 2 upgraded**
- **evidence-pointer (ĐÃ ĐO, 4/4 SỐNG):** `runs/2026-07-31-S164-4gd-khkk-fanout/sub-cicd-verify-k7.md` **23.745 B** · `…/sub-cicd-verify-k6.md` **12.886 B** · `…/sub-cicd-verify-k5-k4c.md` **10.869 B** · `…/sub-cicd-verify-k4ab.md` **24.451 B**
- **tag gốc:** `[run-442, push-de-nuot-deploy, ancestry-proof, tien-de-marker, control-duong-tien-commit, admin-control-idempotent, appcmd-map-pid]` · `[run-440, 0-hit-gia-2-lan, grep-F, count-delta, frozen-lanh-tinh, log-CI-nhan-chung-4]`

---

## implementer-backend/2026-05-q1 (18.512 B — "Archive Recent Activity Q1, S21 t3 → S24 Plan AA, 12 entry")

**Đặc-điểm file:** 12 entry. Chia rõ 2 loại: **4 entry "REFUSE"** (vai từ-chối nhận việc, em-main tự làm) + **8 entry "PASS"** (vai làm thật). Header khai nguồn MEMORY.md **38.8 KB**, giữ lại Pattern 1-19.

### Cụm-dedup

| # | Cụm | Thành-phần | Tag |
|---|---|---|---|
| F1 | **Nhật-ký TỪ-CHỐI + 7 tiêu-chí phạm-vi** | S21 t3-t5 (REFUSE 3/3) · S22 (REFUSE 100%) · S23 t4-t11 (**8 plan liên-tiếp, 0 spawn**) · S24 wrap (REFUSE 4/4, ghi rõ tiêu-chí từng ca) | **cao** |
| F2 | **Chuỗi K của Mig 31 (pre-A → K1 → K3 → K5 → K7)** | UI label · schema swap Users→Levels · checkbox thứ 7 · dọn zombie · test hồi-quy | **cao** |
| F3 | **4 "Mismatch" tự khai ở S22** | per-NV opt-in vs default · `PE.changelogs` vắng · Dialog `size="xl"` · `accessToken` ≠ `token` | **cao** |
| F4 | **Plan AA (Chunk B + wrap) — trang matrix read-only cho fe-user** | 3 file mới · thiếu shadcn Card/Badge · Pattern 13/14/15 mới | **vừa** |
| F5 | **Plan M (M2 test + M3 đổi nhãn FE)** | mở-rộng helper không clone · kỷ-luật phạm-vi hẹp + ngưỡng chống-nghịch | **vừa** |

> Ghi-chú dedup: F1 gộp 4 entry nằm rải khắp file vì **cùng một loại nội-dung** (quyết-định không-làm + lý-do theo tiêu-chí); gộp lại mới thấy được tỉ-lệ. F2 giữ **5 mắt-xích riêng** trong 1 cụm vì chúng là **một lần refactor bị chẻ nhỏ có chủ-đích** (mỗi chunk 1 commit, K5 dọn rác của K1).

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

**[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ó.

**[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.

**[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**.

**[vừa] F4 — Plan AA: trang matrix chỉ-đọc cho fe-user**
- **VIỆC:** dựng bản xem quy-trình V2 đã ghim cho phía user (3 file: types ~55 LOC + page ~215 LOC + route).
- **KẾT-LUẬN:** PASS — lọc **ở BE** bằng `?isUserSelectable=true` thay vì lọc ở FE; bảng 10 cột (3 meta + 7 cờ Allow*), rowSpan theo Bước, đủ 3 trạng-thái Loading/Error/Empty, route đặt **trước** `/workspace`. Đúc 3 pattern mới: **13** (mirror trang Designer ở dạng chỉ-đọc = bỏ mutation + tái-dùng DTO dạng subset), **14** (Tailwind JIT phải viết **chuỗi class đầy đủ** trong mảng), **15** (helper dựng hàng phẳng cho rowSpan).
- **BÀI-HỌC:** làm bản chỉ-đọc thì **đẩy điều-kiện lọc về BE** — vừa gọn FE vừa tránh lộ dữ-liệu không được xem. Và phải **kiểm kho `components/ui/` của đúng app TRƯỚC khi import**.
- **BẤT-NGỜ:** `shadcn` của **fe-user thiếu hẳn `Card`/`Badge`** (chỉ có 6 thành-phần) — 2 app tưởng song sinh nhưng kho UI lệch nhau, phải nội-suy bằng `<div>`/`<span>` thuần cho khớp mắt với Designer bên admin.

**[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.

### Token-list (verbatim, implementer-backend/2026-05-q1)

- **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`

---

## implementer-backend/2026-05-q2 (12.442 B — "S25 Plan AB → S29 Plan B Chunk D, 5 entry verbose")

**Đặc-điểm file:** 5 entry, header khai 38.4KB → mục-tiêu ~21KB. Đậm đặc **pattern-number** và **SHA256 mirror-verify**.

> 🔴 **VA-CHẠM SỐ HIỆU PATTERN (fidelity-flag, KHÔNG tự phán — đề-nghị lead định-đoạt):** trong **cùng file này**, nhãn **"Pattern 16 NEW"** được gán cho **hai thứ khác nhau**:
> · dòng 18 (S25 wrap): *"**Pattern 16 NEW**: Preventive systemic batch fix khi audit phát hiện 9 sites cùng bug pattern"*
> · dòng 26 (S27 Plan CA): *"**Pattern 16 NEW** — byte-identical mirror admin → user khi parity confirmed"*
> Chứng-cứ liên-quan (file q4, dòng 53) tự khai đã biết chỗ mờ: *"Pattern 16 baseline **implied** trong recent activity S27 **chưa numbered explicit**"*. ⇒ Khi nén, **cấm gộp 2 mục này thành một** chỉ vì trùng số.

### Cụm-dedup

| # | Cụm | Thành-phần | Tag |
|---|---|---|---|
| G1 | **Plan AB Chunk A (`cdfd542`) — mô-tả 2 LẦN trong file** | entry "S25 Plan AB Chunk A" (dòng 14, dày) + phần đầu entry "S25 wrap" (dòng 18, tóm) — trùng ~60% | **cao** |
| G2 | **S26 Plan AG — cây thư-mục kiểu Outlook cho PE List (Pattern 19)** | 3 chunk gộp 1 commit `0bf6c7e` | **cao** |
| G3 | **S27 Plan CA Chunk B — mirror byte-identical + bẫy PowerShell** | 4 trang master admin→user + gotcha `$_` bị nuốt | **cao** |
| G4 | **S29 Plan B Chunk D — dropdown V2 cho ContractCreatePage** | mirror từ PE, kiểm 4-chỗ Pattern 16-bis | **vừa** |
| G5 | **META: kỷ-luật "chứng mirror bằng HASH, không bằng mắt"** | G2 (`21001E90…`) · G3 (4 SHA256) · G4 (git diff stat) · bộ đếm Pattern 5 **7th → 8th → 9th** | **cao** |

> Ghi-chú dedup: G1 là ca trùng **rõ nhất batch A** — 2 entry cùng tả một commit. Phần **chỉ có ở entry wrap** mà phải giữ: 6 plan em-main tự làm sau đó (AC→AF) + **3 Pattern mới 16/17/18** + lý-do vì sao vai sẽ từ-chối (tiêu-chí #4). Phần **chỉ có ở entry dày**: số dòng cụ-thể, chuỗi `modeName` switch, cách resolve `actorName`, thời-gian build.

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

**[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ố.

**[cao] G2 — Plan AG: cây 2 tầng bằng HTML thuần (Pattern 19)**
- **VIỆC:** bro phản-hồi danh-sách PE phẳng là "đám rừng" → dựng cây Dự-án ▸ Gói-thầu kiểu thư-mục Outlook (`0bf6c7e`, 2 file, +346/-116).
- **KẾT-LUẬN:** gom bằng `useMemo` 2 tầng (`ProjectGroup` / `GoiThauGroup`, khoá chuẩn-hoá `trim`+`toLowerCase`, hiển-thị bản gốc đầu tiên), có nhánh dự-phòng "(Dự án đã xoá)" / "(Chưa phân loại)", sắp theo locale `vi`, **lọc trước khi gom** để trạng-thái rỗng đúng. UI dùng **`<details>/<summary>` thuần trình-duyệt** (fe-user không có Accordion của shadcn) + Tailwind **named group** `group/proj` · `group/gt` + `[&::-webkit-details-marker]:hidden`. Ghi nhớ trạng-thái mở bằng `localStorage` (`pe_list_expanded_groups`, khoá `${projectId}::${normalizedGoiThau}`, mặc-định đóng hết) có `try/catch` phòng chế-độ ẩn-danh. 2 app **giống nhau tới SHA256** (`21001E90…`).
- **BÀI-HỌC:** thiếu thư-viện thì **dùng thẻ HTML gốc**: `<details>` cho sẵn trạng-thái đóng/mở, bàn-phím Space/Enter, **0 dòng JS quản trạng-thái từng nút**. Đổi thước đếm cũng phải đổi đồng-bộ (`rows.length` → `projectGroups.length`), nếu không trạng-thái rỗng sẽ nói dối.
- **BẤT-NGỜ:** anti-pattern được ghi rõ: nếu **hai group lồng nhau trùng tên** `group` (không đặt tên) thì con **kế thừa trạng-thái cha ⇒ cả hai xoay chevron cùng lúc** — một lỗi UI chỉ lộ khi lồng, và named-group là cách chữa.

**[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.

**[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`).

**[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".

### Token-list (verbatim, implementer-backend/2026-05-q2)

- **Pattern#:** `4` `feedback_service_hook_vs_endpoint` (log trong service hook, KHÔNG endpoint `/return-changelog` rời) · `5` mirror 2 app §3.9 (**7th/8th/9th cumulative**) · **`16` (VA-CHẠM — 2 nghĩa, xem cảnh-báo trên)** · **`16-bis`** kiểm 4 chỗ cross-app · **`17`** FE merge synthetic row · **`18`** FE userMap fallback từ dữ-liệu nhúng · **`19`** `<details>`+named group+localStorage · **`12-bis`** cookie-cutter xuyên module · Pattern 7 (per-NV opt-in, nhắc lại)
- **criteria#:** `#4` (tight coupling BE+FE ⇒ vai sẽ REFUSE Plan AC trở đi)
- **surprise:** "**Gotcha S27**: PowerShell `$_` variable in `ForEach-Object` block **bị Bash tool shell-escape eaten**" · "fe-user **no shadcn Accordion** → native browser disclosure widget **free**" · "**Anti-pattern:** nested same-name `group` would inherit parent state → **both rotate sync**" · "Mirror 2 app §3.9 **IDENTICAL post-edit (SHA256 verify match `21001E90...`)**" · "**stash em main WIP** ... race condition trick" (nhắc chéo) · "KHÔNG ops git push (em main verify Reviewer rồi mới push)" (lặp 3 entry) · "FE filter qua **substring summary keyword chứ KHÔNG enum field strict**" · "recommend tooling `git diff fe-admin/X fe-user/X` after every multi-file edit batch" · số dòng lệch `282-287` vs `280-287` cho cùng đoạn
- **guard:** `try/catch` quanh `localStorage` (quota / ẩn-danh) · lọc `pendingMe` **TRƯỚC** khi gom nhóm · `projectGroups.length === 0` thay `rows.length === 0` · SHA256 sau khi ghi (4 file) · `git diff --stat` byte-similar · kiểm **tiền-đề BE** (`Guid? ApprovalWorkflowId = null`) trước khi wire FE · `[&::-webkit-details-marker]:hidden` · named group `group/proj` ⟂ `group` mặc-định · **không** gọi `SaveChangesAsync` mới (caller đã lưu)
- **root-cause:** `return` sớm ở nhánh Drafter ⇒ không có đường chung ⇒ phải rải nhiều lời-gọi log ⇒ gom về `if/else` + 1 `Changelog.Add()` · fe-user thiếu Accordion ⇒ chuyển sang `<details>` gốc · `group` không đặt tên khi lồng ⇒ con kế-thừa trạng-thái cha · Bash tool escape nuốt `$_` ⇒ `ForEach-Object` gãy
- **file:line / định-danh:** `PurchaseEvaluationWorkflowService.cs` `ApplyReturnModeAsync` **:215-378**, Drafter early-return **:282-287** (wrap ghi `:280-287`), `userManager.FindByIdAsync` mirror **:660-667**, caller `TransitionAsync` **:100** · `PeDetailTabs.tsx` ×2 (filter + comment 3-nguồn) · `PE_ENTITY_HEADER` **= 1** · `Summary=$"Trả lại ({modeName}): {summary}"` (`"Người soạn thảo"`/`"1 Cấp"`/`"1 Bước"`/`"Người chỉ định"`) · `ProjectGroup{projectId, projectName, goiThauList[], totalCount}` · `GoiThauGroup{displayName, normalizedKey, items[]}` · `pe_list_expanded_groups` · `__no_project__` · `${projectId}::${normalizedGoiThau}` · `group/proj` · `group/gt` · `group-open/proj:rotate-90` · `PeWorkspaceCreateView.tsx` **:80-89** + **:152-172** · `ContractCreatePage.tsx` ×2 · `CreateContractCommand` @ `ContractFeatures.cs` **:17-36** (`Guid? ApprovalWorkflowId = null`, marker `[Plan B S29 2026-05-22 Chunk E1]`) · `ApplicableType=3` · Mig 25 `IsUserSelectable` · `pattern_master_page_mirror.md` · `feedback_uat_skip_verify`
- **sha / số-đo:** `cdfd542` (3 file +146/-95; BE +83/-49; FE +14/-6 mỗi app) · `0bf6c7e` (2 file +346/-116 = +115 mỗi app) · `06a441c` (6 file, **+962**) · `62b50d1` (2 file, +44 ×2 = **+88**) · SHA256 `21001E90…` · `C1760788…` / `BDF0529E…` / `68213D62…` / `6F482614…` · build: BE 20.27s · fe-user 1907 module 16.62s / 16.05s / 14.14s / 8.84s · fe-admin 1926 module 6.98s / 6.86s / 16.07s · bundle fe-user **1291.33 KB** (gzip 337.00) / 1.32MB (gzip 343KB) · fe-admin **1402.68 KB** (gzip 357.51) / 1.40MB (gzip 358KB) · test mốc **111** (bỏ qua theo UAT) · token ~10k-16k · warn nền: CSS `@import` + `INEFFECTIVE_DYNAMIC_IMPORT realtime.ts` (lặp 3 entry) · tag `[pattern, phase-9, frontend]`

---

## implementer-backend/2026-05-q3 (5.353 B — "S34 curate", 6 entry)

**Đặc-điểm file:** 6 entry NGẮN, phần lớn là **wrap / hồi-cứu / meta** chứ không phải nhật-ký làm việc: S29 wrap · S28 wrap governance · S27 hồi-cứu REFUSE (có **bảng 8 dòng**) · log curate · setup baseline 2026-05-11.

> 🔴 **FIDELITY-FLAG (ĐÃ ĐO TRÊN ĐĨA — đề-nghị lead route sang `reviewer`, vai này KHÔNG tự phán "bịa"):**
> Dòng 16 khai: *"Pattern 7 per-NV admin opt-in flag (**Mig 29 AllowDrafterEdit** + **Mig 30 AllowApproverEditSection1** + **Mig 31 AllowEarlyApprove + AllowDelegate**) đã proven 4×"*.
> **Phép đo hôm nay** (`git grep -i … -- src/Backend`), có **cả 2 control**:
> · control **DƯƠNG** `AllowApproverSkipToFinal` → **54 file có hit** (thước còn răng)
> · control **ÂM** `AllowXyzKhongTonTai` (tên tự bịa) → **0**
> · 4 tên trong claim: `AllowDrafterEdit` **0** · `AllowApproverEditSection1` **0** · `AllowEarlyApprove` **0** · `AllowDelegate` **0** — **bằng đúng control âm**.
> **Tên Mig thật** (từ `git ls-files`, tiền-tố 2026-05-13/14): `…114505_AddAdvancedOptionsToApprovalWorkflows` (28) · `…130144_RefactorAdvancedOptionsToPerLevelAndDrafterUser` (29) · `…160703_AddAllowApproverEditBudgetToLevels` (30) · `…160124_RefactorSkipToFinalToApproverLevel` (31).
> **Giả-thuyết LÀNH (không tự kết-luận):** vai dùng **từ-vựng giao-diện** thay tên code — "EditSection1/2" ↔ `AllowApproverEditBudget`/`AllowApproverEditDetails` (q1 tả checkbox F4 là "Edit Section 2"); cùng lỗi này lặp ở **q1 dòng 19** ("Mig 30 AllowApproverEditSection1") ⇒ là **trôi từ-vựng có hệ-thống**, không phải gõ nhầm một lần.
> **Phần CHƯA giải-thích được:** `AllowEarlyApprove` + `AllowDelegate` **không có đối-ứng nào** ở Mig 31 (thật = `AllowApproverSkipToFinal`). ⇒ khi nén, **KHÔNG chép 4 tên này**; nếu cần giữ ý thì viết "Pattern 7 đã áp 4 lần qua Mig 29/30/31" **bỏ tên cờ**.
> ⚠️ Phụ: dòng 47 khai "**5 patterns** proven cumulative S1-S20" nhưng **liệt-kê 6 mục** (per-chunk · 3-file rule · audit-reuse clone · service hook · FE mirror · VND helper).

### Cụm-dedup

| # | Cụm | Thành-phần | Tag |
|---|---|---|---|
| H1 | **S29 wrap — 5 spawn, bận nhất; race-condition + 1 ca DỪNG GIỮA CHỪNG** | 4 chunk PASS + E3 stopped + 3 bài học vận-hành | **cao** |
| H2 | **S27 hồi-cứu REFUSE — bảng 8 task, tự chấm 2/8 lẽ ra nhận** | bảng + tiêu-chí #1/#2/#4/#7 + Pattern 20 mới | **cao** |
| H3 | **S28 — governance Layer A + 5 điều tự áp** | tag schema · source_path · cấm tự-cho-phép luật liên-dự-án · 1 luật bị BỎ | **vừa** |
| H4 | **Log curate + setup baseline** | 12 entry → q1 (38.8 → ~22-24KB) · khởi-tạo 2026-05-11 | **thấp** |

> Ghi-chú dedup: H1 và H2 **cùng nói về "nên/không nên giao việc"** nhưng ngược chiều — H1 là ca **nhận rồi vỡ** (E3), H2 là ca **lẽ ra nhận mà không nhận được**. Giữ 2 cụm; gộp sẽ mất tính đối-xứng của bài học.

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

**[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.

**[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.

**[vừa] H3 — S28: 5 điều tự áp sau khi bị bắt lỗi**
- **VIỆC:** ghi lại governance Layer A dưới góc nhìn của vai.
- **KẾT-LUẬN:** (1) pattern đã chứng **≥2 lần** thì đủ tư-cách đề-cử lên Layer B; (2) tag bắt buộc `[pattern, phase-<N>, <bc>]`; (3) quy-ước `source_path` = `solution_erp/pattern/<topic>-<date>`; (4) **cấm tự cho phép mình đặt luật xuyên dự-án** — đúc từ ca t4 bro bắt tại trận; (5) luật cũ "mọi tương-tác đều phải lưu" **BỊ BỎ**.
- **BÀI-HỌC:** phạm-vi quyền của một vai phải **thu về chính dự-án của nó**; và luật cũ nên **khai tử công-khai** ("ABANDONED") thay vì để trôi — luật zombie còn hại hơn không có luật.
- **BẤT-NGỜ:** chính vai này ghi nhận mình **quên mất luật cũ** rồi mới phát-hiện luật đó đã bị bỏ — quên đúng thứ đáng quên, nhưng chỉ biết là "đúng" sau khi tra lại.

**[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ỡ.

### Token-list (verbatim, implementer-backend/2026-05-q3)

- **Pattern#:** `7` per-NV admin opt-in (**tên cờ SAI — xem FIDELITY-FLAG**) · `12-bis` cross-module cookie-cutter · `16-bis` 4-place mirror (**reinforced 2×**) · **`20` MỚI** (bộ 5 script PS mirror) · nền "5 patterns S1-S20" (liệt-kê **6** mục)
- **criteria#:** `#1` phán-đoán giữ-hay-cắt (§6.5) · `#2` thiết-kế UX / viết docs · `#4` chuỗi suy-luận sửa lỗi · `#7` lần đầu, chưa tiền-lệ · Case 1 (n file sửa cơ-học giống nhau) · Case 2 (cookie-cutter mirror)
- **surprise:** "**E3 stopped mid-task** ... STOPPED at 'check ContractDetail type' judgment call" · "**stash em main WIP `ContractWorkflowService.cs`** để build verify clean (em main + Implementer **parallel touch BE → race condition trick**)" · "Forward **SEQUENTIAL** chunks A→B→C khi cùng touch BE, **NOT parallel**" · "em main solo **more reliable** than Implementer khi **spec ambiguity > 20%**" · "**2/8 task lẽ ra delegate được** ... nhưng **registry empty → em main forced solo**" · "Net loss **~30 phút**" · "registry KHÔNG load (per pitfall **VIPIX #1+#2**)" · "bro **caught self-authorize cross-project rule mistake**" · "Quên rule cũ 'mọi tương tác mandatory store' — **ABANDONED**" · "Implementer = **busiest agent S29** với 5 spawn"
- **guard:** `git stash` phần WIP trước khi build verify · chunk **tuần-tự** khi cùng chạm BE · ngưỡng **spec ambiguity 20%** để chuyển về em-main · tự-dừng khi gặp judgment call · tag schema bắt buộc `[pattern, phase-<N>, <bc>]` · **cấm tự-cho-phép luật liên-dự-án** · pattern phải proven **≥2×** mới đề-cử Layer B
- **root-cause:** em-main + vai **cùng sửa BE song song** ⇒ cây làm việc bẩn ⇒ build verify không sạch ⇒ phải `stash` · **registry không nạp** ⇒ không spawn được ⇒ 2 việc hợp-lệ bị dồn cho em-main · từ-vựng **UI-section** dùng thay tên cờ trong code ⇒ 4 tên cờ 0-hit (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** (control dương) · `AllowXyzKhongTonTai` = **0** (control âm) · `AllowDrafterEdit` / `AllowApproverEditSection1` / `AllowEarlyApprove` / `AllowDelegate` = **0 / 0 / 0 / 0** · Mig thật: `20260513114505_AddAdvancedOptionsToApprovalWorkflows` · `20260513130144_RefactorAdvancedOptionsToPerLevelAndDrafterUser` · `20260513160703_AddAllowApproverEditBudgetToLevels` · `20260514160124_RefactorSkipToFinalToApproverLevel`

---

## implementer-backend/2026-05-q4 (6.243 B — "late May, S32-S33 verbose entries")

**Đặc-điểm file:** 5 khối nội-dung, nhưng **thứ-tự bị xáo** (xem cảnh-báo). Gồm: S33 Task 5 (HRM scaffold FE) · 2 khối "archived S65 FIFO trim" (S35 G-H2 · S29 Chunk C) · S32 wrap · S32 startup.

> 🔴 **LỖI CẤU-TRÚC — ENTRY BỊ CHẺ ĐÔI (Placement, ưu-tiên cao khi nén):**
> Entry **S33 Task 5** bắt đầu ở **dòng 7-11**, nhưng **phần đuôi của nó bị đẩy xuống dòng 23-27** — nằm **SAU** hai khối không liên-quan (S35 G-H2 và S29 Chunk C, cả hai gắn nhãn *"archived S65 FIFO trim"*, tức được **chèn vào giữa** ở một đợt dồn kho sau).
> Bằng-chứng phần đuôi thuộc về S33 Task 5, không thuộc S29 Chunk C: dòng 25 liệt-kê `types/employee.ts` · `EmployeesListPage.tsx` · `EmployeeCreatePage.tsx` · `Layout.tsx +4 staticMap **Hrm_HoSo**` · "6 fe-user files mirror" — **toàn bộ là HRM**, khớp dòng 9; trong khi S29 Chunk C là **Contract V2 / Mig 33**, 8 file +4265 LOC. Dòng 27 còn ghi `Tag: [mirror-page, phase-10.1, frontend]` = đúng nhãn phase của Task 5.
> ⚠️ **Rủi-ro nếu nén ẩu:** khối "Verify all clean / Files touched / Out-of-scope respected" sẽ bị gán nhầm cho **S29 Chunk C** ⇒ tạo ra một bản ghi sai hoàn-toàn (gán `120/120 test` + file HRM cho một việc Contract). ⇒ **Đề-xuất: nối lại dòng 23-27 về ngay sau dòng 11 TRƯỚC khi nén.**

### Cụm-dedup

| # | Cụm | Thành-phần | Tag |
|---|---|---|---|
| I1 | **S33 Task 5 — scaffold HRM FE 2 app (thân dòng 7-11 + ĐUÔI dòng 23-27)** | 14 file, 3 SHA256 giống hệt, Pattern 16-bis/12-bis lần 4 | **cao** |
| I2 | **2 khối digest "archived S65 FIFO trim"** | S35 G-H2 BE CRUD 4 catalog · S29 Plan B Chunk C Mig 33 | **vừa** |
| I3 | **S32 wrap — backlog Plan G + 4 việc chờ + REFUSE trước** | 11 module / 10 sprint + (a)(b)(c)(d) | **vừa** |
| I4 | **S32 startup — tự đo kho, TỪ CHỐI tự dọn, kiểm RAG sống** | 36.2KB FLAG + 17 pattern + rerank 3 điểm | **cao** |

> Ghi-chú dedup: I2 gộp 2 khối vì **cùng thể loại** (digest đã nén sẵn ở đợt S65, mỗi khối 2-3 dòng + tag) — đây là **lớp chưng-cất**, nén tiếp sẽ mất mã-số kỹ-thuật; đề-nghị giữ gần nguyên.

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

**[cao] I1 — S33 Task 5: scaffold HRM cho 2 app, phạm-vi cực hẹp**
- **VIỆC:** dựng 3 trang/type HRM (Phase 1 chỉ-đọc) mirror sang cả 2 app — tổng **14 file** (3 file MỚI ×2 app + 4 file sửa ×2).
- **KẾT-LUẬN:** ACCEPT vì spec **xác-định 100%**, kiểu **ULTRA-MINIMAL** (không tách component DetailTabs, 6 mục dùng `<details>` nội-tuyến, không CRUD vệ-tinh). 3 file mới **giống hệt tới SHA256**: `CCFC70666568` · `DC859C897C5C` · `C796F25D01AC`. Kiểm sạch toàn bộ: fe-admin 21.4s / fe-user 9.2s / `dotnet build` 1.59s **0 warn 0 err** / `dotnet test` **120/120** (58 Domain + 62 Infra) — và **"Ambiguities encountered: 0"**. Pattern 16-bis + 12-bis đều được ghi là **lần thứ 4**.
- **BÀI-HỌC:** phạm-vi hẹp phải ghi kèm **danh-sách "cố-ý không làm"** (không form vệ-tinh · không sửa nội-tuyến Header · không wire `PermissionGuard` vì em-main giữ Task 6 · không thêm `Bg_*`/`Catalog*`) — nếu không, người đọc sau sẽ tưởng là thiếu sót.
- **BẤT-NGỜ:** cả hai "lần thứ 4" đều **chỉ liệt-kê 3 lần trước** (16-bis: CA HF1 + Chunk D + Task 5; 12-bis: Chunk C + Task 4 + Task 5) ⇒ **số đếm không khớp danh-sách**; cùng loại lệch với "5 patterns/6 mục" ở q3 ⇒ khi nén nên **giữ danh-sách, bỏ số thứ-tự**.

**[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).

**[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).
- **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.

**[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.

### Token-list (verbatim, implementer-backend/2026-05-q4)

- **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 (xem cảnh-báo cấu-trúc) · 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]`

---

== HẾT ==
