[CLAUDE] Docs: S116 closeout — PE negative-quote (STATUS/HANDOFF/session-log + harvest)

- STATUS/HANDOFF prepend S116 (test 486->495 · bundle Dd55jBpj/DVDr1UQ4) + fix
  D1 Tests-row (486->495, 441->450 Infra) + D2 bundle-live-pointer (tooling-auditor
  caught 2 derived-summary lags below the mega-line).
- Session log 2026-07-13-S116-pe-negative-quote.md.
- Harvest: investigator-codebase on-behalf S116 (invest lanes RETURN-only) +
  reviewer/impl-fe/impl-be/test/cicd self-written · monitor diaries (harvest-curator
  self-compact moved-not-cut + tooling-auditor archive-move).
- §5.0 squash 2 wal-trailing (dfa1c8c/0edbc58, post-feature Stop-hook flush) folded here.
- Feature already shipped 88368fd (Run 386 PASS 5m28s); this commit = docs/memory/run-trace
  only -> CI skip (paths-ignore #41).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-07-13 17:40:27 +07:00
parent 88368fd87e
commit 73f2939d8b
11 changed files with 80 additions and 16 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,46 @@
# S116 — PE cho nhập giá ÂM ô "Số tiền báo giá cho hạng mục" (2026-07-13)
**Commit:** `88368fd` · **CI:** Run 386 PASS 5m28s · **State:** Mig 65 · 89 bảng · **495 test** (486→495) · gotcha 81 · bundle admin `Dd55jBpj` / user `DVDr1UQ4`
**Lead:** Opus 4.8 (owner-choice; Fable UP — xác nhận qua fable-real `claude-fable-5`) · HMW-ON · em-main-led
## Đề bài (anh Kiệt FDC ⇄ Duy Nguyen, Zalo)
Ô **"Số tiền báo giá cho hạng mục"** (`form.thanhTien``Quote.ThanhTien`) trong dialog "Thêm NCC vào phiếu" PE **không nhập được số âm**. Yêu cầu cho nhập âm — số âm = **phát-sinh-giảm / hoàn-tiền** của NCC = giá **THẬT** (không phải lỗi). Rule: **ngân sách GIỮ NGUYÊN**; "có giá là được" (≥1 giá thật âm/dương → gửi được; chỉ chưa-nhập-gì mới chặn).
## Pipeline (anh chỉ định: /fable-clone invest-review → /fable-real → hmw Opus MAX → commit+deploy)
| Stage | Workflow | Kết quả |
|---|---|---|
| invest | `wf_eaf52972-d18` 4× investigator-codebase | **FE-ONLY** — BE/DB nhận âm sẵn (0 validator `UpsertQuoteCommand`, `decimal(18,2)` no CHECK); ngân sách safe by architecture (`PeWorkItemBudgets` role-gated, quote-path không đụng) |
| gate | AskUser ×2 (4 decision) | R1 · R2 · R2-revised · R-CEO (dưới) |
| review | `wf_4824f158-b66` 3× reviewer | Lane1 PASS_WITH_FIXES (sign-plumbing) · Lane2/3 PASS |
| deep-pass | `wf_9648d207-21b` 1× reviewer (Fable) | **#53 garble → em-main-solo gate GO-WITH-FIXES** |
| implement | `wf_48834aca-c41` FE‖(BE→test) Opus MAX | 0-garble, 486→495 |
| verify | em-main độc-lập | dotnet test 495 · mirror SHA · BE build |
| deploy | cicd-monitor | Run 386 PASS, byte-level ship-proof |
## Owner-decisions (anh Kiệt chốt)
- **R1** — Contract.GiaTri âm: **CHO PHÉP** (no clamp; `CreateContractFromEvaluationFeatures.cs:88-100` giữ âm; validator tạo-tay `ContractFeatures.cs:46` GiaTri≥0 = đường khác, giữ).
- **R2 (revised)** — submit-guard `Sum()<=0`**`All(x=>x==0)`**. Anh **hỏi lại mid-pipeline**: "1 NCC / 1 hạng mục âm thì có chặn không?" → phát hiện guard cũ **mâu-thuẫn R1** (cho HĐ âm nhưng chặn phiếu tạo ra nó). Guard là **proxy sai** cho ý-định "chưa báo giá" → sửa về đúng ý-định.
- **R-CEO** — ngưỡng CEO: **giữ NET** (`winnerQuoteTotal` net, dòng âm có thể kéo phiếu dưới ngưỡng → CCM finalize bỏ CEO; owner chấp nhận "duyệt theo net cam-kết").
- **R3** — BgVat/ChuaVat giữ nguyên.
## Thay đổi code (FE + 1 BE, KHÔNG migration)
**FE** `PeDetailTabs.tsx` (fe-user + fe-admin byte-identical SHA `3e16dba5`, 112 dòng/app):
- Nút **± toggle** 2 input quote (AddSupplierDialog `:2365` + QuoteDialog `:2917`), ghi giá-**CÓ-DẤU** thẳng vào `form.thanhTien` (2 dialog POST commit trực tiếp, không nút Lưu riêng → nếu chỉ flip cờ `neg` = mất-dấu thầm lặng — review Lane-1 bắt).
- Submit gate `:2226` + toast `>0``!==0`. Ô ngân sách `:2830` giữ ≥0 (không toggle).
- Helper mới `hasRealSelectedQuote` thay 2 FE submit-mirror (`:201` missingForApproval + `:235` submitChecklist) — SUM `computeGiaChaoThau` không phân-biệt all-zero vs net-zero → iterate `ev.details[].quotes` (KHÔNG `suppliers[].quotes` — FE-agent tự sửa spec-shape-error).
**BE** `PurchaseEvaluationWorkflowService.cs:206`: `selectedThanhTien.Sum() <= 0``.All(x => x == 0)` (1 điều-kiện) + stale-comment fix. `Count==0` guard giữ riêng.
**Tests** +9 (486→495): T1 ngân-sách-bất-biến (`PeWorkItemBudgetTests`) · T2 winnerQuoteTotal signed-sum (`PeMultiWinnerTests`) · T3 submit-guard all-zero/pure-neg/net-zero (`PeSubmitGuardAndBypassTests` + helper ext) · T4 CCM-net-threshold (`PeCcmThresholdFinalizeTests`).
## Lessons
- **Owner bắt mâu-thuẫn giữa 2 decision** (R1⇄R2) mà invest+review đều chưa nối — cho dữ-liệu-âm ở đầu-ra nhưng chặn con-đường tạo ra nó. → engine phải nối các quyết-định, không xét độc-lập.
- **Guard là proxy sai:** `Sum()<=0` ≠ ý-định "chưa báo giá" (= `All(==0)`). Sửa về đúng ý-định giải mâu-thuẫn + đúng ngữ-nghĩa.
- **#53-garble ×19 (fable-real, reasoning-lane):** garble ≠ mất-việc. Recover **transcript-forensics** (thấy nó verify CEO-threshold, chưa kịp verdict) → **em-main-solo gate** vẫn có RĂNG (bắt thêm FE mirror `:235`). `parallel()` + schema-nhỏ → 4/5 workflow 0-garble.
- **verify-deployed-byte-level (#77):** cicd curl bundle prod grep feature-string PRESENT (bundle cũ=0) = ship-proof thật, không cache. + bắt bẫy snapshot-while-CI-running → poll-success trước re-snapshot.
- **FE agent verify-on-disk** sửa spec-shape-error (quotes ở `details[]` không `suppliers[]`) — lý do worker phải verify, không tin spec mù.
## Carry NEXT
- Anh UAT incognito (7 điểm → HANDOFF).
- run-trace + agent-memory `.claude/`-gitignored → force-add @closeout.
- PE "lỗi phân quyền vào phiếu" (chờ số-phiếu+user) · BVAAU (`AUTH_ENFORCE_CRM_GATE`+User_ID) · monthly re-tier 2026-08-01.