# STYLE-GATE v2 — bản HỢP NHẤT sau B4 (lead synthesize, 2026-08-12)

> **Cách đọc (B1 trỏ-nền + delta):** nền = `sub-style-fe-b3.md` (25 SF + 10 bẫy) + `sub-style-be-b3.md`
> (20 SB + 7 BS) — VẪN LÀ LUẬT, **trừ các delta dưới đây (delta THẮNG nền)**. Checklist ô đo: DÙNG
> BẢNG Ở ĐÂY (bảng trong 2 file B3 đã bị B4 chứng có false-green/dương-giả — giữ làm vết, không chạy).
> Nguồn vá: `sub-style-review-b4-fe1.md` (11 R) · `-fe2.md` (12 R, fault-inject cây tạm) ·
> `-be2.md` (15 R + T-matrix + đo LocalDB). Lane be1-anchor chết non (skeleton) — phần anchor BE được
> be2 spot-check chồng lấn **9/9 khuôn SỐNG**, lead chấp nhận không relaunch (khai).

## A. DELTA luật FE (đè lên SF tương ứng)

1. **[fe1-R1] +Khuôn bị bỏ sót:** `fe-admin/src/pages/contracts/ContractsListPage.tsx:141` grid
   `[340px_1fr_360px]` + `:265` `{!selectedId && <PipelineTreePanel currentStage={3}
   bodyMaxHClass="max-h-[28rem] lg:max-h-none" />}` — đây là khuôn GĐ3-prod ĐỜI TRƯỚC (KhkkListPage
   tự khai copy từ nó). **Luật mới SF-26:** W2 đặt cây vào panel host PHẢI quyết `bodyMaxHClass`
   tường minh (mặc định của panel đẻ "2 cuộn lồng nhau" — `PipelineTreePanel.tsx:112-115`).
2. **[fe1-R2] SF-21/SF-8 bổ vế GATE:** khuôn thật `WorkflowMatrixViewPage.tsx:85-86` là
   `typeInt === 10 && rawGroup !== null && /^[1-8]$/`. Nhánh `ct` mới PHẢI `typeInt === 3 &&
   rawCt !== null && /^[1-7]$/` — thiếu vế `typeInt === 3` thì `?type=1&ct=5` lọc HD-* trên bộ
   non-HĐ = trang trắng.
3. **[fe1-R3] SF-24 literal sửa:** câu khuôn thật là **"hông truyền ⇒ …"** (6 hit) — gate đo theo
   NGHĨA: JSDoc opt-in phải có mệnh đề "(kh|h)ông truyền ⇒ giữ nguyên/behavior cũ", regex
   `grep -cE "hông truyền"` ≥1 per prop mới.
4. **[fe1-R4] SF-14 sửa tên:** Select của repo = **wrapper `<select>` NATIVE** (`ui/select.tsx:4-19`,
   0 hit radix) — CẤM kéo `@radix-ui/react-select` mới.
5. **[fe1-R5] SF-15 class header đầy đủ:** `flex shrink-0 flex-wrap items-center justify-between
   gap-3 border-b border-slate-200 bg-white px-6 py-3` (`KhkkListPage.tsx:157`).
6. **[fe1-R6/R-7] số đính chính:** SF-10 = **11** mutation vùng `:243-367` · Bẫy-1 anchor
   `removePlan :230-240`, chứng cứng hơn: `setConfirmDelete(true)` **0 caller** · Bẫy-9 sửa:
   `grep 'lũy kế'` KHKK nay = **1 hit** và hit đó là CHÍNH comment khai việc cắt (citation-trap —
   đọc hit phải xem enclosure).
7. **[fe1-R9] C-6 thêm ô dương:** sau W1, `grep -cE "7 bộ|per-loại|S188"
   fe-user/src/components/Layout.tsx` ≥1 (chống xoá-trắng-comment).

## B. DELTA luật BE (đè lên SB tương ứng)

1. **[be2-R1 BLOCKER] SB-13/SB-14/SB-18 + C10/C13 LẬT theo spec v2.1:** SB-13 → *"GIỮ shape hiện
   tại: class `[Authorize]` trần, action DELETE **KHÔNG** policy — `[carry:w8-delete-policy]`"* ·
   SB-14 → comment khai VÌ SAO CHƯA gắn (dẫn chốt (82) + BS-3) · **SB-18 BỎ** (reflection-test
   policy) · C10 kỳ vọng **=0** · C13 bỏ, KHÔNG viết test âm pin-lỗ (phản-ca T-14).
