Files
solution-erp/.claude/workflows/runs/2026-08-08-S182-khkk-awf-mirror-ncc/sub-cicd-monitor-0.md
2026-08-08 01:47:30 +07:00

15 KiB
Raw Blame History

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)

  • 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 · ~LinesContractSigningPlanLevelOpinions 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=1D6/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ánsrc/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=00 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 ChoDuyetphả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%' 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 opinionkhô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