wal: flush 20260808T0147

This commit is contained in:
pqhuy1987
2026-08-08 01:47:30 +07:00
parent 220ddc8fc8
commit 13b0038f78
6 changed files with 356 additions and 12 deletions

View File

@ -11,13 +11,15 @@ chain:
[x] D3 anh chốt = C "Mờ ô chưa nghe" (YC-014, sổ + owner-decisions-08-08-2026.md) ⇒ W3 HỦY [x] D3 anh chốt = C "Mờ ô chưa nghe" (YC-014, sổ + owner-decisions-08-08-2026.md) ⇒ W3 HỦY
[x] B3 2× fable-real reviewer — R1 PASS_WITH_FIXES 2C/5M (F1-F12) · R2 PASS_WITH_FIXES 1C/5M+7m (§8 FINAL) [x] B3 2× fable-real reviewer — R1 PASS_WITH_FIXES 2C/5M (F1-F12) · R2 PASS_WITH_FIXES 1C/5M+7m (§8 FINAL)
[x] B4 spec FINAL land (spec-khkk-awf-mirror-ncc-08-08-2026.md — merge 2 review, R-1..R-6 + D5/D6 bậc-2 lead-chọn-CÓ) [x] B4 spec FINAL land (spec-khkk-awf-mirror-ncc-08-08-2026.md — merge 2 review, R-1..R-6 + D5/D6 bậc-2 lead-chọn-CÓ)
[!] wf: B5 ĐỢT-1 fan-out Opus 5 MAX — 4 lane: BE(W0+W1 5-fix) ‖ FE(W2 a-h gồm e) ‖ TEST(14 ca) ‖ PRECHECK(W0.4 prod đọc-only) [x] B5 ĐỢT-1 wf_a07b53c8-47e — PRECHECK trả trọn (LỆCH-Q2-nhưng-GO: pin-lệch=0, bom chưa nổ 8/8; T11/D5 có răng THẬT trên prod; #8 Finalize=8/88 CỜ CHỊU LỰC cấm đụng; A2 cần seed local mới có răng) · W0 XONG (baseline 645, mig×2, seed 8 row) · 3 lane code ĐỨT GIỮA (BE ~40% AdminFeatures-only · FE ~85% chưa compile · TEST skeleton)
[ ] B5 ĐỢT-2 (sau đợt-1): full-suite verify → reviewer pre-commit → lead COMMIT MỘT PHÁT (R-1) → push → cicd 4-chân → acceptance prod đọc-only [!] wf: B5 ĐỢT-1bis 3 Agent TIẾP-TỪ-ĐĨA — BE làm nốt Guards+Features+Service+W1d-pin · FE vá compile (import khkk helpers + prop lockedCode signature + nối biến) · TEST viết 14 ca
[ ] B5 ĐỢT-2: full-suite → reviewer pre-commit → lead COMMIT MỘT PHÁT (R-1) → push → cicd 4-chân → acceptance prod đọc-only
wf: run-id=wf_a07b53c8-47e | script=hmw.js scriptPath | trạng-thái=ĐỢT-1-4-lane-đang-chạy | artifact=runs/2026-08-08-S182-khkk-awf-mirror-ncc/sub-{implementer-backend,implementer-frontend,test-specialist,cicd-monitor}-0.md | relaunch=Workflow resumeFromRunId wf_a07b53c8-47e (lane xong = cached; đọc journal.jsonl trước khi tin cached-rỗng) wf: run-id=wf_a07b53c8-47e (đợt-1 đóng) + 3 Agent-lẻ đợt-1bis | trạng-thái=1bis-đang-chạy | artifact=sub-implementer-backend-0.md (Edit tiếp) · sub-implementer-frontend-1.md (Edit tiếp) · sub-test-specialist-2.md (Edit tiếp) | relaunch=đọc 3 artifact + git diff → spawn tiếp phần thiếu; KHÔNG resumeFromRunId (cache-hit vô dụng với lane empty)
next: khi wf_a07b53c8-47e xong → đọc 4 sub-file + return → xử BLOCKED/LỆCH nếu có → đợt-2 (full-suite + reviewer pre-commit + commit 1 phát [CLAUDE] Workflow: ... + push + cicd-monitor 4-chân) next: 3 lane 1bis xong → dotnet build + tsc + npm build ×2 + FULL suite (kỳ vọng 659 = 645+14) → reviewer pre-commit commit 1 phát push cicd 4-chân
lưu ý đợt-2: T8 cần seed phiếu ChoDuyet local (prod 0 mẫu) · A2 chỉ có răng khi seed 1 level cờ=true local · eoffice bundle SẼ rotate (W2e menuKeys fe-user đã chạm)
verify: wc -c runs/2026-08-08-S182-khkk-awf-mirror-ncc/sub-implementer-*.md sub-test-*.md sub-cicd-monitor-0.md (4 file ≥2KB) ; dotnet build exit-0 ; git status (code đổi CHƯA commit đúng, chờ đợt-2) verify: dotnet build exit-0 ; npx tsc --noEmit ×2 exit-0 ; dotnet test = 659/0fail ; git status codefiles đổi CHƯA commit (đúng chờ đợt-2)
treo-chờ-anh (từ bookend @open, KHÔNG chặn YC-013): slot(67) upload-khi-duyệt · VK-5 nhánh A/B · (72) mfe-eval · ring5-audit slot(41) treo-chờ-anh (từ bookend @open, KHÔNG chặn YC-013): slot(67) upload-khi-duyệt · VK-5 nhánh A/B · (72) mfe-eval · ring5-audit slot(41)

View File

@ -27,6 +27,14 @@ xem 3 Bước × Cấp × NV + badge role · **Sửa tại chỗ** (dialog per-N
`sub-invest-be-fable.md` · `sub-invest-db-fable.md` · `sub-invest-fe-fable.md` · `spec-khkk-awf-mirror-ncc-DRAFT.md` · `sub-review-1-fable.md` · `sub-review-2-fable.md` · `spec-khkk-awf-mirror-ncc-08-08-2026.md` · `implement-synthesis.md` `sub-invest-be-fable.md` · `sub-invest-db-fable.md` · `sub-invest-fe-fable.md` · `spec-khkk-awf-mirror-ncc-DRAFT.md` · `sub-review-1-fable.md` · `sub-review-2-fable.md` · `spec-khkk-awf-mirror-ncc-08-08-2026.md` · `implement-synthesis.md`
## Đo nền W0 (R-3 — số đo TƯƠI, lane implementer-backend, 2026-08-08)
`baseline-W0: 645 PASS (45D+600I)` — 0 fail, 0 skip. Lệnh: `dotnet test SolutionErp.slnx` chạy **sau** W0.1/W0.2, HEAD nền `936d1aed` + 0 sửa mã. Domain 45 (407 ms) · Infrastructure 600 (35 s). ⇒ **W1 đạt khi FULL suite = 645 + 14 = 659, 0 fail.**
W0.1 (mig ×2 DB): `SolutionErp_Dev` + `SolutionErp_Design` cùng từ `20260731085624` (Mig 71) → **`20260806070242`** (Mig 72) ✅. *(Khai: local có BA DB — `SolutionErp` base ở `appsettings.json:3` bị `appsettings.Development.json:3` override nên KHÔNG phải DB runtime; lượt đầu tôi trỏ nhầm vào nó, đã chạy lại đúng 2 DB — chi tiết `sub-implementer-backend-0.md §1`.)*
W0.2 (seeder): boot API 1 lần (`ASPNETCORE_ENVIRONMENT=Development`) ⇒ **8 row `KHKK-N1..8`, ApplicableType=10, IsActive=1, IsUserSelectable=1**, mỗi row 3 Bước / 9 Cấp (roster local resolve ĐỦ người). Đã tắt API sau khi seed.
## Chống-#53 (7/7 vai garble @bookend-open phiên này) ## Chống-#53 (7/7 vai garble @bookend-open phiên này)
Mỗi sub **GHI ARTIFACT LIÊN TỤC TRONG LÚC LÀM** vào đúng 1 file `sub-*` của mình; return chỉ cần VERDICT-header + con-trỏ. Return garble ⇒ đĩa cứu. Mỗi sub **GHI ARTIFACT LIÊN TỤC TRONG LÚC LÀM** vào đúng 1 file `sub-*` của mình; return chỉ cần VERDICT-header + con-trỏ. Return garble ⇒ đĩa cứu.

View File

@ -0,0 +1,221 @@
# sub-cicd-monitor-0 — ĐỢT-1 lane PRECHECK (W0.4 prod-state, ĐỌC-ONLY)
> Run: `2026-08-08-S182-khkk-awf-mirror-ncc` · Role: cicd-monitor · Spawn: đợt-1 (W0.4)
> SPEC: `spec-khkk-awf-mirror-ncc-08-08-2026.md` MỤC 2 W0.4 (`:24`) + R-2 (`:10`) + F11 audit.
> 🔴 R-2: PROD chỉ ĐỌC. 0 lệnh ghi (không `scp`, không tạo file remote, không INSERT/UPDATE/DELETE).
> ⚠️ Naming: TASK gọi artifact `sub-cicd-monitor-0.md`; boilerplate RUN-TRACE gọi `sub-cicd-monitor-3.md`.
> Ghi vào **`-0`** theo TASK (cụ thể hơn). Cả 2 đều trong run-folder ⇒ ISOLATION OK.
## Trạng thái ghi (chống-#53 — đổ ruột liên tục)
- [x] S0 connectivity + control dương
- [x] Q1 ApprovalWorkflows type-10 — **KHỚP**
- [x] Q2 ContractSigningPlans GROUP BY ApprovalGroup, Phase — 🔴 **LỆCH**
- [x] Q3 COUNT ContractSigningPlanLevelOpinions — **13** (dồn hết vào N4)
- [x] Q4 audit pin-lệch (F11) — **KHỚP 0 row** (control mạnh)
- [x] BONUS đo thêm: flag-state 88 level + pin/opinion per-Code (de-risk W1d + W2d)
- [x] VERDICT = **LỆCH ở Q2, hệ-quả-chặn-W1 = 0**
---
## S0 — Connectivity + CONTROL DƯƠNG
Lệnh (khuôn S177, 0 file remote — `-Q` inline, KHÔNG `scp`):
`ssh vietreport-vps 'sqlcmd -S .\SQLEXPRESS -d SolutionErp -E -W -s "|" -Q "..."'`
| Phép | Kết quả | Ý nghĩa |
|---|---|---|
| `DB_NAME()` / `SUSER_SNAME()` | `SolutionErp` / `WIN-8M58521HH5D\Administrator` | integrated auth OK, KHÔNG cần password |
| `GETDATE()` srv | `2026-08-08 01:32:45` | đo tươi |
| `COUNT(*) ApprovalWorkflows` | **17** | >0 ⇒ bảng đọc được |
| `COUNT(*) ContractSigningPlans` | **12** | >0 ⇒ bảng đọc được. 🔴 **memory-pack ghi 3 phiếu (001-003) — nay 12** (đã lớn từ S177) |
| `COUNT(*) MenuItems` | **200** | ✅ **khớp ĐÚNG mốc prod đã biết (MEMORY anchor 200)** |
| `COUNT(*) Permissions` | **2221** | ✅ **khớp ĐÚNG mốc prod đã biết (anchor 2221)** |
🔑 **Control dương MẠNH:** 2 mốc độc lập (`MenuItems`=200, `Permissions`=2221) trùng khít anchor đã ghi từ #438 ⇒ thước ĐÚNG DB, ĐÚNG instance, không phải DB rỗng/nhầm. Mọi kết quả `0` phía dưới do đó là **vắng thật**, không phải hỏng thước.
**Tên bảng verify tại chỗ** (`INFORMATION_SCHEMA.TABLES LIKE 'ContractSign%'`) — 7/7 có mặt:
`ContractSigningPlans` · `~Approvals` · `~Attachments` · `~Changelogs` · `~DossierItems` · `~LevelOpinions` · `~Lines`
`ContractSigningPlanLevelOpinions` là tên ĐÚNG (không đoán).
---
## Q1 — `ApprovalWorkflows` ApplicableType=10
Kỳ vọng: 8 row `KHKK-N1..8`, `IsActive=1`. → ✅ **KHỚP 8/8**
| Code | Version | IsActive | IsUserSelectable |
|---|---|---|---|
| KHKK-N1 | 1 | 1 | 1 |
| KHKK-N2 | 1 | 1 | 1 |
| KHKK-N3 | 1 | 1 | 1 |
| KHKK-N4 | 1 | 1 | 1 |
| KHKK-N5 | 1 | 1 | 1 |
| KHKK-N6 | 1 | 1 | 1 |
| KHKK-N7 | 1 | 1 | 1 |
| KHKK-N8 | 1 | 1 | 1 |
🟢 **Bom per-type (R-1/`AdminFeatures.cs:339-343`) CHƯA NỔ** — 8/8 IsActive=1, 0 nhóm Archived-oan ⇒ **không cần recovery-tạo-version-mới sau W2**.
🟢 Cả 8 đều `IsUserSelectable=1`**D6/W2g (FE lọc `isActive`) an toàn**: auto-pin không mất mục tiêu; nút Ghim chưa ai bỏ.
🟢 Cả 8 đều `Version=1` ⇒ chưa ai bấm "Tạo phiên bản mới" trên panel type-10 ở prod (khớp cảnh báo R-1 "bom armed nhưng chưa kích").
**Đối chứng per-type (context cho R-6):** `GROUP BY ApplicableType` toàn bảng —
type 1 = 3 row / **1** active · type 3,4,5,6,7,9 = 1 row / 1 active · **type 10 = 8 row / 8 active**.
⇒ Type-1 đang đúng-hành-vi per-type (3 version, 1 active). Type-10 là ca DUY NHẤT cần per-Code ⇒ khẳng định phạm vi hẹp của W1a là đúng, và **T2 (regression type-1 Code-mới) có dữ liệu thật để soi**.
---
## Q2 — `ContractSigningPlans` GROUP BY ApprovalGroup, Phase
Kỳ vọng (SPEC `:24` + memory-pack): **mọi phiếu Phase=3 (DaDuyet TERMINAL)**.
→ 🔴 **LỆCH: chỉ 3/12 là Phase=3.**
| ApprovalGroup | Phase | n |
|---|---|---|
| 1 | **1** | 5 |
| 1 | **98** | 1 |
| 2 | **1** | 1 |
| 3 | **1** | 1 |
| 4 | 3 | 3 |
| 7 | **1** | 1 |
Tổng = 12 ✅ (khớp `COUNT(*)`=12 ở S0 — 2 phép độc lập cộng khít).
**Enum đọc từ NGUỒN, không đoán**`src/Backend/SolutionErp.Domain/ContractSigningPlans/ContractSigningPlanPhase.cs:10-14`:
`DangSoanThao=1` (Nháp) · `ChoDuyet=2` · `DaDuyet=3` (Terminal) · `TraLai=98` · `TuChoi=99`.
⇒ Giải mã: **8 phiếu Nháp · 1 phiếu Trả-lại · 3 phiếu Đã-duyệt · `ChoDuyet` = 0.**
🔑 **Vì sao memory-pack sai mà KHÔNG phải memory hỏng:** memory ghi *"phiếu 001-003 đều test, Phase=3"***vẫn đúng nguyên văn tới hôm nay** (001/002/003 = đúng 3 phiếu Phase=3, nhóm 4). Cái đổi là **thế giới**: 9 phiếu MỚI (004-012) đẻ ra sau khi memory được ghi. ⇒ Không phải "ký ức sai", mà là **ký ức đóng băng thời điểm** — SPEC `:24` chép lại mốc cũ rồi phổ quát hoá thành "đều Phase=3", đó mới là chỗ gãy.
**Ý nghĩa cho W1b (2 vế TÁCH, cấm nén):**
-**Vế 1 — tiền đề SPEC SAI:****9 phiếu ở phase submit-được** (8 Nháp + 1 TraLai), không phải 0. Chúng SẼ đi qua rào `EnsureWorkflowGroupMatchAsync` mới ở `SubmitAsync`.
-**Vế 2 — hệ quả = 0:** rào chỉ chặn khi pin-lệch, mà pin-lệch **đo được = 0** (Q4, control mạnh). ⇒ **0 phiếu bị kẹt submit sau W1b.** Không cần data-fix, không cần thông báo user.
- Phụ: `ChoDuyet=0`**0 phiếu đang treo giữa workflow** ⇒ W1c (changelog PUT, lọc `Phase != DaDuyet && != TuChoi`) trên prod hiện chạm **9 phiếu** (8 Nháp + 1 TraLai), **không** chạm 3 phiếu N4 đã duyệt. Test T8 cần phiếu `ChoDuyet`**phải seed local, prod không có mẫu**.
---
## Q3 — `COUNT(*) ContractSigningPlanLevelOpinions`
**13** (control dương cùng lượt: `~Approvals`=18 · `~Changelogs`=71 · `~Lines`=20 — 3 bảng anh em đều >0 ⇒ thước sống, 13 là số thật).
Phân bổ theo Code (đo riêng, cross-check khớp 13):
| Code | levels | opinions |
|---|---|---|
| KHKK-N1 | 11 | 0 |
| KHKK-N2 | 11 | 0 |
| KHKK-N3 | 11 | 0 |
| **KHKK-N4** | 11 | **13** |
| KHKK-N5 | 11 | 0 |
| KHKK-N6 | 11 | 0 |
| KHKK-N7 | 11 | 0 |
| KHKK-N8 | 11 | 0 |
⇒ 8 workflow × **11 level** = 88 level, đều nhau. Toàn bộ 13 chữ ký nằm ở N4 (đúng nhóm có 3 phiếu DaDuyet).
---
## Q4 — Audit pin-lệch (F11)
Kỳ vọng: 0 row → ✅ **KHỚP: 0 row.**
🔑 **CONTROL DƯƠNG cho một kết-quả-RỖNG** (bài học `#435` chân-lý-rỗng: `0==0` xanh mà 0 bit): chạy **CHÍNH câu đó bỏ vế `<>`** ⇒ trả **12 row**, liệt kê được từng dòng:
| MaKeHoach | Group | Phase | IsDeleted | pinned_code | khớp? |
|---|---|---|---|---|---|
| KHKK/2026/005 | 1 | 98 | 1 | KHKK-N1 | ✅ |
| KHKK/2026/006 | 1 | 1 | 1 | KHKK-N1 | ✅ |
| KHKK/2026/007 | 1 | 1 | 1 | KHKK-N1 | ✅ |
| KHKK/2026/008 | 1 | 1 | 1 | KHKK-N1 | ✅ |
| KHKK/2026/009 | 1 | 1 | 1 | KHKK-N1 | ✅ |
| KHKK/2026/011 | 1 | 1 | 0 | KHKK-N1 | ✅ |
| KHKK/2026/012 | 2 | 1 | 0 | KHKK-N2 | ✅ |
| KHKK/2026/010 | 3 | 1 | 0 | KHKK-N3 | ✅ |
| KHKK/2026/001 | 4 | 3 | 0 | KHKK-N4 | ✅ |
| KHKK/2026/002 | 4 | 3 | 0 | KHKK-N4 | ✅ |
| KHKK/2026/003 | 4 | 3 | 0 | KHKK-N4 | ✅ |
| KHKK/2026/004 | 7 | 1 | 0 | KHKK-N7 | ✅ |
⇒ 0-row của audit là **VẮNG THẬT**, không phải phép-trích-rỗng. Xác nhận **element-wise 12/12** bằng mắt, không chỉ tin `COUNT=0`.
🔑 **Bịt điểm-mù CẤU TRÚC của chính câu audit F11:** câu spec lọc `w.Code LIKE 'KHKK-N%'`**mù** với phiếu pin vào workflow NGOÀI họ (vd type-10 code lạ `QT-KHKK-V2-001`, hoặc workflow type khác). Bịt bằng **đẳng thức đếm**: `ApprovalWorkflowId IS NOT NULL` = **12** và JOIN-in-family = **12** ⇒ 12=12 ⇒ **0 phiếu pin ngoài họ, 0 phiếu chưa pin.** (Nếu 2 số lệch nhau thì audit F11 sẽ báo "sạch" một cách sai.)
---
## BONUS — 2 phép đo thêm (đọc-only, de-risk W1d + W2d)
### B1. Pin per-Code + soft-delete ⇒ đúng con số 409 mà W1d sẽ in ra
| Code | pins_total | pins_softdeleted | pins_submittable (Phase 1/98) | opinions |
|---|---|---|---|---|
| KHKK-N1 | **6** | **5** | 6 | 0 |
| KHKK-N2 | 1 | 0 | 1 | 0 |
| KHKK-N3 | 1 | 0 | 1 | 0 |
| KHKK-N4 | 3 | 0 | 0 | **13** |
| KHKK-N7 | 1 | 0 | 1 | 0 |
| KHKK-N5 / N6 / N8 | **0** | 0 | 0 | 0 |
- 🔴 **F2/T11 có DỮ LIỆU THẬT trên prod:** `KHKK-N1` bị pin bởi **6 phiếu, trong đó 5 XOÁ-MỀM**. Thiếu `IgnoreQueryFilters()` thì guard W1d(i) sẽ đếm **1** thay vì **6** ⇒ báo sai và (nếu qua được các rào khác) để lọt dangling FK. Đây không còn là ca giả định.
- **W1d(ii)** trên prod: xoá `KHKK-N4`*"3 phiếu đang gắn + 13 chữ ký"*. Xoá `KHKK-N1`*"6 phiếu đang gắn + 0 chữ ký"*.
- 🔴 **D5 có răng THẬT:** `N5/N6/N8` hiện **0 pin + 0 opinion****không có D5 thì 3 nhóm này XOÁ ĐƯỢC từ UI** (rào (i)/(ii) đều cho qua) → đúng ca seeder-tái-sinh M-1. Và vì cả 8 nhóm đều **Version=1 = version CUỐI**, sau D5 **không nhóm nào xoá được** — đúng chủ đích.
### B2. Flag-state THẬT của 88 level type-10 ⇒ nghiệm thu trước cho W2d (D3=C)
| # | Cờ | TRUE/88 |
|---|---|---|
| 1 | `AllowReturnOneLevel` | 0 |
| 2 | `AllowReturnOneStep` | 0 |
| 3 | `AllowReturnToAssignee` | 0 |
| **4** | **`AllowReturnToDrafter`** | **88 (100%)** |
| 5 | `AllowApproverEditDetails` | 0 |
| 6 | `AllowApproverEditBudget` | 0 |
| 7 | `AllowApproverSkipToFinal` | 0 |
| **8** | **`AllowApproverFinalize`** | **8** (= đúng 1/workflow) |
| 9 | `AllowApproverDelete` | 0 |
*(cột DB xác nhận tại chỗ qua `INFORMATION_SCHEMA.COLUMNS` — 9 cờ trên `ApprovalWorkflowLevels` khớp ĐÚNG 9 ô spec W2d liệt.)*
-**6 ô disabled (#1,#2,#3,#5,#6,#9) = 0/88** ⇒ lệnh *"GIỮ `checked` state THẬT, CẤM ép false"* render ra 6 ô **bỏ-tick + mờ**. Không có ca "mờ mà đang tick" gây rối mắt. Ép-false hay giữ-thật **hôm nay nhìn y hệt nhau** ⇒ 🔴 acceptance A2 **KHÔNG phân biệt được 2 cách làm trên dữ liệu prod**; muốn có răng phải **seed 1 level cờ=true ở LOCAL** rồi soi. (Ca kinh điển "chân-lý-rỗng" — báo trước để W2 không tự nghiệm-thu hớ.)
-**#4 = 88/88** ⇒ spec nói *"thường TRUE"***nói nhẹ**: thực tế **100%**. Chốt `forced-true-disabled + tooltip riêng` là đúng, và ô này sẽ hiện **tick** ở mọi level.
-**#8 = 8/88, đúng 1 mỗi workflow** ⇒ *"Finalize SỐNG"* không phải phòng xa: đây là **cờ chịu lực** (kết-thúc-tại-cấp) của cả 8 nhóm. Nếu W2 lỡ disable/ép-false #8**gãy hành vi finalize cả 8 nhóm prod**. Đây là ô nguy hiểm nhất trong 9 ô.
-**#7 = 0/88** ⇒ "GIỮ NGUYÊN" `:1292` là no-op trên dữ liệu hiện tại.
---
## VERDICT
### `LỆCH` — lệch ĐÚNG 1 chỗ (Q2), và chỗ đó **KHÔNG chặn W1**
| Q | Kỳ vọng | Đo được | Kết |
|---|---|---|---|
| Q1 | 8 row KHKK-N1..8 IsActive=1 | 8/8 IsActive=1, IsUserSelectable=1, Version=1 | ✅ **KHỚP** |
| Q2 | mọi phiếu Phase=3 | 3 DaDuyet · 8 Nháp · 1 TraLai · **0 ChoDuyet** | 🔴 **LỆCH** |
| Q3 | (không nêu) | **13**, dồn hết vào KHKK-N4 | ghi nhận |
| Q4 | 0 row pin-lệch | **0 row** + control 12/12 khớp element-wise | ✅ **KHỚP** |
**Kết luận cho lead — 3 câu:**
1. 🟢 **Bom per-type CHƯA NỔ.** 8/8 nhóm `IsActive=1`, `Version=1` ⇒ chưa ai bấm "Tạo phiên bản mới" trên panel type-10 prod. **Không cần recovery sau W2.** Nhưng bom vẫn **armed** ⇒ R-1 (cấm commit W2 thiếu W1a) giữ nguyên hiệu lực.
2. 🔴 **Tiền đề "phiếu đều Phase=3" của SPEC `:24` SAI** — thực tế 9/12 phiếu đang ở phase submit-được. **Nhưng** vì pin-lệch = 0 (đo, có control), **0 phiếu sẽ kẹt submit sau W1b**. Đề nghị: sửa câu `:24` cho khớp thực tế, **không** đổi thiết kế W1b.
3. 🟢 **W1 đi tiếp được.** Không phát hiện gì cần migration (R-4 an toàn), không cần data-fix prod.
**Cảnh báo mang sang wave sau:**
- W1d **BẮT BUỘC** `IgnoreQueryFilters()` — prod có **5 phiếu xoá-mềm** pin `KHKK-N1`; thiếu nó guard đếm 1 thay vì 6 (F2 = ca THẬT, không giả định).
- W2 acceptance A2 **không có răng trên dữ liệu prod** (6 ô đều false sẵn ⇒ ép-false và giữ-thật nhìn giống hệt). Muốn chứng thì **seed local 1 level cờ=true**.
- **#8 `AllowApproverFinalize` = ô chịu lực** (8/8 workflow đang dùng) — tuyệt đối không đụng.
- T8 (changelog phiếu `ChoDuyet`) **không có mẫu trên prod** (ChoDuyet=0) ⇒ phải seed local.
**R-2 tuân thủ:** 8 lệnh `ssh`, **100% `SELECT`**. 0 `INSERT/UPDATE/DELETE`, 0 `scp`, 0 file tạo trên VPS (dùng `-Q` inline, không `-i`). 0 lệnh chạm CI/deploy.
---
## PHỤ LỤC — ảnh chụp GIỮA CHỪNG lane W1 (⚠️ KHÔNG phải review)
⚠️ **Caveat mạnh:** lane `implementer-backend` đang chạy SONG SONG với tao (`git status` lúc 01:37 cho ` M ApprovalWorkflowV2AdminFeatures.cs`). Đây là **snapshot giữa chừng**, KHÔNG phải phán quyết — theo bài học *stale-diagnostic-in-background-agent* (#68), chỉ tin bản đo chạy SAU khi lane đóng. Ghi lại vì phát hiện B1 là **đầu vào trực tiếp** cho đúng đoạn code đang được viết ngay lúc này.
- ✅ Lane **đã ý thức** `IgnoreQueryFilters()`: **18 lần** trong file, có cả comment 🔴 `:991` tuyên "LÀ BẮT BUỘC, không phải trang trí", và phủ **8/8 bảng `*LevelOpinions`** (`:1000-1050`) + 8 nhánh orphan-check (`:1089-1118`), gồm `ContractSigningPlans` (`:1118`).
- ⚠️ **Nhánh W1d(i) — đếm PIN — chưa thấy trong ảnh chụp này.** `grep "ApprovalWorkflowId == def.Id"` chỉ ra **1 hit `:885`** (đó là khối changelog W1c), và `:1206` vẫn còn comment CŨ *"Sau UAT khi link với PE/Contract thật cần check usage trước khi delete."* ⇒ tại thời điểm chụp, rào đếm-pin có vẻ **chưa land** (hoặc đang gõ dở).
-**B1 vẫn còn hiệu lực và cần tới tay người viết W1d(i):** `KHKK-N1` bị pin bởi **6 phiếu, 5 trong đó XOÁ-MỀM**. Đếm-pin mà thiếu `IgnoreQueryFilters()` ⇒ ra **1** thay vì **6**. (Nhánh opinions đã đúng rồi — chỗ cần soi là nhánh PIN, không phải nhánh opinions.)
- Đề nghị lead: chuyển B1 sang lane BE, và để reviewer đo lại SAU khi lane đóng — **không** lấy phụ lục này thay cho review.
**END sub-cicd-monitor-0 — VERDICT = LỆCH (Q2 premise) · W1 GO · 0 blocker**

View File

@ -14,7 +14,30 @@
- W1d DELETE guard (`:1193-1205`) - W1d DELETE guard (`:1193-1205`)
## 1. W0 — prep + đo nền ## 1. W0 — prep + đo nền
_(đang chạy)_
### W0.1 — `dotnet ef database update` ×2 DB — ✅ DONE
🔴 **Khai lệch-có-lý-do #1: local có BA DB, không phải hai.** Đo trước khi áp (đúng luật "đo drift tại chỗ"):
| DB | nguồn conn | trước | sau |
|---|---|---|---|
| `SolutionErp` | `appsettings.json:3` Default (**base — KHÔNG dùng ở dev**, bị `appsettings.Development.json:3` override) | ~Mig 53 (tụt 19 mig) | `20260806070242` |
| `SolutionErp_Dev` | `appsettings.Development.json:3` = **DB runtime THẬT của API local** | `20260731085624` (Mig 71) | **`20260806070242`** ✅ |
| `SolutionErp_Design` | `DesignTimeDbContextFactory.cs:15` (ef CLI mặc định) | `20260731085624` (Mig 71) | **`20260806070242`** ✅ |
- Spec nói "Dev + Design"; memory-pack Pattern-2 định nghĩa Dev = `SolutionErp_Dev` (explicit conn) ⇒ **2 DB spec đòi = `_Dev` + `_Design`, cả 2 ĐÃ lên Mig 72.**
- Lượt chạy ĐẦU tôi trỏ nhầm `--connection` vào `SolutionErp` (base). **Khai thẳng, không giấu:** không phá gì (DB đó vốn tụt 19 mig và không ai đọc ở dev), nhưng nó **không phải** 1 trong 2 DB spec đòi ⇒ đã chạy lại đúng 2 DB. Nếu chỉ nhìn lượt đầu mà tuyên "xong W0.1" thì đó là **PASS giả**.
- Lệnh đo: `sqlcmd -S "(localdb)\MSSQLLocalDB" -d <db> -Q "SELECT TOP 1 MigrationId FROM __EFMigrationsHistory ORDER BY MigrationId DESC"`
### W0.2 — boot API → 8 row `KHKK-N1..8` — ✅ PASS
- Trước boot: `SELECT ... WHERE Code LIKE 'KHKK-N%'` trên `SolutionErp_Dev` = **0 row** (control âm — thước sống).
- Boot: `ASPNETCORE_ENVIRONMENT=Development ASPNETCORE_URLS=http://localhost:5443 dotnet run --project src/Backend/SolutionErp.Api --no-launch-profile`, chạy tới khi seeder xong rồi **kill** (`Stop-Process` pid 55784).
- Sau boot: **8/8** row `KHKK-N1..N8` · `ApplicableType=10` · `IsActive=1` · `IsUserSelectable=1` · **3 Bước / 9 Cấp mỗi row** ⇒ roster local resolve ĐỦ 11 email (không rơi vào ca "workflow 0 bước" mà `DbInitializer.cs:544-548` cho phép).
- Seeder: `SeedKhkkGroupWorkflowsAsync` `DbInitializer.cs:549`, idempotent PER-CODE `:610` `if (await db.ApprovalWorkflows.AnyAsync(w => w.Code == code)) continue;`**KHÔNG đụng** (memory-pack cấm).
### W0.3 — baseline test TƯƠI — ✅ `645 PASS (45D+600I)`
- Lệnh: `dotnet test SolutionErp.slnx`, chạy SAU W0.1/W0.2, **0 sửa mã** tại thời điểm đo.
- Domain 45 / 407 ms · Infrastructure 600 / 35 s · **Failed 0 · Skipped 0**.
- Ghi `run.md` mục "Đo nền W0" (R-3). Trùng số lead đo @bootstrap (645) nhưng **đo lại độc lập**, không chép.
- ⇒ Cửa W1/W4: FULL suite = **645 + 14 = 659**, 0 fail.
## 2. W1 — 5 fix ## 2. W1 — 5 fix
_(chưa bắt đầu)_ _(chưa bắt đầu)_

View File

@ -18,10 +18,82 @@
- [ ] build fe-user - [ ] build fe-user
## §1 — Đo tiền-đề từ đĩa (TRƯỚC khi gõ) ## §1 — Đo tiền-đề từ đĩa (TRƯỚC khi gõ)
(đang đo)
## §2 — Thay đổi từng task | # | Tiền-đề spec | Đo trên đĩa | Verdict |
(đang làm) |---|---|---|---|
| P1 | `:164-172` TYPE_CODE_TO_INT | `ApprovalWorkflowsV2Page.tsx:164-172` ĐÚNG (`TYPE_INT_CONTRACT_SIGNING_PLAN=10` `:149`) | HELD |
| P2 | `:299-301` currentType | `:299-301` `overview.data?.types.find(...)` ĐÚNG | HELD |
| P3 | `:357/:620/:650-651/:798/:920-933` | `TypePanel :357` · `Designer :620` · `defaultCode :650-651` · POST code `:798` · ô Mã `:920-933` — ĐÚNG cả 5 | HELD |
| P4 | 6 ô D3 `:1235-1266`, `:1268-1276`, `:1277-1285`, `:1320-1328` | #1 `:1232-1240` · #2 `:1241-1249` · #3 `:1250-1258` · #4 `:1259-1267` · #5 `:1268-1276` · #6 `:1277-1285` · #7 `:1292-1302` · #8 `:1307-1315` · #9 `:1320-1328` | HELD (số ô #1-#4 lệch ±3 dòng so spec, khớp nội dung) |
| P5 | `:944-961` ngưỡng CEO | `:943-961` ĐÚNG | HELD |
| P6 | `menuKeys.ts:31-32` = 2 const `AwV2_*` | fe-admin `:31-32` + fe-user `:31-32` ĐÚNG, 0 key KhkkN | HELD |
| P7 | BE const để mirror | `MenuKeys.cs:127-134` `AwV2_KhkkN1..8` (VÀO `All`) | HELD |
| P8 | 4-place Layout staticMap | `fe-admin/Layout.tsx:188` `/^KhkkN[1-8]$/` ĐÃ CÓ (K3 S164/S165) ⇒ **chỗ-4 KHÔNG cần chạm** | HELD |
| P9 | `khkkGroupWorkflowCode` / `khkkGroupMenuLabel` | `types/khkk.ts:159-168`, 2 app SHA identical `b26bf674…` | HELD |
| P10 | 🔴 spec W2g *"2 file KhkkCreatePage vốn khác nhau — không cần byte-equal"* | **SAI**: `sha256sum` 2 file = `332986ae6bf1904a4db434c796164caa2768beaaebdc7843dd2b1cdd729d17da` **BẰNG NHAU** + `diff` 0 dòng | **LỆCH** → sửa mirror BYTE-EQUAL, giữ SHA-pair |
| P11 | `WorkflowDto` (local) có `isActive`? | `KhkkCreatePage.tsx:57-62` = `{id, code, name, isUserSelectable}`**THIẾU `isActive`** | phải thêm field (BE có: `DefinitionDto.isActive :79`) |
| P12 | 🔴 spec W2h *"thêm ~1 prop `subtitle`"* | `components/PageHeader.tsx:3-11` chỉ có `title/description/actions` (bài S37 lặp lần 3) | **LỆCH** → xem §4 FLAG-1 |
| P13 | build = phép thử TS thật | `package.json` build = `tsc -b && vite build`, `noUnusedLocals`+`noUnusedParameters`+`erasableSyntaxOnly` ON | HELD |
## §2 — Thay đổi từng task (đã gõ xong, chờ build)
**File chạm (4):**
1. `fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx` (a,b,c,d,f,h)
2. `fe-admin/src/lib/menuKeys.ts` (e)
3. `fe-user/src/lib/menuKeys.ts` (e)
4. `fe-admin/src/pages/khkk/KhkkCreatePage.tsx` + `fe-user/…` (g, `cp` → SHA-pair)
### W2a — parse kind
- `khkkGroupMatch = typeCode?.match(/^KhkkN([1-8])$/) ?? null``khkkGroup: number|null`
- `selectedTypeInt = khkkGroup !== null ? TYPE_INT_CONTRACT_SIGNING_PLAN : typeCode ? TYPE_CODE_TO_INT[typeCode] : null`
- queryKey GIỮ NGUYÊN (`['approval-workflow-v2-overview', selectedTypeInt]`) ⇒ 8 panel nhóm + panel type-10 **dùng chung 1 ô cache**, invalidate làm tươi cả 9. Khai trong comment.
- Regex CHUỖI (không `Number()`) — `KhkkN03` không lọt (bài học L1 #11).
- +import `{ khkkGroupMenuLabel, khkkGroupWorkflowCode } from '@/types/khkk'` (không chép chuỗi `KHKK-N`).
### W2b — synthetic slice
- Tách `typeBucket` (raw BE) ⟂ `currentType` (lát cắt). `useMemo([khkkGroup, typeBucket])`.
- `versions = typeBucket.history.filter(d => d.code === khkkGroupWorkflowCode(g))` · `active = versions.find(v=>v.isActive) ?? null` · **KHÔNG đọc `typeBucket.active`** (1-trong-8).
- Giữ shape `TypeSummaryDto` ⇒ TypePanel/Designer không biết mình đang xem nhóm hay loại.
### W2c — lockedCode xuyên suốt
- Page: `lockedCode = khkkGroup !== null ? khkkGroupWorkflowCode(khkkGroup) : null`
- `TypePanel({type, lockedCode, onSaved})``<Designer lockedCode={lockedCode} …>`
- `defaultCode = lockedCode ?? DEFAULT_CODE_BY_TYPE[applicableType] ?? 'QT-V2-001'` — ô hiện ĐÚNG mã sẽ gửi (không hiện `QT-KHKK-V2-001` rồi gửi `KHKK-N3`).
- Ô Mã `disabled={isEdit || lockedCode !== null}` + hint "Mã khoá theo nhóm — version tự tăng."
- POST `code: lockedCode ?? code` (bất-biến theo CẤU TRÚC, không dựa vào input disabled).
- **+`key={typeCode ?? 'all'}` trên `<TypePanel>`** — NGOÀI spec, lý do: chuyển N1→N3 khi Designer đang mở thì `cloneFrom/editTarget` (state trong TypePanel) còn trỏ bản N1 trong khi `lockedCode` đã là N3 ⇒ POST đẻ `KHKK-N3` mang ruột N1. Remount xoá state. Khai ở đây.
### W2d — D3=C danh-sách-chốt
Cờ chung `const isKhkk = applicableType === TYPE_INT_CONTRACT_SIGNING_PLAN` (1 nguồn, 8 chỗ dùng).
| Ô | Cờ | Xử lý |
|---|---|---|
| #1 | allowReturnOneLevel | `disabled={isKhkk}` + title chung + **`checked={entry.…}` GIỮ** + mờ text |
| #2 | allowReturnOneStep | ↑ |
| #3 | allowReturnToAssignee | ↑ |
| #4 | allowReturnToDrafter | `disabled` + `checked` THẬT + **title RIÊNG** "KHKK hiện luôn trả về người soạn khi Trả lại — cờ chưa phân nhánh" + **KHÔNG mờ** (giữ `text-slate-700`) |
| #5 | allowApproverEditDetails | như #1 |
| #6 | allowApproverEditBudget | như #1 |
| #7 | allowApproverSkipToFinal | **GIỮ hành vi** (kể cả `checked` ép-false của K3 S165); chỉ thay 3 biểu thức trùng → `isKhkk`, giá trị y hệt |
| #8 | allowApproverFinalize | **SỐNG** — 0 chạm |
| #9 | allowApproverDelete | như #1 (rose→slate khi mờ) |
- `levelFlagsPayload` **0 dòng đổi** (echo state thật).
- +1 dòng giải thích trong hộp tím (chỉ khi `isKhkk`) — nói rõ ô mờ vẫn hiện đúng giá trị DB, và 2 ô còn tác dụng.
### W2e — menuKeys ×2 app
+8 const `AwV2_KhkkN1..8` mirror `MenuKeys.cs:127-134`, sau `:32` cả 2 app. Comment viết theo app (fe-admin trỏ `Layout.tsx:188`; fe-user khai "cố ý không có Designer") — **cố ý lệch chữ, không lệch const**.
### W2f — Ngưỡng CEO
`disabled={isEdit || isKhkk}` + helper-text "KHKK không dùng ngưỡng máy — NĐUQ quyết…" + POST `ceoApprovalThreshold: isKhkk ? null : (…)`.
### W2g — auto-pin lọc isActive ×2 app
- `WorkflowDto` (local, `:57-62`) **+`isActive: boolean`** — BE có (`AwAdminOverviewDto`, đối chiếu `DefinitionDto.isActive`).
- `find(w => w.isActive && code-match)`; filter `isUserSelectable` ở query GIỮ.
- Sửa 2 câu thông báo cho khớp điều-kiện MỚI (3 vế: đúng mã ∧ đang áp dụng ∧ đã ghim) — nếu không sẽ nói "chưa có quy trình" trong khi nó đang nằm đó ở bản archived (sai-thầm-lặng).
- `cp` fe-admin → fe-user, SHA-pair `89dfa607…` **BẰNG NHAU**.
### W2h — comment + tiêu đề
- Vá comment `:641-644`: câu cũ "2 ô duyệt sớm sống cho MỌI loại" **SAI**#7 vẫn khoá cho type-10 từ K3 S165. Viết lại + trỏ tại-chỗ.
- PageHeader: title = `Quy trình duyệt (Mới): {khkkGroupMenuLabel(g)}` · description = phụ đề nghiệp vụ (loại + nhóm N{n} + mã khoá + "7 nhóm còn lại không đổi"). Xem FLAG-1.
## §3 — Verify (build ×2 + proof) ## §3 — Verify (build ×2 + proof)
(chưa chạy) (chưa chạy)

View File

@ -11,13 +11,31 @@
## §0 — Ghi liên tục (chống-#53). Mục CHƯA CHẠY chỉ có tiêu-đề + "CHƯA ĐO". ## §0 — Ghi liên tục (chống-#53). Mục CHƯA CHẠY chỉ có tiêu-đề + "CHƯA ĐO".
## §1 — Đọc nền (prod code sites) ## §1 — Đọc nền (prod code sites) — ĐÃ ĐỌC
CHƯA ĐO | Vế | Site trên đĩa (HEAD hôm nay) | Hành vi HÔM NAY | ⇒ test |
|---|---|---|---|
| W1a deactivate | `ApprovalWorkflowV2AdminFeatures.cs:339-343` `Where(w.ApplicableType == typeEnum && w.IsActive)` + comment `:339` "only ONE active per type" | tắt **MỌI** active cùng type ⇒ POST 1 nhóm = tắt cả 8 | T1 ĐỎ · T3 ĐỎ · T2 XANH (type-1 giữ per-type) |
| nextVersion | `:334-337` `Where(w.Code == request.Code).Max(Version)` | ĐÃ per-Code sẵn (bất đối xứng với `:340`) | nền của T1/T3 |
| Group-match Create | `ContractSigningPlanFeatures.cs:479-486` regex `^KHKK-N([1-8])$`, code ngoài họ CHO QUA (`:477-478` chủ đích) | CÓ ở Create | (đã có test cũ, xem §4) |
| Group-match UpdateDraft | `:603-607`**chỉ** `EnsureWorkflowTypeAsync` | KHÔNG check nhóm | T4 ĐỎ |
| Group-match Submit | `ContractSigningPlanWorkflowService.cs:176-187` (rào hạng mục) → `:189 LoadStepsAsync` | KHÔNG check nhóm | T5 ĐỎ |
| Pin-check IsActive/Selectable | `:603-607` không đọc 2 cờ | pin bản archived OK | T7 ĐỎ |
| Changelog PUT | `:863-865` gate `ApplicableType is DuyetNcc or DuyetNccPhuongAn` | type-10 KHÔNG ghi vết | T8 ĐỎ · T9 (vế 0-row) XANH-mù ⇒ phải kèm control-dương |
| DELETE | `:1193-1205` `KeyNotFoundException` + `Remove` trần, 0 usage-check | KHKK/PE/Contract = **FK Restrict**`DbUpdateException` 500 · Leave/Ot/… = **loose 0-FK****200 xoá êm** | T10-T14 ĐỎ |
Sự-thật nền dùng để dựng lưới:
- 8 module có cột `ApprovalWorkflowId`: Contract · ContractSigningPlan · LeaveRequest · OtRequest · Proposal · TravelRequest · VehicleBooking · PurchaseEvaluation (`grep --include=*.cs src/Backend/SolutionErp.Domain/`).
- `ContractSigningPlanConfiguration.cs:50-53` FK **Restrict** + `:55` `HasQueryFilter(!IsDeleted)` ⇒ T11 phân biệt được "guard đọc soft-deleted" (409) vs "guard mù" (DbUpdateException).
- `WorkflowAppsConfiguration.cs:12-26` LeaveRequest **KHÔNG khai FK** cho `ApprovalWorkflowId`/`LeaveTypeId` ⇒ T12 seed Guid tự do; và guard là RÀO DUY NHẤT (chứng-nhân trong test).
- `NotFoundException : Exception` (`Common/Exceptions/ApplicationException.cs:3`) — KHÔNG họ hàng `KeyNotFoundException` ⇒ T13 đỏ thật hôm nay.
- `ApprovalWorkflowConfiguration.cs:26` UNIQUE (Code, Version) ⇒ T14 seed được 2 version cùng Code.
## §2 — Baseline đo tươi ## §2 — Baseline đo tươi
CHƯA ĐO - `git status` lúc mở lane: **`src/` SẠCH** (implementer-backend chưa land) ⇒ số đo dưới là nền TRƯỚC cả 5 fix.
- Lệnh: `dotnet test SolutionErp.slnx --nologo --verbosity minimal -p:BuildInParallel=false -maxcpucount:1` (chạy nền).
- Kết quả: xem §7 (điền khi job xong).
## §3 — T1-T3 (deactivate-per-Code, home UpdateAwDefinitionTests) ## §3 — T1-T3 (deactivate-per-Code, home UpdateAwDefinitionTests)