[CLAUDE] Docs: S113 closeout — Supplier import v2 SHIP prod (Run #497) + Mig 64/477 test/gotcha #80

- STATUS/HANDOFF: S113 mega-line + state (Mig 63->64, test 458->477, gotcha 79->80, prod 22->37 NCC, bundle acprMqfB/Ck3BTDdA Run #497)
- gotcha #80: ! null-forgiving tren external-parsed data -> NRE an (build 0/0 khong bat; chi test-before-critical bat)
- ef-core skill: Mig 64 row + pointer-flush (session-start H1 drift, kill recurring latest-Mig class)
- session log 2026-07-12-S113-supplier-import-v2.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-07-12 19:24:49 +07:00
parent 0efc22364b
commit ccdcbe79f0
6 changed files with 71 additions and 11 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,41 @@
# S113 — Supplier import v2 (SHIP PROD, Run #497)
**Date:** 2026-07-12 · **Owner:** anh Kiệt · **Lead:** Opus 4.8 (1M) Max · **Mode:** HMW-ON · **Commit:** `5fa11b5` (feature) + closeout.
## Đề bài (anh Kiệt — 7 yêu cầu R1-R7)
Nâng Supplier Excel-import (S112 Phase B) → v2: R1 đọc đúng file thật · R2 nút "Tải file mẫu" · R3 nhập Mã NCC thủ công sau upload · R4 thiếu Mã NCC→nháp/ẩn · R5 dedup theo MST · R6 không MST&không Mã NCC→không public · R7 badge Public/ẩn.
## Flow (invest → spec → decision → implement → test → review → deploy)
1. **Đọc file thật** `Data base - 1_7_2027.xlsx` (Sheet1, header ROW 4, 30 cột, MST=c12, TÊN VIẾT TẮT=c4→Code, 4 NCC). Phát hiện: file KHÔNG có cột "Mã NCC" → xác nhận R3 = user tự nhập.
2. **`/fable-clone investigator-codebase` invest ensemble** (`wf_e7896ca6-901`, 5 lăng-kính cùng vai): schema · import-service · fe-ux · visibility · dedup. 4/5 structured + lane visibility **#53-garble** → em-main grep recover.
3. **Spec 3-mục** (`runs/2026-07-12-S113-supplier-import-v2-invest/spec-...md`) + trích 30 token header THẬT byte-exact.
4. **Anh Kiệt 3 decision:** **D1=A** (Mã NCC = field Code hiện có) · **D2** (Code=cổng public; MST=cảnh-báo-mềm không chặn; existing-public giữ; import-scope) · **D3** (nới quyền: mọi user có quyền cập-nhật danh-mục = `Suppliers.Update`, không admin-only).
5. **HMW-Opus implement** (BE⟂FE disjoint parallel): implementer-backend + implementer-frontend.
6. **test-specialist** +19 test (477 PASS) → **reviewer** GO-WITH-ADJ (2 must-fix) → deploy.
## Ship
- **Mig 64 `AddSupplierPublishState`:** +IsPublic bit (cổng công-bố) + backfill 37/37 prod→public + filtered-unique đổi `[Code]<>''` (nhiều nháp) + no-new-table (89) + reversible Down.
- **BE:** dedup MST-primary+Code-backstop (chống 500) · re-bake 30-token LayoutValid · `PublishSupplierCommand` riêng (né #73) · `GET /import/template` ClosedXML · authz `Policy=Suppliers.Update`.
- **FE 2-app SHA-mirror:** badge Public/Nháp · nút Công bố · filter · nút Tải-file-mẫu · cột Mã editable · cảnh-báo-MST · picker PE+HĐ lọc `published=true`.
## 4 BUG caught + fixed PRE-PROD (giá trị multi-agent)
1. **#53-garble invest-visibility-lane** → em-main-solo grep recover (identify consumers).
2. **null-Code NRE `NewSupplier` (gotcha #80)** — test-specialist bắt: `Code=r.Code!.Trim()` NRE khi ô cột-4 trống (path R4 lõi, 500 cả batch); `!` che → build 0/0 không thấy. Fix `(r.Code??"").Trim()` + flip characterization-test.
3. **authz-403 Import-nút** — em-main verify-chéo: FE Import `action="Create"` ⧣ BE `Policy=Suppliers.Update` → khớp Update 2-app.
4. **draft-leak-picker + CreateSupplier-quên-IsPublic** — reviewer 2 must-fix: 4 picker (PeDetailTabs+ContractCreate ×2) chưa truyền `published=true` (draft lọt → mã HĐ hỏng) + `CreateSupplierCommand` không set IsPublic → NCC tạo tay thành nháp. Fix cả 2.
## cicd Run #497 PASS (5m14s)
Bundle admin `Cixlt_MA``acprMqfB` (CSS `CfunQHbd`) / user `BhBOG1dn``Ck3BTDdA` (CSS `DGqqHQXS`) rotate ×2 ship-proof · Mig 64 prod (history-top + IsPublic-col + 37/37-backfill + sys.tables 89) · byte-verify `Tải file mẫu`×1/`Đã công bố`×3/`Nháp (ẩn)`×4/`import/template`×1/`published`×5 PRESENT · smoke health/admin/eoffice 200 + `import/template`-401-live + `suppliers?published=true`-401-live + bogus-404-control.
## State
**Mig 64 · 89 bảng · 477 test (45D+432I) · gotcha 80 · menu 54 · bundle admin `acprMqfB`/user `Ck3BTDdA` (Run #497) · prod 37 NCC/17 user.**
## Lessons
- `!` null-forgiving trên external-parsed data = mìn (gotcha #80); build-0/0 không bắt, chỉ test-before-critical bắt.
- Verify-chéo BE⟷FE bắt authz-mismatch (build-pass ≠ contract-match); policy-name là string, compiler không check → verify Program.cs auto-reg.
- Cardinality (S87/S88): đổi visibility = grep MỌI consumer; reviewer bắt FE sót wiring dù spec đã flag (recover-lane).
- #53-garble ×2 → SendMessage-resume in-session finish sạch (1/3 lifetime) + em-main ground-truth qua git/grep (không tin self-review sau garble).
## NEXT
- **anh Kiệt (UAT):** test luồng end-to-end + upload file "Database NCC" ĐẦY ĐỦ (file test chỉ 4 dòng) xác nhận LayoutValid=true.
- **em:** OQ4 open (MST-A-Code-B silent-precedence — anh refine nếu muốn conflict-error) · nice-to-have reviewer (stale-comment service + Mig64-Down-edge + template-roundtrip-test) · monthly re-tier 2026-08-01.

View File

@ -1332,6 +1332,16 @@ for h in resp.points: # ← .points không phải iterable trực tiếp
---
### 80. `!` null-forgiving trên dữ-liệu-parse-ngoài → NRE ẩn (build 0/0 KHÔNG bắt) (Session 113)
**Triệu chứng:** Import Excel NCC v2 dòng thiếu NCC (ô cột-4 trống): preview classify New/draft KHÔNG lỗi + báo "sẽ lưu nháp", nhưng Confirm nổ `NullReferenceException` **500 CẢ batch** (all-or-nothing DB 0 ghi). Đúng path R4 lõi "thiếu NCC nháp" của feature.
**Cơ chế:** `SupplierExcelImportService.NewSupplier` gán `Code = r.Code!.Trim()`. Parser (`ReadRaw`) trả `null` cho ô trống (KHÔNG phải `""`). Toán tử `!` (null-forgiving) TẮT cảnh-báo nullable của compiler **build 0 warn / 0 err** mìn. Sibling `entity.IsPublic = !string.IsNullOrWhiteSpace(row.Code)` null-safe nhưng `NewSupplier` thì KHÔNG. Empty-string `""` chạy OK; chỉ `null` nổ FE round-trip ô blank thành JSON `null` (không phải `""`) reachable đường-thường.
**Fix:** `Code = (r.Code ?? string.Empty).Trim()` null + "" đồng nhất Code="" + IsPublic=false (nháp, ẩn khỏi filtered-unique `[Code]<>''`). **Quy tắc:** `!` null-forgiving trên dữ-liệu-parse-từ-ngoài (Excel/JSON/form-input) = MÌN dùng `?? default` chứ KHÔNG `!`. Build-0/0 KHÔNG bảo-đảm null-safety khi `!` che. Chỉ **test-before-critical** bắt được (test-specialist viết characterization-test reproduce NRE em-main fix prod flip test sang NotThrow+draft). Liên-quan #73 (absolute-set null-clobber).
---
## Checklist debug bug mới
1. Build pass không? fail check using + package version compat