diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 88e7ea5..2936fc1 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -74,7 +74,7 @@ **(46)** 🔴 **Thước "17,1K" là thước MA — chọn 1 trong 3 ngã** (`harness-refine` A1, nó **cố ý không đặt số** vì số là quyền anh): **(i) WIRE** thêm key `tiers.l1_hot.self_target_bytes` để máy soi được · **(ii) RETIRE** gỡ luật khỏi 5 diary vì thước thật là 25.600 · **(iii) RELABEL** giữ nhưng ghi rõ "advisory". Bằng chứng nó **đang tốn công thật**: `investigator-codebase:58` nén về ~17,1K @S155, **3 ngày sau 20.646 B lại**; áp thước này thì **9/23 vai vượt** trong khi mọi máy in "0 over". **(47)** `cicd-monitor/archive/_INDEX.md` **19.484/20.480 = 95,14%**, tăng ~81 B/ngày ⇒ **chạm cap ~2026-08-09**. A1@S153 chưa land, `harness-refine` đã kích chốt-dừng ⇒ lead {thi-hành nguyên văn | HỦY ghi rõ}; nếu chọn **nâng cap 20.480** thì là quyền anh. **(48)** `harvest-curator/MEMORY.md` **19.456 B = 0 byte đổi kể từ S154**; món nợ **tự khai 2 lần** (budget `:192` từ S107 · diary `:5` từ S153) chưa trả lần nào — 3 lối ra đều là số/nhịp của anh. -🔸 **Không chặn W1-W2:** 5 câu thiết kế spec KHKK (Q3 · Q6 · Q11 + O-Q1 · O-Q2) · nguồn thứ 4 `SOL-PRO-SP-001` chưa ai đọc · 2 đường kết thúc sớm W3 (`AllowApproverFinalize` · `CeoApprovalThreshold` — anh từng phán **PHÁ VỠ** @S155) · thứ tự chạy wave. +🔸 **Không chặn W1-W2:** 5 câu thiết kế spec KHKK (Q3 · Q6 · Q11 + O-Q1 · O-Q2) · nguồn thứ 4 `SOL-PRO-SP-001` chưa ai đọc · 2 đường kết thúc sớm W3: `AllowApproverFinalize` **ĐÃ PORT @K3 S165** (OG-9 đảo "PHÁ VỠ" @S155 — owner @S164 verbatim *"chỗ budget chốt chặn là a Chương… bài toán trần budget xem như đóng"*; ⚠️ **gate-K3 F-1 MAJOR — checkbox opt-out màn duyệt CHƯA có UI, đầu việc #1 lần sau**: hiện `?? true` ⇒ MỌI lượt anh Chương bấm Duyệt đều KẾT THÚC phiếu — muốn phiếu lên CEO tạm thời chỉ có cách admin BỎ cờ ở Designer trước; vá = mirror checkbox `PeWorkflowPanel.tsx:778` vào KhkkWorkflowPanel ×2 + DTO expose cờ level) · `CeoApprovalThreshold` KHÔNG BAO GIỜ port (OG-3 human-gate vĩnh viễn) · thứ tự chạy wave. 🔸 **Treo CÓ CHỦ ĐÍCH, không phải quên:** lỗ hổng an ninh HĐ (`ContractWorkflowService.cs:48-66` Reject-trước-guard + controller class-trần) — anh chốt *"chỗ hợp đồng cứ từ từ"*. **Wave 5 chạm HĐ ⇒ phải hỏi lại anh trước khi chạy.** 🧊 *Segment cũ (S153) giữ nguyên bên dưới làm lineage:* 2026-07-26 chiều (**S153** — phiên-logic 6, 1 cửa-sổ: **adopt TRỌN thư Ctx soft-memory `0df10df4` + OWNER-VETO 3-vai + RENAME hub-form + bookend 2 đầu**). Governance-only, 0 prod-code (test 532 giữ, `git diff` src/tests/fe rỗng). Chi tiết = `docs/STATUS.md` dòng CURRENT + log `changelog/sessions/2026-07-26-1600-S153-ctx-adopt-3vai-rename.md`. ⭐ Bài đắt nhất phiên: **GAP-#9** — máy tự-build của SE tái hiện đúng bẫy F-1 mà thư hub kể (closed-mode: *xoá con-trỏ trung thực* → xanh RẺ HƠN *giữ* → trượt ⇒ gradient thưởng đường SAI; vá bằng TRƯỢT). Bài phụ: **đo wall-clock phải gồm nhánh ĐẮT** (ước lệch 2.2-5.4×) · **fixture-hỏng-cho-xanh trông y hệt máy-đúng-cho-xanh** (phải soi RUỘT fixture) · **bẫy parse `]` trong comment** nuốt non array (18 vs 23). diff --git a/docs/STATUS.md b/docs/STATUS.md index b1f83a8..b8b667e 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -3,7 +3,7 @@ > **Update rule:** trước khi bắt đầu 1 task → ghi row `🔥 In Progress`. Xong → `✅ Recently Done`. > **Tiering rule (S40):** chỉ giữ **state hiện tại + 3 session gần nhất** ở file này. Session cũ hơn → `docs/changelog/sessions/`. Full history pre-S40 → `docs/_archive/STATUS-preS40-fullhistory.md`. (Tránh over-context — xóa double, không cắt nội dung.) -**🔥 CURRENT (S161→S162 + S164/S165 2026-07-31 — phiên-LOGIC **L9**: chuỗi SP-002 B0→B3 spec ĐÓNG BĂNG + **B4 K1 danh mục loại HĐ SHIP** gate PASS-WITH-FLAGS; lineage L8 giữ dưới):** Mig **71** (K1 Mig70 `AddContractCatalogEntries` danh mục 86 dòng + K2 Mig71 `AddKhkkApprovalGroupCatalogAndFinalizeRuntime` — phiếu KHKK +nhóm duyệt N1-N8 backfill=1 + Line +hạng-mục + unique 3-cột) · **97 bảng** · **607 test PASS** (45D+562I, đo tươi @S165 gate-K2) · gotcha **85** · counter **39** · roster 23 · **MenuItems prod 142 GIỮ** (key `KeHoachKyKet` sinh **policy** không sinh menu-row — H1 bác nghi-vấn stale) · Policies **224** · Menu keys **56** (+`ContractCatalog` K1 — 4 policy sinh ra, giới-hạn khai: chưa endpoint nào tiêu thụ, controller mirror khuôn Catalogs `Roles=Admin` #82) · bundle prod → **canonical ở `:479`, KHÔNG chép số ở đây** (B1). 🔴 *Cặp `DXoYDy7R`/`o8KDluD1` từng ghi ở dòng này **chưa bao giờ là trạng-thái cuối** — `e5123ff` ship thêm 6 file FE-src SAU snapshot; đo live @S164: admin `B43R6Y17` · user `NLI5umBg`.* Contracts prod **0**. +**🔥 CURRENT (S161→S162 + S164/S165 2026-07-31 — phiên-LOGIC **L9**: chuỗi SP-002 B0→B3 spec ĐÓNG BĂNG + **B4 K1+K2 SHIP prod-verified + K3 code-xong-gate-đóng** (K4a→K8 + checkbox-opt-out-màn-duyệt = lần `/tiep` sau); lineage L8 giữ dưới):** Mig **71** (K1 Mig70 `AddContractCatalogEntries` danh mục 86 dòng + K2 Mig71 `AddKhkkApprovalGroupCatalogAndFinalizeRuntime` — phiếu KHKK +nhóm duyệt N1-N8 backfill=1 + Line +hạng-mục + unique 3-cột) · **97 bảng** · **614 test PASS** (45D+569I, đo tươi @S165 sau K3) · gotcha **85** · counter **39** · roster 23 · **MenuItems prod 142 GIỮ** (key `KeHoachKyKet` sinh **policy** không sinh menu-row — H1 bác nghi-vấn stale) · Policies **256** · Menu keys **64** (+`ContractCatalog` K1 [4 policy chưa endpoint tiêu thụ — khai #82] · +8 `AwV2_KhkkN1..N8` K3 [designer 8 nhóm, All=64 máy-đếm]) · bundle prod → **canonical ở `:479`, KHÔNG chép số ở đây** (B1). 🔴 *Cặp `DXoYDy7R`/`o8KDluD1` từng ghi ở dòng này **chưa bao giờ là trạng-thái cuối** — `e5123ff` ship thêm 6 file FE-src SAU snapshot; đo live @S164: admin `B43R6Y17` · user `NLI5umBg`.* Contracts prod **0**. ① **5 wave KHKK SHIP (window-3, S161):** W1 `a8027c0` (Mig 69, 7 bảng, 89→96) · W2 `6cbc6ad` (CRUD+Dossier 15 policy) · W3 `df52fa0` (duyệt V2 3-trạm, test-before RED→GREEN) · W6 `761d818` (đường-ống HĐ V2) · W7 `3119cb9` (bản-cứng **1-MỐC** theo owner: form điền đủ → duyệt nếu có → upload bản cứng = xong). Mỗi wave: gate 5-anchor → hmw → test → reviewer → F-0 → cicd 3-chân. 3 quyết owner: mã `KHKK/{YYYY}/{Seq:D3}` · **bỏ "MB"** · GĐ4 1-mốc. ② **Cây 4-folder GĐ (window-4, S162) — `bfc7b79`, cicd PASS 5/5 Run #432:** lệnh owner *"Tạo folder: Duyệt NCC / Kế hoạch Hợp đồng. / Duyệt Hợp đồng / Hợp đồng cứng."* + AskUser chốt **DƯỚI TỪNG GÓI THẦU** + **áp CẢ trang GĐ khác**. 3 file MỚI ×2 app (`PipelineStageFolders` · `PipelineTreePanel` · `usePipelineStages`) + 3 trang ×2 app, **SHA-pair 6/6**, thuần FE 0-mig. designer SHIP-READY (tự soi ảnh bắt 4 lỗi build-không-báo) → reviewer PASS-WITH-FLAGS 8 → lead vá F-1/3/4/6/7 + F-8. 🔴 **CÒN NỢ: trang GĐ3 chưa làm** (owner nói "CẢ", lead giao 3/4 — lead-gap FLAG-3) · F-2 liên-danh N-HĐ chờ W5 · F-5 layout 19rem chờ owner nghiệm thu prod. ③ **Bookend-close 5 vòng, 10 vai:** H1 `PASS_WITH_FLAGS` 10 finding · H2 **`GATE-FAIL` 4** (⭐ `ctx-verifier` chạy thật mà **chưa bao giờ tự APPEND** diary — S153+S158 đều lead-seed; `cicd-monitor` khai "verbatim→archive" thứ `git log -S` chứng **chưa từng vào archive**) · cặp H24 **20 FLAG** (stale 16 = **SÀN** + gap 4, `gap-carry-dropped` **streak 8**) · trio memory **MIXED** (headroom 165.767 tok đơn-điệu giảm 3 mốc · light 9/6 OVERDUE · baseline `measured` 17/23) · orphan run-folder **3 xác nhận** (2 thiếu THẬT + 1 dương-giả TÊN). diff --git a/fe-admin/src/components/Layout.tsx b/fe-admin/src/components/Layout.tsx index eb924a5..dd1ba03 100644 --- a/fe-admin/src/components/Layout.tsx +++ b/fe-admin/src/components/Layout.tsx @@ -173,6 +173,14 @@ function resolvePath(key: string): string | null { if (code === 'DuyetNcc' || code === 'DuyetNccPhuongAn' || code === 'Contract') { return `/system/approval-workflows-v2/${code}` } + // [K3 S164/S165] Họ MỚI `AwV2_KhkkN1..N8` — 8 quy trình duyệt theo NHÓM của + // Kế hoạch ký kết HĐ (mỗi nhóm 1 đội hình 3 trạm PRO → CCM → CEO). Đây là "chỗ + // thứ 4" Pattern 16-bis: BE seed leaf mà thiếu nhánh này thì `resolvePath` trả + // null ⇒ MenuLeaf DROP IM LẶNG khỏi sidebar (gotcha #50). + // 🔴 CHỈ nới họ `AwV2_*` (Designer, fe-admin CÓ trang). KHÔNG nới họ `Khkk_*` + // — fe-admin cố ý KHÔNG có `WorkflowMatrixViewPage` (xem ghi chú khối `Pe_*` + // bên trên); nới ở đó = đẻ leaf chết rơi vào catch-all "chưa được build". + if (/^KhkkN[1-8]$/.test(code)) return `/system/approval-workflows-v2/${code}` } return null diff --git a/fe-admin/src/pages/khkk/KhkkWorkflowPanel.tsx b/fe-admin/src/pages/khkk/KhkkWorkflowPanel.tsx index 0a4486d..e2fa3bc 100644 --- a/fe-admin/src/pages/khkk/KhkkWorkflowPanel.tsx +++ b/fe-admin/src/pages/khkk/KhkkWorkflowPanel.tsx @@ -4,9 +4,17 @@ // Khuôn: `components/pe/PeWorkflowPanel.tsx` — GIỮ sơ đồ Bước→Cấp ✓/●/○, banner "đến lượt bạn" // (`blockedByV2Level` :109-114), 3 nút màu (emerald/amber/red), Dialog + ô ý kiến. // ĐƠN-GIẢN-HOÁ CÓ CHỦ ĐÍCH (spec W3 §②-3): KHÔNG price-picker, KHÔNG ngân sách, KHÔNG file-khi-duyệt, -// KHÔNG 4 return-mode (W3 = TraLai toàn phần), KHÔNG 2 đường kết-thúc-sớm (AllowApproverFinalize / -// CeoApprovalThreshold — anh phán "PHÁ VỠ" S155; W1 đã rào ở Designer type-10). Chốt giá per-Line là -// việc BE làm ở finalize choke-point, KHÔNG phải việc của panel này. +// KHÔNG 4 return-mode (W3 = TraLai toàn phần). Chốt giá per-Line là việc BE làm ở finalize +// choke-point, KHÔNG phải việc của panel này. +// +// 🔴 [K3 S164/S165 — SUPERSEDE] Câu cũ ở đây khai "KHÔNG 2 đường kết-thúc-sớm (AllowApproverFinalize / +// CeoApprovalThreshold — anh phán «PHÁ VỠ» S155; W1 đã rào ở Designer type-10)". KHAI ĐÓ ĐÃ HẾT HIỆU LỰC: +// owner ĐẢO phán quyết S155 (@S164 — "a Chương quyết cái gì cần CEO") ⇒ BE **đã port nhánh finalize cho +// KHKK ở K3 cùng wave**, và Designer type-10 đã GỠ rào (`ApprovalWorkflowsV2Page.tsx` — hằng +// `earlyFinalizeLocked` xoá hẳn, 2 ô tick sống lại). Đừng đọc file này thành "BE không hỗ trợ". +// Panel VẪN không có control riêng cho đường kết-thúc-sớm — nhưng vì lý do KHÁC HẲN: `AllowApproverFinalize` +// là cấu-hình PER-CẤP do admin tick ở Designer, BE đọc lúc duyệt để quyết dừng hay trình tiếp CEO. Người +// duyệt chỉ bấm "Duyệt" như thường; không có gì để panel render thêm. // // 🔴 KHÁC PE Ở CHỖ ĐẮT NHẤT: PE nhận `approvalFlow` đã có `status` Done/Current/Pending do BE precompute. // KHKK BE trả CÂY THÔ (`ContractSigningPlanFeatures.cs:112-117` + `:105-110`) — KHÔNG có `status`. diff --git a/fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx b/fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx index bd64829..964c795 100644 --- a/fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx +++ b/fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx @@ -142,14 +142,23 @@ const LEVEL_ORDERS: LevelOrder[] = [1, 2, 3] // [W1 KHKK S161] "Kế hoạch ký kết HĐ" = slot 10 của `ApprovalWorkflowApplicableType` // (`ApprovalWorkflow.cs:66` trước đó dừng ở `TravelRequest = 9`). Đặt TÊN cho số 10 -// vì nó dùng ở HAI nơi trong file: map deep-link ngay bên dưới + rào 2 ô "duyệt sớm" -// trong Designer — số trần rải 2 chỗ là kiểu sửa 1 quên 1. +// vì nó dùng ở HAI nơi trong file: map deep-link ngay bên dưới + gợi ý mã quy trình +// mặc định — số trần rải 2 chỗ là kiểu sửa 1 quên 1. +// [K3 S164 — GỠ KHOÁ] Chỗ dùng THỨ BA của hằng này (rào 2 ô "duyệt sớm" trong +// Designer) ĐÃ GỠ, xem khối ghi chú ngay dưới. const TYPE_INT_CONTRACT_SIGNING_PLAN = 10 -// Câu giải thích DÙNG CHUNG cho tooltip 2 ô bị khoá + dòng ghi chú (1 chuỗi, không -// chép 3 lần rồi lệch chữ). -const KHKK_EARLY_FINALIZE_NOTE = - 'Chưa hỗ trợ cho quy trình Kế hoạch ký kết HĐ — backend chưa xử lý đường "duyệt sớm", tick vào sẽ KHÔNG có tác dụng.' +// [K3 S164 — SUPERSEDE W1 S161] 🔴 Duyệt sớm ĐÃ MỞ cho KHKK từ K3 (owner đảo phán +// quyết S155 @S164: TP.CCM quyết lên CEO hay kết thúc). Trước đó file này có hằng +// `KHKK_EARLY_FINALIZE_NOTE` + cờ `earlyFinalizeLocked` ép 2 ô "Duyệt thẳng Cấp cuối" +// và "Duyệt là KẾT THÚC" về mờ-và-false cho type 10, vì backend chưa port đường +// finalize. BE đã port nhánh finalize ở K3 CÙNG WAVE ⇒ cờ hết là "cờ chết", khoá lại +// thành khoá SAI (chặn đúng cái owner vừa yêu cầu mở). Gỡ ở CẢ HAI lớp đã rào — +// render (banner + `disabled`) và payload (`levelFlagsPayload` ép false) — vì chỉ gỡ +// 1 lớp thì màn hình và dữ liệu nói hai đằng. +// Banner giải thích đi kèm cũng XOÁ (không đổi chữ): nội dung nó chỉ để cảnh báo +// khoá, hết khoá là hết việc; còn câu supersede mang từ vựng nội bộ ("K3", "S155/ +// S164") nên thuộc về comment này, không đẩy lên UI admin. // FE typeCode → BE int (giống MenuKeys ApplicableType) const TYPE_CODE_TO_INT: Record = { @@ -239,12 +248,11 @@ function makeDefaultLevelEntry(order: LevelOrder, approverUserId: string): EditL // - 2 cờ ĐỔI-ĐƯỜNG-ĐI (SkipToFinal, Finalize): PUT chỉ ECHO — gửi lệch ⇒ 409 // (ApprovalWorkflowV2AdminFeatures.cs:580-585). Vẫn PHẢI gửi đúng giá trị đang lưu. // -// [W1 KHKK S161] `lockEarlyFinalize` = quy trình thuộc loại CHƯA hỗ trợ 2 đường -// "duyệt sớm" (hiện chỉ Kế hoạch ký kết HĐ, type 10) ⇒ ép 2 cờ đó về `false` TẠI ĐÂY, -// không chỉ khoá ô tick ở màn hình: khoá render mà payload vẫn bê giá trị cũ thì -// clone/dữ liệu lạ vẫn đẩy `true` lên BE. Echo của PUT vẫn khớp DB vì mọi bản type 10 -// đều do chính màn này tạo ra với 2 cờ = false ⇒ không sinh 409. -function levelFlagsPayload(e: EditLevelEntry, lockEarlyFinalize: boolean) { +// [K3 S164 — GỠ KHOÁ] Tham số thứ 2 `lockEarlyFinalize` (W1 S161, ép 2 cờ "duyệt sớm" +// về `false` cho type 10) ĐÃ BỎ: mọi loại quy trình nay gửi đúng giá trị admin tick. +// Xoá hẳn tham số chứ không để nó nằm chết — `noUnusedParameters` bật, và param mồ côi +// ở hàm 2 call-site là mồi cho lượt sau tưởng còn rào. +function levelFlagsPayload(e: EditLevelEntry) { return { allowReturnOneLevel: e.allowReturnOneLevel, allowReturnOneStep: e.allowReturnOneStep, @@ -252,8 +260,8 @@ function levelFlagsPayload(e: EditLevelEntry, lockEarlyFinalize: boolean) { allowReturnToDrafter: e.allowReturnToDrafter, allowApproverEditDetails: e.allowApproverEditDetails, allowApproverEditBudget: e.allowApproverEditBudget, - allowApproverSkipToFinal: lockEarlyFinalize ? false : e.allowApproverSkipToFinal, - allowApproverFinalize: lockEarlyFinalize ? false : e.allowApproverFinalize, + allowApproverSkipToFinal: e.allowApproverSkipToFinal, + allowApproverFinalize: e.allowApproverFinalize, allowApproverDelete: e.allowApproverDelete, } } @@ -630,13 +638,10 @@ function Designer({ const isEdit = editTarget !== null const source = editTarget ?? cloneFrom - // [W1 KHKK S161 — chặn "cờ chết"] 2 ô "duyệt sớm" (Duyệt thẳng Cấp cuối / Duyệt là - // KẾT THÚC) ĐỔI ĐƯỜNG ĐI của phiếu. Module Kế hoạch ký kết HĐ chưa port 2 đường này - // ở backend ⇒ để ô tick sống = admin tưởng đã bật mà phiếu vẫn chạy y như cũ (cờ - // chết đánh lừa người cấu hình). Khoá ở CẢ HAI lớp: render (disabled + ghi chú) và - // payload (levelFlagsPayload ép false). - const earlyFinalizeLocked = applicableType === TYPE_INT_CONTRACT_SIGNING_PLAN - + // [K3 S164 — GỠ KHOÁ] `earlyFinalizeLocked` (W1 S161) ĐÃ BỎ HẲN: 2 ô "duyệt sớm" + // nay sống cho MỌI loại quy trình, kể cả Kế hoạch ký kết HĐ (type 10) — BE đã port + // đường finalize ở K3 cùng wave, và owner chốt TP.CCM là người quyết lên CEO hay + // kết thúc. Lý do đầy đủ ở khối ghi chú đầu file (gần `TYPE_INT_CONTRACT_SIGNING_PLAN`). const initialSteps: EditStep[] = useMemo( () => (source ? copyFromDefinition(source) : [makeEmptyStep(1)]), [source], @@ -780,7 +785,7 @@ function Designer({ name: `Cấp ${e.order}`, approverUserId: e.approverUserId, // 9 cờ qua ĐÚNG 1 hàm dùng chung với POST — xem levelFlagsPayload(). - ...levelFlagsPayload(e, earlyFinalizeLocked), + ...levelFlagsPayload(e), })), })), }) @@ -812,7 +817,7 @@ function Designer({ // field (KHÔNG spread `...e`) ⇒ thiếu 1 cờ thì TS vẫn build sạch, ô tick // vẫn hiện, chỉ BE không bao giờ nhận. Từ khi có call-site thứ hai (PUT), // 9 cờ gom vào levelFlagsPayload() dùng CHUNG — thêm cờ mới chỉ sửa 1 nơi. - ...levelFlagsPayload(e, earlyFinalizeLocked), + ...levelFlagsPayload(e), })), })), }) @@ -1278,51 +1283,32 @@ function Designer({ /> Cho phép chỉnh sửa Section ngân sách lúc đang duyệt - {/* [W1 KHKK S161] Ghi chú vì sao 2 ô ngay dưới bị mờ. Chỉ hiện - cho loại quy trình chưa hỗ trợ "duyệt sớm" (type 10 — Kế hoạch - ký kết HĐ); các loại khác giao diện KHÔNG đổi 1 pixel. */} - {earlyFinalizeLocked && ( -
- {KHKK_EARLY_FINALIZE_NOTE} -
- )} {/* Mig 31 (S23 t1) — F2 AllowApproverSkipToFinal admin opt-in per-slot. - Approver tick = skip thẳng Cấp cuối khi đang ChoDuyet. */} -