# sub-implementer-backend-0 — INV-B pe-budget-freeze-daduyet (S133)

Role: implementer-backend. Task: implement TOÀN BỘ BE theo SPEC ② bước 1-8. T0 test ĐÃ TỒN TẠI + RED → mục tiêu TỰ GREEN, KHÔNG sửa test.

## Anchors verified (re-grep TRƯỚC edit — line trôi nhẹ)
- Domain `PurchaseEvaluation.cs`: props end ~:112 (`EndedByLevelFinalize`). Append 11 ApprovedBudget* props.
- `PurchaseEvaluationConfiguration.cs`: precision block :24-44 (mirror ApprovedPrice*/note pattern). HasQueryFilter(!IsDeleted) :74.
- DTO `PeBudgetSummaryDto` :331-349 (17-field positional record) → append `bool BudgetFrozen = false`.
- Query handler `GetPurchaseEvaluationQueryHandler` :853; budget block :905-973 (pairRec load :911-912; peers/lũy kế :917-943; currentProposal :947-951; fullAmount :957-962; DTO ctor :964-972).
- Workflow service `PurchaseEvaluationWorkflowService.cs` (ctor `IApplicationDbContext db, IDateTime dateTime, ...`). Finalize 4 site: :861 (AllowApproverFinalize) · :899 (CCM-threshold) · :931 (all-steps) · :1053 (V1 legacy — CHỈ budget-snapshot, KHÔNG ApplyApprovedPrice). ApplyApprovedPriceOnFinalize :955.
- Guard 7a `SetPeCcmBudgetPeriodCommandHandler` PeWorkItemBudgetFeatures.cs:228 (sau load+NotFound :230-231).
- Guard 7b `AdjustPurchaseEvaluationBudgetCommandHandler` PurchaseEvaluationFeatures.cs:358 (sau load+NotFound :360-361, TRƯỚC isAdmin block :363).
- Enum: DaDuyet=7, ChoDuyet=10, TraLai=98, TuChoi=99.
- NAMING TRAP: PeWorkItemBudget.InitialAmount/AdjustmentAmount = CCM; ProInitialAmount/ProAdjustmentAmount = PRO.

## Regression scan (tests trong acceptance filter)
- PeWorkItemBudgetTests: MỌI queried-PE ở non-terminal (DangSoanThao/ChoDuyet); DaDuyet chỉ là PEER lũy kế → frozen-gate KHÔNG kích cho queried PE → live(else) branch → behavior-preserving qua accumulator. AdjustBudget tests đều DangSoanThao → guard 7b không fire.
- PeCcmBudgetPeriodSetterAuthzTests: mọi seed Phase=ChoDuyet → guard 7a không fire. (Không trong filter nhưng verify để 0 regression FULL.)
- T0 PeBudgetFreezeTests: dùng UpdatePeBudgetPro/Ccm (KHÔNG guard) → snapshot chốt 100tr/200tr, live đổi 999/888, frozen branch đọc snapshot.

## TransitionAsync KHÔNG SaveChanges nội-bộ (:537 "caller có downstream save")
→ Trong T0, snapshot flush qua UpdatePeBudgetPro/Ccm SaveChanges (shared context, pe tracked). Mirror ApprovedPrice pattern (đã proven).

## Backfill pattern (mirror Mig 61/65)
- Plain `migrationBuilder.Sql()` T-SQL, KHÔNG C# ActiveProvider guard (SQLite tests EnsureCreated no-replay = "guard" kiến-trúc). Idempotent WHERE. Down no-op cho backfill data (nhưng Down MUST DropColumn ×11 cho schema).

## Progress log
(cập nhật trong lúc làm)
