All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m27s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
10 KiB
10 KiB
sub-reviewer-0 — adversarial spec review PE lũy kế tạm-tính (S134)
Ghi-đĩa-trong-lúc-làm (chống #53). Append từng issue khi tìm thấy. Return = digest.
Trạng thái
- Đọc spec + investigator
- Đọc code BE (accumulator :29-71 / features :880-1009 / workflow service :280-321, :840-949, :998-1041, :1112, :1128-1155 / seeder :1290-1352 / entity changelog / DTO :331-353)
- Đọc code FE (PeDetailTabs :1290-1600 / PeWorkflowPanel :95-134, :400-539 + SHA tự đo 6 file)
- 8 trục verdict — DONE, xem cuối file
VERDICT: GO_WITH_FIXES (0 Critical / 2 Major spec-fix / 9 nits — Major đều sửa bằng chữ trong spec trước khi hmw chạy)
Issues (append dần)
Trục 3 (D4) — kết quả soi từng nghi vấn
- (a) CLEAR: seeder
DbInitializer.cs:1328-1341set snapshot TRỰC TIẾP (mirror thủ công, KHÔNG gọi helper) → D4 changelog-in-helper KHÔNG gây changelog rác lúc seed/restart. #75-class không xảy ra. - (b) CLEAR: 5 call-site đều là finalize thật (Phase → DaDuyet):
:311admin-override chỉ gọi helper khitargetPhase == DaDuyet;:865level-finalize;:904CCM-miễn-CEO;:937all-steps-done;:1112V1 legacy. Note "Duyệt khi N phiếu trước chưa duyệt" đúng ngữ cảnh cả 5. - (c) SaveChanges: mọi path qua helper đều save — V2/V1 qua
TransitionAsync:301, admin-override:316. OK. - [M1] D4 thiếu shape đầy đủ cho changelog row: helper
ApplyBudgetSnapshotOnFinalizeAsync(evaluation, ct)KHÔNG cóactorUserId.PurchaseEvaluationChangelog(mirrorLogTransitionAsync:1145-1155) cầnEntityType/Action/PhaseAtChange/UserId/UserName/Summary/ContextNote. Spec chỉ nói ContextNote. Nếu hmw tự thêm param actor vào helper → ĐỔI 5 call-site → vi phạm chính acceptance A6 "0 đổi 5 call-site". Spec PHẢI chốt: UserId=null + UserName="Hệ thống" (audit actor tái dựng từ row Transition cùng batch) + EntityType/Action/PhaseAtChange value cụ thể — nếu không hmw sẽ tự quyết 2 hướng ngược nhau. - [N1]
AddAsyncsai pattern codebase:LogTransitionAsync:1145dùngdb.PurchaseEvaluationChangelogs.Add(...)(sync). Spec ghi "AddAsync" — hoạt động nhưng lệch pattern; nên ghi.Add(...). - (d) Chi phí: ComputePendingAsync ~3 query (list + delta1 + delta2), spec ghi "+1 query" là under-count — vẫn chấp nhận được (finalize tần suất thấp). Note.
Trục 2 — construction site
- Grep
new PeBudgetSummaryDtotoàn repo = ĐÚNG 2 siteFeatures.cs:946/:975(khớp spec A2). Session log S74 xác nhận pattern grep này (gotcha #65). Tests KHÔNG construct DTO (chỉ 1 commentPeBudgetFreezeTests.cs:21) → append-cuối không gãy compile chỗ nào. - [M2] Banner PeWorkflowPanel KHÔNG tự ẩn trên phiếu terminal — mâu thuẫn nội tại spec: predicate banner spec item 7 =
pendingPriorPes.length > 0 && !budgetFrozen— KHÔNG có gatereadOnly/phase. Phiếu TraLai/TuChoi/Nháp đi nhánh live (Features.cs:957) → pending fields FILL → banner "cần duyệt phiếu trước trước phiếu này" hiện trên phiếu ĐÃ từ chối/trả lại = sai ngữ cảnh. Edge-6 spec claim "banner duyệt tự ẩn (không còn action duyệt)" — SAI với predicate đã cho (banner không phụ thuộc action; V2-banner :418 có!readOnly, action buttons :439 cónext.length > 0— banner spec không kế thừa gate nào). Fix: thêm!readOnly && evaluation.phase === ChoDuyet(hoặc tối thiểu!readOnly && next.length > 0) vào predicate banner + sửa câu edge-6 cho khớp. Dialog-block đã tự gate quaisApproveAction— không dính.
Verify PASS (positive — chống trượt kép)
- SHA tự đo (git hash-object): PeDetailTabs 2-app IDENTICAL
5534addb9a78...✅ khớp spec; PeWorkflowPanel IDENTICALa83a58e1d8e1...✅; types KHÁC2bf7736≠ab4a2ec✅ (spec errata lead đúng — số investigator cũ47c97df1/9d88e671stale). Measured-label gate PASS. - Trục 1 (chính-xác bất động): ComputeAsync untouched ⇒ mọi consumer bất động — live
:962, snapshot:1018, backfill Mig 67 one-time, seeder direct-set:1334-1340. ComputePendingAsync chỉ ĐỌC (AsNoTracking window y hệt) + fill field MỚI + AddAsync changelog — không mutate field cũ nào. Không có đường lệch số cũ. - Soft-delete peer:
PurchaseEvaluationConfiguration.cs:84HasQueryFilter !IsDeleted ✅ tự áp cho ComputePendingAsync (cùng DbSet). - FE công thức khớp:
row6 = row2 + row4(:1318),r5 = row1 + r3per-cột (:1325) → row5_tạm = r5+delta1, row6_tạm = row6+delta2 đúng đại số. Render sites lũy kế toàn FE = CHỈ PeDetailTabs (:1315-1316/:1498/:1503 + r5 qua colCalc) — grep 2 app không còn site nào khác (không print/export/list) → spec liệt đủ, S89 miss-2-sites không tái diễn. - Trục 4 predicate: delta1 NOT IN (1,7,10,99) = {98, legacy 2-6} đúng (10/7 đã trong row1 chính-xác — không double-count); delta2 NOT IN (1,7,99) ⊃ {10 có winner} = gap chính đề bài ✓ + {98, legacy} có winner hợp lý; pendingPriorPes NOT IN (7,99) ⊇ cả 2 delta-set + nháp list-only (D1) — 3 tập nhất quán. EF translate:
!array.Contains(p.Phase)→ NOT IN, enum non-null → không bẫy null-semantics. - Trục 5 dialog: block chèn TRÊN bộ chọn giá chỉ RENDER thêm —
priceMissing(:514) + footer disabled (:522) không đổi → acceptance B3 "diff vùng disabled = 0" đo được. - D4 mọi path SaveChanges: V2/V1 →
TransitionAsync:301; admin-override →:316. Changelog entity + DbSet có sẵn (IApplicationDbContext.cs:63). - Baseline test 520 khớp (S133 suite 520/0). A5/A6/B1/B2/B3/B4/C1-C9 đều có lệnh đo được.
Trục 6 — acceptance đo được: PASS có 1 gap
- [N5] A3 live-JSON verify cần DATA chưa tồn tại: Dev DB multi-pair (cùng ProjectId+WorkItemId ≥2 phiếu) = 0 (invest PHẦN 5). A3/A4 curl cần dựng 2 phiếu cùng cặp trước — spec nên thêm 1 dòng "tạo 2 phiếu cùng Dự án × Hạng mục qua UI/API trước khi curl" để hmw không bịa acceptance đã-pass.
- B2 "phiếu frozen KHÔNG render (gate đầu nhánh)" hơi code-read-feel — chấp nhận được vì C7 (test DTO defaults) + gate biến grep được; có thể thêm "grep điều kiện
budgetFrozenxuất hiện trong block tạm-tính".
Trục 7 — edges THÊM ngoài 7 edge spec
- Tie CreatedAt bằng nhau: strict
<→ 2 phiếu cùng timestamp không thấy nhau — pre-existing y hệt ComputeAsync (nhất quán, không mới). Không cần fix. - [N2] MaPhieu nullable: entity
string? MaPhieu(:10, gen at createFeatures.cs:158nhưng data cổ/nullable schema). DTO specPePendingPriorPeDto(Guid Id, string MaPhieu, int Phase)non-null → nênstring?+ FE fallback "(chưa có mã)". - [N9] Danh sách pending KHÔNG render link: approver click link phiếu Nháp → 403 S89 guard (
Features.cs:893-898) → toast lỗi khó hiểu. Spec nên chốt "plain text mã + badge, KHÔNG link". - Peer có winner nhưng 0 quote IsSelected: delta2 = 0 nhưng count đếm peer → sub-dòng "+0đ từ 1 phiếu" — mirror y semantics PrevSelectedCount hiện tại (chấp nhận, nhất quán).
- DaDuyet-no-snapshot fallback [N7]:
Features.cs:926-928phiếu DaDuyet mà SnapshotAt null → nhánh live → BudgetFrozen=false → tạm-tính HIỆN trên phiếu DaDuyet dị dạng này. Pre-existing class (nhánh live đã hiện số live cho row1/2 từ S133) — sau backfill Mig 67 + 5 site + seeder về lý thuyết không còn row nào — ghi nhận để không ngạc nhiên UAT, không fix. - Performance: ComputePendingAsync ~3 query (list + delta1 + delta2) mỗi GET detail non-frozen + mỗi finalize — bảng PE nhỏ, peers per cặp ít, chấp nhận. Spec ghi "+1 query" là under-count [N4].
- List dài không cap: thực tế peers/cặp < 10 — không cần cap; nếu muốn phòng thì
.Take(20)— optional.
Trục 8 — mâu thuẫn nội tại / thiếu cho hmw
- M1 (D4 shape) + M2 (banner gate vs edge-6) = 2 mâu thuẫn/thiếu chính — đã ghi trên.
- [N1] Spec ghi
AddAsync— pattern codebase =db.PurchaseEvaluationChangelogs.Add(...)(:1145). Dùng Add cho khớp. - [N3] Spec "map inline nhỏ nếu chưa có map dùng chung" — map ĐÃ CÓ:
PurchaseEvaluationPhaseLabel(types cả 2 app, PeWorkflowPanel đang dùng :465) → chỉ thẳng dùng nó, khỏi inline. - [N6] Cite "mirror pattern LogTransition
:1073-vùng" — thực tếLogTransitionAsyncở:1128-1155. Số dòng lệch (hmw grep ra được nhưng sửa cho sạch). - [N8] Sub-dòng "{n} phiếu chưa duyệt": chốt n = count TƯƠNG ỨNG delta (row1/5 → pendingSubmittedCount; row2/6 → pendingSelectedCount), KHÔNG dùng
pendingPriorPes.length(list ⊇ delta-set, dùng nhầm → "từ 3 phiếu" khi delta chỉ từ 1). - INV-2 [A‖B] song song: DTO field names + JSON shape đã pin cứng trong spec → B không chờ A, race DTO-shape KHÔNG thật. OK.
CHECKLIST-8-TRỤC (verdict per trục)
- Chính-xác bất động: PASS — ComputeAsync untouched + 4 consumer enumerated + ComputePendingAsync read-only.
- R1-S133 frozen: PASS (2 site construction verified duy nhất; DTO append-cuối an toàn; fallback DaDuyet-no-snapshot = pre-existing note N7).
- D4-trong-helper: PASS-WITH-M1 — seeder direct-set (không gọi helper) nên KHÔNG changelog rác; 5 site đều finalize thật + SaveChanges đủ; NHƯNG spec thiếu shape row (actor/EntityType/Action/vị trí trong nhánh wiKey) → M1.
- Predicate mới: PASS — 3 tập nhất quán, không double-count, EF translate OK.
- FE: PASS-WITH-M2 — công thức + vị trí + priceMissing không đụng; banner thiếu gate readOnly/phase → M2.
- Acceptance: PASS — đo được trừ A3 cần seed data (N5).
- Edge sót: 5 edge mới soi — không cái nào block; 3 thành nits (N2/N7/N9).
- Mâu thuẫn/thiếu: 2 Major + 5 nits — đều sửa được bằng chữ trong spec, không cần đổi thiết kế.