From 914c5a40f2cf9f7c92006b5b668732b33f8f2e23 Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Wed, 29 Jul 2026 21:03:45 +0700 Subject: [PATCH] wal: flush 20260729T2103 --- .claude/WAL.md | 3 +- .../agent-memory/test-specialist/MEMORY.md | 2 + .../sub-test-specialist-2.md | 45 +++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 .claude/workflows/runs/2026-07-29-S161-khkk-w2-crud/sub-test-specialist-2.md diff --git a/.claude/WAL.md b/.claude/WAL.md index d73bdd5..9132160 100644 --- a/.claude/WAL.md +++ b/.claude/WAL.md @@ -17,7 +17,8 @@ chain: - [x] **W1 ĐÓNG — cicd VERDICT=PASS 6/6** (Run #427 5m40s · 566 · Mig 69 prod 96 bảng · menu 142 GIỮ · marker 3-tầng · đo-CỜ 13/13/13/2 + control-âm Khkk_G1 + log `0 added + 91 upgraded` khớp dự-đoán · label type-10 VN sống). Synthesis `runs/2026-07-29-S161-khkk-w1-schema/w1-synthesis.md`. Tồn: setup WF tay trước W5 · UAT non-Admin (nv.test KHÔNG tồn tại prod) · 51-bis GẤP (cicd _INDEX 99,2%/167B) · F-4 - [x] gate 5-anchor W2 PASS 5/5 (A4 line-drift content-found; A5 KeHoachKyKet∈All:180 THẬT; phân xử lệch: mã sinh @CREATE per spec, entity-comment W1 sai → worker sửa) - [x] wf W2 đợt-1 `wf_6f0aad26-229`: 2/2 lane chết #53 NHƯNG sub-md cứu trọn (BE 16.5KB — 15 endpoint policy 15/15 + 4 quyết D1-D4 lead duyệt cả 4 · FE 4 file SHA-pair) → lead solo đuôi FE 4-place (WfView 2-app-2-đường + whitelist matrix +10) → **build BE 0W/0E + FE ×2 PASS + coming-soon 0/0**; harvest 2 diary xong -- [!] wf: W2 đợt-2 `wf_7490179a-70b` test-specialist ĐANG CHẠY (2 test PIN + 2-4 test acceptance, sàn ≥568) → về thì: lead suite + reviewer → commit/push → cicd +- [x] wf W2 đợt-2 `wf_7490179a-70b`: test-specialist chết #53 sau khi file LAND — **8 test** (2 PIN + 6 acceptance, gồm không-đốt-mã-khi-409 + fixture-có-interceptor cho soft-delete) → lead suite tươi **574/0** (566+8 ≥ sàn 568); harvest diary xong +- [!] reviewer W2 ĐANG CHẠY nền (trục chính: hợp-đồng FE↔BE đứt-2-bờ [2 lane viết song song] · IDOR D3 · picker-khớp-rào · test-coverage-claim · boundary 0-diff) → PASS thì commit/push → cicd - [ ] đợt-2 SAU khi wf về: test-specialist + database-agent (song song) → lead acceptance §③-B (build·test·sqlcmd 96·Down sạch·đo CỜ) → reviewer → commit/push → cicd-monitor 3-chân-kiềng - [ ] setup thủ công TRƯỚC W5 (SAU W1 vì type=10 cần mig 69): dựng workflow type=3 + type=10 3-trạm qua Designer (seed chỉ 1 trạm) - [ ] housekeeping @pause/closeout kế: nén MIND-1 5.447B (trần `_mind` 83,2% — MIND-5 chắc vượt) · re-carry 2 ý D rơi (vá-GỐC-Phase-3.5-refresh {mới-nêu} CÒN SỐNG + khuôn-5-đợt→sổ bền {gần-chốt}) · STATUS:6 bổ arc dry-run S160 (mù có lý do — land 15:27 trước pipeline) · hạ nhãn DR/WF-3-trạm đã-định-đoạt · E đếm 7 sub-file (không 6) · mở lại slot-45 trần `_mind` (tiền-đề lật: file SỐNG 83,2%) · quyết bookkeeping OVERDUE:light (cặp H24 chạy @S159 bookend mà `last_audit` đứng 27) diff --git a/.claude/agent-memory/test-specialist/MEMORY.md b/.claude/agent-memory/test-specialist/MEMORY.md index ebd8286..71a2008 100644 --- a/.claude/agent-memory/test-specialist/MEMORY.md +++ b/.claude/agent-memory/test-specialist/MEMORY.md @@ -1,5 +1,7 @@ # Test-Specialist Agent — Persistent Memory +- **S161-W2 (07-29) KHKK CRUD 8 test `[hmw wf_7490179a-70b · chết #53 sau khi file LAND (sub-md dừng §0.1) · lead chạy suite tươi 574/0 = 566+8, cả 8 tên đúng]`:** `Application/ContractSigningPlanCrudTests.cs` (7) + extend `Api/AuthorizePolicyRegressionTests.cs` (PIN reflection). 2 PIN spec + 6 acceptance: happy-path Lines=winners + SUM IsSelected đúng NCC (bẫy `w.Id` vs `w.SupplierId`) · second-create 409 **`_AndDoesNotBurnCode`** (mã không đốt khi Conflict — phép đo sequence side-effect) · workflow-guard 2 nhánh Conflict/NotFound · no-winner · **soft-delete-then-recreate + changelog giữ** · owner/phase guard. Phát hiện đắt (sub-md §0.1): **TestApplicationDbContext KHÔNG wire AuditingInterceptor ⇒ `Remove()` = xoá CỨNG ⇒ đo "xoá-tạo-lại" trên xoá-cứng = ĐO NHẦM CƠ CHẾ** (rào 1-PE-1-KH dựa global-filter che row soft-deleted, không phải row biến mất) → dựng fixture riêng CÓ interceptor cho nhóm delete-test. Baseline đo tươi trước khi viết (không tin số đề bài). Tag `[s161-w2, khkk-8-test, khong-dot-ma-khi-409, fixture-interceptor-cho-soft-delete, do-nham-co-che-xoa-cung]` + - **S161 (07-29) W1 KHKK: 4 test schema+codegen `[hmw wf_a95c9909-14b · chết #53 LÚC ĐỢI full-suite (việc thật đã xong) → sub-md 10KB ghi-từng-mục cứu trọn; lead chạy full-suite tươi 566/0 + điền §2/§4 on-behalf]`:** File MỚI `Common/ContractSigningPlanSchemaTests.cs` — 4 test: (1) PIN-spec `BayBang_MoiBang_CoAuditFields_VaQueryFilter` (quét EF model theo NAMESPACE → `BeEquivalentTo` đúng bộ 7 tên [bảng #8 lọt = ĐỎ] + audit-fields + soft-delete assert 2-CHIỀU [BaseEntity phải KHÔNG có IsDeleted] + seed IsDeleted=true chứng filter là BIỂU-THỨC-THẬT); (2) PIN `SigningPlanCodeGenerator_Format_KhkkYearSeqD3` — regex + tuyệt-đối 001→002 + row LastSeq==2 + **đẩy đồng hồ 2027 → KHKK/2027/001 reset** (thiếu vế này `\d{4}` xanh cả khi năm hardcode); (3) #57 xoá-mềm-thêm-lại KHÔNG nổ UNIQUE + vế đối chứng 2-dòng-sống PHẢI DbUpdateException (không có = xanh cả khi index chưa từng tạo); (4) pin slot 10 enum. **Fault-inject 2 chiều trên bảng-kỳ-vọng rồi revert** — chứng `GetDeclaredQueryFilters()` phân biệt 2 thế giới. Learned: (1) **prompt lead ≠ đĩa** — LevelOpinion là AuditableEntity nhưng CỐ Ý KHÔNG filter (lý-do ghi 2 chỗ: site orphan-purge phải thấy row soft-deleted) ⇒ test theo CODE + assert cái-cố-ý thành invariant có răng; (2) EF Core 10: `GetQueryFilter()` obsolete-prone → `(IReadOnlyEntityType).GetDeclaredQueryFilters()`; (3) Serializable trên SQLite = graceful-map, tiền lệ `SqliteDbFixture.cs:36-42` + PE codegen test — KHÔNG chế fixture mới; (4) KHÔNG `Remove()` đo xoá mềm (TestCtx không wire AuditingInterceptor → xoá CỨNG, bài S155). Tag `[s161, w1-4-test, faultinject-2-chieu, test-theo-code-khong-theo-prompt, clock-2027-reset, 53-luc-doi-suite]` - **S155 (07-27) ĐỢT 2 PE xóa-khi-đang-duyệt + màn "Đã xóa" [run 2026-07-27-S155-pe-delete-approver · sub-d2-test-4, diary 13.9KB ghi-trong-lúc-làm]:** +10 `PeDeleteByApproverTests.cs` (T21 lũy kế **4 HƯỚNG KHÁC NHAU** · T22/T22b/T22c màn Đã xóa · T23/T24/T24b cờ+lượt · T25 authz reflection · T25b chiều thuận · T26 changelog). Full **561 = 560 PASS / 1 FAIL** (45D + 516I); 0 hồi quy (Infra cũ 506 nguyên vẹn). **1 FAIL = T26 GIỮ ĐỎ = BUG PROD**: `PeSoftDeleteFeatures.cs` Add changelog (:126) → `Remove(pe)` (:142) → `CascadeDeleteTiming.Immediate` cascade **ngay tại Remove**; dependent đang `Added` bị cascade ⇒ **Detached** (không phải Deleted) ⇒ 0 row; `AuditingInterceptor` chạy ở SavingChanges = SAU điểm mất ⇒ không cứu. Đo bằng ChangeTracker diag (`Added→Detached`, `rowsInDb=0`), KHÔNG suy đoán. Grep cùng-lớp: site DUY NHẤT có hình "Add con → Remove cha → 1 SaveChanges". REPORT lead, KHÔNG tự sửa. **Hạ tầng MỚI:** `SoftDeleteFixture` (ngay trong file test, ~90 LOC) = fixture ĐẦU TIÊN của repo wire `AuditingInterceptor` — BẮT BUỘC khi đo `Remove()`=xóa-mềm, vì `TestApplicationDbContext` KHÔNG wire ⇒ mặc định **XÓA CỨNG** ⇒ màn "Đã xóa" rỗng ⇒ assert đúng ở CẢ HAI thế giới (đúng bẫy vacuous). Số học T21 nhớ kỹ: xóa phiếu ChoDuyet ⇒ PrevSubmitted↓ · PendingSelected↓ · **PrevSelected + PendingSubmitted BẤT ĐỘNG** (seed thêm peer DaDuyet + TraLai để 2 số "không đổi" ≠ 0 ở cả 2 phía). Tag `[s155, pe-delete-by-approver, cascade-detach-added-child, softdelete-fixture, 4-huong]` diff --git a/.claude/workflows/runs/2026-07-29-S161-khkk-w2-crud/sub-test-specialist-2.md b/.claude/workflows/runs/2026-07-29-S161-khkk-w2-crud/sub-test-specialist-2.md new file mode 100644 index 0000000..382eb01 --- /dev/null +++ b/.claude/workflows/runs/2026-07-29-S161-khkk-w2-crud/sub-test-specialist-2.md @@ -0,0 +1,45 @@ +# sub-test-specialist-2 — W2 KHKK: test (đợt-2) + +> Run: `2026-07-29-S161-khkk-w2-crud` · vai: test-specialist · index **2** (index 0/1 = 2 lane implementer đợt-1; KHÔNG đè). +> Luật: ghi TỪNG MỤC ngay sau khi làm xong mục đó (W1 5/7 + W2-đợt-1 2/2 chết #53 — file này là thứ duy nhất sống). +> Ranh giới: CHỈ ghi `tests/**`. Test đỏ vì bug production ⇒ DỪNG + báo, KHÔNG tự sửa. + +--- + +## §0 — BASELINE ĐO TƯƠI (không tin số trong đề bài) + +Lệnh: `dotnet test SolutionErp.slnx --nologo --verbosity minimal -p:BuildInParallel=false -maxcpucount:1` +(HEAD lúc bắt đầu: nhánh `main`, W2-BE + W2-FE đã land trên đĩa.) + +``` +Passed! - Failed: 0, Passed: 45, Skipped: 0, Total: 45 — SolutionErp.Domain.Tests.dll +Passed! - Failed: 0, Passed: 521, Skipped: 0, Total: 521 — SolutionErp.Infrastructure.Tests.dll +``` + +**TRƯỚC = 45 + 521 = 566, 0 fail** — khớp con số đề bài giao (566 = 45D + 521I). Không có drift. + +--- + +## §0.1 — ĐỌC ĐĨA TRƯỚC KHI VIẾT (test theo CODE, không theo prompt — luật S34) + +| Thứ cần biết | Đo được trên đĩa | file:line | +|---|---|---| +| Rào (i) phase PE | `pe.Phase != DaDuyet` → `ConflictException` | `ContractSigningPlanFeatures.cs:312-313` | +| Rào (ii) winner | `winners.Count == 0` → `ConflictException` | `:317-318` | +| Rào (iii) 1-PE-1-KH sống | `AnyAsync(PurchaseEvaluationId == pe.Id && Phase != TuChoi)` → Conflict | `:323-328` | +| Rào (iv) workflow-type | `wfType is null` → **NotFound**; `!= 10` → **Conflict** | `:197-200` (helper `EnsureWorkflowTypeAsync`) | +| Phép tính tiền | `SUM(Quote.ThanhTien WHERE q.PurchaseEvaluationSupplierId == w.Id && q.IsSelected)` | `:366-368` | +| Mồi D4 | `ProposedAmount = peReference` | `:379` | +| D2 drafter | `DrafterUserId = actorId` (currentUser, KHÔNG `pe.DrafterUserId`) | `:353` | +| Mã phiếu | gen TRƯỚC `Add` (codegen tự-commit, bài S88) | `:339` | +| Delete allow-list | `{DangSoanThao, TuChoi}`, owner ∥ Admin, `Remove()` TRƯỚC `Add(changelog)` | `:1115-1126` | +| Codegen format | `KHKK/{yyyy}/{LastSeq:D3}`, bảng dùng chung `WorkflowAppCodeSequences` | `ContractSigningPlanCodeGenerator.cs:18-42` | +| FK cứng duy nhất | `ApprovalWorkflowId` → `ApprovalWorkflows` (Restrict) ⇒ **buộc seed workflow thật** | `ContractSigningPlanConfiguration.cs:39-42` | +| loose-Guid (KHÔNG FK) | `PurchaseEvaluationId / ProjectId / DepartmentId / DrafterUserId` | `ContractSigningPlanConfiguration.cs:30-33` | + +**Hệ quả chọn fixture:** không handler nào của W2 cần `UserManager` ⇒ **không cần `IdentityFixture`**. +NHƯNG `TestApplicationDbContext` KHÔNG wire `AuditingInterceptor` ⇒ `Remove()` = xoá **CỨNG** (bài S155). +Đo "xoá xong tạo lại được" trên xoá-cứng là **đo nhầm cơ chế** (rào iii dựa vào *global query filter* che row soft-deleted, chứ không phải row biến mất). +⇒ dựng fixture riêng có interceptor (xem §2). + +---