Files
solution-erp/.claude/workflows/runs/2026-08-04-S171-khkk-ui-mirror-pe/run.md
pqhuy1987 3a6eb92cda
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
[CLAUDE] Docs: S172 closeout — verdict 998ea55 PASS + bookend 5 vòng/11 sub + vá stale
Verdict sản phẩm: `998ea55` (KHKK 3-panel mirror Duyệt NCC) = PASS.
Test-gate #447 644/644 Failed 0 khớp baseline tách-phần Δ0 · bundle 4/4 rotate
khớp log CI tới byte · 2 endpoint MỚI 401 · mig repo 71 = prod 71 set-diff 0/0
hai chiều, tables 97 · smoke 8/8. Không rollback, prod khoẻ.

Chân #44/#85 đóng bằng đo — nấc cũ "credential UAT chết" SAI (tra nhầm account
đời cũ; bộ sống ở HANDOFF slot 64). Phạm vi = 2 tổ-hợp vai
(Drafter+Procurement · CostControl+DeptManager); CHƯA loại nhánh vai thường
khác vẫn 403 — siết 5 site/3 file sau ring2 ESCALATE-1.

Bookend-close 5 vòng / 11 sub → runs/2026-08-05-S172-bookend-close/:
H1 DRIFT 7 · H2 GATE-HOLD 6 · lead-stale 9 FLAG(SÀN)+1 ESCALATE ·
lead-gap 8 FLAG(3 HIGH) · ring1 67/69 · ring2 ĐẠT 17/17 ·
trio MIXED → 2 action/15 bác → MIXED-PASS 50/59 · ctx-audit TRUOT 3 FLAG.

Vá stale @closeout:
- STATUS:479 bundle hash (stale 2 phiên, lần 2 cùng ô) → Ajv-MaCz/YsXRkBSR
- STATUS:6 counter 42→46, deep 2/15→6/15; Recently Done S171-S172
- HANDOFF segment @S172: E-7 + 5 acceptance có nhà (trước đó 0 hit/6 sổ bền),
  carry re-stamp sau 4 phiên bỏ, 4 site neo tuyệt đối (1 site sai DẤU), slot 67-72
- skills/README ×2 ổ số cứng nằm cạnh chính con trỏ B1
- gotcha #87 (mã pre-auth 411/415 trả lời sai câu hỏi authz)
- run.md S171 hết mồ côi (0→4 hit) · 8 dir rỗng mis-land đã dọn
- MIND-3 neo xuất xứ sai 2 trường (ts tương lai + HEAD stale) — ctx-audit F-1

§L.c completeness-gate: vòng 4/5 (V4 không-nhịp) | phép ĐẠT 2 / TRƯỢT 2 / vacuous 0.
2 TRƯỢT cùng một bệnh: S169·S170·S171 chạy xong mà 0 session-log durable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 10:18:34 +07:00

18 KiB
Raw Blame History

run: 2026-08-04-S171-khkk-ui-mirror-pe

Phiên: S171 (L10 window-3) · Ngày: 2026-08-04 · Lead: em-main (Opus 5) Đề-bài owner (verbatim): "OK hiện thì điều chỉnh lại giao diện chỗ KKHĐ lại nhé, lấy của Duyệt NCC làm mẫu: Panel 1: Hình gửi 1 · Panel 3: Duyệt và History tương tự (Hình 2) · Panel 2 - Giữa: Cấu trúc tương tự như duyệt NCC (Phần content nhập phía trên, duyệt ghi chú phía dưới) - Hình 3." Engine: /fable-real investigator-codebase (LỆNH-A single deep-pass) → /fable-clone reviewer (LỆNH-B ensemble) → giao Opus 5 MAX thực-thi.


Bối-cảnh ẢNH (subagent KHÔNG xem được ảnh — lead mô-tả lại, đây là nguồn duy nhất về ý-đồ UI)

Owner gửi 3 screenshot màn Duyệt NCC (PE) đang chạy prod, làm MẪU cho KHKK:

Ảnh 1 — Panel 1 (trái) + khung tổng:

  • Header trang: icon clipboard + Duyệt NCC + badge đếm 45.
  • Panel trái: ô search placeholder Tìm mã / tên gói thầu / dự án… · dropdown Tất cả trạng thái · cây thu-gọn được 📅 Năm 2026 kèm badge 45.
  • Panel giữa khi chưa chọn: empty-state icon + Chọn phiếu ở danh sách.
  • Panel phải khi chưa chọn: empty-state Quy trình duyệt sẽ hiện khi chọn phiếu. (có nút ✕).

