wal: flush 20260812T2215

This commit is contained in:
pqhuy1987
2026-08-12 22:15:40 +07:00
parent 091073f910
commit 2fed7540ad
3 changed files with 149 additions and 10 deletions

View File

@ -45,13 +45,25 @@ bằng 7 HD-* (data-op) + duyệt/history y KHKK (gộp Q1/Q3) + van-xả mirror
[x] WQ5a data-op: seeder QT-HD-V2-001 → IsActive=false từ-lúc-sinh (env mới) + UPDATE Dev
0/0 verified (bền — idempotent-check là existence, né #75). Prod UPDATE cùng câu SQL
@deploy-verify
[!] WQ3-TEST (4 Fact finalize, kỳ vọng 697) WQ3b-FE (panel 3 món ×2 + tái-vị-trí máy
duyệt về panel-3 mọi host). wf: wf_2989df28-eb6 · artifact sub-wq3-test.md + sub-wq3b-fe.md
[ ] Lead: verify-local vòng 2 (layout mới + finalize) → commit WQ* theo cụm →
DEPLOY: dọn workspace runner → push gom → CI → cicd-verify + prod UPDATE Q5a +
STATUS:476 lật → báo anh kiểm
[x] WQ3-TEST GREEN 4/4 (697, probe-vi-phân 3-thế-giới-hỏng) · WQ3b-FE #53-giữa-chừng →
lead hoàn thiện on-behalf (copy-byte content fe-admin + gắn panel 5 host + import) →
tsc/build/eslint ×2 PASS, SHA cặp khớp, content 0-render-panel
[x] COMMIT 2 cụm: 69860362 (BE Mig73+finalize+tắt-seeder+test697) · 59cb1217 (FE layout
KHKK-thuần + 3 món + máy-duyệt-panel-3 ×5 host) · 091073f9 (docs)
[x] VERIFY-LOCAL vòng-2: Mig73 áp Dev sạch · panel-3 sống (meta HD-DV v01 + sơ đồ + 2 nút,
KHÔNG nhân đôi bộ nút — content đã gỡ) · layout KHKK-thuần (1 cột trái Select-loại+
Search+Select-trạng-thái+cây · panel-3 dashed)
[x] DEPLOY: đĩa VPS 4.7→26.6GB (xóa repo-archive 13GB cache + IIS-log cũ — an toàn, #89) →
push f8f2ef96..091073f9 → CI #600 RUNNING đúng sha (gotcha #91 guard: updated_at mới,
app KHÔNG câm) · Q5-prod: QT-HD-V2-001 → 0/0 (2 HĐ pin đều ZZTEST-K8 terminal, an toàn)
⇒ type-3 user-selectable = 7 HD-* đúng "mỗi loại 1 quy trình"
[!] cicd-monitor #600 (agent nền): test-gate 697 · Mig73-prod · bundle rotate ×2 · smoke →
verdict PASS = anh vào kiểm được. wf agent a7b1... artifact sub-cicd-deploy-yc030.md
[ ] NEXT-em @closeout: lật STATUS:476 row Tests 693→697 (chỉ SAU cicd-verify — B1) ·
bật cron.archive_cleanup Gitea (chống #89 tái phát — cần restart, để sau CI xong) ·
sổ YC-029/030 vế-thi-công · session-log · un-delete a79dd579 Dev nếu cần
next: chờ wf_2989df28-eb6 → gate → commit → deploy.
next: chờ cicd-monitor #600 → báo anh verdict deploy.
verify:
commits: git log --oneline -8 # 2ae62f3f ed3b39ff 7b291014 09ebd20e 1cc16a2d + wal

View File

@ -3,14 +3,112 @@
Agent: test-specialist · Run: 2026-08-12-S190-soltuon-dieu-chinh-hd
Ranh: Write CHỈ {ContractApproveFinalizeTests.cs, sub-wq3-test.md}. KHÔNG commit. KHÔNG chạm production code.
File test: `tests/SolutionErp.Infrastructure.Tests/Application/ContractApproveFinalizeTests.cs` (MỚI, 4 `[Fact]`).
## Ca đã viết
(CHƯA VIẾT — đang đọc BƯỚC 1)
Đối tượng đo: `src/Backend/SolutionErp.Infrastructure/Services/ContractWorkflowService.cs:527-535`
(`if (matchingLevel.AllowApproverFinalize && applyLevelFinalize)``EndedByLevelFinalize=true`
`FinalizeToDaPhatHanhAsync:576`). Đây là **test-bù** (mã BE land trước, chưa commit), KHÔNG phải test-before.
**Hình chung của cả 4 ca (điều kiện SỐNG CÒN):** quy trình **2 Bước** — Bước 1 có **2 Cấp**, Bước 2
có 1 Cấp — người bấm Duyệt luôn là **Bước 1 Cấp 1**, tức còn NGUYÊN 2 chặng phía sau.
Bài S164-K3: cắm cờ finalize ở slot CUỐI thì "phát hành + có mã" xảy ra ở CẢ HAI thế giới ⇒ F-1
xanh mà chứng minh 0. Hình này được **chốt bằng assert** `AssertFinalizeSlotIsNotTheLastAsync`
(steps==2 ∧ levels(Bước 1)==2) gọi ngay trước mỗi lượt bấm, để lần "gọn hoá seed" sau này không âm
thầm làm rỗng cả file.
| Ca | Tên `[Fact]` | Biến bật/tắt | Kết quả kỳ vọng |
|---|---|---|---|
| F-1 | `ApproveV2_LevelFinalizeFlag_WithDefaultApply_PublishesContractAndSetsRuntimeFlag` | cờ Cấp `AllowApproverFinalize=true` · **KHÔNG truyền** `applyLevelFinalize` | `Phase=DaPhatHanh` · `EndedByLevelFinalize=true` · `MaHopDong="PFIN1/HĐTP/SOL&FIN1/01"` · con-trỏ Bước/Cấp + SLA `null` |
| F-2 | `ApproveV2_LevelFinalizeFlag_ButApproverOptsOut_AdvancesNormally` | cờ Cấp `true` · `applyLevelFinalize:false` | `Phase=ChoDuyet` · `LevelOrder=2` · `StepIndex=0` · cờ `false` · `MaHopDong=null` |
| F-3 | `ApproveV2_NoLevelFinalizeFlag_EvenWhenApplyRequested_AdvancesNormally` | cờ Cấp `false` · `applyLevelFinalize:true` | như F-2 |
| F-4 | `ApproveV2_AfterLevelFinalize_FurtherApproveThrowsConflict_AndChangesNothing` | finalize xong rồi bấm Duyệt tiếp ×2 người | `ConflictException("*DaPhatHanh*không hỗ trợ*")` ×2 · KHÔNG đổi gì |
### Vì sao ba ca F-1/F-2/F-3 không thay thế nhau (ma trận chứng `&&`)
- F-1 giữ **vế dương** + đo luôn ngữ nghĩa **OPT-OUT** (mặc định `true``IContractWorkflowService.cs:25`)
— cố ý KHÔNG truyền tham số; truyền tay `true` là bỏ mất vế đó.
- F-2 tắt vế **ý người duyệt**; F-3 tắt vế **cấu hình Cấp**. Cài `||` thay `&&` ⇒ ĐỎ cả hai.
Bỏ đọc tham số ⇒ chỉ F-2 đỏ. Bỏ đọc cờ cấu hình ⇒ chỉ F-3 đỏ.
- F-3 còn có assert **tiền-đề** đọc lại `AllowApproverFinalize` từ DB — chốt "biến đang tắt đúng là
biến định tắt", nếu không thì seed sai cờ làm F-3 trùng hệt F-2 mà không ai biết.
### F-4 — assert chọn theo MÃ, không theo mong muốn
Đọc `TransitionAsync` trước khi viết: sau finalize `Phase=DaPhatHanh` ⇒ lượt Duyệt kế tiếp trượt hết
mọi nhánh (reject `:55` / trình `:96` / duyệt `:159` đều đòi phase khác, `isAdmin` false) và rơi
xuống câu cuối `:268` `ConflictException("Transition {from} → {to} không hỗ trợ.")`.
⇒ assert đúng là **409 + không đổi gì**, không phải "im lặng bỏ qua". Hai người bấm, hai lý do đời thực:
(a) chính người vừa chốt bấm lại (double-click), (b) NV Cấp 2 — người bị cắt lượt, màn hình cũ còn nút Duyệt.
Vế "không đổi gì" đo cả `MaHopDong` (không cấp mã lần 2), `ContractApprovals` count==1,
`ContractLevelOpinions` count==1 — một cài đặt "ném lỗi SAU khi đã ghi" vẫn xanh nếu chỉ assert exception.
### 2 quyết định hạ tầng
1. **`IdentityFixture`, KHÔNG dựng fixture riêng có `AuditingInterceptor`** (memory-pack gợi ý mirror
`ContractDeleteGuardTests.cs:69`). Lý do: service đòi `UserManager<User>` THẬT, còn interceptor chỉ
phục vụ ô audit/xoá-mềm — file này không đo ô nào trong số đó. Khuôn đúng là file cùng đối tượng đo:
`Services/ContractWorkflowServiceApproveV2Tests.cs:25` (đã chạy xanh với `ContractCodeGenerator`
+ transaction `Serializable` trên SQLite).
2. Mỗi ca một fixture ⇒ DB SQLite in-memory riêng ⇒ `ContractCodeSequences` không dính nhau, mã HĐ
mong đợi luôn `/01`. `NoOpNotificationService` dùng lại stub `internal`
`Services/PurchaseEvaluationWorkflowServiceGuardTests.cs:188` (không chép bản thứ hai).
## Kết quả chạy
(CHƯA ĐO)
### (1) Lọc — 4/4 PASS
```
dotnet test tests/SolutionErp.Infrastructure.Tests \
--filter FullyQualifiedName~ContractApproveFinalizeTests --nologo -p:BuildInParallel=false -maxcpucount:1
Passed! - Failed: 0, Passed: 4, Skipped: 0, Total: 4, Duration: 3 s
```
### (2) CHỨNG RĂNG — probe VI-PHÂN (sửa TEST, KHÔNG chạm production)
Không được chạm mã BE ⇒ không fault-inject được. Thay bằng: thế-giới-hỏng **tương đương chính xác**
với việc gọi CHÍNH service thật bằng tham số/seed khác ⇒ đổi đúng 1 chữ TRONG TEST, chạy trên mã
production THẬT. Backup file trước, revert sau, `diff` chứng khớp byte.
| Probe (thế giới hỏng mô phỏng) | Đổi | Kết quả |
|---|---|---|
| BE bỏ qua nhánh finalize (luôn advance) | F-1 thêm `applyLevelFinalize:false` | **ĐỎ**`Expected contract.Phase to be DaPhatHanh {9} … but found ChoDuyet {10}` (`:240`) |
| BE bỏ đọc tham số `applyLevelFinalize` | F-2 bỏ `applyLevelFinalize:false` | **ĐỎ**`Expected … ChoDuyet {10} … but found DaPhatHanh {9}` (`:302`) |
| BE dùng `\|\|` thay `&&` (ý người duyệt tự cấp quyền) | F-3 seed `allowFinalizeAtS1L1:true` (+ tạm bỏ assert tiền-đề để nhìn vế KẾT QUẢ đỏ) | **ĐỎ**`Expected … ChoDuyet {10} … but found DaPhatHanh {9}` (`:359`) |
`Failed: 3, Passed: 1` — F-4 giữ xanh đúng như thiết kế (không phụ thuộc 2 biến bị lật).
Revert: `diff backup ↔ file` **RỖNG** (khớp byte) · `grep -c PROBE` = **0**.
🔴 F-4 KHÔNG có probe vi-phân: thế-giới-hỏng của nó ("nhánh duyệt nhận cả HĐ đã phát hành") chỉ dựng
được bằng cách sửa production ⇒ khai thẳng. Răng của F-4 là **nội tại**: `ThrowAsync<ConflictException>`
đỏ nếu không ném, + `WithMessage("*DaPhatHanh*…")` khoá đúng lý do, + count==1 khoá vế "không ghi".
### (3) Full suite — 697/0, khớp đúng dự đoán 693+4
```
dotnet test SolutionErp.slnx --nologo -p:BuildInParallel=false -maxcpucount:1
Passed! - Failed: 0, Passed: 45, Skipped: 0, Total: 45 - SolutionErp.Domain.Tests.dll (59 ms)
Passed! - Failed: 0, Passed: 652, Skipped: 0, Total: 652 - SolutionErp.Infrastructure.Tests.dll (39 s)
tổng = 45 + 652 = 697 (baseline 693 → Δ+4, 0 hồi quy)
```
### (4) Audit ranh giới (isolation)
```
git status --porcelain | grep -E "tests/|sub-wq3-test"
M .claude/workflows/runs/2026-08-12-S190-soltuon-dieu-chinh-hd/sub-wq3-test.md
?? tests/SolutionErp.Infrastructure.Tests/Application/ContractApproveFinalizeTests.cs
```
Đúng 2 file. `src/**` dirty là của lane WQ3-BE (có từ trước lượt này), tôi KHÔNG chạm.
KHÔNG commit · KHÔNG ghi `agent-memory/**` (spawn-record trả về cho em main harvest).
## Ghi chú
(đang đọc)
- **0 bug production phát hiện** — cả 4 ca xanh trên mã BE vừa land, không ca nào phải giữ ĐỎ,
không ca nào phải nới assert.
- **Spec ⟂ code:** không thấy lệch. Cột `Contract.EndedByLevelFinalize` (`Contract.cs:60`, Mig
`20260812141501_AddContractEndedByLevelFinalize`) có write-site DUY NHẤT là nhánh finalize —
F-2/F-3 khoá luôn chiều "không được gắn nhãn kết thúc sớm cho lượt duyệt thường", tức bảo vệ đúng
lý do cột này tồn tại thay vì suy từ config (bài #78).
- **Vùng KHÔNG phủ ở lane này** (khai để lead không đọc nhầm phạm vi):
- `ContractsController.cs:71` `ApplyLevelFinalize: body.ApplyLevelFinalize ?? true` — lớp
controller/DTO; test này gọi service trực tiếp nên chỉ chứng mặc định **ở tầng service**
(`IContractWorkflowService.cs:25`). Hai chỗ đang khớp nhau, nhưng đó là 2 mặc định riêng biệt.
- Ca `skipToFinal=true` **cùng lúc** `applyLevelFinalize=true` (comment `:515-521` khai skip thắng
`return` trước). Không viết: nằm ngoài 4 ca được giao, và đã có `ApproveV2_SkipToFinal_*`
(`ContractWorkflowServiceApproveV2Tests.cs:348`) neo nhánh skip.
- FE panel (lane WQ3b) — ngoài phạm vi.

View File

@ -1,7 +1,36 @@
# WQ3b-FE — port 3 mon KHKK vao panel duyet HD + tai-vi-tri may duyet ve panel-3 (S190 · YC-030)
> Worker #53 chết giữa (68 tool-use, artifact 0 ruột). Trạng thái lúc chết: panel ×2 + types ×2 +
> content fe-user + MyContractsPage-comment ĐÃ sửa; THIẾU copy content fe-admin + gắn panel 5 host.
> **LEAD hoàn thiện on-behalf phần cơ học còn lại** (copy-byte + 5 khối render cùng khuôn) — khai.
## Việc đã làm
## Ô acceptance
1. (worker) `ContractWorkflowPanel.tsx` ×2 app: port 3 món KHKK — ô tích "Cấp này KẾT THÚC quy
trình" (`allowApproverFinalize` 3 hit) + meta `{workflowCode} v{NN}` + banner
`endedByLevelFinalize` (7 hit cụm) + gửi `applyLevelFinalize` trong body Duyệt. SHA cặp
`2edbe534901d` ×2 KHỚP.
2. (worker) `types/contracts.ts` ×2 app (KHÔNG phải cặp SHA — vốn khác nhau by-design, HEAD
`1d4d649f``3d2da3bd`): nở `workflowCode`/`workflowVersion`/`endedByLevelFinalize` +
body `applyLevelFinalize?`/`skipToFinal?` (fe-admin :194-220).
3. (worker) `ContractDetailContent.tsx` fe-user: GỠ khối isV2 render máy duyệt (comment lật :15,
:144 — 0 hit code-only).
4. (lead) copy-byte content → fe-admin: SHA cặp `7ab04d9d939e` ×2 KHỚP.
5. (lead) Gắn `<ContractWorkflowPanel contract={detail.data}>` (vỏ `section border-brand-200`,
điều kiện `approvalWorkflowId != null` — đúng khối cũ) vào **5 host**: MyContractsPage panel-3
(TRÊN WorkflowHistoryPanel — khuôn KhkkListPage) · ContractDetailPage ×2 app (aside) ·
InboxPage fe-user · ContractsListPage fe-admin. Mỗi host +1 import.
## Ô acceptance (lead đo @S190 sau hoàn thiện)
- tsc ×2 EXIT=0 · build ×2 EXIT=0 (`✓ built`) · eslint fe-user **47** / fe-admin **42** = baseline KHÔNG TĂNG
- SHA cặp: panel `2edbe534901d`×2 ✅ · content `7ab04d9d939e`×2 ✅
- Content 0 render panel (grep code-only = 0; 2 hit còn lại là comment lineage) ✅
- 5/5 host `<ContractWorkflowPanel` = 1 mỗi host ✅
- Hành-vi (ô tích finalize gửi body · banner · meta) = DEFERRED-TO-LOCAL-VERIFY vòng-2 của lead
## DEFERRED/DỪNG
- IDE diagnostics never-read/cannot-find giữa chuỗi edit = snapshot dở (#68) — bác bằng tsc EXIT=0 ×2.
- `skipToFinal` có surface body nhưng FE chưa có đường bấm (khai trong types comment :212) — đúng
scope; ô-tích chỉ cho `applyLevelFinalize`.