# sub-review-fable-b3 — B3 REVIEW CHỐT CUỐI 4 spec (tổng v2 + cụm-1 + cụm-2 + cụm-3)

> Engine /fable-real · reviewer adversarial · đối chiếu CHÉO liên-spec → GO/NO-GO + build-order cuối cho B4.
> Luật đan xen: đọc 1 spec → ghi section NGAY.

## A. Mâu thuẫn LIÊN-SPEC
(ĐANG LÀM — nháp sau spec tổng, chờ đối chiếu 3 cụm)

### A-0. Nội-bộ spec tổng (phát hiện khi đọc lượt 1)
- **[A-0a] QĐ5 vs OG-3-nâng-cấp TỰ MÂU THUẪN trong CÙNG file:** `spec-4gd-khkk-tong-quat:15` QĐ5 còn nguyên *"enforce (`CeoApprovalThreshold`) = wave opt-in sau"* trong khi `:52` ra lệnh *"**XÓA ý 'enforce máy = wave opt-in sau' khỏi mọi spec**"*. Spec tổng tự vi phạm lệnh của chính nó → phải sweep cụm 1-3 xem câu này còn rớt ở đâu.
- **[A-0b] Đếm sai nội bộ:** header ② ghi "8 quyết định (v2)" nhưng liệt 9 items (:11-19, QĐ9 budget-freeze mới @S164); END `:54` ghi "6 OG" nhưng bảng ④ có 8 hàng (OG-1/2/3/5/6/7/8/9). Con số header/footer chưa sync sau khi chèn QĐ9 + OG-8/9.
- **[A-0c] QĐ9 mang cờ "⚠️ MERGE-PENDING: B2-cụm-1 lane phóng TRƯỚC lời này — lead merge vào spec chi tiết khi lane về" (`:19`) → kiểm tra cụm-1/cụm-3 đã nhận merge chưa (schema snapshot → K2, hook finalize → K3).

### A-1. Drift số menu/policy — **MÂU THUẪN THẬT, cụm-1 CHƯA nhất quán với cụm-2** 🔴
- `spec-cum1:8` vá-1 neo **TUYỆT ĐỐI**: *"Menu keys 55→64 · Policies 220→256 — khai 2 row STATUS cùng lúc"* ⟂ `spec-cum2:9` vá-2 **CẤM đúng kiểu neo đó**: *"Drift khai DELTA (+49/+196), CẤM neo 2 mốc tuyệt đối (64→113 chỉ đúng nếu cụm-1 land trước — thứ tự ship đang cho song song)"*. Cụm-2 BIẾT về mốc 64 của cụm-1 và phòng hộ cho mình, nhưng **cụm-1 không được sửa ngược** → 2 spec dạy builder 2 luật ngược nhau về CÙNG 2 row STATUS.
- Thêm: 9 key của cụm-1 trải 2 wave (`ContractCatalog` = K1 +1 · 8 `AwV2_KhkkN*` = K3 +8) ⇒ "55→64" chỉ đúng tại K3-land; khai trọn ở K1 = STATUS nói dối suốt cửa sổ K1→K3.
- Số học nội bộ tự khớp (9×4=36: 220→256 ✓ · 49×4=196: 256→452 ✓) — lỗi KHÔNG nằm ở arithmetic mà ở **kiểu neo**.
- **Fix 1 dòng (trước B4):** sửa cụm-1 vá-1 thành DELTA per-wave (+1 @K1 · +8 @K3) + mọi commit khai drift kèm lệnh đo lại `MenuKeys.All` thực tế (mượn nguyên câu cụm-2 vá-2). Verdict: **MÂU THUẪN #1 — phải vá spec trước build.**