2. **[be2-R2] BS-3 nâng thành LUẬT CẤM có số:** key `Contracts` đo LocalDB Dev = **12/13 role
   `CanDelete=0`, Drafter=0** (control: `KeHoachKyKet` Drafter=1) ⇒ gắn policy đợt này = khoá oan
   CHẮC. Điều kiện mở W8: policy + grant data cho Drafter **CÙNG commit**; trước W8 đo lại **prod**
   (Dev ≠ prod — admin có thể đã sửa tay).
3. **[be2-R13] +Cảnh báo GHOST-WIRE cạnh K7:** comment `ContractChangelog.cs:10-13` nói "AuditBehavior
   tự populate" — **file KHÔNG TỒN TẠI** (0 hit ngoài chính comment). Changelog là GHI TAY (2 site
   hiện có: `CreateContractFromSigningPlanFeatures.cs:247` · `CreateContractFromEvaluationFeatures.cs:121`)
   ⇒ W5 PHẢI `Add()` tay, đừng tin comment.
4. **[be2-R14] SB-15 đổi khuôn HÌNH:** fixture theo **Contract-family** (`ContractSigningPlanCrudTests.cs:77`
   `KhkkFixture` / `ContractCatalogTests.cs:83` — 5 fixture private-per-file cùng nếp, dựng cái thứ 6
   là ĐÚNG khuôn); K12 PE giữ làm nguồn LÝ-DO. Fixture PHẢI wire `AuditingInterceptor` (thiếu =
   assert-rỗng-nghĩa).
5. **[be2-R7] Ca "V1-nháp phase 2" TRÙNG DangSoanThao(2)** — thay bằng **phase 3 `DangGopY`**
   (legacy `<DangInKy`, Dev có 2 HĐ sống) chống vá-quá-tay THẬT (worker chép hình pattern
   `is not (DangSoanThao or TuChoi or TraLai)` của KHKK sẽ đánh rơi phase 1/3/4).

## C. T-MATRIX W5 — 13 ca + 1 phản-ca (nguồn chân lý duy nhất, thay mọi danh sách ca cũ)

| # | Ca | Actor | Kỳ vọng |
|---|---|---|---|
| T-1 | DangSoanThao(2), HĐ mình | Drafter-chủ | OK + assert 3-trong-1 (khuất query thường · `IgnoreQueryFilters` IsDeleted/DeletedBy · changelog đúng 1 dòng ContextNote/PhaseAtChange/UserId) |
| T-2 | TraLai(98) | Drafter-chủ | OK (Dev 0 row ⇒ fixture seed thẳng Phase) |
| T-3 | TuChoi(99) | Drafter-chủ | OK |
| T-4 | DangGopY(3) legacy | Drafter-chủ | OK — chống vá-quá-tay |
| T-5 | ChoDuyet(10) | Drafter-**chủ** | 409 (khuôn KHKK; approver-delete = Q4) |
| T-6 | DaPhatHanh(9) | **Admin** | 409 — đã gen mã, không bao giờ xoá |
| T-7 | DangSoanThao, HĐ người khác | người lạ | 403 |
| T-8 | DangSoanThao, HĐ người khác | **Admin** | OK — ca DƯƠNG vế `\|\| isAdmin` |
| T-9 | `DrafterUserId=null` | actor `UserId=null` | KHÔNG xoá được (nấc-0 — chặn `null==null`) |
| T-10 | id không tồn tại | người lạ | 404 (khoá thứ tự NotFound→Forbidden) |
| T-11 | ChoDuyet, HĐ người khác | người lạ | 403 (không phải 409 — khoá Forbidden→Conflict) |
| T-12 | Reason 2001 ký tự | Drafter-chủ | ValidationException |
| T-13 | Reason "   " | Drafter-chủ | OK + ContextNote==null (trim-to-null; gộp vào T-1 được) |
| T-14 | **phản-ca — KHÔNG VIẾT** test reflection-authz cho Delete (2 chiều đều cấm — chiều phủ-định pin lỗ, ĐỎ oan khi W8) | | |

File test: `tests/SolutionErp.Infrastructure.Tests/Application/ContractDeleteGuardTests.cs`
(tên CỤ THỂ — CẤM glob; `[Fact]` ≥13; 680→680+N pin 2 ô STATUS: dòng session DELTA + row canonical
`:476` sau cicd-verify — be2-R12).

## D. CHECKLIST Ô ĐO — bản THAY THẾ (fault-inject-proof; nối `;` không `&&`, `grep -c … || true`)

