# Session 24 turn 1 — 2026-05-15 — Plan AA User Workflow Matrix view + Sidebar widen

**Dev:** Claude Opus 4.7 1M (em main + 3 sub-agent spawn)
**Duration:** ~2h
**Base commit:** `86d8806` (Plan U S23 t11 sidebar truncate)
**Final HEAD:** `<this Docs commit>` (after `ee776d5` Chunk A + `c667802` Chunk B)
**Total commits Plan AA:** **3** (A BE+Layout + B FE page + C Docs)

## 🎯 Trigger session

Bro UAT screenshot 2026-05-15 ~15:40, sau Plan U S23 t11 deploy:

> Cho hiển thị đầy đủ nhé, cần thì sliderbar to 1 chút cũng đc.
> Với thêm 1 menu là: Luồng duyệt phía trên Danh sách → Hiển thị ma trận phân quyền của cấu hình quy trình duyệt trong admin page ra ngoài để User có thể biết đc (Chú ý chỉ những quy trình có ghim).

2 yêu cầu:
1. **Sidebar widen + revert truncate** — Plan U S23 t11 vừa add `truncate` + tooltip "..." → bro muốn hiển thị đầy đủ label custom Mig 27 (DisplayLabel admin set).
2. **Menu "Luồng duyệt" + matrix view page** — read-only cho user xem cấu hình workflow V2 ghim trước khi tạo phiếu.

## 🌳 Q&A clarify (2 lượt)

