diff --git a/.claude/workflows/runs/2026-07-13-pe-negative-quote/run.md b/.claude/workflows/runs/2026-07-13-pe-negative-quote/run.md index 6be2644..196e674 100644 --- a/.claude/workflows/runs/2026-07-13-pe-negative-quote/run.md +++ b/.claude/workflows/runs/2026-07-13-pe-negative-quote/run.md @@ -10,7 +10,8 @@ - [x] gate — anh Kiệt CHỐT: R1 = CHO HĐ âm (no-op) · R2 = **NỚI guard** `Sum()<=0`→`All(==0)` (anh hỏi lại: 1-NCC-âm phải gửi được, mâu thuẫn R1 → refine). Implement = FE + 1 điều-kiện BE guard + test. - [x] Stage 2 — review (fable-clone 3× reviewer `wf_4824f158-b66`): Lane1 PASS_WITH_FIXES (sign-plumbing baked) · Lane2/3 PASS (mirror clean · budget invariant + #81 safe) - [x] gate — anh Kiệt CHỐT CEO-threshold = NET (no-op, giữ hiện tại) -- [!] Stage 3 — review (fable-real, reviewer deep-pass) trên spec CUỐI +- [x] Stage 3 — review (fable-real `wf_9648d207-21b`) **#53 garble → em-main-solo gate GO-WITH-FIXES** (Fable UP nhưng cut mid-work 0-verdict; em verify R2-FE trên đĩa → bắt thêm mirror `:235` + iterate-quotes) +- [!] Stage 4 — implement (hmw Opus 4.8 MAX: FE + BE + test) - [ ] gate — owner-decisions (nếu invest/review lòi ra) - [ ] Stage 4 — implement (hmw Opus 4.8 MAX) - [ ] Stage 5 — verify (test-before "ngân sách giữ nguyên" + build 2-app) @@ -27,4 +28,5 @@ ## wf run-id - Stage 1 invest: `wf_eaf52972-d18` (task `wslm7eymc`) — 4 lane investigator-codebase, DONE 4/4 clean, HMW-ON, lead Opus 4.8 - Stage 2 review: `wf_4824f158-b66` (task `wjhpvoyum`) — 3 lane reviewer, DONE Lane1 PASS_WITH_FIXES + Lane2/3 PASS -- Stage 3 fable-real: `wf_9648d207-21b` (task `we5wjoxiq`) — single reviewer deep-pass, model:'fable' + effort max (outage→null→fallback Opus) +- Stage 3 fable-real: `wf_9648d207-21b` (task `we5wjoxiq`) — single reviewer deep-pass, model:claude-fable-5 (Fable UP); #53 garble → em-main-solo gate GO-WITH-FIXES +- Stage 4 implement: `wf_48834aca-c41` (task `wf609ys7l`) — FE-2app ‖ (BE-guard→tests), Opus 4.8 MAX effort max diff --git a/.claude/workflows/runs/2026-07-13-pe-negative-quote/spec-pe-negative-quote-13-07-2026.md b/.claude/workflows/runs/2026-07-13-pe-negative-quote/spec-pe-negative-quote-13-07-2026.md index 47570cb..6b60933 100644 --- a/.claude/workflows/runs/2026-07-13-pe-negative-quote/spec-pe-negative-quote-13-07-2026.md +++ b/.claude/workflows/runs/2026-07-13-pe-negative-quote/spec-pe-negative-quote-13-07-2026.md @@ -20,7 +20,7 @@ 3. **Submit gate — AddSupplierDialog** `:2226` `if (detailId && form.thanhTien > 0)` → `!== 0` (âm tạo quote; rỗng/0 vẫn skip = "chưa báo giá"). Toast `:2239` cùng sửa. 4. **KHÔNG đụng** `parseVnd`/`formatVndInput` (dùng-chung ô ngân sách cùng file `:51-52`) + **KHÔNG** truyền allowNegative cho ô ngân-sách `thanhTienNganSach:2830` (giữ ≥0). 5. **Submit-guard R2-revised (BE + FE mirror):** BE `PurchaseEvaluationWorkflowService.cs:206` `selectedThanhTien.Sum() <= 0` → `selectedThanhTien.All(x => x == 0)` (hoặc `!Any(x => x != 0)`). Chặn **CHỈ khi chưa nhập giá nào**; có ≥1 giá thật (âm/dương, kể cả 1 NCC 1 hạng mục âm) → CHO gửi. Message giữ. `Count==0` (chưa chọn NCC) vẫn chặn riêng. - - ⚠️ **FE mirror `:201` (Lane-3 nuance):** hiện là `gia <= 0` với `gia`=winnerQuoteTotal=**SUM** — KHÔNG được giữ sum-based (sẽ chặn net-âm mà BE cho → lệch FE/BE). Đổi sang semantics **"không có giá thật nào"** = `!selectedQuotes.some(q => q.thanhTien !== 0)`. Implementer verify FE có list quote đã-chọn tại `:201` (đang render nên có); nếu chỉ có SUM thì phải lấy thêm list. + - 🔴 **FE = 2 mirror (em-main gate S116, KHÔNG phải 1):** `:201` (`missingForApproval`, `gia <= 0`) VÀ `:235` (`submitChecklist` label "Giá chào thầu > 0"). CẢ HAI dùng `computeGiaChaoThau`→`ev.winnerQuoteTotal`=**SUM** (`:75-80`) — SUM KHÔNG phân-biệt all-zero vs net-zero (+100,−100=0) → phải **iterate list quote đã-chọn** `ev.suppliers[].quotes`, predicate "có ≥1 selected quote `thanhTien !== 0`". Đổi CẢ `:201` (block khi `!hasReal`) + `:235` (ok khi `hasReal`, label → "Đã nhập giá chào thầu"). Net-zero-có-giá-thật = submittable. **Semantics giữ:** rỗng/0 = "chưa báo giá" (skip tạo quote); âm = quote thật (≠ rỗng). `formatVndInput(-n)→''` là lý do PHẢI tách magnitude+dấu (không chỉ nới parseVnd). @@ -43,13 +43,19 @@ **Lane-3 test notes:** T1 (budget unchanged) hơi tautological (0 đường ghi budget từ quote) — giữ regression-lock, bề-mặt rò thật = T2. T3 cần seed ≥2 quote TRÁI DẤU/1 winner → mở rộng helper `SeedWinnerWithQuoteAsync` (`PeSubmitGuardAndBypassTests.cs:106-136`, hiện 1 quote/winner); base `:104` không đủ nửa "mixed net>0 submittable". **✅ Owner-confirm CEO-threshold:** anh Kiệt CHỐT dùng **NET** (giữ hiện tại, no-op) — chấp nhận dòng âm có thể kéo phiếu dưới ngưỡng CEO. +## ✅ Cổng cuối — em-main-solo gate (fable-real #53 garble recovery, S116) +Fable-real deep-pass (`wf_9648d207-21b`, model=`claude-fable-5` — Fable UP) chạy 124K tok/35 tool-call NHƯNG **#53 return-garble** (cut mid-investigation ở CEO-threshold, 0 verdict trong transcript). Per playbook `feedback_agent_return_garble_recover` → **em-main-solo gate:** +- **Verdict: GO-WITH-FIXES.** 7 agent (invest 4 + review 3) hội-tụ sạch; sign-plumbing (Lane-1) baked; R-CEO verified trên đĩa (fable-real transcript đọc đúng `winnerQuoteTotal SUM` `:884-889`). +- **1 FIX em-main gate bắt thêm:** R2-revised FE = **2 mirror** (`:201`+`:235`) iterate-quotes-KHÔNG-sum (bake change #5) — điều fable-real chưa kịp soi. +- Còn lại SOUND → **GO implement.** + ## ③ Checklist (vai · deliverable · acceptance ĐO ĐƯỢC) | # | Vai | Deliverable | Acceptance đo được | |---|---|---|---| | C1 | implementer-frontend | ± toggle + neg-state cho 2 input quote (`:2365`, `:2917`) × 2 app | Nhập `-5.000.000` → ô hiện `-5.000.000` (KHÔNG trắng); reopen quote âm hiện đúng dấu; SHA `fe-user`==`fe-admin` | | C2 | implementer-frontend | Submit gate `:2226` `>0`→`!==0` + toast `:2239` × 2 app | Thêm NCC với quote âm → tạo quote (không bị nuốt im lặng); rỗng/0 vẫn "chỉ tạo NCC" | | C3 | implementer-frontend | Ô ngân-sách `:2830` GIỮ ≥0 (không allowNegative) | Nhập `-` vào ngân sách hạng mục → bị chặn/không nhận | -| C3b | implementer-backend + implementer-frontend | Submit-guard R2-revised: BE `:206` `Sum()<=0`→`All(==0)` + FE mirror `:201` × 2 app | Phiếu **1 NCC 1 hạng mục âm → GỬI được**; phiếu selected toàn 0 → vẫn chặn "chưa báo giá" | +| C3b | implementer-backend + implementer-frontend | Submit-guard R2-revised: BE `:206` `Sum()<=0`→`All(==0)` + **2 FE mirror** `:201`+`:235` (iterate quotes, KHÔNG sum) × 2 app | Phiếu **1 NCC 1 hạng mục âm → GỬI được**; net-zero-có-giá → gửi được; selected toàn 0 → vẫn chặn "chưa báo giá" | | C4 | test-specialist | 4 test regression-lock (ngân-sách-giữ-nguyên + winnerTotal + submit-guard + CCM-threshold) | `dotnet test` xanh; test #1 chứng budget bất-biến khi quote âm | | C5 | reviewer + cicd | build 2-app + deploy | `npm build` 2-app pass; bundle rotate; prod nhập âm được (anh UAT incognito) |