Viet bu docs cho S60/S61/S62 (ship code prod-verified nhung chua closeout — drift bat o /session-start S63 qua git log). - Reconcile stray reviewer cwd-misland: MOVE 2 file con fe-admin/.claude -> canonical + pointer (no overwrite 31KB) + xoa stray - Commit harvest S61/S62: cicd-monitor MEMORY (Run #286) + gotcha #63 (EF RenameColumn sai-semantics) + #64 (Design-DB vs Dev-DB data-migrate) - Count-flush 4 file: Mig 49->50, tables 93->88, test 240->263 (45D+218I), gotcha 62->64, menu 57->53, Budget module REMOVED->PeWorkItemBudgets - Session-log bu 2026-06-12-S60-S62-pe-budget-workitem-softwarning.md. Docs-only -> CI skip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
10 KiB
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-startS63 khigit logcho thấy docs dừng S59 (6bf28bf) còn HEAD =7926c21(S62) + Mig 50. Nguồn log: 4 commit37122f0→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
ConflictExceptiongộp mọi mục thiếu 1 lần, áp cả Admin (TransitionAsyncsubmit branch). - FE pre-check
missingForApprovalcùng predicate → disable nút + tooltip liệt kê đủ (computeGiaChaoThauextract single-source).
- BE
- 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
AutoApproveper cấp + LevelOpinion CHỈ slot chính chủ (KHÔNG gắn chữ ký NV bị skip) + Changelog. - Pointer: k<max → Cấp k+1; hết bước → Bước 2 Cấp 1; workflow 1 bước → terminal DaDuyet. TraLai resubmit áp lại idempotent (opinion UPSERT).
- Audit 3 tầng: Approval row
- Test: +14
PeSubmitGuardAndBypassTests(240 → 254 PASS). ⚠️ Reviewer die mid-run (gotcha #53 class) → em main self-gate evidence-checklist PASS 0 blocker.
6db195d (14:30) — Gỡ hành động "Từ chối" (chỉ còn Duyệt / Trả lại)
- Domain policy: xóa MỌI transition →
TuChoiở cả 4 policy (NccOnly + NccWithPlan + ForV2Schema + FromDefinition) →NextPhaseshết trả TuChoi, nút FE tự biến mất. - Service guard S60: chặn
targetPhase=TuChoimọi caller kể cả Admin (đứng TRƯỚC mọi branch — spec bỏ hẳn, không escape hatch); message hướng dẫn dùng Trả lại / Xóa nháp. - FE ×2 app: filter
next.filter(p != TuChoi)PeWorkflowPanel (SHA256 identical); dialog/isCancel giữ dead-safe để flip lại dễ. - Enum TuChoi + phiếu TuChoi cũ + tab filter "Từ chối" GIỮ display (data cũ render bình thường). SlaExpiryJob chỉ Contract — PE không auto-TuChoi, không ảnh hưởng.
- Test 254 → 256 (59 Domain + 197 Infra). Spec-change tests cùng commit (Domain flip
BothPolicies_TuChoi_Removed...+ NEW V2SchemaPolicy fact; Infra NEWTargetTuChoi_WithRejectDecision_Throws_TuChoiRemoved_S60; guard #45 test cũ giữ PASS).
S61 (2026-06-13 01:07) — 79ef8da Mig 50: ngân sách gói thầu theo Excel anh Kiệt + XÓA module Budget cũ
- Mig 50
ReplaceBudgetModuleWithPeWorkItemBudgets: bảng mớiPeWorkItemBudgets(1 record/cặp Dự án × Hạng mục, UNIQUE filtered[IsDeleted]=0) + drop module Budget cũ + PE/Contracts dropBudgetId+ backfillBudgetManualAmount→BudgetPeriodAmountTRƯỚC DropColumn (phiếu UAT giữ số) + DELETE menu/permissionBg_*IN-list children-first. - BE:
PUT {id}/budget/pro(role Procurement) +{id}/budget/ccm(role CostControl, Adjustment cho phép ÂM) fail-closed Forbidden-TRƯỚC-side-effect +EnsureTrackedAsyncrace-safe (catch unique → re-fetch winner, lỗi khác rethrow) + auto-create record khi tạo phiếu +budgetSummaryDTO (lũy kế trình-trước/chọn-thầu-trước/đề-xuất-kỳ-này + full fallback dự-trù-PRO + canEdit flags) + submit-guard (3) đổi predicateBudgetPeriodAmount→ "chưa nhập Ngân sách kỳ này" + PATCH budget-adjust absolute-set 2 field mới + Contract GIỮBudgetManual*(HĐ nhập tay không đổi) + kế thừa HĐ mapBudgetPeriodAmount. - FE ×2 app SHA256 identical: bảng "TỔNG HỢP NGÂN SÁCH TRÌNH KÝ" — block A (full đầm + ban hành + V0 hiệu chỉnh + dự trù PRO + ghi chú, editable theo
canEditPro/canEditCcm) + block B 9 dòng công thức Excel (5=1+3, 6=2+4, 7=full−5, 8 tự nhập default 7, 9=4+8) + tô màu vượt ngân sách#C00000/ âm đỏ / red-soft row8>row7 + "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) − 14BudgetPolicyTests(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ằngAddColumn+Sql(UPDATE backfill)+DropColumn. SQLite test (EnsureCreatedtừ 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.
- #63 EF tự sinh
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+ FEVndInlineEditkhông bậtallowNegative→ chặn cứng "âm không lưu được" (testing báo qua anh Kiệt). - BE:
AdjustPurchaseEvaluationBudgetCommandValidatorGỠ ruleExpectedRemainingAmount.GreaterThanOrEqualTo(0)→ cho lưu số âm (mirror tiền lệ LeaveBalanceAllowsNegativeRemaining). GIỮBudgetPeriodAmount>0+ submit-guard "đã nhập NS kỳ này" không đổi. (PurchaseEvaluationFeatures.cs:317validator.) - FE ×2 app SHA256 identical: (a)
allowNegativecho VndInlineEdit row 8; (b) banner amber "Vượt ngân sách — vẫn lưu & gửi duyệt được" trongPeBudgetSummaryTablekhicmpPeriod<0 || cmpFull<0. Tô màu đỏ cũ GIỮ NGUYÊN. - Spec change: flip test
AdjustBudget_Validator_ExpectedRemainingNegative_FailsValidation→_PassesValidation(âm giờ hợp lệ); testBudgetPeriodZero_FailsValidationGIỮ (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
7926c21PASS ~4m41s — bundle ROTATE adminDsGZlNzT→0xKYGhhf+ userDTL_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-startquagit log(session-log disk + MEMORY count đều lag). Lesson H1: đầu session luôngit logtrước, đừng tin MEMORY count. - Reviewer cwd-relative mis-land (S62): reviewer cd
fe-admin/→ Write MEMORY relative-path → 3 file rơife-admin/.claude/agent-memory/reviewer/(patternfeedback_agent_cwd_relative_memory_mislandS54). 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 guardLockDemoSampleUsersAsync(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.