| Câu | User chốt |
|---|---|
| Permission strategy BE | **Relax class-level [Authorize]** (gotcha #44 fix permanent từ S18) + server-side filter `IsUserSelectable=true` cho non-admin. Reuse endpoint hiện có, KHÔNG duplicate logic. |
| Matrix display layout | **Table 2D full 7 Allow* cột** (Bước/Cấp/NV/Mô tả + 7 cột flag Trả lại 4 mode + Edit Section 2 + Edit Budget + Skip Cấp cuối). User thấy đầy đủ quyền per slot Approver giống admin Designer read-only. |

## 🔍 Pre-A Investigator audit (🟦, ~32K tokens)

5Q audit verify trước code:

| Q | Verdict |
|---|---|
| **Q1 Endpoint permission** | ✅ Gotcha #44 đã fix permanent từ S18 (2026-05-08). Class-level chỉ `[Authorize]` bare, per-method admin Workflows.Create. Handler `GetAwAdminOverviewQuery` KHÔNG có IsUserSelectable filter — cần ADD param + conditional. |
| **Q2 Menu seed pattern** | ✅ `DbInitializer.cs:1429-1437` peOrder global increment. Permission seed `1541-1547` cho 7 role. Accounting KHÔNG trong list (admin manual grant). |
| **Q3 Admin Designer FE** | ✅ `ApprovalWorkflowsV2Page.tsx` 975 lines. 7 Allow* VI labels có sẵn line 892-948. AwLevelDto 13 fields. KHÔNG có usePermission wrap. |
| **Q4 Sidebar widen impact** | ✅ `w-72 xl:w-80` (288/320px) SAFE — sidebar 288 + main 992 → workspace 260+732 fit @ 1280px. `w-80 xl:w-96` THRESHOLD RISKY. |
| **Q5 ApplicableType enum** | ✅ {DuyetNcc=1, DuyetNccPhuongAn=2, Contract=3}. FE wire 2 type PE. |

**Surprises critical:**
- ⚠️ **PE Workspace là 2-panel** không phải 3-panel → memory `feedback_responsive_laptop_breakpoint.md` stale, sẽ update memory sau Plan AA.
- 📝 **Order strategy**: "Luồng duyệt" Order=2 first → cần shift existing leaves +1 → DbInitializer cần UPDATE Order existing (KHÔNG chỉ INSERT-if-not-exists).
- 🔸 Contract=3 chưa wire FE — chỉ DuyetNcc + DuyetNccPhuongAn.

## 🌳 Plan AA execution 3 chunk per-commit

### Chunk A — BE filter + menu seed + sidebar widen (`ee776d5`)

👤 **Chủ trì Solo** — Cross-stack tight coupling architectural decision. 6 files +73/-24 LOC.

#### BE changes (4 files)

**`MenuKeys.cs:85`** thêm helper:
```csharp
// [Plan AA S24 t1] User read-only view ma trận phân quyền của workflow V2
// admin Designer đã ghim (IsUserSelectable=true). Suffix _WfView distinct
// với admin PeWf_* (Designer write).
public static string PurchaseEvaluationWorkflowView(string typeCode) => $"Pe_{typeCode}_WfView";
```

**`DbInitializer.cs:1429-1437`** thêm tree.Add LuongDuyet (Order=2 first child) cho 2 type PE:
```csharp
tree.Add((MenuKeys.PurchaseEvaluationGroup(code), label, ..., peOrder++, "FileCheck"));
// [Plan AA S24 t1] "Luồng duyệt" leaf phía trên "Danh sách"
tree.Add((MenuKeys.PurchaseEvaluationWorkflowView(code), "Luồng duyệt", ..., peOrder++, "Network"));
tree.Add((MenuKeys.PurchaseEvaluationList(code), "Danh sách", ..., peOrder++, "List"));
tree.Add((MenuKeys.PurchaseEvaluationCreate(code), "Thao tác", ..., peOrder++, "Plus"));
tree.Add((MenuKeys.PurchaseEvaluationPending(code), "Duyệt", ..., peOrder++, "CheckCircle2"));
```

**`DbInitializer.cs:1447-1459`** refactor INSERT-only loop → INSERT-OR-UPDATE-Order:
```csharp
var existingItems = await db.MenuItems.ToDictionaryAsync(m => m.Key);
foreach (var (key, label, parent, o, icon) in tree)
{
    if (existingItems.TryGetValue(key, out var existing))
    {
        if (existing.Order != o) { existing.Order = o; reordered++; }
        continue;
    }
    db.MenuItems.Add(new MenuItem { Key = key, ... });
}
```

Idempotent: skip nếu Order match, UPDATE nếu mismatch. Shift existing prod rows Order+1 safe.

**`DbInitializer.cs:~1553`** SeedPurchaseEvaluationPermissionDefaultsAsync thêm WfView vào menuKeys list cho 7 role Read.

**`ApprovalWorkflowV2AdminFeatures.cs:89-110`** GetAwAdminOverviewQuery + handler:
```csharp
public record GetAwAdminOverviewQuery(
    int? ApplicableType = null,
    bool? IsUserSelectable = null) : IRequest<AwAdminOverviewDto>;

// Handler:
if (request.IsUserSelectable is bool ius)
    query = query.Where(d => d.IsUserSelectable == ius);
```

**`ApprovalWorkflowsV2Controller.cs:21-26`** pass-through:
```csharp
public async Task<ActionResult<AwAdminOverviewDto>> Overview(
    [FromQuery] int? applicableType,
    [FromQuery] bool? isUserSelectable,
    CancellationToken ct)
    => Ok(await mediator.Send(new GetAwAdminOverviewQuery(applicableType, isUserSelectable), ct));
```

#### FE Layout changes (2 files mirror rule §3.9)

**`fe-user/src/components/Layout.tsx:78`** resolvePath regex extend:
```ts
const peMatch = key.match(/^Pe_([^_]+)_(List|Create|Pending|WfView)$/)
// ...
if (action === 'WfView') return `/purchase-evaluations/workflow-matrix?type=${typeInt}`
```

**`fe-user` + `fe-admin` Layout.tsx**:
- Sidebar `w-60 xl:w-72` → `w-72 xl:w-80` (+48px lg, +32px xl)
- Revert Plan U S23 t11 truncate × 5 sites:
  - fe-user: MenuGroup line 188 + MenuLeaf line 251 + StaticLeaf line 292
  - fe-admin: MenuGroup line 148 + MenuLeaf line 208
- Keep `min-w-0 flex-1` parent + `shrink-0` icon/chevron + `title` tooltip (no harm — accessibility bonus)

**Verify Chunk A:** `dotnet build SolutionErp.slnx` PASS clean 0 err 2 warn pre-existing DocxRenderer.

### Chunk B — FE WorkflowMatrixViewPage (`c667802`)

🟨 **Implementer Case 2** — Cookie-cutter mirror Designer Designer read-only single file. 3 files +305 LOC.

#### NEW `fe-user/src/pages/pe/WorkflowMatrixViewPage.tsx` ~215 LOC

```tsx
// useQuery GET /approval-workflows-v2?applicableType=N&isUserSelectable=true
// PageHeader "Luồng duyệt — {label}" + Network icon
// Loading/Error/Empty 3 state rõ ràng
// WorkflowCard per ghim version:
//   header: code/version + badge "Đang dùng" emerald + "Được ghim" amber Pin
//   table 10 cột read-only:
//     Bước rowSpan | Cấp | NV duyệt + 7 Allow* flag (✓ emerald / — slate)
// FlagCell helper TS indexed-access type union 7 keys
```

#### NEW `fe-user/src/types/approvalWorkflowV2.ts` ~55 LOC

Subset DTO mirror BE `AwAdminOverviewDto`:
- `AwLevelDto` 13 fields (7 Allow*)
- `AwStepDto`, `AwDefinitionDto`, `AwTypeSummaryDto`, `AwAdminOverviewDto`
- Field name khớp BE record positional param (`history` not `versions`)

#### MODIFIED `fe-user/src/App.tsx` +2 LOC

```tsx
import { WorkflowMatrixViewPage } from '@/pages/pe/WorkflowMatrixViewPage'
// trong <Routes>:
<Route path="/purchase-evaluations/workflow-matrix" element={<WorkflowMatrixViewPage />} />
```

**Verify Chunk B:** `npm run build` fe-user PASS clean 0 TS err, 1907 modules, 2.61s, bundle 1282 KB.

### Chunk C — Cumulative Reviewer + Docs (this commit)

🟥 **Reviewer** adversarial pre-commit cumulative ~25K tokens:

| Category | Verdict |
|---|---|
| Wire BE/feature claim verify | ✅ End-to-end Controller → Mediator → Handler → FE useQuery wired |
| Schema integrity | ✅ 0 Mig mới, DbInitializer idempotent, 7 Allow* count match |
| Security | ✅ Class-level [Authorize] preserved, GET any-auth OK, POST admin policy. 1 Low note: non-admin pass `isUserSelectable=false` leak workflow chưa ghim (defer follow-up) |
| Code quality | ✅ BE 0 err + fe-user PASS + **fe-admin PASS 1926 modules 740ms 0 TS err** (Reviewer tự verify bonus) |
| Test coverage | ✅ Accept Phase 9 UAT exception per `feedback_uat_skip_verify` |

**Anti-fiddle:** 0% drift, ~346 LOC khớp spec.
**Mirror 2 FE app §3.9:** Sidebar widen + remove truncate mirror cả 2 ✓. WorkflowMatrixViewPage fe-user only (admin Designer riêng).
**Smart Friend guard:** Active, 0 critical/major/minor blocker.

#### Docs deliverables Chunk C

- `docs/STATUS.md` Recently Done newest entry S24 t1
- `docs/HANDOFF.md` Last updated S24 t1 prepend
- `docs/changelog/sessions/2026-05-15-s24-turn1-plan-aa-workflow-matrix.md` (file này)
- `.claude/agent-memory/investigator/MEMORY.md` FIFO entry S24 Pre-A
- `.claude/agent-memory/implementer/MEMORY.md` FIFO entry S24 Chunk B
- `.claude/agent-memory/reviewer/MEMORY.md` FIFO entry S24 cumulative verify

## 📊 Stats Plan AA chốt

| Metric | Trước (S23 t12) | Sau (S24 t1) | Δ |
|---|---|---|---|
| Migrations | 31 | 31 | 0 (no Mig mới) |
| DB tables | 59 | 59 | 0 |
| Endpoints | ~145 | **~146** | +1 (GET filter param) |
| FE pages | 34 | **35** | +1 (WorkflowMatrixViewPage) |
| Unit tests | 111 | 111 | 0 (UAT mode skip) |
| Gotchas | 47 | 47 | 0 |
| Memory entries user-level | 21 | 21 | 0 (no new — recommend memory responsive update sau) |
| Skills | 6 | 6 | 0 |
| Sub-agents | 4 | 4 (3 spawn S24 t1) | — |
| **Commits Plan AA** | — | **3** | `ee776d5` (A BE+Layout) · `c667802` (B FE) · this (C Docs) |

## 🎯 Multi-agent ROI Plan AA

| Spawn | Cost | Verdict | Catch / Value |
|---|---|---|---|
| 🟦 Investigator Pre-A | ~32K | ✅ | 5Q audit + 3 surprises (PE 2-panel + Order strategy + Contract enum unwired). Saved em main blind code 4 wrong assumptions. |
| 🟨 Implementer Chunk B | ~14K | ✅ | 3 files +305 LOC cookie-cutter Designer read-only mirror. Build PASS. shadcn fe-user no Card/Badge fallback inline div pattern. |
| 🟥 Reviewer Chunk C | ~25K | ✅ | 0 critical/major/minor block. Bonus catch fe-admin build (~7s) verify mirror app PASS. 1 Low note non-admin filter leak workflow chưa ghim defer. |
| 👤 Chủ trì Solo Chunk A + coordinate | ~80K | ✅ | BE 4 file + Layout 2 file cross-stack architectural decisions. Order shift refactor INSERT→UPDATE idempotent. |

**Total cost cumulative Plan AA:** ~150K (~25% solo equiv). Multi-agent leverage cache 70-90% per session.

## 📋 Pattern reinforced

1. **Gotcha #44 relax class-level [Authorize] PROVEN cross-stack reuse** — Workflows endpoint từ admin-only sang any-auth read accessible. Mảnh pattern reusable cho future read-only user view (Form template view, Department list view, etc.).

2. **DbInitializer INSERT-OR-UPDATE-Order pattern** — Idempotent re-deploy safe shift existing prod rows. Cross-project reusable cho menu re-ordering / label rename / icon change without migration.

3. **Plan U truncate revert pattern** — widen container + drop truncate + keep `min-w-0 flex-1` + `shrink-0` + `title` tooltip. Reusable cross-project sidebar / nav menu / breadcrumb.

4. **Read-only admin Designer mirror page** — Implementer Case 2 pattern: drop edit mutations + reuse types + filter via query param. Reusable cho future user view của admin config (Role permission view, Workflow template view, etc.).

## ⏭ Pending S24+

- 🟢 Bro UAT verify Plan AA deploy sau ~3-5 phút CI Run #210+
- 🟩 CICD Monitor spawn post-deploy verify bundle hash 2 app + smoke endpoint `?isUserSelectable=true` + DbInitializer Order shift idempotent
- 🟡 Plan B Contract V2 wire (Mig 32+33) — HIGH priority next (pre-allocated 5-6 chunk in HANDOFF)
- 📝 Memory `feedback_responsive_laptop_breakpoint.md` update: PE Workspace 2-panel (NOT 3-panel) — defer Chunk D bonus
- 🔍 Discovery #3 anomaly CI trigger docs-only (3× reinforced) — Investigator follow-up
- 🔍 Discovery #4 ASP.NET enum body deserialization (LOW polish)
- 🔧 Gotcha #47 paths-ignore agent-memory (pending bro chốt)
- ⚠️ Security follow-up: BE enforce `isUserSelectable=true` mandatory cho non-admin (Reviewer Low note)

## References

- Files: [WorkflowMatrixViewPage.tsx](../../../fe-user/src/pages/pe/WorkflowMatrixViewPage.tsx) · [types/approvalWorkflowV2.ts](../../../fe-user/src/types/approvalWorkflowV2.ts) · [MenuKeys.cs:85](../../../src/Backend/SolutionErp.Domain/Identity/MenuKeys.cs) · [DbInitializer.cs:1429-1459](../../../src/Backend/SolutionErp.Infrastructure/Persistence/DbInitializer.cs)
- Rules: §3.9 mirror 2 FE app, §6.5 KEEP narrative, §7 test timing
- Cross-ref skill `permission-matrix` (menu structure) + `contract-workflow` (workflow V2)
- Memory cross-ref `feedback_per_nv_permission_scope.md` (7 Allow* per-Level slot) + `feedback_uat_skip_verify.md` (Phase 9 test-after)

---

## 🔁 Phase 2 — Polish iteration UAT feedback (4 commit post Plan AA core)

Sau Run #210 deploy Plan AA core (3 commit `ee776d5..ac2c859`), bro UAT iterate 4 polish chunks back-to-back theo visual feedback. Em main solo 4× CSS/UX polish (KHÔNG spawn agent — criteria #6 < 30 min trivial + criteria #2 UX decision).

### Polish 1 — Hotfix container px-2 (`da218f1`)

**Trigger:** Bro UAT: "cho nó dịch hết sang bên trái nhé"

Container `space-y-4 px-6 py-5` → `space-y-4 px-2 py-5` (24px → 8px). PageHeader + WorkflowCard + Table cùng shift left -16px sát sidebar border.

Verify: `npm run build` fe-user PASS 486ms, bundle 1282.59 KB unchanged.

### Polish 2 — Redesign v1 panel-per-NV color (`4d60598`)

**Trigger:** Bro UAT screenshot admin Designer: "cho hiển thị rõ ràng các thông tin như thế này luôn. Giữa các cấp duyệt khác nhau cho màu sắc khác nhau, Giữa các phòng ban khác nhau cho màu sắc khác nhau."

Drop table 11 cột symbol khó hiểu (↶/✎/⏩) → panel-per-NV mirror admin Designer line 853-949:
- Step (Phòng) container có unique color cycle 5 màu (`STEP_PALETTE`: blue/purple/emerald/amber/pink)
- Cấp badge ring có unique color cycle 5 màu (`LEVEL_PALETTE`: violet/sky/teal/orange/rose)
- "QUYỀN DUYỆT {NV name} {email}" header amber-700 uppercase
- 7 checkbox label tiếng Việt nguyên văn (read-only disabled + accent-emerald)
- Grid `grid-cols-1 sm:grid-cols-2` cho 4 return mode + col-span-2 cho 3 long label

**Tailwind JIT discipline:** Palette arrays với full class strings (KHÔNG dynamic interpolation `bg-${color}-100` — JIT purge):
```ts
const STEP_PALETTE = [
  { bg: 'bg-blue-50/40', border: 'border-blue-200', headerBg: 'bg-blue-100', headerText: 'text-blue-800' },
  // ... 4 more
] as const
```

Verify: `npm run build` fe-user PASS 423ms, bundle 1282.91 KB (+0.32 KB).

### Polish 3 — Redesign v2 HTML table rowSpan tận dụng full width (`fbbd361`)

**Trigger:** Bro UAT: "Đúng rồi hiển thị như vậy nhưng dạng table cho đẹp và rõ ràng, tận dụng hết toàn bộ layout rộng"

Refactor panel-per-NV → HTML `<table>` 4 cột với rowSpan:

```
┌────────────────┬──────┬──────────────┬─────────────────────────────────────┐
│ Bước (Phòng)   │ Cấp  │ NV duyệt     │ Quyền duyệt (grid 2-col 7 checkbox) │
├────────────────┴──────┴──────────────┴─────────────────────────────────────┤
│ Bước 1 (Phòng Cung ứng) - Step bg blue - rowSpan 5                         │
│                          ├ Cấp 1 - rowSpan 4 OR-of-4 ├ NV 1 ├ 7 checkbox  │
│                                                       ├ NV 2 ├ 7 checkbox  │
│                                                       ├ NV 3 ├ 7 checkbox  │
│                                                       ├ NV 4 ├ 7 checkbox  │
│                          ├ Cấp 2 - rowSpan 1          ├ NV 5 ├ 7 checkbox  │
│ Bước 2 (Phòng Kiểm soát) - Step bg purple - rowSpan N                      │
│ ...                                                                        │
└────────────────────────────────────────────────────────────────────────────┘
```

Helper `buildStepRows(step)` build flat `Row[]` với metadata: `(isFirstInStep, isFirstInCap, rowSpanStep, rowSpanCap)`. Render flat `<tr>` với conditional `<td rowSpan={}>`.

`colgroup` width hint: 160px / 100px / 240px / 1fr (rest). Tại 1280-1366px viewport: Quyền duyệt cell ~400-500px → grid `md:grid-cols-2` fit 7 label OK.

Color preserved 2 layer (Step bg + Cấp badge cycle). NV + Quyền duyệt cell `bg-white/80` lighten Step tone.

Verify: `npm run build` fe-user PASS 522ms, bundle 1284.22 KB (+1.31 KB).

### Polish 4 — Wrap fix sidebar label về đầu hàng (`ee0902a`)

**Trigger:** Bro UAT screenshot sidebar: "1. Duyệt Nhà Cung Cấp - Thầu phụ" wrap 2 dòng với dòng 2 "(NCC -TP)" indent sau icon thay vì về đầu hàng. "Cho nó về đầu hàng nhé, thu nhỏ lại cũng đc."

**Root cause:** Flex container `items-center` + inner-flex `[icon][text]` → 2nd line wraps within INNER SPAN (indent past icon area).

**Fix pattern (5 sites: 3 fe-user + 2 fe-admin mirror §3.9):**
- Restructure flex → block + inline-block icon + inline text + absolute ChevronDown right
- `inline-block` icon + `inline` text share single line box → wrap continuation text về left edge container
- ChevronDown absolute để KHÔNG bị đẩy xuống khi label wrap multi-line

```diff
- <span className="flex min-w-0 flex-1 items-center gap-2">
-   <Icon className="h-4 w-4 shrink-0" />
-   <span title={label}>{label}</span>
- </span>
- <ChevronDown className="h-3.5 w-3.5 shrink-0 ..." />

+ <Icon className="mr-1.5 -mt-0.5 inline-block h-4 w-4 align-middle" />
+ <span className="align-middle" title={label}>{label}</span>
+ <ChevronDown className="absolute right-2 top-2 ..." />
```

**Smaller text scale:**
- MenuGroup non-top button: `text-[13px]` → `text-[12px] leading-snug`
- MenuLeaf top level: `text-sm` → `text-[12px] leading-snug`
- MenuLeaf deep: `text-[12px]` → `text-[11px] leading-snug`
- StaticLeaf: `text-sm` → `text-[12px] leading-snug`

Verify: `npm run build` fe-user PASS 432ms + fe-admin PASS 494ms.

---

## 📊 Stats S24 chốt cuối cumulative

| Metric | S23 t12 chốt | S24 chốt | Δ |
|---|---|---|---|
| Migrations | 31 | 31 | 0 (Plan AA no Mig mới) |
| DB tables | 59 | 59 | 0 |
| Endpoints | ~145 | **~146** | +1 (GET filter param `isUserSelectable`) |
| FE pages | 34 | **35** | +1 (WorkflowMatrixViewPage) |
| Unit tests | 111 | 111 | 0 (UAT mode skip, baseline confirmed post-Plan AA) |
| Gotchas | 47 | 47 | 0 |
| Memory user-level | 21 | 21 | 0 (no new — pattern captured trong agent MEMORY) |
| Skills | 6 | 6 | 0 |
| Sub-agents | 4 | 4 (4 spawn S24) | — |
| **Commits S24** | — | **7** | `ee776d5..ee0902a` push remote |

---

## 🎯 Multi-agent ROI S24 cumulative

| Owner | Cost | Verdict | Value catch |
|---|---|---|---|
| 🟦 Investigator Pre-A | ~32K | ✅ | 5Q audit + 3 surprises (PE 2-panel correction + Order shift strategy + Contract enum unwired). Saved em main 4 blind code assumptions. |
| 🟨 Implementer Chunk B | ~14K | ✅ | 3 files +305 LOC mirror Designer read-only. shadcn no Card/Badge → fallback inline div pattern. |
| 🟥 Reviewer Chunk C | ~25K | ✅ | 0 blocker. Bonus catch fe-admin build verify mirror app PASS. |
| 🟩 CICD Monitor Run #210 | ~12K | ✅ | 4/4 wire verify + bundle rotate + Order shift idempotent confirm prod. |
| 🟦🟨🟥 3 agent S24 wrap flush | ~12K | ✅ | Cumulative MEMORY drift patches (Implementer +3 patterns 13/14/15, Reviewer +4 anti-patterns, Investigator +1 entry). |
| 👤 Chủ trì Solo Chunk A + 4 polish + coordinate | ~80K | ✅ | BE 4 file + Layout 2 file + 4 polish iteration UAT feedback ~250 LOC cumulative. |

**Total cost cumulative S24:** ~175K (~28% solo equiv). Cache leverage 70-90% per session.

---

## 📋 Patterns reinforced (cross-project reusable)

1. **Gotcha #44 relax class-level [Authorize] PROVEN cross-stack reuse** — Workflows endpoint từ admin-only sang any-auth read accessible (cumulative S18 → S24). Pattern reusable cho future user view của admin config.

2. **DbInitializer INSERT-OR-UPDATE-Order idempotent pattern** — Shift existing prod rows Order qua re-deploy without migration. Add `existingItems = ToDictionaryAsync(m => m.Key)` + UPDATE if mismatch logic. Cross-project reusable cho menu re-order / label rename / icon change.

3. **Tailwind JIT palette array pattern** — Full class strings (KHÔNG dynamic interpolation `bg-${color}-100`). `const PALETTE = [{bg: 'bg-blue-50/40', ...}, ...] as const` cycle theo `idx % length`. Cross-project reusable cho menu hierarchy color coding, status badge, category tone.

4. **HTML table rowSpan flat row builder helper** — Build `Row[]` với metadata `(isFirstInStep + isFirstInCap + rowSpanStep + rowSpanCap)`. Cleaner than nested loop. Cross-project reusable cho hierarchical data table (org chart, BOM, schedule).

5. **Hanging-indent reverse CSS fix** — Sidebar label dài wrap về đầu hàng (under icon) via `inline-block` icon + `inline` text + absolute right ChevronDown. Drop flex container wrapper. Cross-project reusable cho long-label nav menu / breadcrumb / list item.

6. **Read-only admin Designer mirror page** — Implementer Case 2 pattern: drop edit mutations + reuse types + filter via query param. Cross-project reusable cho future user view của admin config (Role permission view, Workflow template view).

7. **UI/UX polish iteration discipline** — UAT visual feedback iteration KHÔNG cần Reviewer spawn mỗi chunk (cost overhead ROI thấp). BUNDLE cumulative verify cho heavy chunk, SKIP < 30 min polish (criteria #6 + #2 REFUSE). S24 validated: ~25K bundle vs ~100K multi-spawn polish.

---

## ⏭ Pending S25+

- 🟢 Bro UAT verify Plan AA polish iteration cumulative qua 4 deploy Run #211-#214
- 🟡 **Plan B Contract V2 wire** (Mig 32+33) — HIGH priority next (pre-allocated 5-6 chunk in HANDOFF cũ S23)
- 📝 **Implementer agent MEMORY curate** — ~31.5KB > 25KB threshold, archive S20-S22 old entries → `archive/2026-05-S20-S22.md`. Defer next session spawn.
- 📝 **CICD Monitor agent MEMORY curate** — ~43KB also over threshold, archive Run #186-#200 old entries.
- 🔍 Discovery #3 anomaly CI trigger docs-only (3× reinforced S23) — defer Investigator follow-up
- 🔍 Discovery #4 ASP.NET enum body deserialization (LOW polish)
- 🔧 Gotcha #47 paths-ignore agent-memory (pending bro chốt)
- ⚠️ Security follow-up Low note: BE enforce `isUserSelectable=true` mandatory non-admin (Reviewer Low note) — defer
