# implementer-backend/2026-05-q1 — gist
distill-gen: 1
source-verbatim: .claude/agent-memory/implementer-backend/archive/2026-05-q1.md (12 entry → 5 cụm)
pointer-style: substring

## TỔNG-HỢP (3-5 ý)
1. **Tỉ-lệ TỪ-CHỐI gần tuyệt-đối cả quý** (S21 t3-t5: 3/3 · S22: 100% · S23 t4→t11: 8 plan liên-tiếp 0 spawn · S24: 4/4) theo 7 tiêu-chí phạm-vi — verbatim tự đối-chiếu khớp cảnh-báo Anthropic "tightly interdependent coding"; ranh-giới đo bằng độ ràng-buộc chéo lớp (tiêu-chí #3/#4), không bằng kích-thước diff, và mỗi lần từ-chối ghi kèm tiêu-chí thì tỉ-lệ mới đọc được về sau.
2. **Chuỗi K Mig 31 (pre-A → K1 → K3 → K5 → K7):** refactor đổi chỗ lưu cờ Users→Levels có chunk dọn-rác riêng (K5) + grep-về-ZERO 5 biến-thể tên + test 3 xoá/3 thêm triệt-tiêu giữ baseline 104; mất dữ-liệu prod CÓ CHỦ-ĐÍCH (Option A không backfill, khai đích danh 4 tài-khoản); nợ kỹ-thuật được ĐẶT LỊCH (test vỡ để lại cho K7), không bỏ quên.
3. **Mục "Mismatches discovered" tự khai 4 chỗ đoán sai** (per-NV mặc-định vs cờ admin bật riêng → Mig 30 · `PE.changelogs` vắng ⇒ TS2339 · Dialog `size="xl"` không tồn-tại · `accessToken` ≠ `token` ⇒ 401) — giả-định hình-dạng API/thư-viện phải kiểm bằng đọc, không bằng trí nhớ; bẫy `accessToken` cắn 2 vai khác nhau (tái-xuất ở cicd-monitor Run #221).
4. **3 pattern FE mới 13/14/15 từ Plan AA** (read-only mirror Designer · Tailwind JIT full-class-string · rowSpan flat-row builder); shadcn fe-user là subset thiếu hẳn `Card`/`Badge` — 2 app tưởng song sinh nhưng kho UI lệch nhau.

## Entries

**[cao] F1 — Nhật-ký TỪ-CHỐI: vai này ghi cả việc mình KHÔNG làm**
- **VIỆC:** phân-loại việc được giao theo 7 tiêu-chí phạm-vi, trả lại việc không hợp.
- **KẾT-LUẬN:** S21 t3-t5 **từ-chối 3/3**; S22 **từ-chối 100%**; S23 t4→t11 **8 plan liên-tiếp em-main tự làm, 0 lần spawn** (N+O 5 site lookup · P Controller ~6 LOC · Q CSS 8 LOC · R+S+T5 xoá dữ-liệu prod ~720 dòng qua 4 file `.sql` · T cờ DemoSeed ~25 LOC · U sidebar 25 LOC); S24 **4/4 từ-chối đúng**, mỗi ca ghi rõ commit + tiêu-chí (`da218f1` #6 vặt <30 phút · `4d60598` #2 luồng UX · `fbbd361` #2 · `ee0902a` #6). Lý-do chính: tiêu-chí **#3** (xuyên >2 lớp) + **#4** (chuỗi suy-luận sửa lỗi).
- **BÀI-HỌC:** ranh-giới "được giao" phải quyết theo **độ ràng-buộc chéo lớp**, không theo kích-thước diff — và mỗi lần từ-chối phải **ghi lại kèm tiêu-chí** thì tỉ-lệ mới đọc được về sau.
- **BẤT-NGỜ:** cả một quý mà tỉ-lệ từ-chối gần như tuyệt-đối; verbatim tự đối-chiếu: "REFUSE rate 100% **match Anthropic warning 'tightly interdependent coding'**" — tức vai tự xác-nhận giới-hạn của chính mô-hình chia việc, chứ không cố nhận cho có.
→ substring:"tightly interdependent coding"

**[cao] F2 — Chuỗi K Mig 31: đổi chỗ lưu cờ F2 từ `Users` sang `ApprovalWorkflowLevels`**
- **VIỆC:** 5 chunk nối tiếp trong S23 t1 (pre-A → K1 → K3 → K5 → K7).
- **KẾT-LUẬN:** **pre-A** đổi nhãn slot Designer (`ApprovalWorkflowsV2Page.tsx:873`, tra `fullName` từ `usersList` đã có sẵn ~`:500`, khuôn `.find` đã dùng ở `:535`). **K1** Mig 31 ADD-DROP **không BACKFILL** (Option A) — 6 file BE, cắt **-37 LOC** nhánh Drafter SUBMIT (`PurchaseEvaluationWorkflowService.cs:121-157`), 4 chỗ vỡ biên-dịch vá bằng sentinel `false` + marker K2, giữ tham-số thứ 8 `bool skipToFinal` để K2 dùng lại. **K3** checkbox thứ 7 = 7 tiểu-mục nguyên-tử (type → default → propagate `?? false` → vị-trí cuối nhóm → viết lại banner `~:623` → body mutation). **K5** dọn zombie: xoá endpoint `PATCH /allow-skip-final`, xoá field DTO/Command/Handler, viết lại 6 chỗ chú-thích lỗi-thời, xoá cột "Skip cuối" FE — rồi **grep 5 từ-khoá ra ZERO** toàn `src/Backend` + `fe-admin/src` (+42/-94 LOC / 9 file). **K7** xoá 3 test Drafter đã hết nghĩa + thêm 3 test Approver, helper mới `SeedApproverF2WorkflowAsync` (2 Bước × 2 Cấp để chứng skip **không** rơi sang Bước kế) ⇒ **104 PASS, baseline không đổi vì 3 xoá + 3 thêm triệt tiêu nhau**.
- **BÀI-HỌC:** refactor đổi chỗ lưu-trữ phải có **chunk dọn rác riêng** (K5) và kết bằng **grep-về-0 trên nhiều biến-thể tên** (PascalCase / kebab / camel / nhãn tiếng Việt / tên icon) — không thì cờ chết vẫn còn dấu vết khắp nơi. Test cũng phải **xoá thứ đã hết nghĩa**, đừng chỉ thêm.
- **BẤT-NGỜ:** (i) chấp-nhận **mất dữ-liệu prod có chủ-đích** — Option A không backfill, khai đích danh 4 tài-khoản mất giá-trị (`fin.pp`, `pm.nv`, `nv.test`, `truong.nguyen`); (ii) `UserConfiguration.cs` **không tồn-tại** — `User` được cấu-hình inline trong `ApplicationDbContext.OnModelCreating` ~`:86` nên không có `HasDefaultValue`, EF vẫn tự nhận đổi prop; (iii) test hỏng do K1 được **cố-ý để lại** cho K7 (`...ReturnModeTests.cs:253`), tức nợ kỹ-thuật được **đặt lịch** chứ không bỏ quên.
→ substring:"SeedApproverF2WorkflowAsync"

**[cao] F3 — Bốn "Mismatch" tự khai ở S22**
- **VIỆC:** ghi lại 4 chỗ hiểu sai giữa ý bro và cách vai định làm.
- **KẾT-LUẬN:** (1) "người điều chỉnh cũng là người duyệt" — vai định cho **mặc-định** Approver luôn được phép, bro chỉnh thành **cờ admin bật riêng** (thành Mig 30); (2) `PE.changelogs` **không có** trong `PeDetailBundle` ⇒ dựng lịch-sử trong `BudgetAdjustSection` **fail TS2339**, phải bỏ và hoãn sang S23 bằng endpoint fetch riêng; (3) `Dialog size="xl"` **không tồn-tại** (chỉ `sm|md|lg`) ⇒ dùng `lg` cho iframe xem trước; (4) field đăng-nhập là **`accessToken`**, không phải `token` ⇒ `seed-test-users-prod.ps1` **401** ở lần đầu, sửa thành `$authResp.accessToken`.
- **BÀI-HỌC:** phải **chốt "mặc-định vs admin bật riêng" TRƯỚC** khi mở rộng phạm-vi quyền — đây là loại nhầm không lộ ra ở compile mà lộ ở nghiệm-thu. Ba cái còn lại chung một gốc: **giả-định về hình-dạng API/thư-viện phải kiểm bằng đọc, không bằng trí nhớ**.
- **BẤT-NGỜ:** vai chủ-động mở mục "Mismatches discovered" để ghi **cái mình đoán sai** — và mục này về sau là phần giá-trị nhất của entry; nhầm về `accessToken` còn tái-xuất ở cicd-monitor (Run #221) ⇒ cùng một bẫy cắn **hai vai khác nhau**.
→ substring:"Mismatches discovered"

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

**[vừa] F5 — Plan M: mở-rộng helper + đổi nhãn có kỷ-luật**
- **VIỆC:** M2 thêm 2 test ca-biên "Bước 1 không lùi được"; M3 đổi nhãn hiển-thị Phase 98 "Trả lại" → "Cần chỉnh sửa lại".
- **KẾT-LUẬN:** M2 **mở-rộng** `SeedWorkflowAsync` thêm 2 tham-số tuỳ-chọn mặc-định `false` (**không clone helper**, 4 test cũ không vỡ) ⇒ **106/106**; khẳng-định assert đúng chỗ: `Summary` là chuỗi cố-định `"Chuyển phase {from} → {to}"` nên câu "không lùi được" phải tìm ở **`ContextNote`**. M3 đổi đúng **8 dòng / 4 file × 2 app**, và liệt-kê rõ **4 nhóm KHÔNG đụng** (nút hành-động `← Trả lại` là **động-từ**, nhãn 4 mode, chú-thích lý-do, `types/contracts.ts` + `types/budget.ts` ngoài phạm-vi).
- **BÀI-HỌC:** phân-biệt **"nhãn trạng-thái" ⟂ "động-từ hành-động"** khi đổi chữ — cùng một chuỗi "Trả lại" mà một chỗ phải đổi, một chỗ không. Danh-sách **KHÔNG-đụng** có giá-trị ngang danh-sách đã-đụng.
- **BẤT-NGỜ:** vai tự đặt **ngưỡng chống-nghịch: "anti-fiddle <20% LOC"** và tự biện-hộ vì sao 2 chuỗi inline ngoài spec vẫn được sửa (cùng ngữ-nghĩa "tham-chiếu trạng-thái") — tự ràng mình bằng con-số trước khi bị hỏi.
→ substring:"Cần chỉnh sửa lại"

## Phụ-lục token (coverage)

- **gotcha#:** `#45` (bug Trả lại — lý-do REFUSE) · `#44` (Authorize policy — Pattern 10, 5 test ~50 LOC) · "45 gotcha" (mốc S21) / "46 gotchas unchanged" (mốc S22)
- **criteria# (7 tiêu-chí phạm-vi):** `#2` luồng UX · `#3` xuyên >2 lớp · `#4` chuỗi suy-luận sửa lỗi · `#6` vặt <30 phút
- **Pattern#:** `2` EF migration 3-file rule · `3` audit-reuse **EXTEND không clone helper** · `5` mirror 2 app strict · `7` per-NV admin opt-in · `8` tách endpoint narrow scope · `9` defense-in-depth FE+BE guard pair · `10` Reflection-based Authorize regression · `11` test-infra helper cookie-cutter · `12` `InternalsVisibleTo` · **`13`** read-only mirror Designer (MỚI) · **`14`** Tailwind JIT full class string (MỚI) · **`15`** rowSpan flat row builder (MỚI) · `16-18` (S25 wrap, chỉ nhắc ở header) · `19` (S26 Plan AG, chỉ nhắc ở header)
- **surprise:** "**Surprise:** shadcn fe-user KHÔNG có `Card`/`Badge`" · "`UserConfiguration.cs` **file không tồn tại** — User entity configured inline `ApplicationDbContext.OnModelCreating` ~line 86" · "Dialog `size=\"xl\"` **NOT supported**" · "API auth field `accessToken` **không phải** `token` ... FAIL 401" · "`PE.changelogs` **KHÔNG có** trong PeDetailBundle ... build **FAIL TS2339**" · "accept **lose 4 prod user value** `fin.pp` + `pm.nv` + `nv.test` + `truong.nguyen`" · "3 deleted + 3 added **cancel out**, baseline preserved" · "REFUSE rate 100% **match Anthropic warning 'tightly interdependent coding'**" · "Grep ... **ZERO results**" · "Test compile error ... **left for K7 chunk** (spec exclude test scope)" · "**Anti-fiddle threshold <20% LOC respected**" · "**Cookie-cutter 0 (lần đầu pattern)**" · "2 pre-existing **DocxRenderer** warn" (lặp 3 lần)
- **guard:** `HasDefaultValue(false)` (`ApprovalWorkflowConfiguration.cs`) · sentinel `false` + marker comment K2 tại 4 site vỡ biên-dịch · tham-số tuỳ-chọn mặc-định `false` giữ tương-thích 4 test cũ · grep-về-ZERO 5 biến-thể (`AllowDrafterSkipToFinal` · `allow-skip-final` · `allowDrafterSkipToFinal` · `Skip cuối` · `FastForward`) · `InternalsVisibleTo` (Pattern 12) · guard đôi FE+BE (Pattern 9) · `dotnet build` 0 err + `npm run build` 0 TS err trước khi khai PASS · ngưỡng **anti-fiddle <20% LOC** · route đặt **trước** `/workspace`
- **root-cause:** `PeDetailBundle` không phơi `changelogs` ⇒ TS2339 khi dựng lịch-sử tại chỗ · Mig 31 chuyển chỗ lưu ⇒ 4 site Application vỡ biên-dịch + 1 test tham-chiếu prop đã xoá (`:253`) · `EditLevelEntry` không có `approverFullName` (chỉ `LevelDto` BE có `approverUserName`) ⇒ phải tra từ `usersList` · shadcn fe-user là **subset tối-thiểu** ⇒ thiếu Card/Badge · `Summary` là chuỗi cố-định ⇒ nội-dung động phải nằm ở `ContextNote` (`service:96-99` → `LogTransition`)
- **file:line / định-danh (BE):** `ApprovalWorkflow.cs` (+`AllowApproverSkipToFinal`; narrative **:78-80**; docstring **:108**) · `User.cs` (−1 prop) · `ApprovalWorkflowConfiguration.cs` (**:22-24** narrative, **:87** storage-move comment) · `PurchaseEvaluationWorkflowService.cs` **:121-157** (−37 LOC), **:287-333** (F1 edge), **:96-99** (comment → ContextNote) · `UserFeatures.cs` · `SetUserAllowDrafterSkipToFinalCommand(+Handler)` · `UsersController.cs` + `SetAllowDrafterSkipToFinalBody` · `PurchaseEvaluationFeatures.cs` **:401** · `ApprovalWorkflowV2AdminFeatures.cs` **:58** · `IPurchaseEvaluationWorkflowService.cs` · `PurchaseEvaluationDtos.cs` · `ApplicationDbContext.OnModelCreating` **~:86** · `DbInitializer.cs` (cờ DemoSeed ~25 LOC)
- **file:line / định-danh (test + FE):** `PurchaseEvaluationWorkflowServiceReturnModeTests.cs` **:253** (vỡ, để cho K7), **:241** (chèn 2 `[Fact]` mới) · helper `SeedWorkflowAsync` / `SeedApproversAsync` / **`SeedApproverF2WorkflowAsync`** (2 Bước × 2 Cấp) / `BuildPeAtLevel2` · test xoá: `SkipToFinal_DrafterAllowed_SetsPointerToFinalLevel` · `SkipToFinal_DrafterDenied_NonAdmin_Throws` · `SkipToFinal_AdminBypass_Succeeds` · test thêm: `ApproveV2_SkipToFinal_AdminTickFlag_SetsPhaseDaDuyet` · `ApproveV2_SkipToFinal_FlagOff_NonAdmin_ThrowsConflictException` · `ApproveV2_SkipToFinal_FlagOff_Admin_BypassesFlagCheck` · `ApplyReturnMode_OneLevel_AtStep1Level1_ResetsToBuoc1Cap1_KeepsChoDuyet` · `ApplyReturnMode_OneStep_AtStep1_ResetsToBuoc1Cap1_KeepsChoDuyet` · `ApprovalWorkflowsV2Page.tsx` **:873**, **~:500**, **:535**, banner **~:623**, stale **:75** + **:504** (hoãn K6) · `UsersPage.tsx` + `types/users.ts` · `types/purchaseEvaluation.ts` (`PurchaseEvaluationPhaseLabel[98]`, `PeDisplayStatusLabel.TraLai`) · `PeWorkflowPanel.tsx` · `fe-user/src/types/approvalWorkflowV2.ts` (~55 LOC, 5 record type `AwLevelDto`/`AwStepDto`/`AwDefinitionDto`/`AwTypeSummaryDto`/`AwAdminOverviewDto`) · `fe-user/src/pages/pe/WorkflowMatrixViewPage.tsx` (~215 LOC) · `fe-user/src/App.tsx` route `/purchase-evaluations/workflow-matrix` · `seed-test-users-prod.ps1` → `$authResp.accessToken` · `scripts/plan-r-*.sql` + `plan-s-*.sql` + `plan-t5-*.sql` + `plan-t-backup.sql`
- **số-đo:** test **84 → 104 → 106** · mig **29 → 30 → 31** · endpoint ~**146** (+3) · user prod **33** (13 cũ + 20 mới) · diff K5 **+42/-94** (9 file) · K7 **+175/-92** (1 file) · K3 **+26/-7** · M2 **+94** · M3 **+4/-4 ×2 = 8 LOC** · bundle fe-admin **1395 / 1395.74 KB** · fe-user **1275 / 1282 KB** (1907 module, 2.61s) · commit S24 **7** (`a1a910f..ee0902a`, 1 spawn + 6 solo) · token/chunk ~5k-14k · MEMORY nguồn **38.8 KB** · `~720 rows wiped`

### Bổ-sung coverage @P3-gate S170 — ⚠️ LƯỚI GREP THUẦN, KHÔNG PHẢI DỮ KIỆN (đoạn trích sau mỗi token là phần ĐẦU DÒNG verbatim chứa token — dòng archive là mega-line nên đoạn trích thường KHÔNG liên-quan token; CHỈ dùng để coverage-diff grep trúng, đọc nội-dung thì tra verbatim theo token — reviewer P3 @S170)

- `#1` — Cleanup zombie F2 endpoint + UsersPage column + DTO field + stale narrative comments (Reviewer Major #1 + Major #2 + Minor #3 + Minor #4). Pattern pos…
- `Mig 25` — FE user read-only matrix view workflow V2 ghim (Mig 25 IsUserSelectable). 3 file: (1) CREATE `fe-user/src/types/approvalWorkflowV2.ts` (~55 LOC) — s…
- `Mig 29` — Em main classified ALL S22 work as cross-stack reasoning chain (BE Mig + Service guard + DTO + FE Designer + FE Section + FE types + tests) → REFUSE…
- `PurchaseEvaluationWorkflowServiceReturnModeTests.cs:253` — Mig 31 schema swap F2 storage Users → ApprovalWorkflowLevels. Pattern Mig 29 ADD-DROP no-BACKFILL Option A (accept lose 4 prod user value `fin.pp` +…
