-**Miglatestrepo:****Mig 48 `20260609020759_AddProjectMasterFields`**(S55;AddColumn-only,Project+Year/Investor/Location/Packagenullable,NOnewtable;kèm`SeedRealMasterDataAsync`ungated).PrevMig47FilterMasterCatalog...+46AddSlaFieldsToItTicket.Path`src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/`.Prodcheck`sqlcmd __EFMigrationsHistory ORDER BY MigrationId DESC TOP 5`.⚠️Table-count:`sys.tables`(is_ms_shipped=0)count =**93**(S56Run#379verified,Mig48col-onlynodelta);narrative also93now—reconciled.Don'tFAILon92↔93conventiondiff.
-**Miglatestrepo:****Mig 50 `20260612173224_ReplaceBudgetModuleWithPeWorkItemBudgets`**(S61;DROPSoldBudgetmoduletables+addsPeWorkItemBudgets—schemanet-reduce).PrevMig49`AddWorkItemToPurchaseEvaluation`+48AddProjectMasterFields.Path`src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/`(50mig.csnon-designertotal).Prodcheck`sqlcmd __EFMigrationsHistory ORDER BY MigrationId DESC TOP 5`.⚠️Table-count:`sys.tables`(is_ms_shipped=0,exclmighist) =**88**(S62Run#286verified—S61Budget-replaceDROPPEDtables93→88).Narrative-93isSTALEpre-S61—whencommittouchesnoschema,88iscorrect,don'tFAILon88↔93.Alwayscross-refCOMMITscopevsambientcount.
# Verification anchors that caught/cleared issues in adversarial review
**Rule:** For changes claiming FE-admin/FE-user mirror + a validation relaxation, run these independent checks rather than trusting the spec's self-description.
**Why:** Specs say "2 files byte-identical" and "added allowNegative to ONE field" — both are claims to verify, not facts. The diff context can show pre-existing sibling code (e.g. another `allowNegative` field) that looks like part of the change but isn't.
**How to apply:**
- **Twin-file identity:** `sha256sum` both mirrored files — equal hash proves byte-identical (don't eyeball). S62: both PeDetailTabs.tsx = same sha256.
- **Isolate true additions:** `git diff -U0 -- <file> | grep '^\+' | grep -i <token>` shows ONLY added lines, filtering out unchanged context. S62: spec mentioned `allowNegative` but full-context grep showed 2 occurrences — `-U0` proved only 1 was actually added (the other, `bs.adjustmentAmount` CCM row, was pre-existing and already negative-by-design). Prevented a false "scope bleed" flag.
- **allowNegative bleed:** when a field gains `allowNegative`, confirm sibling currency inputs that must stay positive (e.g. budget input) do NOT have it. S62: row8 has it (1268), row3 budget input (1189) does not. Correct.
- **Guard-still-intact:** when relaxing one validation rule, grep the related submit/transition guard separately and read ±4 lines to confirm it wasn't loosened in the same edit.
description: S62 review PASS — PE "vượt ngân sách" hard-block→soft-warning; validator drop ExpectedRemaining>=0, FE amber banner + allowNegative row8
metadata:
type: project
---
# S62 — PE budget over-spend: hard-block → soft-warning (PASS)
Reviewed 2026-06-13. Anh Kiệt FDC directive: phiếu PE khi giá trị NCC vượt ngân sách → ô "Giá trị thực hiện dự kiến còn lại" (row8) ra âm → bị chặn lưu. Chốt: cho lưu, chỉ cảnh báo mềm.
**Why:** Adversarial review of a small (4-file, ~20 LOC) validation-relaxation change. Verdict PASS.
**How to apply:** When a future change touches PE budget validation or `ExpectedRemainingAmount`, recall these verification anchors:
- BE submit guard `PurchaseEvaluationWorkflowService.cs:198` = `BudgetPeriodAmount is null || <= 0` → adds "chưa nhập Ngân sách kỳ này". This is the budget>0 enforcement; must NOT be loosened when relaxing row8.
- FE mirror of that guard: `PeDetailTabs.tsx:178` (`budgetPeriodAmount == null || <= 0`). BE+FE predicate kept in sync (S61 lineage).
**Negative-safety chain (row8 can now be negative — verified no break):**`row8 = expectedRemainingAmount ?? row7`; `row9 = row4 + row8`; `cmpFull = full - row9`; `cmpPeriod = row3 - row4`. All pure additive — no division by row8, no sqrt, no unsigned cast. BE projection (PurchaseEvaluationFeatures.cs:1038) + CreateContractFromEvaluation clone (line 155) pass the value through with zero arithmetic.
**Precedent:** mirrors LeaveBalance allowing negative balance (cited in code comment as the in-repo precedent for a domain quantity going negative).
description: Tạo/sửa/revert EF Core 10 migration cho SOLUTION_ERP. Dùng khi thêm entity mới, thay đổi schema, rollback migration, debug DesignTimeDbContextFactory fail. Đã có 49 migration sẵn (Init → AddWorkItemToPurchaseEvaluation Mig 49, S57bis). Snapshot + Designer + Migration 3-file rule bắt buộc commit đủ.
description: Tạo/sửa/revert EF Core 10 migration cho SOLUTION_ERP. Dùng khi thêm entity mới, thay đổi schema, rollback migration, debug DesignTimeDbContextFactory fail. Đã có 50 migration sẵn (Init → ReplaceBudgetModuleWithPeWorkItemBudgets Mig 50, S61). Snapshot + Designer + Migration 3-file rule bắt buộc commit đủ.
when-to-use:
- "thêm migration"
- "EF Core migration"
@ -16,7 +16,7 @@ when-to-use:
> **Context:** .NET 10 + EF Core 10 + SQL Server. DbContext: `ApplicationDbContext` ở `Infrastructure/Persistence/`. Startup: `SolutionErp.Api`.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.