### A-2. 3-site grant cụm-2 ⟂ 9-key cụm-1 — NHẤT QUÁN về máy, THIẾU 1 câu ranh giới
- Cụm-2 3-site (`spec-cum2:25` — seed `:1780` · `SeedKeHoachKyKetAccessAsync:2415` · `KhkkKeys():2185`) áp cho **49 row KHKK user-facing**. Cụm-1 vá-1 (`spec-cum1:8`) chỉ đưa 9 key **VÀO All** — KHÔNG nói vào `KhkkKeys()`. Đọc máy: đúng như vậy là CHUẨN —
  - 8 `AwV2_KhkkN*` = menu-con dưới `ApprovalWorkflowsV2` (System subtree, admin-only theo QĐ7 tổng `:17` CHỪA System + AwV2); grant qua **All-loop-Admin `DbInitializer.cs:2058`** là đủ. Đưa vào `KhkkKeys()` ⇒ 13 vai thường được CanRead menu designer = quyền-lệch (fe-user không có route → #50 drop im lặng, không vỡ UI nhưng bẩn grant + họ #85 menu-rộng-hơn-API).
  - `ContractCatalog` = leaf nhánh Catalogs/Master, KHÔNG thuộc cây `KeHoachKyKet` → không có lý do vào `KhkkKeys()`. Acceptance cụm-1 chỉ đo "login **Admin** thấy leaf ×2 app" — tức đợt này user thường KHÔNG thấy danh mục (chưa grant role thường). Chấp nhận được (danh mục = master admin nhập) nhưng nên khai 1 câu để owner biết.
- **Rủi ro thật:** comment `:2204` *"quên 1 bên = quyền lệch"* + cụm-2 vá-15 dạy khuôn TRONG-All có thể xúi builder "cho đủ bộ" nhét cả 9 key vào `KhkkKeys()`. **Fix 1 dòng:** thêm vào cụm-1 vá-1: *"9 key này KHÔNG vào `KhkkKeys()`/site-2/3 — chỉ All (+Admin-loop)"*. Verdict: KHÔNG mâu thuẫn, cần 1 câu chốt ranh.
- Designer fe-user: KHÔNG thấy và KHÔNG NÊN — fe-user không route System subtree; đường thấy duy nhất = fe-admin (K6 CHỪA `ApprovalWorkflowsV2 :1801`).

### A-3. Chuỗi ApprovalGroup K2→K4b→K5→K7 — **VẼ ĐỦ 1 ĐƯỜNG** ✅
- K2 CHỦ DUY NHẤT (cột + backfill=1 + DROP-unique-cũ + param `group` + DTO `:34` + picker create cụm-1 vá-4; luật l3-F6 "K3 chỉ đọc" giữ nguyên `spec-tong:13`) → K4b tiêu thụ (queryKey += group `spec-cum2:10` vá-3 · phase+group từ searchParams vá-5 · preset `?group=` vá-6) → K5 group-by `approvalGroup` + hiện đủ 8 + fallback NULL-group (vá-9) → K7 test T2 (gộp 2 line cùng NCC) khai tường minh phụ-thuộc Mig-71-DROP-unique, chạy SAU K2 (`spec-cum3:15` vá-5 — chính cụm-3 tự vá cái sót của nền mình). Verdict: chuỗi schema→API→list→tree→test KHÉP KÍN, không mắt xích nào vô chủ.

### A-4. Freeze QĐ9 — cột runtime KHỚP 3 spec ✅ · **tổng hứa snapshot+display-gate mà KHÔNG cụm nào nhận** 🔴
- Cột runtime `EndedByLevelFinalize`: tổng `:46` OG-3-SỬA "(b) forward-provision vào Mig 71 ⇒ K3 0-mig" **KHỚP** `spec-cum1:14` vá-7 (Mig 71 item 6 + tên mig + ghost-window K2→K3) **KHỚP** `spec-cum3:17` vá-7 (B6 đổi neo, thừa nhận "vật K3 MỚI ĐẺ, hiện 0 hit"). ✅ 3 spec cùng 1 cột trên bảng plan KHKK.
- 🔴 **Snapshot + display-gate của QĐ9 tổng (`:19` "mirror nguyên khuôn PE Mig 67: snapshot 11 cột + display-gate `frozen`") KHÔNG AI LÀM**: Mig 71 cụm-1 chỉ 6 item — 0 cột snapshot; không vá nào ở 3 cụm nhắc display-gate. Thiết kế THẬT trong cụm = **copy-at-create + immutability**: line đã mang bản chép (`PeReferenceAmount` `Line.cs:6,:16-20` + `TenHangMuc` + `ApprovedAmount` per-line) + phase-guard line-edit kéo về K2 (cụm-1 vá-6) + test-khóa 1-site-DaDuyet (cụm-1 vá-2 — verify đĩa: `ContractSigningPlanWorkflowService.cs:37-38` khai "ĐÚNG MỘT nhánh set DaDuyet", `:129/:134` ReturnOrReject chỉ TraLai/TuChoi, `:278` site duy nhất, `:289-292` helper choke-point + lệnh grep tự-kiểm) + **răng đo = cụm-3 vá-10** (`spec-cum3:22` sửa NGUỒN sau finalize → reload → số KHÔNG đổi, đo CẢ 2 nhánh CCM-finalize VÀ lên-CEO — phép đo TRƯỢT ĐƯỢC ✓ đạt sàn-sự-thật).
- **Phán:** thiết kế cụm HỢP LÝ HƠN khuôn Mig 67 (KHKK line vốn denormalized-at-create, PE thì display JOIN live nên mới cần snapshot) — nhưng tổng QĐ9 đang là VĂN STALE hứa vật không ai giao. Fix: sửa đoạn QĐ9 tổng thành "freeze KHKK = copy-at-create + phase-guard + test-khóa 1-site + phép đo 2-nhánh vá-10; KHÔNG mig snapshot mới; nếu vá-10 đo FAIL (lộ chỗ display đọc live) → lúc đó mới mở mig snapshot bổ sung". Không sửa thì builder K2 sẽ đi tìm "11 cột snapshot" không tồn tại trong cụm-1. Verdict: **MÂU THUẪN #2 — phải vá TỔNG theo cụm trước build.**
- 🪤 Bẫy **":342 hai file khác nhau"**: tổng QĐ2 dùng `:342-347` = `ContractSigningPlanFeatures.cs` (rào 1-PE-1-phiếu, K2 ĐỔI); cụm-1 vá-2 dùng `:342` = `ContractSigningPlanWorkflowService.cs` (`ReturnOrRejectAsync`, CẤM sửa — DaDuyet unreachable). Builder đọc lướt sẽ sửa nhầm site. Spec build phải ghi TÊN FILE cạnh mọi ":342".

### A-5. Cây GĐ3: cụm-3 ĐẺ VIỆC cho K5 mà spec cụm-2 KHÔNG BIẾT 🔴
- `spec-cum3:14` vá-4(b) gắn nhãn tường minh "**K5-liên-đới**: builder GĐ3/GĐ4 nối qua TẬP `lines[].contractId` của các phiếu KHKK thuộc gói (∪ `pe.contractId` legacy)" — nhưng `spec-cum2:28` checklist K5 chỉ có "GĐ2 by `approvalGroup` + GĐ3/4 by `c.type` (đã có `:240`)", **0 chữ về `lines[].contractId`**. Cây hiện đi `pe.contractId` ĐƠN (`usePipelineStages.ts:214-218`, khớp giới-hạn HĐ[0] đã khai S162 `CreateContractFromEvaluationFeatures.cs:145`).
- Hậu quả nếu không vá: K5 (land trước) build đúng spec cụm-2 → K7 bridge ghi `Line.ContractId` per-line → HĐ thứ 2+ **vô hình trên cây** (vá-4(a) `pe.ContractId ??=` chỉ cứu HĐ đầu); muốn sửa phải chạm lại 2 file pipeline SAU K5 → re-run SHA-identical + build ×2 ngoài kế hoạch.
- **Fix 1 dòng vào cụm-2 K5:** "GĐ3/4 nguồn HĐ = ∪ `lines[].contractId` ∪ `pe.contractId` legacy (forward-provision — trước K7 tập lines rỗng, cây không đổi hành vi)" + acceptance "phiếu 2 line 2 HĐ → cây hiện 2" (chạy được từ K7). Verdict: **MÂU THUẪN #3 — phải vá cụm-2 trước build K5.**

### A-x. Drift consumer-count tổng ⟂ cụm-1
- Tổng `:12` QĐ2: *"acceptance rà **16 consumer** (10 BE + 6 FE — l3-F18)"* ⟂ `spec-cum1:23` checklist K2: *"bảng **18 consumer** (17 nền + :712-713)"*. Cụm-1 THẮNG theo luật vá (nền 17 + vá-8 thêm 1 = 18) nhưng tổng chưa sửa số ⇒ builder đọc tổng trước sẽ rà thiếu 2. Fix 1 dòng ở tổng khi ship.

### A-6. OG-7 UI-hướng-1 — 2 cửa tạo phiếu nhóm 2 KHÔNG chỏi, nhưng NÚT PHỤ chưa có chủ (nháp sau cụm-2)
- Cửa 1: KhkkCreatePage + picker nhóm (cụm-1 vá-4, thuộc K2). Cửa 2: leaf "Thao tác" preset `?group=` (cụm-2 vá-6, K4b — 0 phụ thuộc K3, chỉ cần picker vá-4). 2 cửa cùng đổ về 1 form — KHÔNG trùng/chỏi.
- ⚠️ Nhưng OG-7 tổng `:45` hứa UX cụ thể: *"PE có phiếu rồi → hiện phiếu + **nút phụ 'Tạo thêm cho nhóm khác'** (thay 409 chặn hẳn)"* — soi CẢ 43 vá 3 cụm: KHÔNG vá nào nhận việc "nút phụ" này (cụm-1 vá-4 = picker nhóm; K2 đổi picker PE `:1201-1202` = gỡ filter PE-đã-có-phiếu; cụm-2 vá-6 = preset group; cụm-3 = K7/K8 không đụng). Acceptance cụm-1 "tạo phiếu nhóm 2 TỪ UI ×2 app" đo OUTCOME nhưng không ép UX hướng-1 mà owner đã ratify verbatim. **Verdict: LỖ CHỦ-VIỆC (mâu thuẫn hứa⟂giao) — fix 1 dòng gán về K2** (cùng site picker `:1201-1202`: chọn PE đã-có-phiếu → hiện danh sách phiếu hiện có + nút "Tạo thêm cho nhóm khác" thay vì message chặn); không nên hạ lời OG-7 vì đó là lời chốt của owner.

## B. Vá-đè-vá (vá cụm sau có phá vá cụm trước?) — soi 43 vá (12+15+16), 7 cặp nghi

| # | Cặp | Verdict |
|---|---|---|
| B-1 | cụm-1 vá-6 (kéo phase-guard line-edit K4b→K2) ⟂ cụm-2 K4b checklist | ✅ SẠCH — cụm-2 final K4b (`spec-cum2:26`) KHÔNG còn nhắc phase-guard ⇒ move được cụm-2 tôn trọng, không mồ côi không trùng |
| B-2 | cụm-2 vá-6 (Thao tác preset VỀ K4b) ⟂ tổng K4b "chờ K2 param" | ✅ SẠCH — dependency K2 giữ nguyên, chỉ gỡ phụ thuộc K3 giả; đúng điều kiện "chỉ cần picker vá-4 cụm-1" |
| B-3 | tổng K3 "mở resolvePath **fe-admin** `Layout.tsx:166-172`" ⟂ cụm-2 vá-8 "**KHÔNG nới regex fe-admin** đợt này" | ⚠️ KHÔNG phá nhau NHƯNG dễ đọc nhầm thành phá: 2 HỌ key khác nhau trên CÙNG file/hàm — K3 nới cho `AwV2_KhkkN*` (designer, System subtree, vẫn hiện sau K6) · vá-8 cấm nới cho `Khkk_*` 48-leaf (KeHoachKyKet bị ẩn admin + fe-admin THIẾU `WorkflowMatrixViewPage` — L1-BÁC-2). **Cần 1 câu ranh:** "fe-admin resolvePath: THÊM pattern `AwV2_KhkkN*` — CẤM pattern `Khkk_*`" kẻo builder K3 tiện tay nới cả hai |
| B-4 | cụm-1 vá-2 (CẤM sửa `WorkflowService.cs:342`) ⟂ tổng QĐ2 "đổi rào :342-347" | ✅ KHÔNG đụng về máy — 2 file trùng số dòng (`Features.cs:342-347` rào 1-PE-1-phiếu PHẢI đổi · `WorkflowService.cs:342` unreachable CẤM đụng — verify đĩa `:129/:134/:278`); bẫy-đọc-lướt, spec build ghi TÊN FILE cạnh mọi ":342" |
| B-5 | cụm-3 vá-4(b) (K5 nối `lines[].contractId`) ⟂ cụm-2 K5 checklist | 🔴 KHÔNG phá vá cũ nhưng ĐẺ VIỆC vào wave cụm-2 mà spec cụm-2 không chứa = A-5 mâu-thuẫn #3 — vá spec cụm-2 trước build K5 |
| B-6 | cụm-3 vá-7 (B6 đổi neo `EndedByLevelFinalize` "vật K3 mới đẻ") ⟂ cụm-1 vá-7 (cột vào Mig 71/K2, K3 wire) | ✅ SẠCH — nhất quán: cột đẻ ở K2-mig (nằm im, ghost-window đã khai), K3 wire, K7-B6 neo tùy nhánh; 3 spec cùng 1 cột (A-4 ý 1) |
| B-7 | cụm-3 vá-12 (TRƯỚC K2 rào per-PE ⇒ phiếu ZZTEST khóa TOÀN BỘ PE thật) ⟂ cụm-1 K2 đổi rào | ✅ SẠCH — cụm-3 nhận thức đúng thứ tự (K8 CUỐI sau mọi wave + nghiêng phương án (b) PE-ZZTEST-riêng); không đè, chỉ ràng thêm điều kiện chạy-sau |

**Verdict B: 0 vá cụm sau LÀM VÔ HIỆU vá cụm trước.** 7 cặp nghi xét: 5 sạch · 2 cần câu-ranh/vá-spec (B-3 câu ranh 2-họ-key · B-5 = A-5).

## C. Build-order cuối + gate

**Thứ tự chốt: K1 → K2 → K3 → K4a → K4b → K5 → K4c → K6 → K7 → K8** (10 đơn vị build; khớp đề xuất lead, KHÔNG đổi trình tự — chỉ siết gate). Lý do giữ K4a SAU K3 dù "0 phụ thuộc code" (l3-F8 cho song song): tránh cửa-sổ-prod 8-leaf-chưa-lọc (trước K2 API không có param `group` ⇒ 42 leaf hiện CÙNG nội dung) + nhãn 8 group cần anh gật (cụm-2 vá-7) — kéo song song chỉ khi owner chấp nhận 2 điều đó tường minh.

| Wave | PRE-gate owner | Mig | Restart BE | Deploy | Test-k |
|---|---|---|---|---|---|
| K1 | **OG-6 ✅ (soát 86 dòng — chặn SEED, code build trước được) + OG-5 hỏi-khi-gặp** | Mig 70 (3-file) | ✅ | BE + FE ×2 (leaf catalog) → #77 | +k khai (seed-idempotent, CRUD) |
| K2 | 0 (OG-1/7 ✅ rồi) — PRE-spec-fix A-1 (lead) | Mig 71 (6 item, DROP unique cũ, backfill=1) | ✅ | BE + FE ×2 (picker+create) → #77 | +k (Σ trước==sau · 409-có-line · UI-nhóm-2 · 18-consumer) |
| K3 | OG-2 ✅ + **verify đội hình LIVE trước seed** + OG-9 ✅ | 0-mig (cột đã forward-provision K2) | ✅ (seed 8 WF + sweep ép-false) | BE + fe-admin (designer + resolvePath AwV2) → #77 | +k (8 WF · pin-sai-nhóm 409 · giữ-cờ-qua-Create · danh-tính roster · finalize 2-nhánh) |
| K4a | **8 nhãn đính file OG-6 — anh gật** | 0-mig | ✅ (seed 49 row + 3-site) | BE + fe-user (route regex) → #77 | +k (idempotent ×2 restart · SQL JOIN designer · 42 leaf bấm được) |
| K4b | 0 (sau K2) | 0 | — | FE ×2 → #77 | acceptance leaf→leaf đổi kết quả không F5 |
| K5 | 0 (sau K2) — PRE-spec-fix A-5 (lead) | 0 | — | FE ×2 → #77 | SHA-identical 4 file ×2 app + Σ sub-folder == tổng |
| K4c | 0 (sau K3) | 0 | — | fe-user → #77 | WfView lọc `Code==='KHKK-N'+n` |
| K6 | 0 (sau K3 — acceptance đếm 8 designer) | 0 | — | fe-admin → #77; `git diff --stat -- fe-user/src` = 0 | liệt kê ĐÚNG N mục lá (dư/thiếu 1 = FAIL) |
| K7 | câu III không chặn code (default dialog); acceptance "ai bấm" treo [CHỜ-ANH] | 0 | ✅ (grant `Contracts.Create`) | BE + FE ×2 → #77 | 8+2 authz 2-chiều + idempotency-double-click + cây 2-HĐ |
| K8 | **câu V (nghiêng (b) PE-ZZTEST-riêng) + câu III + B0-form 14/14 fail-closed + dặn team notification** | 0 | — | 0 code | 17 bước chụp + QĐ9 đo 2 nhánh + rollback theo ID |

**3 spec-fix bắt buộc TRƯỚC wave tương ứng (lead tự làm, không cần owner):** ① A-1 cụm-1 vá-1 → DELTA per-wave (trước K1-commit-STATUS) · ② A-4 tổng QĐ9 → khai thiết kế copy-at-create thay snapshot-hứa (trước K2) · ③ A-5 cụm-2 K5 +1 việc `lines[].contractId` (trước K5). **5 vá 1-dòng cùng đợt:** A-0a sweep "opt-in sau" · A-0b đếm header tổng · A-x 16→18 consumer · A-6 nút-phụ gán K2 · SHA 2-file→4-file thống nhất (tổng `:30` theo cụm-2 `:28`).

## D. Rủi ro còn hở (top-5 sau 43 vá — mỗi cái 1 guard)

1. **QĐ9 không có snapshot dự phòng** — nếu phép đo 2-nhánh (cụm-3 vá-10) FAIL vì lộ chỗ display đọc live (kiểu PE pre-Mig-67) → phát sinh mig ngoài kế hoạch giữa chuỗi wave. *Guard:* chạy phép đo 2-nhánh trên **Dev NGAY SAU K3 land** (integration test hoặc tay), đừng để nó xuất hiện lần đầu ở K8-prod.
2. **Ghost-window K2→K3 + seed cờ sớm = config-lie #78** — `AllowApproverFinalize=true` cấp CCM-Chương seed TRƯỚC khi nhánh (a) land thì designer nói dối. *Guard:* K3 checklist đặt "seed cờ" là mục CUỐI + gate `grep AllowApproverFinalize=true` 0-hit trước mục đó (đúng thứ tự OG-3 tổng `:46` đã ghi — giữ nguyên, đừng để builder đảo).
3. **fe-admin resolvePath 2-họ-key, 2 wave chạm cùng hàm** (B-3) — builder K3 nới luôn `Khkk_*` → 48 leaf hiện trên fe-admin thiếu `WorkflowMatrixViewPage` → leaf chết. *Guard:* acceptance K3 thêm phép-âm "fe-admin sidebar đếm group `Khkk_*` = 0".
4. **Bridge authz chỉ gate FE = giả-an-toàn** — cụm-3 vá-1 nói "gate nút = HỘI 2 policy" + test 403, nhưng không câu nào bắt ENDPOINT bridge mang policy server-side; nếu builder gate FE-only, test 403 viết theo FE cũng giả-pass. *Guard:* 1 câu spec K7 "endpoint bridge mang CẢ 2 `[Authorize]` attribute (stack = AND: `KeHoachKyKet.Read` + `Contracts.Create`)" + test = **curl trần bỏ FE**.
5. **OG-6 transcribe gõ-tay-từ-ảnh, 4 ô ⚠️** — seed sai tên/vai → labelBackfill churn + khối ký GĐ4 in sai vai (TEXT không ràng máy — limitation QĐ4 đã khai). *Guard:* 4 ô ⚠️ phải được anh chốt TƯỜNG MINH trong lượt soát OG-6 (không để builder tự đoán); acceptance K1 "lệch giải trình từng dòng" giữ nguyên có răng.

*(Ngoài top-5, đã có guard sẵn trong spec: B0-form fail-closed vá-9 · rollback theo ID vá-11/13 · phiếu-ZZTEST-khóa-PE-thật vá-12 nghiêng phương án (b).)*

## E. GO/NO-GO per wave

| Wave | Verdict | Điều kiện |
|---|---|---|
| K1 | **GO (code) + GATE seed** | Seed land CHỈ SAU OG-6 ✅ (86 dòng + 4 ô ⚠️ anh chốt); code/mig/test build trước được |
| K2 | **GO** | OG-1/OG-7 ✅ rồi; PRE = spec-fix A-1 (1 dòng, lead) |
| K3 | **GO** | OG-2/OG-9 ✅; verify đội hình LIVE + sweep ép-false ≥4 site + seed-cờ mục CUỐI (D-2) |
| K4a | **GO + GATE nhãn** | 8 nhãn (kèm N2 sửa "phá dỡ") anh gật trong file OG-6 trước seed menu |
| K4b | **GO** | Sau K2 |
| K5 | **GO** | Sau K2; PRE = spec-fix A-5 (1 dòng, lead) |
| K4c | **GO** | Sau K3 |
| K6 | **GO** | Sau K3 (acceptance đếm 8 designer) |
| K7 | **GO** | Sau K2+K3; câu III chỉ treo acceptance "ai bấm" [CHỜ-ANH], không chặn code (default dialog người-có-quyền); server-side hội-2-khóa theo D-4 |
| K8 | **NO-GO tới khi đủ 4 điều kiện** | (1) mọi wave land · (2) câu V chốt — nghiêng (b) PE-ZZTEST-riêng · (3) câu III chốt · (4) B0-form 14/14 điền đủ (fail-closed) — NO-GO này là ĐÚNG THIẾT KẾ (wave cuối), không phải lỗi spec |

**Tổng: GO = 9/10 · NO-GO = 1/10 (K8, by-design).** Toàn chuỗi GO cho B4 với điều kiện 3 spec-fix nặng (A-1/A-4/A-5) + 5 vá 1-dòng land TRƯỚC wave tương ứng — tất cả lead tự làm được, owner-gate còn lại: OG-6 (K1-seed + K4a-nhãn) · câu III/V (K7-acceptance/K8).

## F. Digest trình owner (≤15 dòng)

1. Em rà chéo 4 spec final (tổng v2 + cụm-1 12 vá + cụm-2 15 vá + cụm-3 16 vá = 43 vá): **GO 9/10 wave — chỉ K8 dry-run chờ anh** (câu V + câu III + form 14 người), đúng thiết kế wave cuối.
2. Thứ tự build chốt: **K1→K2→K3→K4a→K4b→K5→K4c→K6→K7→K8** (bảng gate/mig/deploy ở mục C).
3. Phát hiện **8 mâu-thuẫn liên-spec — 0 cái chạm máy-đã-chốt, toàn vá được bằng sửa chữ spec**: 3 nặng phải vá trước build + 5 nhẹ 1-dòng.
4. Nặng ①: cụm-1 còn neo số tuyệt đối (55→64) trong khi cụm-2 cấm đúng kiểu neo đó — đổi cụm-1 sang khai DELTA per-wave.
5. Nặng ②: tổng QĐ9 hứa "snapshot 11 cột + display-gate mirror PE Mig 67" nhưng KHÔNG cụm nào nhận việc này — thiết kế thật trong cụm (copy-at-create + khóa sửa + phép đo 2 nhánh trượt-được) HỢP LÝ HƠN, em đề nghị sửa văn tổng theo cụm.
6. Nặng ③: cụm-3 giao thêm việc cho cây K5 (nối HĐ qua `lines[].contractId`) mà spec cụm-2 không biết — thêm 1 dòng vào K5 kẻo HĐ thứ 2 trở đi vô hình trên cây.
7. Vá-đè-vá: soi 7 cặp nghi trên 43 vá — **0 vá cụm sau phá vá cụm trước**; 2 chỗ cần câu ranh giới (regex fe-admin 2 họ key · ":342" trùng số dòng ở 2 file khác nhau).
8. OG còn treo: **OG-6** (anh soát 86 dòng danh mục + 8 nhãn menu — chặn seed K1 + K4a) · OG-5 (hỏi khi gặp) · câu III "ai bấm bridge" · câu V "PE test riêng hay PE thật".
9. Rủi ro #1: nếu phép đo freeze 2-nhánh FAIL (lộ chỗ đọc live) → phát sinh mig ngoài kế hoạch — em đề nghị chạy phép đo này trên Dev ngay sau K3, không đợi K8.
10. Rủi ro #2: cầu KHKK→HĐ phải khóa quyền ở SERVER (2 tầng policy), không chỉ ẩn nút FE — test bằng curl trần.
11. Rủi ro #3: quyền `Contracts.Create` hiện chưa cấp cho vai nào ngoài Admin — K7 sẽ cấp cho Drafter + Procurement (default, anh đổi được ở câu III).
12. Test: baseline 590 (STATUS:472) — mỗi wave khai +k, không wave nào được giảm.

## END

END sub-review-fable-b3 — TOTAL: 8 mâu-thuẫn (3 nặng A-1/A-4/A-5 + 5 nhẹ-1-dòng) · 0 vá-đè-vá (7 cặp xét, 2 cần câu-ranh) · GO=9/NO-GO=1