Ảnh 2 — Panel 3 (phải), thứ tự dọc:

  1. Tiêu đề Quy trình duyệt + dòng meta QT-DN-V2-001 v04 · Quy trình Duyệt NCC.
  2. Banner xanh lá: Phiếu đã kết thúc tại Phòng Kiểm soát Chi phí · Cấp 2 — không qua CEO.
  3. Các Bước: Bước 1 — Phòng 1 + chip tên phòng (Phòng Cung ứng); trong mỗi bước là các Cấp (Cấp 1 đã duyệt / Cấp 2 đã duyệt), mỗi cấp liệt kê NV kèm badge vai (NS PRO, NS CCM); người duyệt thật in đậm, các NV cùng cấp còn lại hiện dạng / Tên mờ. Bước chưa tới làm mờ toàn khối + chip Kết thúc tại Cấp 2. Có badge đặc-biệt ⚑ Duyệt thay CEO.
  4. File đính kèm khi duyệt — phụ đề File do người duyệt tải lên trong quá trình duyệt. + hàng file (tên · size · nút xem · nút tải).
  5. Lịch sử duyệt (6) — mỗi entry: badge hành-động (Duyệt xanh / Trả lại vàng kèm → Lùi về Cấp 2) · tên người · [Bước N — Cấp M] · timestamp HH:mm:ss dd/M/yyyy · nội-dung ghi-chú nhiều dòng.

Ảnh 3 — Panel 2 (giữa), owner khoanh ĐỎ phần dưới:

  • Trên = content nhập: bảng số liệu có dòng đánh số (8. Giá trị thực hiện dự kiến còn lại, 9. Giá trị tổng thực hiện dự kiến — dòng tổng nền xanh đậm, số âm màu đỏ trong ngoặc) → card THÔNG TIN CHỌN THẦU (① TÊN NCC/TP ĐƯỢC CHỌN nền xanh nhạt · 23. GIÁ ĐỀ XUẤT TỪ PRO/CCM Min/Max · Giá chốt duyệt + badge Giá NCC · 4. Bảng so sánh giá hàng attachment · 5. Link hồ sơ + nút Copy).
  • Dưới (KHOANH ĐỎ) = 5. Ý KIẾN CẤP DUYỆT (SIGN-OFF THEO WORKFLOW): nhóm theo BƯỚC → PHÒNG, mỗi nhóm có header trái (BƯỚC 1 — PHÒNG 1 + chip tên phòng) và meta phải (2/2 cấp đã duyệt · 5 NV tham gia); bên trong là lưới 2 cột các card ý-kiến: Cấp N — Tên người + badge ✓ Đã duyệt + nội-dung ghi-chú (nhiều dòng, giữ xuống dòng) + timestamp; ý-kiến rỗng render placeholder (duyệt — không ý kiến). Bước chưa duyệt render — Chưa có ý kiến duyệt.
  • Cây trái trong ảnh này cho thấy cấu trúc 4 folder theo giai-đoạn dưới mỗi gói thầu: Duyệt NCCKế hoạch Hợp đồngDuyệt Hợp đồngHợp đồng cứng (3 cái sau đang badge SẮP), và card phiếu mang chip GẤP (PRO) · ⚑ Kết thúc · Phòng 2 · Đã duyệt + mã PE/2026/A/019 + timestamp + tên người.

Ground-truth lead đã ĐO trước khi phóng (để lane khỏi đo lại)

Sự-thật Bằng-chứng
PE 3-panel grid = lg:grid-cols-[400px_1fr_360px] fe-admin/src/pages/pe/PurchaseEvaluationsListPage.tsx:397
PE panel 1/2/3 = PeListPanel · PeDetailTabs · PeWorkflowPanel cùng file :658 :678 + imports :28 :31
KHKK list hiện là 2-panel lg:grid-cols-[19rem_minmax(0,1fr)] + PipelineTreePanel fe-admin/src/pages/khkk/KhkkListPage.tsx:197-198
KHKK detailtrang riêng full-width, KhkkWorkflowPanel render INLINE ngay dưới header (không phải rail phải) fe-admin/src/pages/khkk/KhkkDetailPage.tsx:411 + comment :9
Cả PE lẫn KHKK đều SHA256 IDENTICAL giữa fe-admin ⟂ fe-user đo @S171: 4/4 cặp file same=YES
LOC PE Workspace 147 · PE List 807 · PeDetailTabs 179.376 B · PeWorkflowPanel 63.472 B · Khkk Detail 1.093 · Khkk List 361 · KhkkWorkflowPanel 491
KHKK chưa có components/khkk/ — mọi thứ nằm trong pages/khkk/ ls fe-admin/src/components/khkk/ → không tồn tại

