15 KiB
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.mdMỤC 2 W0.4 (:24) + R-2 (:10) + F11 audit. 🔴 R-2: PROD chỉ ĐỌC. 0 lệnh ghi (khôngscp, không tạo file remote, không INSERT/UPDATE/DELETE). ⚠️ Naming: TASK gọi artifactsub-cicd-monitor-0.md; boilerplate RUN-TRACE gọisub-cicd-monitor-3.md. Ghi vào-0theo 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)
- S0 connectivity + control dương
- Q1 ApprovalWorkflows type-10 — KHỚP
- Q2 ContractSigningPlans GROUP BY ApprovalGroup, Phase — 🔴 LỆCH
- Q3 COUNT ContractSigningPlanLevelOpinions — 13 (dồn hết vào N4)
- Q4 audit pin-lệch (F11) — KHỚP 0 row (control mạnh)
- BONUS đo thêm: flag-state 88 level + pin/opinion per-Code (de-risk W1d + W2d)
- 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: có 9 phiếu ở phase submit-được (8 Nháp + 1 TraLai), không phải 0. Chúng SẼ đi qua rào
EnsureWorkflowGroupMatchAsyncmớ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ọcPhase != 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ếuChoDuyet⇒ 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-N1bị pin bởi 6 phiếu, trong đó 5 XOÁ-MỀM. ThiếuIgnoreQueryFilters()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/N8hiệ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Ữ
checkedstate 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" là nói nhẹ: thực tế 100%. Chốt
forced-true-disabled + tooltip riênglà đú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"
:1292là 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:
- 🟢 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. - 🔴 Tiền đề "phiếu đều Phase=3" của SPEC
:24SAI — 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:24cho khớp thực tế, không đổi thiết kế W1b. - 🟢 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 pinKHKK-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 🔴:991tuyê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ồmContractSigningPlans(: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à:1206vẫ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-N1bị pin bởi 6 phiếu, 5 trong đó XOÁ-MỀM. Đếm-pin mà thiếuIgnoreQueryFilters()⇒ 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