# S133 — PE freeze ngân sách phiếu DaDuyet (2026-07-17, closeout @S134 `/tiep`)

**Commit:** `d4fbced` (16 file, +7831/−63) PUSHED `9e874ca..d4fbced` · **CI:** Run **#398** VERDICT **GO 5/5** · **Test:** 509→**520** (45D+475I, 0 fail) · **Mig:** 66→**67** · **Bundle:** admin `3NCdyODe`/css `B--dUHcm` · user `CKsoiNCI`/css `BuntB63r` · **Lead:** Fable 5 (owner-choice) · HMW-ON · run-folder `.claude/workflows/runs/2026-07-17-S133-pe-budget-freeze-daduyet/`

## Đề bài (UAT anh Kiệt FDC — chat 2026-07-16 17:24–17:27)

Cập nhật ngân sách (`PeWorkItemBudgets` live, per Dự án × Hạng mục) làm **phiếu ĐÃ DUYỆT đổi số theo** (retro-display). Yêu cầu chốt:
- **R1** — phiếu DaDuyet **bất biến**, không đổi gì hết.
- **R2** — ngân sách đúng nhất cho phiếu mới = bản cập nhật **gần nhất** (live giữ nguyên semantics).
- **R3** — "tức lại database tí là oki": DB delta nhỏ, **không sinh bảng mới** (snapshot AddColumn).

## Giải pháp ship

- **Mig 67 `AddPeApprovedBudgetSnapshot`** — 11 AddColumn snapshot budget-summary lên PE (`ApprovedBudgetSnapshotAt` + `ApprovedBudgetPro{InitialAmount,AdjustmentAmount,Note}` + `ApprovedBudgetCcm{InitialAmount,AdjustmentAmount,Note}` + `ApprovedBudgetPrev{SubmittedTotal,SubmittedCount,SelectedTotal,SelectedCount}`), reversible Down DropColumn ×11, **no new table (89 giữ)**.
- **Chốt tại choke-point:** helper `PeBudgetAccumulator` 4-site (`PurchaseEvaluationFeatures/Service :862/:901/:934/:1109`) — mọi nhánh vào DaDuyet (CEO-final · level-finalize · CCM-threshold) đi qua cùng snapshot logic.
- **Backfill** `Sql()` OUTER APPLY khớp accumulator cho phiếu DaDuyet cũ — prod **18/18, chua=0** (không phiếu nào sót).
- **Display-gate `budgetFrozen`:** phiếu DaDuyet đọc snapshot + badge **🔒 "Ngân sách chốt tại thời điểm duyệt"** tại "Tổng hợp ngân sách trình ký" (FE 2-app SHA-mirror `5534addb`, `PeDetailTabs :1352`); phiếu chưa duyệt đọc live (R2).
- **+2 terminal-guard:** chặn sửa "NS kỳ này (CCM)" / "Điều chỉnh ngân sách" trên phiếu đã kết thúc — lỗi rõ nghĩa, **chặn cả Admin**.

## Pipeline (6 stage — engine-đắt H21 + hmw staged)

1. **`/fable-real invest`** `wf_348f6155-167` — ⚠️ #53 return-fail, **disk sub-file 22.5KB cứu trọn** (`sub-investigator-codebase-0.md`); lead verify 7/7 claim PASS.
2. **Spec + errata** — `spec-pe-budget-freeze-daduyet-17-07-2026.md` (errata: query = `GetPurchaseEvaluationQuery` bundle prop `BudgetSummary`).
3. **hmw implement Opus MAX ×3 invocation staged:**
   - **INV-A** (`wf_d9639faa-97a`, 0-garble): **T0 RED thật** (`Failed 1/1` — found 999tr @`PeBudgetFreezeTests.cs:184`, prod sạch) + FE 4 file badge amber, npm build ×2 PASS.
   - **INV-B** (`wf_4bef08b8-730`, #53 ×2): lane xong 6 file + `PeBudgetAccumulator` — **chết trước mig** → **em-main-solo Mig 67** (scaffold + backfill; apply 2 DB, Dev catch-up Mig 66 lag; build 0/0; filter 44/44 **T0 RED→GREEN**).
   - **INV-C** (`wf_75c6bfd3-754`, #53 lần 3 → disk): +T1-T9 vào `PeBudgetFreezeTests` (9 vị trí `:426`→`:821`); full suite 519/0.
4. **Reviewer pre-commit** (`wf_f9c0b939-181`, return sạch): **PASS_WITH_FIXES 0M/2S/2N** — 2 SHOULD = **#81-EXT indirect-assignment**: admin-override `PurchaseEvaluationWorkflowService.cs:308` (`= targetPhase`) + seeder `DbInitializer:1323` (`= current`) gán Phase thẳng **bypass choke-point** → phiếu vào DaDuyet không snapshot. Lead FIX cả 2 + test `AdminOverride` site-5 + comment `[S133 site 5]` → filter **11/11**, FULL **520/0**, npm ×2 PASS.
5. **Commit + push** `d4fbced`.
6. **cicd-monitor** (`wf_4eadbcf6-c25`, return sạch — verdict về SAU `/pause`, lead harvest-liền): **VERDICT GO 5/5** — Run #398 success gate 520 · Mig 67 prod (history-top + 11 cột + backfill 18/18 chua=0 + 89 bảng) · bundle rotate ×2 (CSS cũng rotate — badge sinh utility-class mới, benign #69) · byte-verify #77 `budgetFrozen`=1 + VN-string 🔒=1, old-control 0 · smoke 4×200 + PE 401.

## Closeout @S134 `/tiep` (phiên kế — S133 `/pause` giữa chờ cicd)

- **Sàn-3:** flag ①（orphan run-folder — chưa synthesis) + ④ (wal-commit chạm `runs/`) — cả 2 trỏ đúng mạch S133 đang dở, nhất quán WAL (không phải sổ-trống-giả-sạch).
- **Re-verify độc lập verdict GO 4/4 KHỚP** (mục 8 WAL dặn spot-check vì harvest-liền chưa re-verify): mig prod top = `20260717032812_AddPeApprovedBudgetSnapshot` (ssh sqlcmd) · health/live 200 · bundle-hash ×2 app khớp byte (curl index.html) · `dotnet test --filter PeBudgetFreeze` **11/11**.
- **Docs closeout:** STATUS (Mig 67 · 520 · Recently-Done S133 · bundle hoist S117→S133) + HANDOFF (segment S133, re-stamp 9 carry, UAT script) + gotchas **#81-EXT** (giữ count 83) + root CLAUDE.md (row PE + lịch sử mig) + session-log này + `close-synthesis.md` đóng orphan.

## ⚠️ #53 events (×3 + 1 overwrite-lesson) — 0 mất việc

- fable-real invest + INV-B ×2 + INV-C: đều **recover từ disk sub-file** (ghi-đĩa-trong-lúc-làm).
- **Lesson MỚI:** 2 invocation cùng role+index chung run → `sub-<role>-0.md` bị **OVERWRITE** (file INV-A bị INV-C đè). Multi-invocation chung run-folder phải đánh index/tên riêng per-invocation.

## Carry

- 🆕 **UAT freeze-NS** `[carry:uat-s133-budget-freeze]` — script 3 bước trong HANDOFF (badge 🔒 / phiếu mới số MỚI / guard sửa-NS lỗi rõ nghĩa).
- **hook-cap bắn ×2 phiên này** (inv-cb 19.7KB · reviewer 20.4KB — đều dưới cap anh 25.6K/30.7K ⇒ em không tự nén theo S122) `[carry:hook-vs-budget-cap]`.
