# run — S159 arc-2 · Tổng quan pipeline 4 GĐ + điều chỉnh menu eOffice

- **run-id:** `2026-07-29-S159-tong-quan-pipeline-menu` · phiên-LOGIC **L8**, window 1 (arc thứ 2 sau bookend)
- **mode:** RUN-TRACE (≥3 task) · lead = **Fable 5** (anh `/model` — chủ dự án đổi, 0 caveat)
- **Nguồn lệnh:** ảnh chụp eoffice + text anh 2026-07-29 (đánh dấu 3 vùng đỏ) + 4 câu AskUser đã chốt

## 🔴 OWNER CHỐT — NGUYÊN VĂN (2026-07-29, gõ vào sổ ngay để không bốc hơi — bài ring2 M-1 @S158)

**Lệnh khung (text kèm ảnh):**
> *"Hiện điều chỉnh cái giao diện này lại theo thế này. Đổi vai main sang Fable nhé. Hiện là tổng quy trình mình đã đi (…): GĐ1: Duyệt NCC (1→6) CEO chốt đã xong · GĐ2: Đề xuất Ký kết hợp đồng (7→12) — Có 1 bước email thì bỏ đi cũng đc, thay bằng bước bắt các phiếu từ Giai đoạn 1 → Để nối tiếp làm tiếp. Đây là 1 đầu phiếu mới hoàn toàn · GĐ3: 12→18 (Duyệt Hợp đồng — Cái này mình đã có làm trước rồi, tận dụng lại) · GĐ4 (Bước còn lại): Ký cứng hợp đồng các thứ → có thể nằm ngoài do vướng chữ ký cứng, ký nháy các thứ, có thể ko cần đủ các bước chỉ cần cho upload các file đã ký cứng lên là đc. Đọc lại nắm đủ nội dung. Xong báo lại tao trước khi làm."*

**4 câu chốt (AskUser cùng ngày):**
- **❶ Ranh GĐ:** *"Đúng như em map"* ⇒ GĐ2 = b.7→**12** (b.12 = chốt con số) · GĐ3 = b.**13**→18 · GĐ4 = b.19→21 + ký cứng upload · **tên phiếu/menu GĐ2 = "Đề xuất ký kết hợp đồng"** (spec đổi nhãn theo).
- **❷ Tổng quan:** *"Pipeline 4 GĐ đầy đủ"* — GĐ1+GĐ3 data thật · GĐ2/GĐ4 khung "sắp triển khai".
- **❸ Ẩn 3 nhóm (verbatim):** *"Chỉ nên hiển thị ở trang admin là đc → Để tao khỏi nhầm giữa cái đã public và cái đang làm"* ⇒ `IsVisible=0` cho 3 root `Hrm` `Off` `Personal` (fe-admin thấy đủ, eOffice ẩn — đúng comment entity `MenuItem.cs`).
- **❹ Mở HĐ + DANH MỤC (verbatim):** *"Hiện đang phát triển → Nên cứ cho hiển thị hết để mọi người góp ý, còn duyệt NCC → thì cứ để như cũ nhé. Phần đó đã tốt rồi."*
  🔴 **Ghi sổ rủi ro CÓ Ý THỨC:** lead đã nêu lỗ hổng authz HĐ (`ContractsController` `[Authorize]` trần 22 endpoint ghi + Reject-trước-guard, @S156) và rằng mở menu = phơi lỗ cho toàn bộ NV. **Anh tái khẳng định hiển thị hết, KHÔNG vá đợt này** (giữ "chỗ hợp đồng cứ từ từ"). Đây là quyết định chủ dự án, đã cảnh báo, chấp nhận trong giai đoạn phát triển. **PE (Duyệt NCC) KHÔNG chạm.**

## Ánh xạ 4 GĐ ↔ đĩa (đã báo, anh xác nhận)

| GĐ | Bước | Đĩa |
|---|---|---|
| 1 Duyệt NCC | 1→6 | ✅ module PE (`DaDuyet=7`) |
| 2 Đề xuất ký kết HĐ | 7→12 | 📋 spec KHKK 5-wave (nay đổi nhãn); b.7 email **BỎ** — thay bằng tạo-phiếu-từ-PE-`DaDuyet` = **spec W2 đã đúng sẵn** ⇒ Q8 (email external) **ĐÓNG: out-of-scope** |
| 3 Duyệt HĐ | 13→18 | ✅ tận dụng Contract V2 (verdict `LAI` @S156) |
| 4 Ký cứng | 19→21 | 🔸 ngoài hệ thống — upload file đã ký (Contract attachments sẵn nền); thiết kế sau |

