# S87 (2026-06-24) — PE multi-winner + budget=0 + cấp-KẾT-THÚC (Mig 58) + Section B 3-cột (Mig 59)

**HMW-mode ON · ~14 spawn (nhiều #53 truncate → recover-disk + reviewer cross-verify + em-main self-gate) · 2 commit prod-verified.**

## Bối cảnh
Anh forward PDF `CCM - 4 ý can sua PM.pdf` (OneNote export) — 4 ý CCM muốn sửa, anh dặn **BỎ mục 3** ("CCM sửa Ngân sách-kỳ-này", nói riêng sau). Rồi anh giao thêm Section B 3-cột qua screenshot + "workflow chạy review test kỹ".

## (1) CCM 3-fix — Mig 58 `AddPeMultiWinnerAndLevelFinalize` (`f1b10e9`, Run #344 PASS ~5m11s)
- **① Multi-winner:** chọn NHIỀU NTP/NCC/ĐVDV cùng trúng → tổng CP = SUM. Thêm `PurchaseEvaluationSupplier.IsWinner` (nguồn-sự-thật mới, backfill từ `SelectedSupplierId` 13/13 phiếu cũ). `SelectWinnerCommand(Guid? SupplierId)` → `(IReadOnlyList<Guid> SupplierIds)` multi-toggle; `winnerQuoteTotal` 3 site đổi predicate `==SelectedSupplierId` → `IsWinner`. `SelectedSupplierId` giữ làm "primary" (= 1 winner ? đó : null). FE bảng so sánh tick-nhiều-ô + "Tổng chi phí đã chọn (N đơn vị)".
- **② Budget=0 hợp lệ:** submit-guard nới `effectiveBudget is null || <= 0` → `is null` only (0 cố-ý đi qua, null chặn). Fallback ProInitial CHỈ khi null (bảo-vệ 0-cố-ý khỏi bị override→null-block). 3 validator `GreaterThan(0)` → `GreaterThanOrEqualTo(0)`.
- **④ Cấp KẾT THÚC quy trình:** `ApprovalWorkflowLevel.AllowApproverFinalize`. **Diễn-tiến thiết-kế:** ban đầu em làm opt-in checkbox (an-toàn S73) → anh "cho bật trong quy trình thì hay hơn" → đổi **AUTO** theo cấu-hình-cấp: nhánh `if (matchingLevel.AllowApproverFinalize)` trong ApproveV2Async tự DaDuyet bỏ CEO, GỠ runtime-flag `finalizeByApprover` (revert 9 site plumbing). FE bỏ checkbox → banner "Cấp này KẾT THÚC quy trình" + bắt chọn giá-chốt. Designer per-level checkbox.
- **⚠️ Self-review bắt 8 chỗ FE keyed `selectedSupplierId`** (null khi ≥2 winner → vỡ submit-guard/checklist/`computeGiaChaoThau`/`canCreateContract`/display) → đổi `isWinner`. → bài học `feedback_cardinality_change_grep_consumers`.
- Test 377→**395** (+20 −2 finalize-auto-rework). Bundle admin `aKuNip19`→`CXCKlNay`/user `BUsL0HeQ`→`Di40ewrF`. cicd #344: Mig 58 applied, backfill 13/13, 2 bundle rotate, select-winner 401 wired.

## (2) Section B 3-cột — Mig 59 `AddCcmBudgetPeriodToPurchaseEvaluation` (`9b1ee0c`, Run #345 cicd-verifying)
- Anh muốn Section B (THỰC HIỆN) tách 3 cột Dự án|PRO|CCM giống Section A. **AskUserQuestion** chốt scope → **"mỗi cột nhập kỳ-này RIÊNG"**.
- BE: `PurchaseEvaluation.CcmBudgetPeriodAmount decimal(18,2)?` + `SetPeCcmBudgetPeriodCommand` (role-gate Admin|CostControl fail-closed, GTE0, absolute-set) + endpoint `PATCH /budget/ccm-period` + DTO. (⚠️ command rơi `PeSuggestedPriceFeatures.cs` — lệch chỗ, dọn monthly.)
- FE: Section B `<BudgetRow>` list → `<table>` 3 cột. Helper `colCalc(period, colFull, remaining)` tính per-cột (PRO: budgetPeriodAmount/proFull · CCM: ccmBudgetPeriodAmount/ccmFull · Dự án —). Số chung row1/2/4/6 `colSpan=3`.
- **Workflow `wf_aafb18fe-d9d`** (anh yêu cầu "review test kỹ"): BE→FE+test→3-lane review. math PASS (traced colCalc no-column-crossing) · authz PASS (setter persist+role-gate, 402 test) · **mirror+FE return NULL** (#53) → em-main self-gate: mirror byte-identical 2 app + 402 test + 2 npm build.
- Test 395→**402** (+7 `PeCcmBudgetPeriodSetterAuthzTests`).

## Lessons
- **Cardinality single→multi = grep MỌI consumer** (`feedback_cardinality_change_grep_consumers`) — impl sửa write-site, sót dependent read-site, vỡ feature vừa thêm; build+test+reviewer PASS vẫn miss (lỗi logic-runtime case multi).
- **Design-dialog tránh rework:** item-4 auto-vs-optin (anh chốt auto qua workflow-config) + Section B scope (AskUserQuestion per-cột-kỳ-này) — hỏi-trước-build chỗ ambiguous-high-rework.
- **Workflow review-lane value dù lane NULL:** 2/5 lane (FE-impl + mirror-review) return NULL, nhưng math+authz lane verify disk độc-lập → trust PASS + em-main self-gate phần NULL. Workflow vẫn hữu-ích cho review (≠ phải mọi-lane-return).
- **#53 truncate dày-đặc session này** — impl-backend, test-spec, impl-frontend ×2, workflow FE+mirror; TẤT CẢ recover-disk OK (git/build/test = source-of-truth). Carry `feedback_agent_kill_recovery` + `feedback_workflow_fanout_reliability`.

## State THẬT cuối S87
**Mig 59 · 88 bảng (cicd flag 89 — drift cũ KHÔNG do Mig 58/59 AddColumn-only) · 402 test (45D+357I) · gotcha 73 · menu 54 · bundle admin `CXCKlNay`/user `Di40ewrF` (Run #344; #345 rotate pending).**

## NEXT
- **Anh:** restart CLI (carry). UAT (Ctrl+F5): multi-winner tick-nhiều + tổng · budget=0 submit · Designer bật "Duyệt là kết thúc" cấp anh Chương · Section B cột CCM nhập kỳ-này (tài khoản CCM).
- **Em:** curate L1 over-cap carry · monthly 2026-07-01 (dọn CCM-setter lệch-chỗ · re-ground sys.tables 88→89 · STATUS/HANDOFF re-tier · ef-core SKILL Mig→59).
- **Pending product:** Section B cột Dự-án wire (chờ anh) · Ngưỡng giá CEO Mig 54 Designer · "C" chuyển phiếu→dự án chờ spec.
