# S60–S62 (2026-06-12 → 06-13) — PE workflow polish + ngân sách per-gói-thầu (Mig 50, XÓA module Budget) + soft-warning vượt ngân sách > **Closeout muộn (viết bù S63 2026-06-15):** 3 session product S60/S61/S62 ship CODE prod-verified nhưng KHÔNG closeout docs cùng lúc (UAT realtime deadline anh Kiệt FDC — đúng pattern "code committed, docs mù" P1). Drift lộ ở `/session-start` S63 khi `git log` cho thấy docs dừng S59 (`6bf28bf`) còn HEAD = `7926c21` (S62) + Mig 50. Nguồn log: 4 commit `37122f0`→`7926c21` + cicd-monitor MEMORY (Run #286) + reviewer stray reconcile. > > **Driver xuyên suốt:** anh Kiệt (FDC) UAT realtime trên eoffice prod — chuỗi chỉnh PE module theo phản hồi thực tế. (PE module "CÒN CHỈNH NHIỀU" như CLAUDE.md cảnh báo.) **Net state sau S62:** Mig **50** · **88** bảng (was 93 — Mig 50 XÓA module Budget) · **263** test (45 Domain + 218 Infra) · **64** gotcha (#63/#64) · menu **53** (was 57 — gỡ 4 Bg_*) · bundle admin **`0xKYGhhf`** / user **`C81ZdG9G`** (Run #286). --- ## S60 (2026-06-12) — 2 commit: ràng buộc gửi duyệt + gỡ "Từ chối" ### `37122f0` (11:53) — Ràng buộc đủ 4 thông tin mục 3 + bypass người soạn trong chuỗi duyệt - **Rename mục 3** "Chọn NCC / TP thắng thầu" → **"Đơn vị NCC/TP được chọn"** (anh Kiệt chốt chữ) ×2 app + wording phụ nhất quán. - **Guard gửi duyệt đủ CẢ 4** (anh chốt): đơn vị được chọn + giá chào thầu >0 + ngân sách (Budget link HOẶC nhập tay) + bảng so sánh đính kèm. - BE `ConflictException` gộp mọi mục thiếu 1 lần, áp **cả Admin** (`TransitionAsync` submit branch). - FE pre-check `missingForApproval` cùng predicate → disable nút + tooltip liệt kê đủ (`computeGiaChaoThau` extract single-source). - **Bypass drafter-in-chain** (luật GENERIC theo cấp, anh chốt): V2-only, **BƯỚC ĐẦU only** — người soạn là approver cấp k → auto qua Cấp 1..k khi gửi. - Audit 3 tầng: Approval row `AutoApprove` per cấp + LevelOpinion CHỈ slot chính chủ (KHÔNG gắn chữ ký NV bị skip) + Changelog. - Pointer: krow7 + "Chưa chọn" khi count=0 + banner phiếu chưa gắn Hạng mục + ô "Ngân sách kỳ này" ở create/header + **XÓA pages/components/types budgets + routes + menuKeys + Layout staticMap (4-place)**. - **Test:** +22 `PeWorkItemBudgetTests` (auto-create ×3, ensure/race ×2, authz matrix PRO ×5 + CCM ×3, budgetSummary aggregates ×5, adjust ×4) − 14 `BudgetPolicyTests` (xóa theo module) − 1 test via-BudgetId → **263 PASS** (45 Domain + 218 Infra, 0 fail). - **database-agent advise adopted:** không FK vật lý PE/Contracts→Budgets (DropColumn không cần DropForeignKey) + DropIndex TRƯỚC DropColumn (SQL 5074) + IN-list thay LIKE `Bg_%` (underscore wildcard + miss root) + không Serializable wrap (nested-tx conflict codegen). - **Reviewer PASS-with-minor 0 blocker** (verdict-first survived); 2 minor đã sửa trước commit (comment adjustMut absolute-set + dead key budgetId). Note: F4 approver-edit-budget UI entry tạm drafter-only, BE vẫn cho approver scope — chờ UAT anh Kiệt. - **⚠️ Scaffold-bug caught → 2 gotcha NEW:** - **#63** EF tự sinh `RenameColumn(BudgetManualAmount→ExpectedRemainingAmount)` SAI semantics (drop+add cùng type → EF heuristic đoán rename) → thay bằng `AddColumn` + `Sql(UPDATE backfill)` + `DropColumn`. SQLite test (`EnsureCreated` từ model) KHÔNG bắt được. - **#64** `dotnet ef database update` áp **Design DB** (`DesignTimeDbContextFactory`, 0 rows) ≠ runtime Dev DB → `Sql()` backfill CHƯA TỪNG chạy trên data thật trước prod. Guard: cicd brief BẮT BUỘC mục DATA-PRESERVE spot-check sau deploy. --- ## S62 (2026-06-13 11:13) — `7926c21`: vượt ngân sách = cảnh báo mềm (cho lưu số âm row 8) - **Root cause:** ô "Giá trị thực hiện dự kiến còn lại" (row 8 bảng Tổng hợp) khi giá trị NCC vượt ngân sách → số dư còn lại ra ÂM; BE validator `ExpectedRemainingAmount>=0` + FE `VndInlineEdit` không bật `allowNegative` → chặn cứng "âm không lưu được" (testing báo qua anh Kiệt). - **BE:** `AdjustPurchaseEvaluationBudgetCommandValidator` GỠ rule `ExpectedRemainingAmount.GreaterThanOrEqualTo(0)` → cho lưu số âm (mirror tiền lệ LeaveBalance `AllowsNegativeRemaining`). GIỮ `BudgetPeriodAmount>0` + submit-guard "đã nhập NS kỳ này" không đổi. (`PurchaseEvaluationFeatures.cs:317` validator.) - **FE ×2 app SHA256 identical:** (a) `allowNegative` cho VndInlineEdit row 8; (b) banner amber "Vượt ngân sách — vẫn lưu & gửi duyệt được" trong `PeBudgetSummaryTable` khi `cmpPeriod<0 || cmpFull<0`. Tô màu đỏ cũ GIỮ NGUYÊN. - **Spec change:** flip test `AdjustBudget_Validator_ExpectedRemainingNegative_FailsValidation` → `_PassesValidation` (âm giờ hợp lệ); test `BudgetPeriodZero_FailsValidation` GIỮ (budget>0 vẫn enforced). - **Build FE ×2 PASS + test 263 PASS** (45 Domain + 218 Infra, 0 fail/skip). **Reviewer PASS 0 issue** (row8 âm an toàn arithmetic additive-only — row9=row4+row8, cmpFull=full−row9, no division/sqrt/unsigned-cast; submit guard nguyên; mirror byte-identical; no scope creep). - **cicd Run #286** sha `7926c21` PASS ~4m41s — bundle ROTATE admin `DsGZlNzT→0xKYGhhf` + user `DTL_bjzQ→C81ZdG9G` (FE×2 changed, đúng). DATA-PRESERVE spot-check 8/8 phiếu UAT giữ số (gồm phiếu 1.243.820.600 đ anh Kiệt). --- ## Quality + lessons - **3 session deadline-driven KHÔNG closeout docs** = drift S59→S62 (Mig/table/test/gotcha/menu/bundle đều lệch). Bắt được ở S63 `/session-start` qua `git log` (session-log disk + MEMORY count đều lag). **Lesson H1: đầu session luôn `git log` trước, đừng tin MEMORY count.** - **Reviewer cwd-relative mis-land (S62):** reviewer cd `fe-admin/` → Write MEMORY relative-path → 3 file rơi `fe-admin/.claude/agent-memory/reviewer/` (pattern `feedback_agent_cwd_relative_memory_misland` S54). Reconcile S63: MOVE 2 file con vào canonical + MERGE pointer (KHÔNG overwrite 31KB) + xóa stray. - **EF migration data-migrate (gotcha #63/#64):** test xanh + "applied-local OK" ≠ migration đúng — SQLite test dựng từ model (không replay migration), `ef database update` áp Design-DB 0-rows. Guard duy nhất = đọc file migration sau scaffold + cicd DATA-PRESERVE spot-check sau deploy. ## Carry / NEXT (cho session sau) - **test-after guard** (deadline trade-off): `PeWorkItemBudgetTests` đã có; cân nhắc thêm guard `LockDemoSampleUsersAsync` (S58) + suppliers asymmetric authz (S59) — vẫn pending. - **F4 approver-edit-budget UI** tạm drafter-only (BE cho approver scope) — chờ anh Kiệt UAT chốt mở UI cho approver. - **schema-diagram §16+** Mig 32-50 ERD debt (giờ +Mig 50 Budget-drop) — monthly audit 2026-07-01. - **cicd-monitor L1 MEMORY 63.6KB** over-cap lần 5 → curate L2 gấp (H2 flag). - **Bundle/Run** S60/S61 run numbers (#283-#285) chưa truy verbatim — nếu cần, đọc cicd-monitor MEMORY recent entries.