## Stages

- [x] **T1 — FE Tổng quan pipeline** ✅ — `frontend-designer` land `UserDashboardPage.tsx` **585+/143−** (1 file, 0 dep mới, khai `NO-SCREENSHOT` trung thực); return dính #53 nhưng **file chính + sub-file 15.803B đã trên đĩa TRƯỚC khi chết** — mất 0 B. ⭐ Designer **BÁC 2 dữ kiện lead đưa** (đọc BE thật): `pendingMe` KHÔNG phải query-param (là endpoint `/inbox` mảng phẳng) · `Paged<T>` field là `total` không phải `totalCount`.
- [x] **T4a — reviewer diff** ✅ `PASS_WITH_FLAGS — 7 FLAG (0H/2M/5L)` (`sub-reviewer-diff-tongquan.md` 16.761B; §A 12-claim verify độc lập + 7 giả-thuyết-tự-bác; chết #53 ×1 giữa chừng, resume sạch). **Disposition TỪNG DÒNG:**
  · FLAG-1 [M] aria trùng tên → **FIX**: prop `group` BẮT BUỘC + `aria-label` (TS tự bắt đủ 6 call-site)
  · FLAG-2 [M] 2 chuỗi Anh → **FIX**: "Toàn trình 4 giai đoạn" · "Tải lên bản hợp đồng đã ký cứng"
  · FLAG-3 [L] câu GĐ2 nói quá → **FIX**: bỏ mệnh đề "sẵn sàng nối tiếp"
  · FLAG-5 [L] queryKey đôi → **FIX SÂU HƠN đề nghị**: 🔴 đề nghị gốc (dùng chung key, giữ queryFn trả `.length`) sẽ **VỠ CACHE** InboxPage (cache mảng ⟂ number cùng key) — fix đúng = chung key + chung queryFn (mảng) + `select: d => d.length`; request +2→+1
  · FLAG-4 [L] đích `/inbox` số lệch nguồn → **PARK** (lệch có sẵn pre-diff, fix cần param/endpoint mới — ngoài scope, ghi nợ)
  · FLAG-6 [L] 2 nút Thử-lại trùng → **PARK** (reviewer tự đính chính giữa chừng; cosmetic, chấp nhận)
  · FLAG-7 [L] nhãn GĐ4 → **NO_CHANGE** (đúng spec owner ❷/❹ — GĐ4 cố ý ngoài hệ thống)
- [x] **T4b — build độc lập SAU mọi edit** ✅ PASS 454ms (`tsc -b && vite build`) · 6/6 `group` land · diagnostics IDE giữa chừng = snapshot dở (#68), bác bằng compiler thật
- [x] **T4c — commit đợt-1 `b5799fc` push → cicd `PASS 9/9`** (bundle user `DptYR4wL`→`0GO8mgrG`, 3 marker dương + 2 control âm, smoke 4/4; return garble #53 lần 10 — đĩa `cicd-verify-b5799fc.md` 9.930B trọn)

## ĐỢT-2 (3 lệnh mid-turn của anh, 11:24-11:5x)

**Owner verbatim:** ① *"sửa giao diện nhanh"* ② ảnh tab strip 4 giai đoạn trên phiếu + *"cho hiển thị các tab phía trên như này nhé"* ③ *"Phần menu cũng hiển thị đúng như vậy luôn. (Trình ký Hợp đồng đổi thành Duyệt hợp đồng, ký cứng -> Hợp đồng cứng)"* · UAT anh Kiệt (chat FDC): *"cho đánh số thứ tự phía trước giúp anh — 01 . HĐ thầu phụ"*.
🔴 **Bộ tên 4-GĐ CHỐT MỚI NHẤT (supersede nhãn ❶ sáng nay):** Duyệt NCC · **Kế hoạch ký kết HĐ** · **Duyệt hợp đồng** · **Hợp đồng cứng** — tin-mới-nhất-thắng; nhãn "Đề xuất ký kết hợp đồng" (❶) chỉ sống 1 buổi, spec sẽ ghi cả 2 đời.

- [x] **Đ2-a — đánh số 01.-07. nhóm HĐ** ✅ prod LIVE tức thì (`renamed=7`, prepend-only **0 ký tự VN qua wire** — né E-010) + seeder `typeLabels` + labelBackfill 7 entry
- [x] **Đ2-b — menu 4-GĐ prod** ✅ SQL qua **file UTF-16 + scp** (3 nhãn VN mới an toàn): `ren_pe=1 ren_ct=1 perm=26` (=13×2, khớp toán) · root order: Duyệt NCC(25) → Kế hoạch ký kết HĐ(26) → Duyệt hợp đồng(31) → Hợp đồng cứng(32) · bẫy shell VPS = PowerShell 5.1 không nhận `&&`
- [x] **Đ2-c — `PePipelineStrip` mirror ×2 app** ✅ byte-identical (`ef7b78…`) + chèn `PeDetailTabs` ×2 giữ **md5 parity** (`5be2ca…`, edit qua python assert count==1) — GĐ1 active, GĐ2/GĐ4 badge "Sắp", stage không-active KHÔNG click (không giả link)
- [x] **Đ2-d — staticMap +2 route** (`KeHoachKyKet`/`HopDongCung` → `/dashboard`; thiếu là sidebar **drop SILENT** — gotcha #50) + đồng bộ 3 nhãn + 3 aria-group dashboard
- [x] **Đ2-e — build FE ×2 PASS + test 562 PASS** (seeder đụng ⇒ chạy đủ) → **commit `1a47a61` push** (8 file, 190+/18−)
- [!] **Đ2-f — cicd verify `1a47a61`** đang chạy (kèm check seeder-re-run không double-insert: 12 root, 7 nhãn `01.` nguyên)

🔸 fe-admin sidebar: ~~2 root placeholder drop silent~~ → **đổi @đợt-3**: admin CÓ staticMap + ComingSoonPage (skeleton có con ⇒ group rỗng bên admin sẽ xấu; mirror luôn cho sạch).

## ĐỢT-3 — ảnh thứ 2 của anh + 🔴 SỰ CỐ REVOKER (phát hiện đắt nhất arc)

**Owner verbatim (ảnh 2, ~11:4x):** ① *"Cho viết hoa - Bên trong cũng phân các list ra tương tự như duyệt NCC"* (→ Kế hoạch ký kết HĐ) ② *"Thêm Sub và đánh số như Duyệt Hợp đồng, viết hoa và các danh sách tương tự như Duyệt NCC"* (→ Hợp đồng cứng).

- [x] **Đ3-a — skeleton menu prod**: 12 row (`Khkk_G1`+4 leaves khuôn Duyệt-NCC · `Hdc_×7` tái dùng nhãn 01.-07.) + `perm=156` (=12×13). Root có con ⇒ **uppercase tự động** (Layout `:247`). Đo: khkk_con=5 · hdc_con=7 ✅
- [x] **Đ3-b — `ComingSoonPage` mirror ×2** (md5 `6aaa7f87…`): PePipelineStrip current=stage + mô tả + banner "cấu trúc menu hiển thị trước để mọi người góp ý" + Quay-lại `navigate(-1)` (không phụ thuộc route riêng app) · route `/coming-soon` ×2 · staticMap **11 leaf** ×2 (query riêng từng mục giữ active-state — gotcha #50)

### 🔴🔴 SỰ CỐ — grant 38-key đợt-1 BỊ REVOKE ở app-restart #423 (cicd đợt-2 mục 9, FAIL 8/9)

- **Đo:** còn 47/494 canread (Admin=38 · Procurement=9 · **11 role = 0**) — mất **447 dòng**. Gốc: `DbInitializer.cs:2203-2236` `RevokeTemporarilyHiddenModulesAsync` **UNGATED mỗi startup**, tập revoke (nhánh **[S92 2026-06-29]** *"anh chốt chỉ Admin thấy"*) **trùng khớp chính xác** grant-set 38 key.
- **Bản chất:** owner hôm nay (❹ *"hiển thị hết để mọi người góp ý"*) **SUPERSEDE chính quyết định S92 của owner** — code còn thi hành quyết cũ. Đúng **chiều-ngược gotcha #75/#76** (`wipe-durability` S91): seeder ungated thắng data-change tay; đổi trạng thái bền = đổi CODE, nghiệm thu = **restart THẬT**.
- 🔴 **Lỗi quy trình của LEAD (nhận):** grant SQL đợt-1 mà **không audit seeder cho revoker ngược** — memory `feedback_wipe_durability_check_reseed` có sẵn bài này, em áp chiều re-add mà không soi chiều revoke. Máy không bắt được; `cicd-monitor` bắt nhờ **đo-sau-restart** + tự đính chính M7 đợt-1 của chính nó (*"đo lúc 11:46, pool recycle ~11:49 ⇒ claim durability chưa từng được chứng minh"*).
- **Xử 2 nhịp:** ① re-grant SQL ngay `regrant=447` → 38/38 = 13/13 (user mất menu ~vài phút quanh #423) ② **gỡ nhánh [S92] khỏi revoker** (giữ Hrm/Off/Personal — ý ❸) + grant bền `reviewKeys += ContractMenuKeys()+MasterMenuKeys()+KhkkKeys()+HdcKeys()` (fresh-env hội tụ; prod đi đường SQL vì nhánh grant key-thường là skip-existing không nâng row false).
- **Đối chứng thuận:** `Khkk_/Hdc_` (156 perms) **không match prefix revoker** ⇒ sống qua #423 — giải thích vì sao menu 4-GĐ + nhãn 01-07 nguyên mà chỉ bộ 38-key rơi.

- [x] **Đ3-c — spec-change 3 test** (S92→S159, *"update test cũ + code chung commit"*): `AdminOnlyModulesRevokeTests` tách `StillHiddenKeys`(3) ⟂ `ReopenedKeys`(8) + ⭐ **REGRESSION-GUARD**: re-add nhánh S92 vào predicate ⇒ test đỏ ngay · `ProcurementMasterAccessSeedTests` ×2 đọc lại isolation bằng **CỜ CAO** (CanRead all-role giữ true). **517/517 PASS lại.**
- [x] **Đ3-d — commit `2a72695` push** (9 file, 234+/27−)
- [x] **Đ3-e — cicd verify `2a72695`: PASS 9/9 — ARC ĐÓNG** (`cicd-verify-2a72695.md`). 🔴 **Mục 6(a) phép PASS/FAIL của arc: ĐÓNG bằng 3 chân kiềng** (cicd tự dựng, không nhận-vì-xanh): ① restart THẬT có chứng (`w3wp` PID 5508 StartTime **12:39:56** trong cửa sổ deploy + log boot-mới `12:40:03` — dùng được vì deploy xoá `logs/` ⇒ log mở lại = boot mới + DLL mtime 12:37:38) ② đo SAU mốc → **494/494 canread, 38 key min=max=13** (đợt-2 chính truy vấn này = 47/494) ③ **control âm chống-Goodhart**: Hrm/Off/Personal non-Admin vẫn **= 0** ⇒ revoker CÒN SỐNG — xanh không phải do giết cả hàm (*"fix kiểu xoá luôn checker cũng xanh y hệt"*).
  · Run #424 success 5m56s · gate 562 · bundle 4/4 rotate + hash cũ rơi text/html · marker ComingSoonPage 2/2 bundle · Khkk=5 · Hdc=7 · root=12 · DUPKEYS=0 · Pe% 197/185.
  · 2 số tự-giải-trình (không phải drift): nhãn `01.-07.` = **14** = `Ct_`7 + `Hdc_`7 đúng thiết kế tái dùng · log **0 dòng `Revoked`** = đúng (hàm chỉ log khi count>0; đợt-2 là 447).
  · ⚠️ **Lưu ý còn hiệu lực:** nhánh grant seeder = **skip-existing, KHÔNG tự nâng row false** — prod đúng nhờ re-grant SQL + revoker thôi lật; prod nào sau này rơi false thì re-grant tay, đừng chờ seeder.
  · Baseline mới: admin js `CC1pGE1F`/css `DWDbm5As` · user js `DeAg7MWm`/css `BQIZ-rUe` · 38-key 13/13.

<!-- moc: 3 đợt đầu đóng @12:5x — arc MỞ LẠI đợt-4 theo 3 lệnh mid-turn anh -->

## ĐỢT-4 — "bỏ hết mấy cái này, hiện list chi tiết đàng hoàng" + tab strip menu + "Bảng cứng"

**Owner verbatim:** ① *"bỏ hết mấy cái này đi, hiện theo list chi tiết đàng hoàng luôn. Nghĩ cách bố trí sao cho nó có danh sách phiếu mà User nhìn vào vẫn thấy đc tổng quan liên kết."* (ảnh khoanh 5 KPI + 2 list gần-đây) ② *"Thêm chữ bảng cứng vào nhé"* (ảnh: `01. HĐ Thầu phụ - Bảng cứng`) ③ *"vậy là tròn 1 quy trình của Solution rồi đấy."* (ghi nhận, không phải lệnh).

- [x] **Đ4-a — designer redesign** (`sub-frontend-designer-dot4.md`, §5 lead on-behalf): BỎ đúng khối khoanh · THÊM tab 4-GĐ mang count + 2 bảng chi tiết (PE 7 cột + HĐ) + cột **Hành trình 4-chấm** per-row + phân trang + tìm kiếm (param BE verify thật §2) · trang **59,8 KB**, build PASS · 🔴 nỗi lo CSS-token của vai (chết #53 đúng lúc verify): **lead verify hộ SẠCH** — 0 class ghép động, 3 lookup-map tĩnh
- [x] **Đ4-b — reviewer** `PASS_WITH_FLAGS — 7 FLAG (0H/2M/5L)` (`sub-reviewer-diff-dot4.md` 28.510B; vì designer chưa kịp tự-review FD4 ⇒ reviewer là lớp duy nhất, trục 6 a11y đo 10/10 mục). **Disposition TỪNG DÒNG:**
  · F1[M] mobile HĐ ngõ-cụt → **FIX** mirror matchMedia trang đích · F2[M] dot "chưa tới" nói chắc điều không biết (3 ca — ca-3: HĐ DangDongDau/DaPhatHanh = GĐ4 thật mà chấm GĐ3) → **FIX** per-dot states + `linked` (contractId THẬT) + `contractDots` suy diễn tuyến-tính-CÙNG-module + aria chỉ kể chấm CÓ thông tin · F3[L] pager race → **FIX** page-state · F4[L] aria-live biến mất khi 0 kết quả → **FIX** role=status sr-only · F5[L] Hành trình mất trên mobile → **FIX** 4-chấm vào sub-line · F7[L] nút X chờ debounce → **FIX** clearSearch() · **F6[L] tab-count vs search → PARK** (chờ UAT; 🔴 **ERRATA commit-message `a2bbcb9`**: câu "co chu thich o code" cho F6 là **SAI** — không có chú thích nào được thêm; disposition thật = PARK. Khai tại đây, không amend)
- [x] **Đ4-c — nhãn "Bảng cứng"**: prod `suffix=7` (UTF-16 file, idempotent NOT-LIKE guard) + seeder tree.Add + labelBackfill 7 entry. 🔸 Theo VERBATIM anh vẽ "Bảng cứng" (nếu ý anh là "Bản cứng" — 1 lệnh đổi được, guard NOT-LIKE vẫn idempotent)
- [x] **Đ4-d — build PASS 495ms · Infra 517 PASS · commit `a2bbcb9` push** (2 file, 1052+/338−)
- [!] **Đ4-e — cicd verify `a2bbcb9`** đang chạy (marker bản mới + control âm "chưa tới"/"HĐ ĐANG SOẠN" vắng + restart-durability lần 2 của 38-key)

## ĐỢT-5 — "y chang Duyệt NCC" 6-leaf + endpoint deleted + bỏ hero + "Tổng quan quy trình"

**Owner:** ảnh khoanh 6 leaf PE + *"Những cái dưới nó cũng y chang thế này luôn nhé, trừ chỗ hợp đồng cứng ra, vì bảng cứng thì ko cần"* · AskUser "Chạy cả 2" (endpoint deleted READ-only + matrix HĐ) · *"Ồ đẹp đấy, vậy bỏ cái trên đi nhé, cái dưới OK đấy. Tổng quan → Viết hoa → Đổi thành gì...? Tổng quan quy trình?"* (lead lấy tên anh đề) · *"xong thì /pause nhé"*.

- [x] BE: `ListDeletedContractsQuery` + GET `/contracts/deleted` [Contracts.Read] (khuôn PE S155; Contract CÓ soft-delete filter `:44`) · phát hiện rẻ: **ApplicableType.Contract=3 — HĐ chung 1 bộ V2** ⇒ matrix = mở rộng trang PE +type=3, 7 leaf WfView trỏ chung `?type=3&ct=N`
- [x] Seeder: Ct ×7 +3 leaf (khớp regex FE) + "Đang duyệt" ×7 backfill + Khkk 6-leaf + Dashboard→"Tổng quan quy trình" + reviewKeys +23 — **KHÔNG SQL tay** (bài revoker: code-first, deploy tự insert/reorder qua upsert)
- [x] FE: BỎ HERO (note verbatim owner trong code) + title + MyContractsPage phase/deleted + MenuLeaf depth-0 caps
- [x] reviewer **PASS_WITH_FLAGS 10 FLAG (0H/4M/6L)** (`sub-reviewer-diff-dot5.md` 22.369B; garble #53 ×2 — lần 2 43-tool/1.3KB = làm-nhiều-ghi-ít, resume "đổ-đĩa-trước-đọc-thêm" ăn). **Disposition:**
  · F1[M] fe-admin thiếu 2 route Khkk → **FIX** (+2 staticMap; Ct_ mới bên admin ẩn `isAdminHidden` — reviewer đã kiểm CLEAN)
  · F2[M] màn Đã-xóa click row = panel trắng/404 (detail KHÔNG IgnoreQueryFilters — nó truy tới `GetContractQuery:471` chứng thật) → **FIX** guard `if (showDeleted) return` tại `selectContract` (chặn cả desktop lẫn mobile, khuôn PE `disabled={deletedView}`)
  · F3[M] chú thích khai "Hộp thư viết HOA" nhưng Hộp-thư đi đường `StaticLeaf` → **FIX** khai đúng phạm vi (đồng bộ StaticLeaf = quyết owner, PARK)
  · F10[M] ⭐ comment "chỗ THỨ HAI" = **số-tự-lan** (grep thật: 3 tệp/17 vị trí — `ApprovalWorkflowV2AdminFeatures` 15 chỗ lớp-khác; "DUY NHẤT" S155 vốn đã sai) → **FIX cả 2 site: BỎ SỐ, nêu LUẬT + lệnh grep tự-kiểm**. 🔴 Lead lặp đúng lỗi errata-của-errata sáng nay — thay số sai cũ bằng số sai mới; cùng lớp meta-count
  · F4[L] **PARK→NEXT-anh**: định nghĩa "Đã duyệt" HĐ (phase=9 Đã-phát-hành bỏ sót phase-8 đã-qua-BOD; nhãn menu ↔ chip đang lệch) — cần owner chốt
  · F5-F9[L] **PARK có ghi** (F5 active-state đôi · F6 asymmetry authz endpoint-mới-chặt-hơn-list-sống [đúng hướng, ghi nhận] · F7 chuỗi trần vs factory · F8 thiếu cột mốc-xóa · F9 client-filter-type trên 100 dòng)
- [x] build fe ×2 + BE PASS · test **562 PASS** · commit **`ea962b1`** push (10 file, 173+/371−)
  · 🔧 bẫy mới ghi sổ: PS 5.1 here-string commit-message vỡ → **`git commit -F <file>`**; lần vỡ đầu chỉ đẩy 2 `wal:` commit (noise, code chưa mất)
- [!] cicd verify `ea962b1` **CHẠY NỀN** (`cicd-verify-ea962b1.md`) — đọc ở phiên sau qua `/tiep` (mục nặng: seeder-run insert menu-delta + endpoint 401-not-404 + durability lần 3)

<!-- ARC-2: 5 đợt · 5 commit (`b5799fc`·`1a47a61`·`2a72695`·`a2bbcb9`·`ea962b1`) · prod-data 7 lượt SQL + đợt-5 code-first 0-SQL -->

- [x] **T2 — Prod menu data** ✅ **LIVE + VERIFY TRỌN** (`prod-backup-pre-apply.txt` → apply `hide=3 upd=99 ins=376` → `prod-verify-post-apply.txt`):
      · Inspect lộ thực-trạng ≠ giả định: Master subtree có matrix 13-row/key (canread 2/13) nhưng **`Ct_*` 28 key = 0 ROW** (chưa từng seed), `Contracts` root 1 row ⇒ INSERT 376 + UPDATE 99, số khớp toán từng đồng (9×11=99 · 28×13+12=376)
      · Verify: **38/38 key min=max=13 canread** · 3 root `IsVisible=0` · control âm **Pe_* 197/185 NGUYÊN** (❹ "để như cũ") · `Hrm_Config*` canread=7 nguyên (vùng đang-làm loại khỏi grant, đúng ý ❸)
      · Thuần cộng: chỉ thêm `CanRead=1, CanCreate/Update/Delete=0` — không rút quyền ai
- [x] **T3 — Spec đổi nhãn** ✅ 2 site (`:26` owner-block + `:222` seed label) — "Đề xuất ký kết hợp đồng"; tên English `ContractSigningPlan*` giữ; ranh GĐ + Q8-ĐÓNG ghi vào spec
- [ ] **T4 — build fe-user + reviewer diff → commit/push → cicd verify**

🔴 Luật run: CẤM điền kết quả trước khi đo. Prod = backup-trước-apply, verify-LIVE-sau (S88).
