6.2 KiB
SPEC CỤM-3 FINAL — K7 + K8 (lead kết tinh sau B2-r cụm-3, @S164)
① Đề bài
K7 cầu KHKK→HĐ (bridge 8v/8test — đường ống S156 ĐÃ vá @W6-S161, verified 3 chân) · K8 dry-run E2E 17 bước. NỀN = sub-invest-fable-b2-cum3.md (22.9KB; L1 chấm 7/7 claim ĐÚNG) — 16 VÁ dưới thắng khi xung đột (từ sub-reviewer-c3-l{1,2,3}.md: L1 PWF 4M · L2 FAIL 5H · L3 FAIL 4H).
② 16 vá
Trục AUTHZ bridge (3-lane hội tụ — nặng nhất):
- 🔴 [L1-F2 ⨯ L2-H1 ⨯ L3-H1/H2] Gate nút bridge = HỘI 2 policy (
KeHoachKyKet.Readclass-level ∧Contracts.Createaction-level) — spec/FE phải khai ĐỦ 2 vế; và 🔴Contracts.Createhiện CHƯA CẤP vai nào (seederCanCreate=isPe⇒ false choContracts) ⇒ thêm việc: grantContracts.Createcho vai được bấm (đề xuất: Drafter + Procurement + Admin — [chờ câu III anh; default người-có-quyền-tạo-HĐ]) + test authz 2 chiều (vai có quyền → 201 · vai thiếu 1 trong 2 vế → 403) + FE gate đúng hội-2-khóa (không lỏng hơn server). Ghi chú: endpointPOST /api/contractshiện[Authorize]trần (#82 treo chủ-đích — KHÔNG sửa đợt này, chỉ khai). - [L1-F9] B0 pre-flight picker đủ 2 vế: workflow type-3 phải
IsActive∧IsUserSelectable(dropdown lọc cả 2 —ContractCreatePage.tsx:334-341); B0 thêm SQL check cả 2 cờ kẻo dialog rỗng. - [L1-F11] Guard bridge re-check
IsActive/IsUserSelectablecủa workflow được pin (khuônContractFeatures.cs:74-88chỉ check tồn-tại+type — forge POST pin được quy trình retired; thêm 2 điều kiện, rẻ).
Trục dữ liệu/cây:
4. 🔴 [L1-F6] pe.ContractId ??= có chiều NGƯỢC — bridge-PE-cũ chạy trước thì ??= no-op im lặng ⇒ HĐ mới vô hình trên cây (cây đi qua pe.contractId ĐƠN usePipelineStages.ts:214-218). Vá 2 tầng: (a) K7 ghi Line.ContractId luôn (nguồn thật) + pe.ContractId ??= chỉ là fallback hiển thị; (b) 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) — không thì OG-1 UI-1-1 (N HĐ/PE) chỉ hiện HĐ đầu.
5. [L3-H3] T2 (gộp 2 line cùng NCC) phụ thuộc Mig 71 DROP-unique — thêm vào danh sách phụ-thuộc K7←K2 (hiện sót); test T2 chạy SAU K2.
6. [L1-F7] Bridge bổ sung theo tiền lệ: nạp Project.Code + Supplier.Code (+2 guard NotFound, khuôn bridge-cũ :63-66/:85-86) cho codegen GenerateAsync · field-map += SlaDeadline · WorkflowDefinitionId pin như 2 tiền lệ (ContractFeatures.cs:105-109 "Cả 2 set ok") — không để null rơi về policy hardcode :647-662.
7. [L1-F10] Acceptance B6 đổi neo: EndedByLevelFinalize phía KHKK là vật K3 MỚI ĐẺ (hiện 0 hit) — B6 neo vào {phase DaDuyet + ApprovedAmount frozen per-line + K3-cột-runtime} tùy nhánh; khai rõ bảng nào cột nào.
8. [L1-F8] Sửa nhãn "7 case" → 8 (bảng T1-T8).
Trục K8 dry-run (L2 — "chưa chạy được như viết"):
9. 🔴 [L2-H5] B0 = FORM NGƯỜI THẬT fail-closed: bảng 14 dòng {bước · người · email · tài khoản} PHẢI ĐIỀN ĐỦ trước giờ-G (12/14 bước đang trống); nguồn = K3-acceptance LIVE; thiếu 1 dòng = KHÔNG chạy.
10. 🔴 [L2-H2] Phép đo QĐ9 phải TRƯỢT ĐƯỢC: sau finalize → sửa NS/giá nguồn (PE hoặc catalog) → reload phiếu → số KHÔNG đổi = PASS (đổi = FAIL); đo CẢ 2 nhánh (a-Chương finalize-CCM VÀ lên-CEO-Trường duyệt) — hiện phép đo 0-bit.
11. 🔴 [L2-H3] Dấu ZZTEST cho phiếu KHKK: mã phiếu auto-gen (KHKK/2026/xxx) KHÔNG mang ZZTEST — bản ghi sống-vĩnh-viễn lại không dấu. Vá: nhét ZZTEST vào field hiển thị được trên list (tên kế hoạch/ghi chú — soi DTO chọn field) + rollback theo DANH SÁCH ID ghi lại từng bước, không theo prefix.
12. 🔴 [L2-H4 ⨯ L1-F5] Câu giờ-G (V) trình anh ĐỦ 2 hệ quả: (i) phiếu KHKK DaDuyet trên PE thật không xóa được kể cả Admin (sqlcmd là lối duy nhất); (ii) 🔴 TRƯỚC K2 rào là per-PE ⇒ phiếu ZZTEST khóa TOÀN BỘ PE/2026/A/049 (không phải chỉ nhóm-4) — nghiêng mạnh về phương án (b) PE ZZTEST riêng; nếu anh chọn (a) PE thật thì bắt buộc chạy SAU K2.
13. [L3-H4] B16 rollback HĐ = NO-OP hiện tại (numeric guard chặn xóa cả khi TuChoi) — rollback plan ghi rõ: HĐ test dọn bằng sqlcmd soft-delete (lệnh cụ thể per bảng: Contracts + ContractApprovals + Changelogs + Line.ContractId=NULL + pe.ContractId=NULL), thứ tự ngược.
14. [L2-M10] Câu III treo → acceptance "ai bấm" để [CHỜ-ANH] — không chốt trong DoD tới khi anh trả; default dialog + người-có-quyền.
15. [L2-M12] DoD cardinality đo được: sau bridge UI-1-1 — đếm SELECT COUNT(DISTINCT ContractId) FROM Lines WHERE PlanId=X AND ContractId IS NOT NULL == số lần bấm; gộp (sau này) = N line 1 ContractId.
16. [L3-Q5] Dialog bridge tái dùng khuôn sẵn (dialog confirm shadcn/ui đã dùng ở Xóa-phiếu S155 + select khuôn ContractCreatePage picker) — 0 khuôn mới (lời 9).
③ Checklist final cụm-3
- K7 (SAU cụm-1 K2+K3): bridge 8v nền + vá 1-8 · test 8+2-authz+idempotency-double-click · acceptance 7 nền + hội-2-khóa 403-path + cây hiện HĐ qua lines[].contractId +
giaTri == Σ ApprovedAmountline chọn (null → message) · baseline ≥590+k. - K8 (CUỐI, sau MỌI wave land + câu V anh chốt): B0-FORM 14 người fail-closed + pre-flight SQL (workflow LIVE 2-cờ + KHKK-N4 + catalog N-row) · 17 bước mỗi bước {người thật · hành động · kỳ vọng · chụp} · QĐ9 đo 2 nhánh trượt-được · ZZTEST field-hiển-thị + rollback theo ID (sqlcmd cả HĐ lẫn phiếu) · notification dặn team trước.
END spec-cum3-final — nền 7/7-claim-đúng + 16 vá · authz-hội-2-khóa đóng · K8 từ "kịch bản" thành "chạy được".