**FE (thay C-1..C-13 của sub-style-fe-b3):**
- **C-1a/b (eslint DELTA — baseline ĐỎ sẵn 47/42):** `npx eslint .` fe-user ≤47 (41E/6W) ·
  fe-admin ≤42 (37E/5W) — KHÔNG TĂNG; file-wave: `Layout.tsx` ≤4E và **0 ruleId mới** (4 lỗi sẵn:
  325/416/453/478 — cái :478 nằm ĐÚNG khối accordion W1 sửa, đổi hình thì lỗi phải ≤ cũ). Đo exit
  bằng `${PIPESTATUS[0]}`, không tin `$?` sau pipe.
- **C-2 (build):** `npm run build` app đụng — baseline `tsc -b` fe-user SẠCH ⇒ ô CÓ RĂNG.
- **C-3 (mirror + guard MISSING):** vòng 4 cặp, `{ [ -f A ] && [ -f U ]; } || echo MISSING` trước
  khi so SHA.
- **C-6:** `grep -c "chung 1 bộ" …Layout.tsx || true` = 0 **VÀ** ô dương `grep -cE "7 bộ|per-loại|S188"` ≥1.
- **C-7:** `grep -A6 "TRANSIENT_QUERY_KEYS = " fe-user/src/components/Layout.tsx | grep -cE "'(ct|view|pendingMe)'"` = 0.
- **C-8:** vế-1 `grep -rnE "get\(['\"]ct['\"]\)" fe-user/src/components/Layout.tsx` ≥1 (mới) ·
  vế-2 `grep -rnF 'workflow-matrix?type=3&ct=' fe-user/src` ≥1 = **ô canh-thoái-lui** (đã xanh
  baseline — label rõ, không phải bằng chứng tiến độ).
- **C-9:** `grep -oE "['\"]HD-(NT-NCC|NT-DV|TP|GK|NCC|DV|MB)['\"]" fe-user/src/pages/pe/WorkflowMatrixViewPage.tsx | sort -u | wc -l` = **7**.
- **C-10:** `grep -rn "onOpenContract" fe-admin/src | grep -vE "hooks/usePipelineStages\.ts|components/pipeline/PipelineTreePanel\.tsx" | wc -l` = 0.
- **C-11:** `grep -F "grid-cols-[400px" …MyContractsPage.tsx` ≥1 **VÀ** ô hoán-đổi
  `grep -c "{!selectedId && <PipelineTreePanel" …MyContractsPage.tsx || true` = **0** (baseline 1).
- **C-12:** `grep -c "window.confirm" …ContractDetailContent.tsx` ≥1 **VÀ**
  `grep -A2 "window.confirm" … | grep -c 'Đã xóa'` ≥1 (message có đích) · `<Dialog` giữ = 1.
- **C-13 (W4 ô RANGE):** `open=$(grep -n "if (awV2Match) {" F)` · `close` = dấu `}` sau đó ·
  `hit=$(grep -n "findContractWfByTypeCode(code)" F)` ⇒ `open < hit < close` và `hit > dòng KhkkN`.
  (Mốc baseline: open=176 · KhkkN=188 · close=189.)
- **C-MIRROR-KHAI (mới, cho W5-iv):** `grep -c "File MIRROR SHA256 identical" <ContractDetailContent
  2 bên>` = 1 mỗi bên (baseline 0/0).
- **C-5 (nguồn danh-sách file):** union `{ git diff --name-only HEAD; git ls-files -o
  --exclude-standard; } | sort -u | grep -E '^fe-'` — git diff MÙ untracked.

**BE (thay 18 ô C của sub-style-be-b3 — bảng be2 nguyên trạng):** dùng đúng bảng
`sub-style-review-b4-be2.md §Ô đo phải sửa` (C1-C21: scope `awk '/class DeleteContractCommandHandler/,/^}/'`
trước khi grep [C3/C5 dương-giả 7-hit/4-hit] · C10 lật =0 · C13 bỏ · C14 neo TÊN FILE cấm glob ·
C16 lật 2 ô STATUS · C17 viết `"A|B|C"` không escape pipe + control-dương 292 · C18 union
ls-files · C19 `--include=*.cs` [14 hit binary hôm nay] · C21 sqlcmd ma-trận CanDelete trước mọi
kết luận policy).

**Dữ liệu đo tay (be2 đo Dev):** ChoDuyet=1 ✅ · DaPhatHanh=2 ✅ · **TraLai=0 · TuChoi=0** ⇒ 2 trạng
thái trung tâm W5 phải TỰ DỰNG khi đo tay (xUnit seed thẳng Phase là đủ) hoặc DEFERRED-TO-DEPLOY.
