# run: 2026-08-04-S171-khkk-ui-mirror-pe **Phiên:** S171 (L10 window-3) · **Ngày:** 2026-08-04 · **Lead:** em-main (Opus 5) **Đề-bài owner (verbatim):** *"OK hiện thì điều chỉnh lại giao diện chỗ KKHĐ lại nhé, lấy của Duyệt NCC làm mẫu: Panel 1: Hình gửi 1 · Panel 3: Duyệt và History tương tự (Hình 2) · Panel 2 - Giữa: Cấu trúc tương tự như duyệt NCC (Phần content nhập phía trên, duyệt ghi chú phía dưới) - Hình 3."* **Engine:** `/fable-real investigator-codebase` (LỆNH-A single deep-pass) → `/fable-clone reviewer` (LỆNH-B ensemble) → giao Opus 5 MAX thực-thi. --- ## Bối-cảnh ẢNH (subagent KHÔNG xem được ảnh — lead mô-tả lại, đây là nguồn duy nhất về ý-đồ UI) Owner gửi 3 screenshot màn **Duyệt NCC** (PE) đang chạy prod, làm MẪU cho KHKK: **Ảnh 1 — Panel 1 (trái) + khung tổng:** - Header trang: icon clipboard + `Duyệt NCC` + badge đếm `45`. - Panel trái: ô search placeholder `Tìm mã / tên gói thầu / dự án…` · dropdown `Tất cả trạng thái` · cây thu-gọn được `📅 Năm 2026` kèm badge `45`. - Panel giữa khi chưa chọn: empty-state icon + `Chọn phiếu ở danh sách`. - Panel phải khi chưa chọn: empty-state `Quy trình duyệt sẽ hiện khi chọn phiếu.` (có nút ✕). **Ảnh 2 — Panel 3 (phải), thứ tự dọc:** 1. Tiêu đề `Quy trình duyệt` + dòng meta `QT-DN-V2-001 v04 · Quy trình Duyệt NCC`. 2. Banner xanh lá: `Phiếu đã kết thúc tại Phòng Kiểm soát Chi phí · Cấp 2 — không qua CEO.` 3. Các **Bước**: `Bước 1 — Phòng 1` + chip tên phòng (`Phòng Cung ứng`); trong mỗi bước là các **Cấp** (`Cấp 1 đã duyệt` / `Cấp 2 đã duyệt`), mỗi cấp liệt kê NV kèm badge vai (`NS PRO`, `NS CCM`); người duyệt thật in đậm, các NV cùng cấp còn lại hiện dạng `/ Tên` mờ. Bước chưa tới **làm mờ toàn khối** + chip `Kết thúc tại Cấp 2`. Có badge đặc-biệt `⚑ Duyệt thay CEO`. 4. `File đính kèm khi duyệt` — phụ đề `File do người duyệt tải lên trong quá trình duyệt.` + hàng file (tên · size · nút xem · nút tải). 5. `Lịch sử duyệt (6)` — mỗi entry: badge hành-động (`Duyệt` xanh / `Trả lại` vàng kèm `→ Lùi về Cấp 2`) · tên người · `[Bước N — Cấp M]` · timestamp `HH:mm:ss dd/M/yyyy` · nội-dung ghi-chú nhiều dòng. **Ảnh 3 — Panel 2 (giữa), owner khoanh ĐỎ phần dưới:** - **Trên** = content nhập: bảng số liệu có dòng đánh số (`8. Giá trị thực hiện dự kiến còn lại`, `9. Giá trị tổng thực hiện dự kiến` — dòng tổng nền xanh đậm, số âm màu đỏ trong ngoặc) → card `THÔNG TIN CHỌN THẦU` (`① TÊN NCC/TP ĐƯỢC CHỌN` nền xanh nhạt · `2–3. GIÁ ĐỀ XUẤT TỪ PRO/CCM` Min/Max · `Giá chốt duyệt` + badge `Giá NCC` · `4. Bảng so sánh giá` hàng attachment · `5. Link hồ sơ` + nút `Copy`). - **Dưới (KHOANH ĐỎ)** = `5. Ý KIẾN CẤP DUYỆT (SIGN-OFF THEO WORKFLOW)`: nhóm theo **BƯỚC → PHÒNG**, mỗi nhóm có header trái (`BƯỚC 1 — PHÒNG 1` + chip tên phòng) và meta phải (`2/2 cấp đã duyệt · 5 NV tham gia`); bên trong là **lưới 2 cột** các card ý-kiến: `Cấp N — Tên người` + badge `✓ Đã duyệt` + nội-dung ghi-chú (nhiều dòng, giữ xuống dòng) + timestamp; ý-kiến rỗng render placeholder `(duyệt — không ý kiến)`. Bước chưa duyệt render `— Chưa có ý kiến duyệt.` - Cây trái trong ảnh này cho thấy **cấu trúc 4 folder theo giai-đoạn** dưới mỗi gói thầu: `Duyệt NCC` → `Kế hoạch Hợp đồng` → `Duyệt Hợp đồng` → `Hợp đồng cứng` (3 cái sau đang badge `SẮP`), và card phiếu mang chip `GẤP (PRO)` · `⚑ Kết thúc · Phòng 2` · `Đã duyệt` + mã `PE/2026/A/019` + timestamp + tên người. --- ## Ground-truth lead đã ĐO trước khi phóng (để lane khỏi đo lại) | Sự-thật | Bằng-chứng | |---|---| | PE 3-panel grid = `lg:grid-cols-[400px_1fr_360px]` | `fe-admin/src/pages/pe/PurchaseEvaluationsListPage.tsx:397` | | PE panel 1/2/3 = `PeListPanel` · `PeDetailTabs` · `PeWorkflowPanel` | cùng file `:658 :678` + imports `:28 :31` | | KHKK **list** hiện là **2-panel** `lg:grid-cols-[19rem_minmax(0,1fr)]` + `PipelineTreePanel` | `fe-admin/src/pages/khkk/KhkkListPage.tsx:197-198` | | KHKK **detail** là **trang riêng full-width**, `KhkkWorkflowPanel` render **INLINE ngay dưới header** (không phải rail phải) | `fe-admin/src/pages/khkk/KhkkDetailPage.tsx:411` + comment `:9` | | Cả PE lẫn KHKK đều **SHA256 IDENTICAL** giữa fe-admin ⟂ fe-user | đo @S171: 4/4 cặp file `same=YES` | | LOC | PE Workspace 147 · PE List 807 · `PeDetailTabs` 179.376 B · `PeWorkflowPanel` 63.472 B · Khkk Detail 1.093 · Khkk List 361 · KhkkWorkflowPanel 491 | | KHKK **chưa có** `components/khkk/` — mọi thứ nằm trong `pages/khkk/` | `ls fe-admin/src/components/khkk/` → không tồn tại | 🔴 **Delta cốt lõi:** PE = **một màn 3-panel hợp nhất** (chọn ở trái → nội-dung giữa → workflow/history phải). KHKK = **list 2-panel** + **detail trang riêng** có workflow **xếp chồng trên** nội-dung. Đây là khoảng cách kiến-trúc, không phải khoảng cách CSS. --- ## taskList snapshot ### Stage A — `/fable-real` (LỆNH-A, 1 lane, `tier:'fable'`) | # | role | label | nhiệm-vụ | |---|---|---|---| | 1 | `investigator-codebase` | `Core investigator-codebase invest s171` | Deep-pass MAP toàn-diện PE-vs-KHKK trên 3 panel + đề-xuất bố-trí đồng-nhất, phân-loại rõ **GIỐNG-được** ⟂ **KHÁC-buộc-phải-khác** (+ lý-do domain). Propose-only. | ### Stage B — `/fable-clone` (LỆNH-B, ensemble ≥3 lane, `tier:'opus'`) | # | role | label | lăng-kính | |---|---|---|---| | 1 | `reviewer` | `Gate reviewer lens-fidelity s171` | Trung-thành ảnh/ý-đồ owner + "tương tự thì giống nhau" | | 2 | `reviewer` | `Gate reviewer lens-feasibility s171` | Khả-thi kỹ-thuật: data-shape KHKK có đủ nuôi 3 panel không | | 3 | `reviewer` | `Gate reviewer lens-regression s171` | Rủi-ro vỡ cái đang chạy: route · policy `KeHoachKyKet` (gotcha #85) · mirror 2-app · Pattern 16-bis | ### Stage C — thực-thi Opus 5 MAX theo `spec-khkk-ui-mirror-pe-04-08-2026.md` (lead ghi SAU khi verify — engine KHÔNG tự ghi). --- ## Stages - [x] A — `/fable-real investigator-codebase` deep-pass (Fable 5, `wf_b76692d4-3a4`, 756s, 33 tool-use, return SẠCH 0-garble) → `sub-investigator-codebase-invest-s171.md` **44.830 B** · **VERDICT=27 giống-được / 6 buộc-khác / 8 data-gap** 🔴 **2 đính-chính vào Ground-truth của lead** (bảng trên SAI 1 dòng): (Đ-1) PE panel 1 **KHÔNG** phải `PeListPanel` — viết INLINE `PurchaseEvaluationsListPage.tsx:399-639`; `PeListPanel` chỉ 1 call-site = `PurchaseEvaluationWorkspacePage.tsx:84` ⇒ **không có component list dùng-lại được**. (Đ-2) **Đã có tiền-lệ mới hơn:** `ContractsListPage.tsx:125` (GĐ3, S168, 255 dòng) = bản PE-3-panel gọt gọn ⇒ KHKK là **mắt-xích lệch duy nhất** giữa GĐ1 và GĐ3; chép khuôn từ GĐ3, KHÔNG chép từ PE 807 dòng. 🔴 **Đảo ước-lượng:** `ContractSigningPlanWorkflowService.cs:534/:547` ĐANG GHI approvals + changelog THẬT ⇒ "Lịch sử duyệt" không phải xây-lại, chỉ thiếu **~5 field DTO + 1 endpoint**. - [x] B — `/fable-clone reviewer` ×3 lăng-kính (`wf_832dadb2-93f`, Opus, 470s, 52 tool-use) — 🔴 **CẢ 3 LANE CHẾT** *"You've hit your session limit · resets 6:10pm"*, engine trả `verdict: null` ×3. **NHƯNG ĐĨA CỨU 2,95/3**: khung-rỗng-lượt-1-2 giữ trọn 74.070 B. · `lens-feasibility` 26.461 B **HOÀN TẤT** — `8 HELD / 6 BROKE / 6 bỏ-sót` · `lens-regression` 24.432 B **HOÀN TẤT** — `8 HELD / 4 BROKE / 10 bỏ-sót` · `lens-fidelity` 23.177 B **DỞ** — §6 (phép đo P3-12) + §8 (lỗ tự khai) trống, VERDICT còn `?`. Lead derive từ §7 bảng của chính nó ≈ 12 HELD / 6 BROKE / 3 bỏ-sót (**nhãn lead-derive, KHÔNG phải lane tự kết**); lead **tự chạy phép §6 còn thiếu** → lật P3-12 - [x] C — lead verify + refute/synthesize → **`spec-khkk-ui-mirror-pe-04-08-2026.md` 35.017 B** (utf8-strict OK, đủ 3 heading ①②③). Post-wave `git status`: **0 residual-write** ngoài run-folder - [x] C-bis — ✅ **3 QUYẾT-ĐỊNH OWNER chốt 2026-08-04**, spec cập-nhật **43.087 B**: (1) panel-1 = **`PipelineTreePanel`** — owner **BÁC** đề-xuất đảo của lead, đọc đủ 4 rủi-ro trên thẻ chọn rồi vẫn chọn ⇒ quyết-định có thông-tin đầy-đủ; lead chuyển 4 rủi-ro thành **mitigation CƯỠNG-HÀNH** + acceptance C-5..C-8 (*chọn HÌNH ≠ chấp-nhận VỠ*) · (2) panel-3 rỗng = **CHỈ hộp dashed y PE** ⇒ **đóng miễn-trừ-thứ-7** · (3) file-khi-duyệt = **upload THẬT** ⇒ BE-7 `+ApprovalAttachment=3` policy `KeHoachKyKet.Update`, bẫy F-6 "UI chết" tự tan - [ ] D — giao Opus 5 MAX thực-thi (giới-hạn phiên reset 18:10, phóng 18:50) ## Synthesis ### Lead refute — 3 chốt lead ÁP LÊN bản invest sau phản-biện 1. 🔴 **Panel 1 KHÔNG dùng `PipelineTreePanel`** (đảo §5 bản invest). 4/5 phát-hiện nghiêm nhất hội tụ đúng chỗ này: `canPe` khoá cây ⇒ user **chỉ có quyền KHKK mất TRẮNG danh-sách** (display-layer khoá authz-layer đang mở — mặt còn lại #82) · trần 200 = **mất đường tới phiếu**, không có "trang 2" · `usePipelineStages.ts:211` cây **tự navigate rời màn 3-panel** · `content` **nuốt `groups`** im lặng (`PipelineStageFolders.tsx:285-287`). Mirror trung-thành = **KHKK dựng cây từ danh-sách CỦA CHÍNH KHKK** (đúng như PE dùng `yearGroups` từ query `pe-list`, KHÔNG dùng `PipelineTreePanel`) ⇒ 3 rủi-ro tan hết. 2. 🔄 **Miễn-trừ 6 → 3.** P1-4 lật GIỐNG-được (khác nguồn dữ-liệu ≠ khác bố-trí) · P2-7 lật GIỐNG-được (`readOnly` là prop HOST, `PurchaseEvaluationsListPage.tsx:663` — không cần workspace) · P3-12 hạ xuống CHỜ-ANH (**lead đo `DbInitializer.cs:554`: 3 Bước × ≤2 Cấp = tới 6 trạm, seed `:620` tự ghi "sao từ workflow Duyệt NCC prod"** ⇒ tiền-đề *"3 trạm ngắn"* SAI đo được). **+ miễn-trừ THỨ 7 không khai** (F-5: §5 âm-thầm đảo P3-16 panel-3 empty-state) → đẩy thành CÂU HỎI 2. 3. ✅ **Giữ quyết-định A, THAY lý-do.** Lý-do cũ (*"cả hai đường generic đều đụng PE prod"*) là **nhị-phân giả** — có đường (iii) adapter 1 chiều nằm trọn trong KHKK, 0 dòng PE. Lý-do đúng: trích `StepOpinionsBox` khỏi `components/pe/` **mâu-thuẫn trực-tiếp acceptance A-10 của chính nó**. *Kết-luận đúng, chứng-minh hỏng* — giữ kết-luận, sửa chứng-minh. ### 6 acceptance của bản invest bị bẻ gãy khi CHẠY THẬT (đã thay bản sửa trong spec) | Gốc | Vỡ thế nào | |---|---| | **A-13** (canh QUYỀN) | Vỡ **cả hai đầu**: `:15` comment chứa "Authorize"+"Policy" ⇒ **bơm tử-số**; `:17 :236 :241` comment *nhắc tên* `[Authorize]` trần ⇒ vế "0 dòng trần" **FAIL trên file 100% sạch** (citation-trap). → thay bằng neo `^\s*\[` + fault-inject chứng răng | | **A-6** (canh bug S155) | **Kiểm NHẦM KHOÁ** — cấm `phase` (vô hại, transient) và bỏ sót `filter`/`view` (đúng 2 khoá làm vỡ) ⇒ `setSearchParams({group,filter:'DaDuyet'})` = **đúng bug S155** vẫn PASS. Inverted check | | **A-10** (canh hồi-quy) | **Đo sai trục** — thiếu `hooks/usePipelineStages.ts` + `components/pipeline/` ⇒ sửa 2 path đó có thể làm **trang PE mất 8 ngăn GĐ2** mà A-10 PASS 100% | | **A-7b** | Chạy trên **chính mẫu PE** ra **3 hit**, không phải 5 ⇒ implementer viết ĐÚNG sẽ FAIL; cách duy nhất PASS = **bỏ số đếm động cho vừa regex** = Goodhart | | **A-7d** | Đo **chuỗi chữ**, mục-tiêu là **vị-trí khối** ⇒ đổi tiêu-đề "Ý kiến duyệt" là PASS trong khi khối vẫn ở panel 3 | | **A-8c** | Grep-trên-source ⇒ khối **UI CHẾT vẫn PASS 100%** (không có đường ghi attachment + khối tự-ẩn khi rỗng ⇒ không bao giờ render trên UAT) | ### 3 khối owner NHÌN THẤY trong ảnh mà bản invest đánh rơi **F-1** EmptyState panel 2 *"Chọn phiếu ở danh sách"* (Ảnh 1 — trạng-thái ĐẦU TIÊN người dùng thấy; bản invest ghi ở §1.2 rồi rơi khỏi §3/§5/§7) · **F-2** chip `⚑ Kết thúc · Phòng N` trên card panel 1 (Ảnh 3 — **0 công BE**, cột Mig 71 đã có) · **F-3** liệt-kê NV mỗi Cấp, người duyệt in đậm + NV khác `/ Tên` mờ (Ảnh 2). ### Tin tốt được verify độc-lập (đừng cắt ngân-sách vì nghi ngờ) Lane feasibility **vào để lật D-2 và không lật được**: `LogSigningPlanTransitionAsync` **6 call-site phủ TRỌN** Pending/Approve/**Reject-Return** (`:204 :283 :296 :311 :325 :393`), `Approvals.Add` ở `:534` **không `if` nào bọc** ⇒ entry *"Trả lại → Lùi về Cấp 2"* của Ảnh 2 **SẼ CÓ dữ-liệu**. Và **A-0 chặn-cửa GỠ ĐƯỢC** — `usePipelineLinkage()` đã có sẵn `khkkByPeId` (`:123-134,147`), **0 query mới**. ### Ngược lại — 1 chỗ RẺ bị ước-lượng sai chiều `FinalizeStepName`/`FinalizeLevelName` **không phải "field append"**: `ContractSigningPlan.cs:51` chỉ có `bool EndedByLevelFinalize`, **không cột nào lưu Bước/Cấp nào**. Phải **dẫn xuất** — port ~20 dòng projection **3 subquery** từ `PurchaseEvaluationFeatures.cs:665-683`. Bản invest cảnh-báo *"dễ sai đắt nhất theo cả hai chiều"* rồi **tự dính chiều còn lại**.