🔴 Delta cốt lõi: PE = một màn 3-panel hợp nhất (chọn ở trái → nội-dung giữa → workflow/history phải). KHKK = list 2-panel + detail trang riêng có workflow xếp chồng trên nội-dung. Đây là khoảng cách kiến-trúc, không phải khoảng cách CSS.


taskList snapshot

Stage A — /fable-real (LỆNH-A, 1 lane, tier:'fable')

# role label nhiệm-vụ
1 investigator-codebase Core investigator-codebase invest s171 Deep-pass MAP toàn-diện PE-vs-KHKK trên 3 panel + đề-xuất bố-trí đồng-nhất, phân-loại rõ GIỐNG-đượcKHÁC-buộc-phải-khác (+ lý-do domain). Propose-only.

Stage B — /fable-clone (LỆNH-B, ensemble ≥3 lane, tier:'opus')

# role label lăng-kính
1 reviewer Gate reviewer lens-fidelity s171 Trung-thành ảnh/ý-đồ owner + "tương tự thì giống nhau"
2 reviewer Gate reviewer lens-feasibility s171 Khả-thi kỹ-thuật: data-shape KHKK có đủ nuôi 3 panel không
3 reviewer Gate reviewer lens-regression s171 Rủi-ro vỡ cái đang chạy: route · policy KeHoachKyKet (gotcha #85) · mirror 2-app · Pattern 16-bis

Stage C — thực-thi

Opus 5 MAX theo spec-khkk-ui-mirror-pe-04-08-2026.md (lead ghi SAU khi verify — engine KHÔNG tự ghi).


Stages

  • A — /fable-real investigator-codebase deep-pass (Fable 5, wf_b76692d4-3a4, 756s, 33 tool-use, return SẠCH 0-garble) → sub-investigator-codebase-invest-s171.md 44.830 B · VERDICT=27 giống-được / 6 buộc-khác / 8 data-gap 🔴 2 đính-chính vào Ground-truth của lead (bảng trên SAI 1 dòng): (Đ-1) PE panel 1 KHÔNG phải PeListPanel — viết INLINE PurchaseEvaluationsListPage.tsx:399-639; PeListPanel chỉ 1 call-site = PurchaseEvaluationWorkspacePage.tsx:84không có component list dùng-lại được. (Đ-2) Đã có tiền-lệ mới hơn: ContractsListPage.tsx:125 (GĐ3, S168, 255 dòng) = bản PE-3-panel gọt gọn ⇒ KHKK là mắt-xích lệch duy nhất giữa GĐ1 và GĐ3; chép khuôn từ GĐ3, KHÔNG chép từ PE 807 dòng. 🔴 Đảo ước-lượng: ContractSigningPlanWorkflowService.cs:534/:547 ĐANG GHI approvals + changelog THẬT ⇒ "Lịch sử duyệt" không phải xây-lại, chỉ thiếu ~5 field DTO + 1 endpoint.
  • B — /fable-clone reviewer ×3 lăng-kính (wf_832dadb2-93f, Opus, 470s, 52 tool-use) — 🔴 CẢ 3 LANE CHẾT "You've hit your session limit · resets 6:10pm", engine trả verdict: null ×3. NHƯNG ĐĨA CỨU 2,95/3: khung-rỗng-lượt-1-2 giữ trọn 74.070 B. · lens-feasibility 26.461 B HOÀN TẤT8 HELD / 6 BROKE / 6 bỏ-sót · lens-regression 24.432 B HOÀN TẤT8 HELD / 4 BROKE / 10 bỏ-sót · lens-fidelity 23.177 B DỞ — §6 (phép đo P3-12) + §8 (lỗ tự khai) trống, VERDICT còn ?. Lead derive từ §7 bảng của chính nó ≈ 12 HELD / 6 BROKE / 3 bỏ-sót (nhãn lead-derive, KHÔNG phải lane tự kết); lead tự chạy phép §6 còn thiếu → lật P3-12
  • C — lead verify + refute/synthesize → spec-khkk-ui-mirror-pe-04-08-2026.md 35.017 B (utf8-strict OK, đủ 3 heading ①②③). Post-wave git status: 0 residual-write ngoài run-folder
  • C-bis — 3 QUYẾT-ĐỊNH OWNER chốt 2026-08-04, spec cập-nhật 43.087 B: (1) panel-1 = PipelineTreePanel — owner BÁC đề-xuất đảo của lead, đọc đủ 4 rủi-ro trên thẻ chọn rồi vẫn chọn ⇒ quyết-định có thông-tin đầy-đủ; lead chuyển 4 rủi-ro thành mitigation CƯỠNG-HÀNH + acceptance C-5..C-8 (chọn HÌNH ≠ chấp-nhận VỠ) · (2) panel-3 rỗng = CHỈ hộp dashed y PEđóng miễn-trừ-thứ-7 · (3) file-khi-duyệt = upload THẬT ⇒ BE-7 +ApprovalAttachment=3 policy KeHoachKyKet.Update, bẫy F-6 "UI chết" tự tan
  • D — Opus 5 MAX thực-thi (wf_6fd33d7a-a20, 1.883s, 91 tool-use, 422K tok) — BE xong 7/7 return SẠCH · FE chết ở lượt TRẢ VỀ (API Error: Connection closed mid-response, verdict:null) nhưng CODE không mất byte nào 🔴 Chống-#53 cứu MỘT NỬA: FE ghi khung rỗng lượt 1-2 đúng luật nhưng KHÔNG append ⇒ nhật-ký 642 B RỖNG ⇒ mất trọn phần NGHĨ. Đối-chứng cùng phiên: 3 lane reviewer + lane BE đều append ⇒ cứu trọn. ⇒ khung rỗng = CẦN, KHÔNG ĐỦ; cái cứu thật là APPEND sau mỗi đơn-vị việc. Lead ghi biên-bản on-behalf vào sub-implementer-frontend-s171.md 🔴 6 IDE diagnostic ĐÁNH LỪA (gotcha #68): báo CS0246 ×2 ở controller + TS2304 BuildStagesOptions — trông như vỡ nặng. Build thật sau khi agent dừng: 0 lỗi cả 3. Tin diagnostic mà rollback thì đã vứt một mẻ việc đúng Đo được: 20/20 acceptance tĩnh ×2 app · mirror SHA256 8/8 byte-identical · E-2 = 0 hit path cấm · build 3/3 PASS · test 643/644 🟢 Hồi-quy chéo B1 CHẶN ĐÚNG (usePipelineStages.ts:292-297 conditional-spread): PE :524 chỉ truyền stage1Contentgiữ nguyên 8 ngăn N1-N8; PipelineTreePanel:314 stage2Options ?? {} ⇒ cây PE/GĐ3 nguyên vẹn 🟡 1 test FLAKY Create_DifferentEvaluationType_DoesNotAffect_Other — full-suite fail 2→1→1, chạy RIÊNG 0/0/0 ⇒ bất-định; PeWorkflowAdminTests.cs tạo new SqliteDbFixture() RIÊNG ×6 test ⇒ flake hạ-tầng test, 0 giao KHKK. 🔴 baseline HEAD sạch CHƯA đo (worktree fail đường-dẫn Dropbox) ⇒ CHƯA được nói "flake có sẵn" 🔴 E-7 THIẾU — bảng "cột nào chết" là thứ CHỈ lane biết (nó quyết bỏ cột nào); lead đọc được kết-quả, không đọc được lý-do ⇒ phải soi diff bằng mắt TRƯỚC UAT, bỏ qua = đúng ca M-7 mất-chức-năng-im-lặng
  • E — còn nợ: E-7 bảng cột-chết · baseline test HEAD · 5 acceptance RUNTIME (B-3 · E-5 UAT fe-user:8080 · E-6 deep-link fe-admin · C-5 negative-control · D-9 vế tự-ẩn)

Synthesis

Lead refute — 3 chốt lead ÁP LÊN bản invest sau phản-biện

  1. 🔴 Panel 1 KHÔNG dùng PipelineTreePanel (đảo §5 bản invest). 4/5 phát-hiện nghiêm nhất hội tụ đúng chỗ này: canPe khoá cây ⇒ user chỉ có quyền KHKK mất TRẮNG danh-sách (display-layer khoá authz-layer đang mở — mặt còn lại #82) · trần 200 = mất đường tới phiếu, không có "trang 2" · usePipelineStages.ts:211 cây tự navigate rời màn 3-panel · content nuốt groups im lặng (PipelineStageFolders.tsx:285-287). Mirror trung-thành = KHKK dựng cây từ danh-sách CỦA CHÍNH KHKK (đúng như PE dùng yearGroups từ query pe-list, KHÔNG dùng PipelineTreePanel) ⇒ 3 rủi-ro tan hết.
  2. 🔄 Miễn-trừ 6 → 3. P1-4 lật GIỐNG-được (khác nguồn dữ-liệu ≠ khác bố-trí) · P2-7 lật GIỐNG-được (readOnly là prop HOST, PurchaseEvaluationsListPage.tsx:663 — không cần workspace) · P3-12 hạ xuống CHỜ-ANH (lead đo DbInitializer.cs:554: 3 Bước × ≤2 Cấp = tới 6 trạm, seed :620 tự ghi "sao từ workflow Duyệt NCC prod" ⇒ tiền-đề "3 trạm ngắn" SAI đo được). + miễn-trừ THỨ 7 không khai (F-5: §5 âm-thầm đảo P3-16 panel-3 empty-state) → đẩy thành CÂU HỎI 2.
  3. Giữ quyết-định A, THAY lý-do. Lý-do cũ ("cả hai đường generic đều đụng PE prod") là nhị-phân giả — có đường (iii) adapter 1 chiều nằm trọn trong KHKK, 0 dòng PE. Lý-do đúng: trích StepOpinionsBox khỏi components/pe/ mâu-thuẫn trực-tiếp acceptance A-10 của chính nó. Kết-luận đúng, chứng-minh hỏng — giữ kết-luận, sửa chứng-minh.

6 acceptance của bản invest bị bẻ gãy khi CHẠY THẬT (đã thay bản sửa trong spec)

Gốc Vỡ thế nào
A-13 (canh QUYỀN) Vỡ cả hai đầu: :15 comment chứa "Authorize"+"Policy" ⇒ bơm tử-số; :17 :236 :241 comment nhắc tên [Authorize] trần ⇒ vế "0 dòng trần" FAIL trên file 100% sạch (citation-trap). → thay bằng neo ^\s*\[ + fault-inject chứng răng
A-6 (canh bug S155) Kiểm NHẦM KHOÁ — cấm phase (vô hại, transient) và bỏ sót filter/view (đúng 2 khoá làm vỡ) ⇒ setSearchParams({group,filter:'DaDuyet'}) = đúng bug S155 vẫn PASS. Inverted check
A-10 (canh hồi-quy) Đo sai trục — thiếu hooks/usePipelineStages.ts + components/pipeline/ ⇒ sửa 2 path đó có thể làm trang PE mất 8 ngăn GĐ2 mà A-10 PASS 100%
A-7b Chạy trên chính mẫu PE ra 3 hit, không phải 5 ⇒ implementer viết ĐÚNG sẽ FAIL; cách duy nhất PASS = bỏ số đếm động cho vừa regex = Goodhart
A-7d Đo chuỗi chữ, mục-tiêu là vị-trí khối ⇒ đổi tiêu-đề "Ý kiến duyệt" là PASS trong khi khối vẫn ở panel 3
A-8c Grep-trên-source ⇒ khối UI CHẾT vẫn PASS 100% (không có đường ghi attachment + khối tự-ẩn khi rỗng ⇒ không bao giờ render trên UAT)

3 khối owner NHÌN THẤY trong ảnh mà bản invest đánh rơi

F-1 EmptyState panel 2 "Chọn phiếu ở danh sách" (Ảnh 1 — trạng-thái ĐẦU TIÊN người dùng thấy; bản invest ghi ở §1.2 rồi rơi khỏi §3/§5/§7) · F-2 chip ⚑ Kết thúc · Phòng N trên card panel 1 (Ảnh 3 — 0 công BE, cột Mig 71 đã có) · F-3 liệt-kê NV mỗi Cấp, người duyệt in đậm + NV khác / Tên mờ (Ảnh 2).

Tin tốt được verify độc-lập (đừng cắt ngân-sách vì nghi ngờ)

Lane feasibility vào để lật D-2 và không lật được: LogSigningPlanTransitionAsync 6 call-site phủ TRỌN Pending/Approve/Reject-Return (:204 :283 :296 :311 :325 :393), Approvals.Add:534 không if nào bọc ⇒ entry "Trả lại → Lùi về Cấp 2" của Ảnh 2 SẼ CÓ dữ-liệu. Và A-0 chặn-cửa GỠ ĐƯỢCusePipelineLinkage() đã có sẵn khkkByPeId (:123-134,147), 0 query mới.

Ngược lại — 1 chỗ RẺ bị ước-lượng sai chiều

FinalizeStepName/FinalizeLevelName không phải "field append": ContractSigningPlan.cs:51 chỉ có bool EndedByLevelFinalize, không cột nào lưu Bước/Cấp nào. Phải dẫn xuất — port ~20 dòng projection 3 subquery từ PurchaseEvaluationFeatures.cs:665-683. Bản invest cảnh-báo "dễ sai đắt nhất theo cả hai chiều" rồi tự dính chiều còn lại.


Ledger — ĐÓNG @S172 (vá mồ-côi, ctx-verifier cờ-2 + lead-gap FLAG-5)

🔴 Vì sao có mục này: file này từng có 0 hit cho cicd · deploy · push · commit · 998ea55 · #446/#447 (6 từ, control-dương canPe=1 trong chính file ⇒ phép grep không rỗng), ledger dừng ở [ ] E. Hệ quả đo được: cicd-verify-998ea55.md mồ côi ngay trong run-folder của nó ⇒ spawn nào PIN run.md để nối việc sẽ không thấy việc duy nhất đang dở. Đây là class mồ-côi-trong-chính-nhà-mình — path đúng, file đúng, chỉ thiếu người trỏ VÀO (đảo chiều của inbound-pointer check S166).

  • E — deploy + verdict. 2 commit: 998ea55 (Contract, 21 file) + bf0200b (Docs, 4 điểm stale hậu-ring S170); push 4b1905d..bf0200b, 0 commit treo.
  • cicd verdict = PASScicd-verify-998ea55.md (26.247 B, 0 ô _(chờ đo)_). Test-gate #447 644/644 Failed 0 khớp baseline tách-phần Δ0 · bundle 4/4 rotate khớp log CI tới byte · 2 endpoint MỚI 401 · mig repo 71 = prod 71 set-diff 0/0 hai chiều, tables 97 · smoke 8/8.
  • Chân #44/#85 đóng @S172🔴 phạm-vi = 2 tổ-hợp vai (Drafter+Procurement · CostControl+DeptManager), chưa loại nhánh vai thường khác vẫn 403. Xem cicd-verify-998ea55.md §6.5.
  • Nợ E-7 bảng "cột nào chết, chuyển đi đâu" (8 cột + 6 KpiCard → card 400px) — 🔴 lý do bỏ cột mất theo lane FE chết ở lượt trả về, phải soi diff bằng MẮT, CẤM suy đoán. Nhà: docs/HANDOFF.md NEXT-em @S172 #1 + [carry:e7-cot-chet-khkk].
  • 5 acceptance RUNTIME chờ UAT — nhà: docs/HANDOFF.md NEXT-em @S172 #2 + [carry:acceptance-runtime-khkk]. Nay (a)-(c) đo được nhờ credential non-admin còn sống (slot 64).
  • Nợ harvest 143.682 B — 4 vai của run này chưa về sổ (invest-cb · impl-be · impl-fe · reviewer×3). Nhà: docs/HANDOFF.md slot (71) + [carry:harvest-no-s168-s171].

🔴 Bài để lần sau khỏi lặp: run-folder PHẢI tự trỏ vào artifact nó đẻ ra. Detector orphan đo TÊN FILE, không đo "ai đang trỏ vào ai" — nên một artifact đầy đủ vẫn có thể chết câm ngay trong nhà của nó. Kiểm rẻ: grep -c '<sha-commit>' run.md phải > 0 sau khi deploy.