wal: pause
This commit is contained in:
@ -1,23 +1,20 @@
|
||||
# WAL — auto-generated, không sửa tay
|
||||
updated: 2026-07-29 | session: S159 (phiên-LOGIC L8, window 1) | branch: main
|
||||
updated: 2026-07-29T14:40+07:00 | session: S159 (phiên-LOGIC L8, window 1 — /pause) | branch: main
|
||||
|
||||
goal: arc-2 UI toàn-trình — đợt 1-4 SHIPPED (4 commit, cicd 3×PASS + 1 FAIL-bắt-revoker); đợt-5 CODE XONG chờ reviewer → commit → push → cicd → /pause (LỆNH ANH: "xong thì /pause")
|
||||
goal: L8 window-1 ĐÓNG CHỦ ĐỘNG (lệnh anh @PAUSE-1). Arc-2 UI toàn-trình ĐÓNG TRỌN 5/5 đợt; còn closeout sổ bền + PARK chờ anh.
|
||||
|
||||
chain:
|
||||
- [x] bookend @open 5 vòng + (49)-(53) — run `2026-07-29-S159-bookend-open`
|
||||
- [x] Đ1 `b5799fc` Tổng quan pipeline — PASS 9/9 · Đ2 `1a47a61` tab strip + menu 4GĐ + 01-07 — 8/8 + mục-9 lộ REVOKER · Đ3 `2a72695` gỡ nhánh S92 + skeleton — PASS 9/9 restart-proof · Đ4 `a2bbcb9` list chi tiết + 7 FLAG + "Bảng cứng" — PASS 9/9 (durability ×2 restart)
|
||||
- [x] Đ5 CODE XONG (build fe-user ✓521ms · test 45+517=562 ✓ · grep mồ-côi 0):
|
||||
· BE: `ListDeletedContractsQuery` (ContractFeatures, chỗ-thứ-2 IgnoreQueryFilters + Where(IsDeleted) + IDOR mirror :297-302) + GET `/contracts/deleted` policy Contracts.Read + D3 note PE cập nhật
|
||||
· Seeder: Ct ×7 nhóm +3 leaf (WfView/Approved/Deleted) + "Đang duyệt" backfill + Khkk 6 leaf + Dashboard→"Tổng quan quy trình" + reviewKeys +23
|
||||
· FE fe-user: matrix +type=3 (HĐ chung 1 bộ V2) + route alias `/contracts/workflow-matrix` + Ct regex +3 action + staticMap Khkk ×2 + MyContractsPage (phase/deleted + banner + ẩn Tạo) + MenuLeaf depth-0 UPPERCASE + UserDashboardPage BỎ HERO + title "Tổng quan quy trình"
|
||||
· fe-admin: leaf mới drop-silent CỐ Ý (tiền lệ WfView-PE)
|
||||
- [!] reviewer đợt-5 ĐANG CHẠY (`sub-reviewer-diff-dot5.md`) — 6 trục, nặng nhất: endpoint vs khuôn PE + detail HĐ-đã-xóa 404? (trục 4)
|
||||
- [ ] SAU verdict: xử FLAG → commit `[CLAUDE] Contract+FE-User: dot-5 ...` → push → message cicd verify (chạy NỀN, kết quả đọc phiên sau) → /pause
|
||||
- [x] bootstrap L8 + Sàn-3 retro-harvest 2 synthesis L7 — run `2026-07-29-S159-bookend-open/` + detector 0-orphan
|
||||
- [x] bookend @open 5 vòng đủ (anh chốt) + thi hành (49)-(53) — 11 artifact ~231KB cùng folder; mind-check phép (10) MỚI fault-inject 4/4
|
||||
- [x] arc-2 5 đợt / 5 commit `b5799fc`→`1a47a61`→`2a72695`→`a2bbcb9`→`ea962b1` — run `2026-07-29-S159-tong-quan-pipeline-menu/` (disposition TỪNG FLAG trong run.md)
|
||||
- [x] cicd 5/5 verdict: PASS 9/9 · PARTIAL 8/9 (mục-9 lộ REVOKER [S92] — xử regrant=447 + gỡ nhánh code + REGRESSION-GUARD test) · PASS restart-proof · PASS durability-2 · **PASS 9/9 đợt-5 VỀ KỊP trước chốt** (`cicd-verify-ea962b1.md`: menu-delta 119→142=+23 khớp toán · endpoint 401-với-control-404 · durability-3 · luật control-âm-ĐỘC-QUYỀN cứu 1 false-fail)
|
||||
- [x] /pause nghi-thức: `_pause-1` + `_context` PAUSE-1 (verbatim anh) + MIND-2 (ctx-curator SUA-7-điểm áp trọn; mind-check đạt=11/0) + `_snapshot-1` + sweep 0-hit
|
||||
- [ ] CLOSEOUT sổ bền (việc /tiep): HANDOFF (❹-risk verbatim + revoker + 5 đợt + bộ tên 4-GĐ chốt: Duyệt NCC · Kế hoạch ký kết HĐ · Duyệt hợp đồng · Hợp đồng cứng · "Tổng quan quy trình") · STATUS CURRENT arc-2 + bundle baseline mới (user `Y6dW_5CM`/`6YIAufJR` admin `D0sXA0fe`/css-frozen) · gotchas +#84 revoker-2-chiều · spec KHKK note W1 tái dùng root/`Khkk_*` · session-log S159 · diary designer/cicd đối chiếu M9
|
||||
|
||||
next: (phiên sau /tiep) đọc verdict cicd đợt-5 (`cicd-verify-<sha>.md`) + closeout sổ bền (HANDOFF: ❹-risk verbatim + revoker + 4 đợt + tên-4-GĐ chốt cuối "Kế hoạch ký kết HĐ"(GĐ2)/"Duyệt hợp đồng"/"Hợp đồng cứng" + "Tổng quan quy trình"; STATUS CURRENT arc-2; gotchas +#84 revoker-2-chiều; spec KHKK note W1 tái dùng root/skeleton Khkk_*; session-log; MIND-2 @close)
|
||||
next: /tiep → đọc `cicd-verify-ea962b1.md` + `_context-s-8.md` PAUSE-1 → làm mục CLOSEOUT `[ ]` trên → rồi PARK chờ anh (xem checklist báo-cáo /pause)
|
||||
|
||||
verify:
|
||||
- prod hiện hành: menu 4-GĐ + 01-07 "- Bảng cứng" + 38-key 13/13 (bền ×2 restart) · bundle user `CLfAftby`/`B6bUSFBt` admin `CC1pGE1F`/`DWDbm5As`
|
||||
- Đ5 CHƯA push — mọi thứ Đ5 mới ở local (kể cả seeder; menu leaf mới CHƯA có trên prod — deploy Đ5 seeder tự insert)
|
||||
- 🔴 3 bài máy phiên này: cut-code = LINE-SCAN so CẢ DÒNG (bracket-count vỡ vì destructure/string; substring ' })' khớp sớm) · python heredoc bash vỡ → Write file script + PowerShell · script chết trước save = file NGUYÊN (đọc "cut X" in ra ≠ đã ghi đĩa)
|
||||
- 🔴 diagnostics IDE stale ×3 lần hôm nay — chỉ tin build sạch chạy SAU (#68); grep 0-hit đối chứng
|
||||
- [ ] closeout: `git log --oneline -6` thấy 5 commit arc-2 + `wal: pause` · `Test-Path .claude/workflows/runs/2026-07-29-S159-tong-quan-pipeline-menu/cicd-verify-ea962b1.md` = True
|
||||
- prod state chốt: menu 4-GĐ 6-leaf ×(1 Khkk + 7 Ct) + Hdc 7 "- Bảng cứng" + root 12 + 38-key 13/13 (bền ×3 restart) + 21-key mới 13/13 · test 562 · Mig 68/89 bảng
|
||||
- 🔴 KHÔNG SQL tay cho menu nữa — code-first seeder (bài revoker); comment nêu LUẬT+grep, CẤM số-đếm (bài FLAG-10 ×2 trong ngày)
|
||||
- 🔴 `git status` mù thư-mục rỗng ⇒ close-gate chạy RIÊNG `find -type d -empty`; dư-lượng spawn tái phát theo cwd vai — dọn cuối MỖI wave
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
# CI/CD Monitor Agent — Persistent Memory
|
||||
|
||||
- **07-29 verify `a2bbcb9` ĐỢT-4 (danh sách toàn trình: 7 FLAG reviewer fix + nhãn 'Bảng cứng') [S159, 2 file code: FE-user 1043/337 + DbInitializer label-only 9/1, 0 mig]:** ✅ **PASS 9/9** — Run **#425** (id=538) success **5m40s**; gate **562**. User bundle rotate `DeAg7MWm`→`CLfAftby` (+css `BQIZ-rUe`→`B6bUSFBt`). 🔑 **ADMIN bundle FROZEN mà vẫn chứng minh được là LÀNH TÍNH (#69 mode-B) — khuôn dùng lại:** `fe-admin` 0 file trong diff ⇒ hash không đổi; nhưng `Last-Modified` của admin js = **13:55:31 NẰM TRONG cửa-sổ deploy** (13:51:15→13:56:55) + size trùng khít đợt-3 ⇒ file **ĐÃ ghi lại**, chỉ là Vite ra byte y hệt. ⟹ 'frozen' + LM-in-window = **re-ship giống hệt**, KHÁC hoàn toàn 'bundle cũ kẹt lại' (cái sau LM sẽ cũ). Đừng phán hash-frozen là fail. 🔑 **Comment-strip DỰ ĐOÁN TRƯỚC rồi mới đo (tiến bộ so đợt-1 là phát hiện SAU):** control âm `chưa tới` grep source ra **2** — soi ra cả 2 nằm trong COMMENT L343-344 giải thích chính FLAG-2 ⇒ **dự đoán bundle=0**, đo xong đúng **0**. Nếu đọc grep-source thô là FAIL oan một control vốn đúng. ⚠️ Ngược lại `Không tìm thấy` bundle=**10** > source=3 (chuỗi phổ thông, có ở module khác) ⇒ marker DƯƠNG chỉ dùng '≥1', **đừng lấy nó làm phép đếm-chính-xác**; chọn chuỗi ĐẶC-THÙ mới đếm-khớp được. **Durability lần 2 (khuôn 3 chân kiềng đợt-3, chạy lại rẻ):** restart thật (log mở lại **13:56:39 `Applying migrations...`** + w3wp 13:56:33) → 38-key **min=13 max=13 keys=38 totcr=494/494 GIỮ** · skeleton 156 · `Hdc_` nhãn `%-%`=7 · root 12 · 3 root vis=0 · **control âm Hrm/Off/Personal non-Admin=0 (revoker còn sống)** · `Pe%` 197/185. ⟹ **2 restart liên tiếp (#424, #425) đều giữ ⇒ fix bền, không phải may.** Evidence: `runs/2026-07-29-S159-tong-quan-pipeline-menu/cicd-verify-a2bbcb9.md`. Tag `[run-425, pass-9-9, admin-frozen-LM-in-window-proof, comment-strip-du-doan-truoc, marker-pho-thong-khong-dem-chinh-xac, durability-restart-2, gate-562]`
|
||||
- **07-29 verify `ea962b1` ĐỢT-5 (menu 6-leaf y-chang-Duyệt-NCC + `GET /contracts/deleted` + bỏ hero) [S159, 10 file 173+/371−, 0 mig]:** ✅ **PASS 9/9** — Run **#426** (id=539) success **5m38s**; gate **562**. JS rotate cả 2 app (admin `CC1pGE1F`→`D0sXA0fe` · user `CLfAftby`→`Y6dW_5CM` +css `B6bUSFBt`→`6YIAufJR`); admin-css FROZEN nhưng **LM 14:44:29 in-window** ⇒ lành tính (#69 mode-B, khuôn đợt-4 dùng lại được ngay). 🔑 **CỨU FALSE-FAIL — luật MỚI về control ÂM:** brief bảo `Toàn trình 4 giai đoạn` phải VẮNG; tao grep repo-wide TRƯỚC khi đo → chuỗi vẫn sống hợp lệ ở `PePipelineStrip.tsx:24` **`aria-label`** (cả 2 app, có từ đợt-2) ⇒ **dự đoán bundle=1 chứ không 0**, đo đúng 1. Áp máy móc là FAIL OAN. Thay bằng `Quy trình mua sắm` (eyebrow hero) — `grep -rc` 2 app = **0 hit mọi file** ⇒ ĐỘC QUYỀN ⇒ bundle=0 = chứng hero đã gỡ (kèm `UserDashboardPage` 63.124→50.142 B). ⟹ **LUẬT: trước khi nhận 1 chuỗi làm control ÂM phải grep repo-wide xác nhận nó ĐỘC QUYỀN cho thứ mình muốn chứng vắng; chuỗi dùng-chung chỉ làm marker DƯƠNG.** 🔑 **SEEDER-INSERT delta khớp TOÁN (khuôn mạnh, tái dùng):** đo baseline TRƯỚC deploy rồi so SAU restart — `Ct_%_WfView` 0→**7** · `_Approved` 0→**7** · `_Deleted` 0→**7** · `Khkk_%` 5→**7** · nhãn `Ct_%_Pending`=`Đang%` 0→**7** · Dashboard `%quy tr%` 0→**1** · **MenuItems 119→142 = +23 = 7+7+7+2 khớp từng đồng** · root 12 · DUPKEYS 0 · **21 key mới đủ 13/13 canread** (nhánh reviewKeys skip-existing ăn vì key HOÀN TOÀN MỚI — khác đợt-2/3 nơi row `false` sẵn có không được nâng). **Endpoint mới:** `GET /api/contracts/deleted` → **401** + **control route bịa → 404** ⇒ 401 CÓ NGHĨA, không phải catch-all (đừng chỉ đo 401 trần). ⚠️ brief ghi `/contracts/deleted` thiếu prefix — route thật có `[Route("api/contracts")]`. **Durability lần 3:** 38-key **494/494** · control âm Hrm/Off/Personal=0 · `Pe%` 197/185 · `Hdc_` nhãn 7 · 3 root ẩn. ⚠️ **Bẫy phạm-vi mới: `Ct_*` nay đẻ thêm 21 key ⇒ `LEFT(MenuKey,3)='Ct_'` cho ra 59 key, KHÔNG so được với đợt trước — phải NOT LIKE 3 hậu tố mới để giữ đúng tập 38 gốc.** Evidence: `runs/2026-07-29-S159-tong-quan-pipeline-menu/cicd-verify-ea962b1.md`. Tag `[run-426, pass-9-9, control-am-phai-DOC-QUYEN, seeder-delta-khop-toan, 401-voi-control-404, durability-restart-3, query-scope-re-anchor, gate-562]`
|
||||
|
||||
- _(**#425 S159 đợt-4 `a2bbcb9` 7-FLAG fix + nhãn 'Bảng cứng' — PASS 9/9**: gate 562, user js `DeAg7MWm`→`CLfAftby`. 🔑 **admin FROZEN vẫn chứng được lành tính**: LM 13:55:31 **in-window** + size trùng ⇒ re-ship byte-identical (#69 mode-B). 🔑 **comment-strip DỰ ĐOÁN TRƯỚC rồi mới đo**: `chưa tới` source=2 (cả 2 trong comment) → dự đoán bundle=0 → đúng 0. ⚠️ `Không tìm thấy` bundle 10 > source 3 ⇒ marker phổ-thông chỉ dùng '≥1'. **Durability lần 2**: 38-key 494/494 giữ + control âm 0)_
|
||||
|
||||
- **07-29 verify `2a72695` ĐỢT-3 (gỡ nhánh S92 khỏi revoker + skeleton menu GĐ2/GĐ4 + ComingSoonPage×2) [S159, 9 file code, 0 mig]:** ✅ **PASS 9/9 — ĐÓNG regression tao bắt ở đợt-2.** Run **#424** (id=537) success **5m56s**; gate **562** giữ nguyên (3 test SỬA theo spec-change, không thêm ⇒ tổng không đổi). Bundle rotate **4/4** (admin `D6Pz7nfV`→`CC1pGE1F`/`B1ErASuI`→`DWDbm5As` · user `Bpb8WEuY`→`DeAg7MWm`/`7oAYFlzM`→`BQIZ-rUe`); marker `Phân hệ đang được triển khai`=1 cả 2 bundle + `Quay lại`=4; hash CŨ→`text/html` = chứng ghi-đè. Menu-tree: Khkk=5 · Hdc=7 · root=12 · DUPKEYS=0 · 3 root vis=0. 🔑 **PHÉP DURABILITY LÀM ĐÚNG (sửa lỗi đo-sớm đợt-2) — khuôn 3 CHÂN KIỀNG, tái dùng cho mọi ca 'fix có bền không':** (i) **CHỨNG MINH có restart THẬT trước khi đo** — `w3wp` PID StartTime 12:39:56 trong cửa-sổ deploy + **dòng ĐẦU log = 12:40:03 `Applying migrations...`** (dùng được vì deploy xoá `logs/` ⇒ log mở lại = boot mới) + DLL mtime; (ii) đo CÁI CẦN: 38-key **min=13 max=13 keys=38, tổng 494/494** SAU restart (đợt-2 chính phép này ra 47/494); (iii) **control ÂM chống Goodhart**: `Hrm/Off/Personal` non-Admin vẫn **=0** ⇒ revoker CÒN SỐNG, nên (ii) xanh KHÔNG phải do vô hiệu hoá cả hàm. **Thiếu (iii) thì 'fix' bằng cách xoá luôn checker cũng xanh y hệt.** Bổ sung: `Revoked` **0 dòng** trong log = ĐÚNG (hàm chỉ log khi `rows.Count>0`; nay 0 row cần lật, so 447 ở đợt-2) — **vắng-mặt phải giải-trình bằng cơ-chế, đừng đọc thành thiếu dữ liệu**. Guard mới có RĂNG 2 vế: `StillHiddenKeys` phải false + `ReopenedKeys` phải TRUE ⇒ re-add nhánh S92 là CI đỏ. ⚠️ Bẫy số: nhãn `01.-07.` đếm ra **14** (kỳ vọng 7) — tách prefix ra `Ct_`=7 + `Hdc_`=7 + other=0, đúng thiết kế 'GĐ4 tái dùng nhãn đã đánh số', KHÔNG phải nhân đôi. ⚠️ seeder grant là **skip-existing** ⇒ không tự nâng row `false` sẵn có trên prod; prod phải re-grant SQL + revoker thôi lật. Evidence: `runs/2026-07-29-S159-tong-quan-pipeline-menu/cicd-verify-2a72695.md`. Tag `[run-424, pass-9-9, durability-3-chan-kieng, restart-proof-w3wp-log-head, control-am-chong-goodhart, gate-562]`
|
||||
|
||||
- **07-29 verify `1a47a61` ĐỢT-2 (tab strip 4 GĐ trên phiếu PE + menu 4-GĐ + đánh số 01-07 nhóm HĐ) [S159, 8 file code 190+/18−, FE×2 + seeder-only BE]:** **PARTIAL 8/9** — Run **#423** (id=536) success **5m44s**; gate **562 EXACT** (45+517). 0 migration (BE chỉ `MenuKeys.cs` const + `DbInitializer.cs` seeder). Bundle rotate **4/4** (admin js `BHodkPNV`→`D6Pz7nfV` css `DiNJjaoo`→`B1ErASuI` — admin-css rotate ĐÚNG vì fe-admin có sửa, khác đợt-1 frozen; user js `0GO8mgrG`→`Bpb8WEuY` css `BARVqkP-`→`7oAYFlzM`). Mirror `PePipelineStrip.tsx` 2 app **md5 IDENTICAL** `ef7b7824…`. 🔑 **Quy luật đếm marker (nối tiếp bài comment-strip): bundle-count = SỐ MODULE chứa chuỗi** (bundler pool hằng-số trong 1 module ⇒ nhiều occurrence cùng file gộp còn 1). USER=2 module→2 · ADMIN=1 module→1, đúng **6/6** (3 marker × 2 app) = 'đúng-BỘI-SỐ'. 2 control ÂM (`Đề xuất ký kết hợp đồng`, `Ký cứng & phát hành`) =0 cả 2 bundle + grep repo-wide 0-hit. Menu-data 5/5: **root=12 (0 double-insert) · DUPKEYS=0** · 2 root mới `KeHoachKyKet` ord26 / `HopDongCung` ord32 vis=1 (13 rows/13 canread mỗi key) · 3 root vẫn vis=0 · rename prefix `Duy…` ×2 · `Pe%` 197/185. 🔴 **FAIL #9 — BẮT ĐƯỢC REGRESSION NGOÀI CHECKLIST:** Serilog `Revoked 9 permission rows … Hợp đồng/Danh mục khỏi non-Admin` ⇒ đo lại grant-38-key của đợt-1: **47/494 canread, 0 key còn 13** (Admin=38 · Procurement=9 · 11 role còn lại=0, mất 447). Gốc = `DbInitializer.cs:2203-2236` **`RevokeTemporarilyHiddenModulesAsync` UNGATED chạy MỖI startup**, tập revoke TRÙNG KHỚP grant-set ⇒ **grant SQL tay không bao giờ bền, phải sửa CODE**. ⚠️⚠️ **BÀI HỌC LỚN NHẤT — bẫy ĐO-TRƯỚC-RECYCLE: pool restart nằm ở CUỐI deploy-window, nên đo DB lúc run còn `running` = đo trạng thái CŨ, KHÔNG chứng minh được 'sống sót recycle'. Mọi mục durability PHẢI đo SAU `status=success`.** (đợt-1 tao đo 11:46 vs restart 11:49 → kết luận sai). ⚠️ deploy `Remove-Item …pi\*` **xoá luôn `logs/`** ⇒ Serilog chỉ còn từ lần deploy cuối, không truy ngược được restart trước. ⚠️ prod Identity table = **`Roles`** không `AspNetRoles` (#472 tái xác nhận). Đợt-2 tự nó ship ĐÚNG — KHÔNG rollback. Evidence: `runs/2026-07-29-S159-tong-quan-pipeline-menu/cicd-verify-1a47a61.md`. Tag `[run-423, partial-8-9, do-truoc-recycle-trap, revoker-ungated-durability, marker-count-per-module, gate-562]`
|
||||
- _(**#423 S159 đợt-2 `1a47a61` tab-strip 4GĐ + menu 4-GĐ — PARTIAL 8/9**: 8 mục ship đúng, **FAIL mục-9 = phát hiện lớn nhất arc**: `RevokeTemporarilyHiddenModulesAsync` UNGATED lật 447/494 row grant SQL tay (còn 47: Admin 38 + Procurement 9). ⚠️ **bẫy ĐO-TRƯỚC-RECYCLE** (đo lúc `running` = trạng thái CŨ) — đã nâng thành luật ở mục 'Recurring bug patterns'. 🔑 marker-count = SỐ MODULE chứa chuỗi. verbatim → `archive/2026-07.md`)_
|
||||
|
||||
- _(**07-29 `b5799fc` ĐỢT-1 (Tổng quan tất cả luồng duyệt) PASS 9/9** — Run #422 5m59s, gate 562, 0 mig, user js `DptYR4wL`→`0GO8mgrG`. 🔑 **marker PHÂN-BIỆT-PHIÊN-BẢN**: designer gốc `Pipeline 4 giai đoạn` vs bản-sau-review `Toàn trình 4 giai đoạn` ⇒ bundle có `Toàn trình`=1 & `Pipeline`=0 ⟹ chứng prod chạy bản ĐÃ-FIX. Dùng khi review sửa CHỮ: chọn cặp chuỗi cũ/mới làm phép phân-biệt. ⚠️ **bẫy hình-dạng-truy-vấn**: control `Pe` ra 182/182 vs kỳ vọng 197/185 vì tao dùng `LEFT(MenuKey,3)='Pe_'` còn gốc là `LIKE 'Pe%'` (+`Permissions`/`Personal`/`PeWorkflows`=+15/+3) ⇒ **LỆCH-số phải truy PHẠM-VI-QUERY trước khi hô drift**. 🔴 M7 lượt này SAI (đo 11:46 TRƯỚC restart ~11:49) → regression lộ ở đợt-2, đóng ở đợt-3/4. Evidence `cicd-verify-b5799fc.md`)_
|
||||
|
||||
|
||||
@ -55,3 +55,8 @@
|
||||
## Run #419 `a52a91e` (2026-07-28) — moved from L1 @S159 curate, byte-exact
|
||||
|
||||
- **07-28 verify `a52a91e` (PE: cảnh báo amber ô Địa điểm khi dự án thiếu Location) [S157, FE-ONLY 2-app, CLEAN-return]:** VERDICT **PASS 9/9** — Run **#419** (id=532) success **5m40s** (09:40:18→09:45:58+07); gate **562 EXACT từ log** (45 Domain + **517** Infra, 0 fail/skip) — khớp canonical `docs/STATUS.md:452` **cả TỔNG lẫn TÁCH 45/517** (khớp thành-phần > khớp tổng). NO-Mig đúng kỳ vọng: `git diff f4494cf..a52a91e -- 'src/Backend/*' '*Migrations*' | wc -l` = **0** ⇒ Mig vẫn 68, 89-table, KHÔNG query DB (không có gì để đối chiếu). 🔑 **Phép quyết định = byte-marker #77, KHÔNG tin status CI:** tải thẳng JS prod, `bytes.count(marker.encode())` 4 chuỗi VN độc lập → **2/2 app, count=2 mỗi app = KHỚP SỐ FILE/app trong diff** (`PeHeaderForm`+`PeWorkspaceCreateView`) — "đúng-BỘI-SỐ" mạnh hơn "tìm thấy ≥1", bắt được cả ca ship nửa-vời. Bundle js rotate CẢ 2 (admin `BK8w3QxU`→**`8-QYcMOg`** · eoffice `DYnxSI0T`→**`DptYR4wL`**; css `DiNJjaoo`/`BRCvEHvC` FROZEN ×2 = #69-REFINE lành tính — utility `text-amber-700`/`text-[11px]` đã có sẵn) real-size 1.675.282/1.575.513B, LM 09:44:33/09:45:30+07 in-window. 🆕 **Control âm 2 TẦNG (tái dụng):** (i) fake-hash→200 nhưng `ct=text/html` 900/876B marker=0; (ii) **hash CŨ cũng rơi về text/html ⇒ bundle cũ BIẾN MẤT khỏi đĩa** = chứng GHI-ĐÈ thật (mạnh hơn hash-delta đơn thuần). Smoke: health live+ready 200 · login 200 tok-468 · `/api/projects` 200 (đúng nguồn `location` feature đọc) · PE/menus/suppliers 200. ⚠️ **Self-correct on-disk:** viết "range=6 file" SAI (đó là số của 2 commit) — range thật **13 commit/26 file** (11 `wal:` xen giữa); đính chính ngay, BE=0 không đổi. Tag `[run-419, pass-9-9, fe-only, marker-2of2-count-exact, old-hash-gone-control, js-rotate-css-frozen, gate-562]`
|
||||
|
||||
|
||||
## Run #423 `1a47a61` (2026-07-29, S159 đợt-2) — moved from L1 @S159 curate, byte-exact
|
||||
|
||||
- **07-29 verify `1a47a61` ĐỢT-2 (tab strip 4 GĐ trên phiếu PE + menu 4-GĐ + đánh số 01-07 nhóm HĐ) [S159, 8 file code 190+/18−, FE×2 + seeder-only BE]:** **PARTIAL 8/9** — Run **#423** (id=536) success **5m44s**; gate **562 EXACT** (45+517). 0 migration (BE chỉ `MenuKeys.cs` const + `DbInitializer.cs` seeder). Bundle rotate **4/4** (admin js `BHodkPNV`→`D6Pz7nfV` css `DiNJjaoo`→`B1ErASuI` — admin-css rotate ĐÚNG vì fe-admin có sửa, khác đợt-1 frozen; user js `0GO8mgrG`→`Bpb8WEuY` css `BARVqkP-`→`7oAYFlzM`). Mirror `PePipelineStrip.tsx` 2 app **md5 IDENTICAL** `ef7b7824…`. 🔑 **Quy luật đếm marker (nối tiếp bài comment-strip): bundle-count = SỐ MODULE chứa chuỗi** (bundler pool hằng-số trong 1 module ⇒ nhiều occurrence cùng file gộp còn 1). USER=2 module→2 · ADMIN=1 module→1, đúng **6/6** (3 marker × 2 app) = 'đúng-BỘI-SỐ'. 2 control ÂM (`Đề xuất ký kết hợp đồng`, `Ký cứng & phát hành`) =0 cả 2 bundle + grep repo-wide 0-hit. Menu-data 5/5: **root=12 (0 double-insert) · DUPKEYS=0** · 2 root mới `KeHoachKyKet` ord26 / `HopDongCung` ord32 vis=1 (13 rows/13 canread mỗi key) · 3 root vẫn vis=0 · rename prefix `Duy…` ×2 · `Pe%` 197/185. 🔴 **FAIL #9 — BẮT ĐƯỢC REGRESSION NGOÀI CHECKLIST:** Serilog `Revoked 9 permission rows … Hợp đồng/Danh mục khỏi non-Admin` ⇒ đo lại grant-38-key của đợt-1: **47/494 canread, 0 key còn 13** (Admin=38 · Procurement=9 · 11 role còn lại=0, mất 447). Gốc = `DbInitializer.cs:2203-2236` **`RevokeTemporarilyHiddenModulesAsync` UNGATED chạy MỖI startup**, tập revoke TRÙNG KHỚP grant-set ⇒ **grant SQL tay không bao giờ bền, phải sửa CODE**. ⚠️⚠️ **BÀI HỌC LỚN NHẤT — bẫy ĐO-TRƯỚC-RECYCLE: pool restart nằm ở CUỐI deploy-window, nên đo DB lúc run còn `running` = đo trạng thái CŨ, KHÔNG chứng minh được 'sống sót recycle'. Mọi mục durability PHẢI đo SAU `status=success`.** (đợt-1 tao đo 11:46 vs restart 11:49 → kết luận sai). ⚠️ deploy `Remove-Item …pi\*` **xoá luôn `logs/`** ⇒ Serilog chỉ còn từ lần deploy cuối, không truy ngược được restart trước. ⚠️ prod Identity table = **`Roles`** không `AspNetRoles` (#472 tái xác nhận). Đợt-2 tự nó ship ĐÚNG — KHÔNG rollback. Evidence: `runs/2026-07-29-S159-tong-quan-pipeline-menu/cicd-verify-1a47a61.md`. Tag `[run-423, partial-8-9, do-truoc-recycle-trap, revoker-ungated-durability, marker-count-per-module, gate-562]`
|
||||
|
||||
@ -140,4 +140,5 @@
|
||||
- 2026-07-17 · S134 #399 `6664a61` PE lũy-kế tạm-tính (0-mig) GO — js+css rotate… · PASS · `2026-07.md` · substring:"S134 (07-17) verify 6664a61"
|
||||
- 2026-07-23 · S155 #407 `4510cea` App chuan-hoa ma du-an normalize + PE attach-gate FE both-apps… · PASS 5/5 · `2026-07.md` · substring:"Run #407 `4510cea` (2026-07-23)"
|
||||
- 2026-07-27 · S155 #416 `b1bae77` Workflow duyet-TAI-CHO + co F6 AllowApproverDelete… · PASS · `2026-07.md` · substring:"Run #416 `b1bae77` (2026-07-27)"
|
||||
- 2026-07-28 · S157 #419 `a52a91e` PE canh-bao amber o Dia-diem khi du-an thieu Location… · PASS 9/9 · `2026-07.md` · substring:"Run #419 `a52a91e` (2026-07-28)"
|
||||
- 2026-07-28 · S157 #419 `a52a91e` PE canh-bao amber o Dia-diem khi du-an thieu Location… · PASS 9/9 · `2026-07.md` · substring:"Run #419 `a52a91e` (2026-07-28)"
|
||||
- 2026-07-29 · S159 #423 `1a47a61` dot-2 tab strip 4GD + menu 4-GD — PARTIAL 8/9, bat regression revoker an 447/494 row · `2026-07.md` · substring:"Run #423 `1a47a61` (2026-07-29"
|
||||
@ -1,5 +1,7 @@
|
||||
# Reviewer Agent — Persistent Memory
|
||||
|
||||
- **S159 (07-29) đợt-5 menu 6-leaf HĐ + endpoint `/contracts/deleted` (PWF 10 FLAG 0H/4M/6L):** 🔴 **con-số TRONG CHÚ THÍCH cũng là khẳng-định đo-được** — diff SỬA comment D3 thành "NAY CÓ 2 chỗ `IgnoreQueryFilters`", `grep -rn` ra **3 file/17 site** (ApprovalWorkflowV2AdminFeatures 15 site); tôi suýt duyệt bằng mắt ⇒ **luật: comment mang số ⇒ chạy phép đếm, và số viết dưới dạng LUẬT ("chỗ duy nhất ⇒ CẤM…") sai thì lan sang người sửa sau**. · class **`mirror-nửa-vời khai thừa phạm-vi`**: comment nói "Hộp thư + Tổng quan viết HOA" nhưng "Hộp thư" render bằng `StaticLeaf` KHÁC component ⇒ đọc comment ≠ đọc call-site. · **cookie-cutter phải soi CẢ phần PE đã CHẶN**: bản HĐ copy list-deleted nhưng bỏ `disabled={deletedView}` ⇒ click row → detail 404 → panel **trắng** (page thiếu nhánh `isError`) + mobile navigate ra trang 404. · mirror 2-app: fe-admin ẩn `Ct_*` (⇒ 21 leaf mới KHÔNG cần route) nhưng `Khkk_*` KHÔNG ẩn ⇒ thiếu 2 staticMap = drop silent #50 — **kiểm hide-filter TRƯỚC khi kêu thiếu mirror**. · `tsc -p tsconfig.app.json` EXIT=0 + `noUnusedLocals` = chứng cắt-hero-không-mồ-côi MẠNH HƠN grep (nhắc lại: fe-user KHÔNG bật `strict`). · `Contracts` ∈ `MenuKeys.All` ⇒ policy `Contracts.Read` có thật; `MenuPermissionHandler` khớp ĐÚNG-KHOÁ, KHÔNG kế thừa root (#82). [→ `runs/2026-07-29-S159-tong-quan-pipeline-menu/sub-reviewer-diff-dot5.md`]
|
||||
|
||||
- **S159 (07-29) dashboard "toàn trình" đợt-4 FE-only (fe-user 984+/337−, PWF 7 FLAG 0H/2M/5L):** 🔴 **`tsc` XANH đo ÍT hơn vẻ ngoài** — `fe-user/tsconfig*.json` KHÔNG có `strict`/`strictNullChecks` (chỉ `noUnusedLocals`) ⇒ **tra cờ tsconfig TRƯỚC khi trích tsc làm bằng chứng**; ngược lại `noUnusedLocals`+pass = bằng chứng MẠNH HƠN grep cho claim "đã gỡ khối X" (compiler chặn nếu sót import). · class **`nhãn khẳng định chiều-ngược`**: dot-map `none='chưa tới'` — không-suy-diễn "đã xong" là đúng, nhưng "chưa tới" CŨNG là khẳng định ⇒ sai cho GĐ ĐỨNG TRƯỚC + sai khi `contractId != null` **có sẵn trong payload**. · pager tính `data.page±1` (keepPreviousData ⇒ stale) thay vì state ⇒ double-click = no-op. · **verify đích điều hướng phải đọc CẢ breakpoint trang đích** (panel `hidden lg:block` ⇒ ngõ cụt mobile; trang đích tự có nhánh `matchMedia` mà caller mới không copy). · regression đo HEAD⟂work bằng `grep -c` từng token: count 5→2 mà KHÔNG mất nhãn (inline-prop → `STAGES[i].title`) ⇒ **đếm lệch phải truy CẤU TRÚC trước khi kêu hồi quy**. · tự tính lại 4 tỉ-số contrast + tra `@theme` mọi `var(--color-*)` (0 dòng `--color-*: initial` ⇒ nấc mặc định còn) — 3/3 số designer ĐÚNG. · lời khai sub-file "766→1.130 dòng" SAI (đĩa 1.413 = 766−337+984). [→ `runs/2026-07-29-S159-tong-quan-pipeline-menu/sub-reviewer-diff-dot4.md` 28,5KB]
|
||||
- **[→ archive/2026-07.md @S159-curate] S155-đợt2 (07-27) PE delete-approver DIFF-review #2 (12 FLAG 2H/4M/6L · ĐỪNG-DEPLOY):** class **`hợp-đồng-đứt-giữa-2-bờ`** (FE type khai cờ, BE DTO không có ⇒ nút chết mà tsc+build+561-test đều xanh) · policy-gate đo DB 11/13 vai `CanDelete=0` · mã ĐỔI giữa lượt soi ⇒ re-đo đĩa TRƯỚC verdict · đổi-chiều-assert ≠ nới-assert.
|
||||
- **[→ archive/2026-07.md @S155-curate] S155 ×2 (07-27) PE delete-approver — DIFF-review đợt 1 tiền-deploy (14 FLAG 4H/6M/3L/1I, DEPLOY-CÓ-RỦI-RO) + SPEC-review (PWF 4H/8M/5L):** 🔴 class MỚI **`HIGH nằm ở git chứ không ở mã`** — 2/3 file migration UNTRACKED mà snapshot ĐÃ tracked ⇒ `commit -a` nạp model-có-cột nhưng bỏ migration ⇒ `Invalid column name` cả 7 module, build+test vẫn xanh ⇒ **gate deploy phải chấm `git status ^??` cho `Migrations/`** · `ExecuteDelete` ngoài transaction TRƯỚC `SaveChanges` = mất dữ liệu không hoàn tác · purge xoá CỨNG chữ ký phiếu XOÁ MỀM ⇒ **test đang KHOÁ chiều ngược, vá xong test đỏ ≠ hồi quy, CẤM nới assert** · ~600 dòng BE = mã chết (`grep api.put`=0) · **xanh-giả bắt bằng phép 2-thế-giới** (còn-gate ⟂ bỏ-gate cùng cho 0 ⇒ 0 thông tin) · CLEAN chứng bằng **liệt kê vét cạn** write-site + sweep gián-tiếp (#81-EXT) · 2 lỗi TỰ BẮT: trích line-number **từ diff** (diff không mang số dòng file) · `grep -v Snapshot` ăn nhầm tên migration · **file bị sửa TRONG LÚC soi ⇒ neo bằng NỘI DUNG DÒNG + ghi bản-đồ-trôi**.
|
||||
|
||||
@ -43,8 +43,8 @@ Em main PHẢI echo **TOÀN BỘ nội dung command body này** (đầy đủ Ph
|
||||
- 🟢 **cicd-monitor** — Run verdict + bundle hash rotate + Mig prod + corpus drift
|
||||
- 🟫 **tooling-auditor** (monitor H1) — CHỐT tooling/docs-freshness 4-mặt + new-alloc audit (chạy ở §L.b(g))
|
||||
- ⬜ **harvest-curator** (monitor H2) — GATE harvest-integrity 5-trục + close-gate run-trace `runs/<id>/` (**DUAL-ACCEPT** — xem §L.b(f)) (chạy ở §L.b(d)(f))
|
||||
- 🔷 **lead-stale-auditor** (monitor **H24**) — view LỆCH source của cái LEAD surface (chạy theo NHỊP §L.b(j), KHÔNG mỗi phiên)
|
||||
- 🔶 **lead-gap-auditor** (monitor **H24**) — cái BỊ THIẾU trong cái LEAD surface (chạy theo NHỊP §L.b(j), KHÔNG mỗi phiên)
|
||||
- 🔷 **lead-stale-auditor** (monitor **H24**) — view LỆCH source của cái LEAD surface (🔴 **VÔ-ĐIỀU-KIỆN mỗi bookend** §L.b(j)(ii); chỉ `deep` theo nhịp — vá FLAG-1 @S159)
|
||||
- 🔶 **lead-gap-auditor** (monitor **H24**) — cái BỊ THIẾU trong cái LEAD surface (🔴 **VÔ-ĐIỀU-KIỆN mỗi bookend** §L.b(j)(ii); chỉ `deep` theo nhịp — vá FLAG-1 @S159)
|
||||
- 🔬 **ring1-audit** (monitor **KIỂM-vòng**, C4 S145) — verify OUTPUT H1+H2 vòng tooling/harvest (AUTO sau đo §L.b(g-bis)) · vai #1/3 round-KIỂM
|
||||
- 🔬 **ring2-audit** (monitor **KIỂM-vòng**, C4 S145) — verify OUTPUT lead-stale+lead-gap vòng soi-lead (AUTO sau cặp H24 — @§L.b(j)(ii-bis) nếu cặp nổ ở close, @[`session-start`](session-start.md) §2.1.8 nếu cặp nổ ở open; *wherever it fires*) · vai #2/3
|
||||
- 🔬 **ring4-audit** (monitor **KIỂM-vòng**, C4 S145) — verify OUTPUT /sleep-recovery gist+Fidelity vòng nén-ngủ (AUTO sau **mỗi lượt nén** `/sleep-recovery-memory-l2` — post-step của skill, *wherever it fires*: AUTO-khi-KÊU @[`session-start`](session-start.md) §2.1.2 HOẶC @§L.b(c) dưới — owner @S152 "vòng 1→5 auto", 🧊 consent retired) · vai #3/3
|
||||
@ -109,7 +109,7 @@ Em main PHẢI echo **TOÀN BỘ nội dung command body này** (đầy đủ Ph
|
||||
🔴 **Scope theo `git %cI`, KHÔNG theo `mtime`** — mtime **không bền** (Dropbox resync ghi đè; R3-m5 chứng, FIX#4 đã bỏ mtime khỏi W0.5 vì đúng lý-do này). *(v1:120 nguyên-bản ghi "mtime-trong-phiên" — đó là bản TRƯỚC khi mtime bị bác; đừng bê nguyên.)*
|
||||
**Acceptance:** chênh-lệch **0**, HOẶC in được danh-sách lệch. 🔸 *Sàn-5 là **informational** — chính vì thế nó **an-toàn để sai**, và cái sai đó đã trả giá bằng việc tự lộ 2 bug ở lần chạy đầu. Nếu nó là cổng-chặn thì 9 dương-giả kia đã chặn cả phiên.*
|
||||
|
||||
- **(j) 🔷🔶 H24 lead-self-audit — counter + spawn theo NHỊP (adopt S122; đối-xứng `session-start §2.1.8`):**
|
||||
- **(j) 🔷🔶 H24 lead-self-audit — counter luôn tick · cặp light spawn VÔ-ĐIỀU-KIỆN mỗi bookend · chỉ `deep` theo NHỊP (adopt S122 · hình B S149 · vá FLAG-1 S159; đối-xứng `session-start §2.1.8`):**
|
||||
- **(i) BÁO dòng-nợ 4-vế (nâng dòng H24 cũ — Item 2 S135):** gọi `powershell.exe -ExecutionPolicy Bypass -File scripts/nhip-no-probe.ps1` → in **1 dòng** `NHIP-NO: kiem light a/e deep b/f <ok|OVERDUE:…> | tran-bo-nho x over-cap | run-chua-gom y` (vế-1 nhịp-kiểm THAY dòng `counter=… light … deep … OVERDUE` cũ, đọc `.session-counter.json` + `h24_cadence`). 🔴 **probe KHÔNG chặn**: lỗi → in `probe-loi (khong chan)` → đi tiếp. 🔴 **Nhịp ĐỌC từ config, CẤM hardcode ở đây** (detector `governance-detectors.ps1` H24-2 **fail-loud** nếu key thiếu — **không** giả-định default).
|
||||
- **(ii) 🔄 HÌNH B — cặp soi-lead BOOKEND VÔ-ĐIỀU-KIỆN @close (đảo @S149; spec `runs/2026-07-24-S149-hoi-tu-bookend/spec-…md` qua gate W2 GO-WITH-FIXES):** spawn cặp (`lead-stale-auditor` + `lead-gap-auditor`) **MỌI closeout, KHÔNG cổng nhịp light, KHÔNG consent**. Cặp trả FLAG-list (khuôn `## FLAG-N — \`class\` — SEV` bắt buộc — M4/C11 soi) → ghi số bằng **MÁY**: `powershell.exe -ExecutionPolicy Bypass -File scripts/h24-signal-write.ps1 -Session S<nn> -Flags '<class,class,…>' -AuditKind light` (procedural max-1/class/phiên · enum-đóng · atomic · 🔴 tôn trọng `_frozen_until_owner` — dải JUMP treo {4,3,3} KHÔNG bị máy reset, nợ của owner).
|
||||
- 🔴 **Đơn-vị tally = phiên-LOGIC (FIX-1 W2):** 2 lượt bookend (@open+@close) cùng phiên = **MỘT quyết định/class** — fire nếu fire ở ≥1 đầu; reset chỉ khi clean CẢ HAI đầu, ≤1 lần/phiên (script thi hành, không phải trí nhớ).
|
||||
|
||||
@ -167,7 +167,7 @@ git check-ignore -q .claude/sessions/session-1 ; echo "exit=$?" # → exit=1
|
||||
- 🔎 **ctx-curator** (vai-1 vòng Ctx, INFORM-only, **+S153 OWNER-VETO hub-form 3-vai**) — KIỂM bản NHÁP block @MỖI `/pause` (5 khoản ngữ-nghĩa, SAU máy-draft; carve-out duy nhất của `pause.md §1`); trả CHỈ văn bản `CTX-CURATOR: DAT|SUA|SKIP-CO-KHAI`.
|
||||
- 🔎 **ctx-verifier** (vai-2 vòng Ctx, INFORM-only, **+S153 OWNER-VETO**) — ĐỐI-CHIẾU 4-khoản (B-vs-`next:` TIN-SỔ-MÁY · C-vs-bản-đồ · D-chưa-đổi-nhãn · con-trỏ) + BẢN NẠP-LẠI ≤15 dòng @MỖI `/tiep`, kết bằng `LEAD-PHAI-NHAC-LAI:` (bằng-chứng nội-dung sàn khoản 3).
|
||||
- 🔴 **Bộ-ba trio-memory = trục CHÍNH-HỆ-MEMORY, KHÁC H1/H2/H24/reviewer** — chạy **TUẦN TỰ** eval→refine→audit, **AUTO 1 vòng/phiên giữa 2 bookend (owner @S151 — 🧊 consent-gated S141 superseded)**; nghi-thức đầy-đủ ở **§2.1.9** (đầu phiên) / `session-end §L.b(k)` (cuối phiên).
|
||||
- 🔴 **2 vai H24 = trục KHÁC H1/H2, KHÔNG gộp:** H1 = tooling-freshness · H2 = harvest-integrity · **H24 = soi chính LEAD**. Class-flag lấy từ **enum ĐÓNG** `lead_self_audit.flag_classes` (`.claude/agent-memory/memory-budget.json`) — **vai KHÔNG tự chế class**. Nhịp = `h24_cadence` (cùng file), **KHÔNG chạy mỗi phiên**.
|
||||
- 🔴 **2 vai H24 = trục KHÁC H1/H2, KHÔNG gộp:** H1 = tooling-freshness · H2 = harvest-integrity · **H24 = soi chính LEAD**. Class-flag lấy từ **enum ĐÓNG** `lead_self_audit.flag_classes` (`.claude/agent-memory/memory-budget.json`) — **vai KHÔNG tự chế class**. Nhịp: **cặp light = VÔ-ĐIỀU-KIỆN mỗi bookend** (hình B) · **deep = theo `h24_cadence.deep_every`**. *(vá FLAG-1 @S159)*
|
||||
- Task match delegate criteria (ACCEPT) → BẮT BUỘC delegate (xem `.claude/agents/README.md` decision tree)
|
||||
- Con cũ rảnh TRONG-session → resume qua SendMessage (khả dụng lại — F2 S100) HOẶC fresh spawn re-inject MEMORY; cross-session luôn fresh spawn
|
||||
- Nạp full context project cho sub-agent spawn, giữ context sống đến cuối session
|
||||
@ -241,7 +241,7 @@ git check-ignore -q .claude/sessions/session-1 ; echo "exit=$?" # → exit=1
|
||||
|
||||
### 2.1.8 H24 lead-self-audit — TICK counter + OVERDUE check (adopt S122 W3; file+shape = W2)
|
||||
|
||||
> Floor H24: 2 vai `lead-stale-auditor` + `lead-gap-auditor` soi **chính LEAD**, chạy **theo NHỊP** chứ KHÔNG mỗi phiên. Nhịp đọc từ `h24_cadence`; trạng-thái đếm ở `.claude/governance/.session-counter.json`. 🔴 **Lead = single-writer** của counter. **W2 sở-hữu file + hình-dạng; W3 (đây) sở-hữu NGHI-THỨC tick.**
|
||||
> Floor H24: 2 vai `lead-stale-auditor` + `lead-gap-auditor` soi **chính LEAD**. 🔴 **Cặp light chạy VÔ-ĐIỀU-KIỆN ở MỖI bookend** (hình B @S149 — KHÔNG cổng nhịp, KHÔNG consent); **chỉ `deep` còn cổng** `h24_cadence.deep_every`. *(vá FLAG-1 @S159: 6 site trong 2 file LỆNH từng ghi "theo NHỊP, KHÔNG mỗi phiên" — mâu thuẫn với chính `:269` cùng file + 2 persona, và **trái hành-vi đo được** [S154 counter≈28, S158 counter=32, đều < light_every=6 mà cặp VẪN chạy]. Lớp persona + STATUS đã vá @S153; lớp LỆNH — nơi thật sự điều-khiển spawn — sót tới S159.)* Trạng-thái đếm ở `.claude/governance/.session-counter.json`. 🔴 **Lead = single-writer** của counter. **W2 sở-hữu file + hình-dạng; W3 (đây) sở-hữu NGHI-THỨC tick.**
|
||||
|
||||
**(a) TICK — content-addressed, KHÔNG blind-increment — 🔄 @S149 CHẠY BẰNG MÁY:** `powershell.exe -ExecutionPolicy Bypass -File scripts/session-counter-tick.ps1 -Session S<nn>` (M2 — thi hành ĐÚNG `_contract` trong counter-file: OR-guard idempotent · 3-nhánh classify · atomic temp+move · fail-loud exit≠0; fault-inject 5/5 @S149). **Prose dưới = MÔ TẢ contract để người đọc hiểu, KHÔNG phải quy trình tay nữa** — tay chỉ khi script exit≠0 (fail-loud → báo anh):
|
||||
```
|
||||
@ -267,7 +267,7 @@ ngược lại → counter += 1 ; last_ticked_session = S<nn> ; last_ticked_head
|
||||
**(d) JUMP:** một class trong `lead_self_audit.flag_classes` lặp **≥ `jump_on_class_repeat`** lần → **kéo audit tới sớm**, bất-kể counter.
|
||||
|
||||
**(e) BÁO dòng-nợ 4-vế (nâng dòng H24 cũ — Item 2 S135):** gọi `powershell.exe -ExecutionPolicy Bypass -File scripts/nhip-no-probe.ps1` → in **1 dòng** `NHIP-NO: kiem light a/e deep b/f <ok|OVERDUE:…> | tran-bo-nho x over-cap | run-chua-gom y` (vế-1 nhịp-kiểm = `counter/light/deep/OVERDUE` thay dòng H24 cũ). 🔴 **probe KHÔNG chặn**: lỗi → in `probe-loi (khong chan)` → đi tiếp. 🔄 **HÌNH B @S149 (bookend-hoá light — spec qua gate W2):** cặp soi-lead (`lead-stale-auditor` + `lead-gap-auditor`) spawn **VÔ-ĐIỀU-KIỆN mỗi đầu phiên** — KHÔNG còn cổng OVERDUE(light); **OVERDUE(deep) GIỮ** (deep-sweep theo `deep_every`). Cặp trả → ghi số bằng máy `scripts/h24-signal-write.ps1` (đơn-vị tally = phiên-LOGIC: @open+@close gộp 1 quyết-định/class — FIX-1). `ring2-audit` AUTO sau cặp, **PIN đích-danh run-folder tươi** (cấm glob-latest — carve-out §A1). Luật sống canonical ở [`session-end`](session-end.md) §L.b(j)(ii) — **B1, mục này chỉ ÁP, không chép phạm-vi**. 🧊 *Bản S146→S149 = "auto khi tới nhịp"; đảo @S149 theo direction anh "chạy đc VIPIX, AI_INFRA" (lineage đầy đủ ở §L.b(j)(ii)).*
|
||||
🔸 **Phạm-vi 1 dòng (chống đọc nới):** gỡ **cổng NGƯỜI**, **giữ cổng NHỊP** (`h24_cadence`) ⇒ auto **khi tới nhịp**, KHÔNG phải mỗi phiên. Khác bookend-hub (hub bỏ counter) — SE chưa theo nhánh đó.
|
||||
🔸 **Phạm-vi 1 dòng (chống đọc nới):** gỡ **cổng NGƯỜI**, **giữ cổng NHỊP** (`h24_cadence`) ⇒ gỡ **CẢ HAI** cổng cho cặp **light** ⇒ **auto MỖI bookend**, không chờ nhịp. **Cổng NHỊP chỉ còn cho `deep`** (`deep_every`). *(vá FLAG-1 @S159 — bản trước ghi "giữ cổng NHỊP ⇒ auto khi tới nhịp, KHÔNG phải mỗi phiên", **trái** `:269` cùng file và trái hành-vi đo được.)* Khác bookend-hub (hub bỏ counter hẳn) — SE **giữ counter** để đo `deep`.
|
||||
⚠️ 🧊 Vế "KHÁC sleep-check §2.1.2 (vẫn CONSENT)" **hết hiệu lực @S152** — owner chốt *"các vòng 1→5 auto luôn"* ⇒ sleep-check CŨNG AUTO-khi-KÊU. **Từ S152: cả 5 vòng (V1 tooling/harvest+ring1 · V2 soi-lead+ring2 · V3 trio · V4 nén-ngủ+ring4 · V5 floor/detector) đều AUTO, 0 cổng consent** — vết M4 (in dòng trạng-thái + WAL khi skip) giữ cho MỌI vòng; auto ≠ chạy-chìm, auto ≠ chạy-mù (fail-safe NO-OP khi thiếu input giữ nguyên). *(Lineage cổng-consent: trio gỡ @S151 · sleep gỡ @S152 — đây là cổng consent CUỐI CÙNG của 5 vòng.)*
|
||||
|
||||
🔴 **Số nhịp = quyền anh.** Bước này **ĐỌC** `h24_cadence`, **KHÔNG** ghi. **CẤM hardcode nhịp ở đây** — copy số vào doc dẫn-xuất chính là drift mà single-source sinh ra để chặn (detector `governance-detectors.ps1` H24-2 **fail-loud** nếu key thiếu, **không** giả-định default).
|
||||
|
||||
@ -11,9 +11,9 @@
|
||||
},
|
||||
"_tick_invariant_note": "Tick invariant (hub dede7ec5 Delta-1, verbatim): moi LAN-CHOT +1; mot cap dung-noi tang DUNG +1, khong +2, khong +0. SE form = tick-at-entry-gate idempotent-per-label (hub Delta-2 recovery-gate +1 = permitted form) - NET +1/session-label EQUIV hub +1/cap on CLOSED pairs; an OPEN pair is transiently +0 until its entry gate fires (hub blessed, 9a35405b block-1 phep dung-noi). Guard song-con = label-convention (session-start 2.1.8: new conversation = new S\u003cnn\u003e label, NEVER reuse). history[] is append-unbounded BY DESIGN =\u003e absence of a marker = never-happened (safe semantics); IF a FIFO cap is ever added, eviction MUST be handled explicitly (absence-vi-bi-day != absence-vi-chua-xay-ra - log-BOUNDED design-note dede7ec5).",
|
||||
"_seed_honesty": "Seeded UNTICKED on purpose. counter=0 and last_ticked_* = null mean \u0027no tick has ever happened\u0027, which is the truth at S121 - the ritual that performs the tick lands in W3. Seeding a fake first tick here would make the very first cadence reading a lie, and H24 exists to catch exactly that kind of invented number.",
|
||||
"counter": 32,
|
||||
"last_ticked_session": "S158",
|
||||
"last_ticked_head": "6c263dde7f85c29165187d44af0e7b302b1bd969",
|
||||
"counter": 33,
|
||||
"last_ticked_session": "S159",
|
||||
"last_ticked_head": "bbc39be3abf80285ca11a665a574427bdb2d602c",
|
||||
"last_ticked_at": "2026-07-28",
|
||||
"last_audit": {
|
||||
"light_at_counter": 27,
|
||||
@ -166,6 +166,11 @@
|
||||
"at": "2026-07-28",
|
||||
"session": "S158",
|
||||
"event": "CLEAN tick (session-counter-tick.ps1 M2): counter 31->32, session S157->S158, head 73166c5->6c263dd. Classify-before-tick: no regression (n=158 >= stored n=157); last_ticked_head reachable (merge-base --is-ancestor exit 0). 4 fields updated, 1 history entry appended, written atomically (temp + Move-Item -Force)."
|
||||
},
|
||||
{
|
||||
"at": "2026-07-28",
|
||||
"session": "S159",
|
||||
"event": "squash-benign (session-counter-tick.ps1 M2, contract fail_loud_on_regress trigger-2 BENIGN branch): counter 32->33, session S158->S159, head 6c263dd->bbc39be. last_ticked_head 6c263dd object EXISTS (cat-file=commit) but NOT reachable (merge-base --is-ancestor exit!=0), counter not regressed => a closeout squash lifted the ticked wal:/session commit out of history (expected drift, not tamper). Trace appended, continue, no owner alarm. Written atomically (temp + Move-Item -Force)."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -44,7 +44,30 @@
|
||||
|
||||
<!-- FLOW-START — entries append bên dưới dòng này, mới nhất ở CUỐI -->
|
||||
|
||||
_(chưa có PAUSE — entry đầu append @`/pause` đầu tiên của phiên)_
|
||||
### PAUSE-1 2026-07-29T14:40:10+07:00
|
||||
|
||||
> anh: xong thì /pause nhé
|
||||
|
||||
**(1) quyết-định đã CHỐT trong cửa này (window-1 = trọn L8 tới giờ):**
|
||||
- Sàn-3 ①: anh chốt *"nối mạch tiếp cho xong hẳn rồi bootstrap"* → retro-harvest 2 synthesis L7.
|
||||
- Bookend: anh chốt **chạy đủ 5 vòng** · arc = **vá 2 chỗ hở spec KHKK**. 5 khoản (49)-(53): *"đã có giải pháp rồi thì làm luôn, tao muốn các vòng này tự động luôn"* → thi hành trọn.
|
||||
- Arc-2 UI (5 đợt): ranh 4-GĐ như lead map (b.12∈GĐ2) · pipeline 4 GĐ đầy đủ · ẩn 3 nhóm = *"chỉ hiển thị ở trang admin — để tao khỏi nhầm giữa cái đã public và cái đang làm"* · ❹ *"đang phát triển → cho hiển thị hết để mọi người góp ý, còn duyệt NCC thì cứ để như cũ"* (RỦI-RO CÓ Ý THỨC — authz HĐ trần, đã cảnh báo, anh tái khẳng định) · đánh số 01-07 (anh Kiệt) · tab strip 4 GĐ trên phiếu · menu = 4 GĐ + 2 tên chỉnh (*"Trình ký Hợp đồng đổi thành Duyệt hợp đồng, ký cứng -> Hợp đồng cứng"*) · *"Thêm chữ bảng cứng vào nhé"* (nhãn theo verbatim "Bảng cứng") · bỏ KPI+gần-đây → *"hiện theo list chi tiết đàng hoàng... vẫn thấy đc tổng quan liên kết"* · 6-leaf y-chang-Duyệt-NCC trừ Hợp-đồng-cứng + AskUser *"Chạy cả 2"* (endpoint deleted + matrix HĐ) · *"Ồ đẹp đấy, vậy bỏ cái trên đi nhé, cái dưới OK đấy"* (bỏ hero) · tên sidebar = **"Tổng quan quy trình"** (anh đề, lead lấy) · *"vậy là tròn 1 quy trình của Solution rồi đấy."*
|
||||
- Sự cố revoker (nhánh [S92] lật grant 38-key @restart #423): xử 2 nhịp regrant=447 + gỡ nhánh code + REGRESSION-GUARD test; bền qua ×2 restart (cicd chứng 3-chân-kiềng).
|
||||
|
||||
**(2) delta còn SỐNG (mang sang cửa sau):**
|
||||
- ~~`cicd-verify-ea962b1.md` đang chạy NỀN~~ → **VỀ KỊP trước chốt: PASS 9/9** (Run #426 5m38s · menu-delta khớp toán 119→142=+23 · 21 key mới 13/13 · endpoint 401-với-control-404 · durability lần 3 · ⭐ cứu 1 FALSE-FAIL: luật control-âm-phải-ĐỘC-QUYỀN). Đọc chi tiết ở `/tiep`.
|
||||
- Closeout sổ bền CHƯA làm: HANDOFF (❹-risk verbatim + revoker + 5 đợt + bộ tên 4-GĐ chốt cuối) · STATUS CURRENT arc-2 · gotchas +#84 (revoker-2-chiều) · spec KHKK note W1 tái dùng root/skeleton `Khkk_*` · session-log S159 · MIND @close.
|
||||
- PARK chờ anh: FLAG-4 định nghĩa "Đã duyệt" HĐ (phase 9 vs ≥8; nhãn↔chip lệch) · (51-bis) `_INDEX` cicd 97,45% chọn split/nâng-cap/để-nguyên · "Bảng cứng" vs "Bản cứng" (theo verbatim, 1 lệnh đổi được) · StaticLeaf "Hộp thư" hoa đồng bộ? · 6 FLAG-L park đợt-4/5 trong 2 file sub-reviewer.
|
||||
- Nhãn ❶ "Đề xuất ký kết hợp đồng" sống 1 buổi → superseded "Kế hoạch ký kết HĐ" (tin-mới-nhất-thắng, ghi cả 2 đời trong spec).
|
||||
|
||||
**(3) con-trỏ:**
|
||||
- run-trace: `.claude/workflows/runs/2026-07-29-S159-bookend-open/` (11 file ~231KB) · `.claude/workflows/runs/2026-07-29-S159-tong-quan-pipeline-menu/` (5 đợt + 3 cicd-verify + 2 sub-reviewer + prod-backup/verify)
|
||||
- commit: `b5799fc` → `1a47a61` → `2a72695` → `a2bbcb9` → **`ea962b1`** (đợt-5, CI đang chạy)
|
||||
- ctx-curator verdict @PAUSE-1: **`CTX-CURATOR: SUA — 7 điểm`** — cả 7 đúng, lead áp trọn vào MIND-2 trước chèn (nặng nhất: verbatim-lách-rào-1 ở mục A · delta `harness-audit` KẾT 42Đ/6T mà MIND-1 bất-biến còn ghi "đang resume" · "FAIL 8/9" phải là "PARTIAL 8/9" theo artifact nguồn — điểm này khớp độc lập với chính lời cicd lượt-5)
|
||||
|
||||
```json
|
||||
{ "session": 8, "anchor": { "subject": "[CLAUDE] Contract+FE-User: dot-5 menu 6-leaf y-chang-Duyet-NCC + GET /contracts/deleted + bo hero + \"Tong quan quy trinh\"", "ts": "2026-07-29T14:40:10+07:00" }, "anchor_from_fallback_since": false, "changed_files": [], "changed_count": 0, "run_id": null }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -66,6 +66,45 @@ Máy chỉ quét phần **DƯỚI** marker đóng khối luật (dòng comment c
|
||||
(`_MIND_TOP_MARKER` trong scripts/session_ctx.py) -- khac khuon `FLOW-START` cua _context (khong may nao doc). -->
|
||||
<!-- MIND-TOP -->
|
||||
|
||||
## MIND-2 — 2026-07-29T14:40:10+07:00 @ ea962b1 (window 1)
|
||||
|
||||
### A. Gói-turn
|
||||
|
||||
- Arc-2 chạy trọn **5 đợt / 5 commit** trong một cửa sổ: pipeline → tab strip + menu 4-GĐ + 01-07 → fix revoker + skeleton → list chi tiết + "Bảng cứng" → 6-leaf y-chang + `GET /contracts/deleted` + bỏ hero + "Tổng quan quy trình". Mỗi đợt qua đủ cổng build/test/reviewer/cicd.
|
||||
- Sự cố đắt nhất: revoker [S92] lật grant 38-key ở restart — xử 2 nhịp, chứng bền ×3 restart bằng 3-chân-kiềng của cicd.
|
||||
- Anh chốt đóng cửa chủ động — xem PAUSE-1. Dư-lượng lúc chốt: 6 file governance dirty (2 file lệnh vá FLAG-1 H24 · detectors filter · HANDOFF/STATUS phần bookend · diary reviewer) — vào commit `wal: pause` cùng lượt.
|
||||
|
||||
### B. Hướng-tiếp + nhánh-đã-loại
|
||||
|
||||
Tiếp @`/tiep`: cicd đợt-5 ĐÃ VỀ **PASS 9/9** ngay trước chốt — đọc chi tiết `cicd-verify-ea962b1.md` (false-fail được cứu + luật control-âm-độc-quyền) → closeout sổ bền **DỞ**: HANDOFF/STATUS đã sửa phần bookend, còn arc-2 + gotcha #84 + session-log → các PARK chờ anh.
|
||||
**Nhánh đã LOẠI:** ① SQL tay cho menu đợt-5 — loại sau bài revoker: code-first qua seeder+deploy bền by-design. ② đợi cicd xong mới /pause — loại vì anh ra lệnh dừng (PAUSE-1); verdict nằm đĩa (và thực tế đã về kịp). ③ matrix HĐ per-loại (7 trang) — loại vì đo ra `ApplicableType.Contract=3` = HĐ chung 1 bộ V2, 7 leaf trỏ chung 1 matrix.
|
||||
|
||||
### C. Kế-hoạch (delta suy-nghĩ)
|
||||
|
||||
Đổi lớn nhất so đầu phiên: tưởng arc-2 là 1 lượt UI nhỏ — thực tế thành 5 đợt liên hoàn do anh duyệt-tới-đâu-chỉnh-tới-đó qua ảnh chụp; khuôn làm việc ổn định lại là {đợt nhỏ → ship → anh nhìn prod → vẽ tiếp}, không phải spec-trước-làm-sau.
|
||||
Delta kỹ thuật: cắt-code hàng loạt = **line-scan so CẢ DÒNG** (bracket-count vỡ vì destructure; substring khớp sớm; script chết trước save = file nguyên — "cut X" in ra ≠ đã ghi đĩa).
|
||||
Delta meta: 2 lần trong 1 ngày em thay số-sai-cũ bằng số-sai-mới (531 task-id · "chỗ THỨ HAI" IgnoreQueryFilters) — comment nêu SỐ là nợ tự-lan, nêu LUẬT + lệnh grep tự-kiểm mới bền.
|
||||
|
||||
### D. Đang-thảo-luận
|
||||
|
||||
- FLAG-4: định nghĩa "Đã duyệt" của HĐ — phase=9 Đã-phát-hành đang bỏ sót phase-8 đã-qua-BOD; nhãn menu ↔ chip lệch — treo-chờ-anh
|
||||
- (51-bis) `cicd-monitor/_INDEX` cận cap: split / nâng cap / để nguyên — treo-chờ-anh
|
||||
- Nhãn "Bảng cứng" theo chữ anh vẽ (PAUSE-1) — nếu ý là "Bản cứng" thì 1 lệnh đổi — treo-chờ-anh
|
||||
- "Hộp thư" đi đường StaticLeaf nên chưa hoa cùng "TỔNG QUAN QUY TRÌNH" — đồng bộ hay để — treo-chờ-anh
|
||||
- Vá GỐC thứ-tự Phase 3.5 (refresh `_mind` sau §2.1.9 + đã nới bất-biến (6), phép (10) đã wire) — phần đổi-nghi-thức còn chờ — mới-nêu
|
||||
- Cơ-chế đặt-chỗ diary hỏng: 6/6 đề-xuất `harness-refine` qua 3 phiên chưa thi hành; vòng phản hồi dương — treo-chờ-anh
|
||||
- Khuôn 5-đợt-liên-hoàn (ship nhỏ → anh vẽ tiếp) chạy tốt hơn spec-lớn cho việc UI — nên thành nếp cho arc UI sau — gần-chốt
|
||||
|
||||
### E. Dòng-sống spawn/engine-run
|
||||
|
||||
- `frontend-designer` ×2 lượt (đợt-1 + đợt-4) — DONE cả 2, mỗi lượt 1 lần #53 nhưng file chính land trước chết — `sub-frontend-designer-tongquan.md` · `sub-frontend-designer-dot4.md`
|
||||
- `reviewer` ×3 lượt (đợt-1: 7 FLAG · đợt-4: 7 FLAG · đợt-5: 10 FLAG) — PASS_WITH_FLAGS cả 3, disposition từng dòng trong run.md — `sub-reviewer-diff-*.md` ×3
|
||||
- `cicd-monitor` ×5 lượt: `b5799fc` PASS 9/9 · `1a47a61` **PARTIAL 8/9** (mục-9 lộ revoker — verdict theo đúng artifact nguồn) · `2a72695` PASS 9/9 restart-proof · `a2bbcb9` PASS 9/9 · `ea962b1` **PASS 9/9** (về ngay trước chốt) — `cicd-verify-*.md` ×5
|
||||
- bookend @open 10 vai (chi tiết mục E MIND-1, không lặp) — delta sau MIND-1: `harness-audit` ĐÃ KẾT **42Đ/6T** (E-10 TRƯỢT: "chạm 14" thật 16 diary) — `.claude/workflows/runs/2026-07-29-S159-bookend-open/harness-audit-return.md`
|
||||
- `ctx-curator` @PAUSE-1 (carve-out §1) — **SUA — 7 điểm, đã áp cả 7 vào bản này** — verdict lưu entry PAUSE-1 `_context`
|
||||
- engine-run máy-$0 cửa này: `mind-check --draft` (đạt=6/TRƯỢT=0) · `machine-block --json` (JSON ở `_context` PAUSE-1) · secrets-sweep @auto-snap · Stop-hook `wal:` flush (nhiều lượt/ngày) · `session-counter-tick` (NO-OP idempotent @pause)
|
||||
|
||||
|
||||
## MIND-1 — 2026-07-29T09:25:15+07:00 @ d79ddaf (window 1)
|
||||
|
||||
### A. Gói-turn
|
||||
|
||||
5
.claude/sessions/session-8/_pause-1.md
Normal file
5
.claude/sessions/session-8/_pause-1.md
Normal file
@ -0,0 +1,5 @@
|
||||
ts: 2026-07-29T14:40:10+07:00
|
||||
head-sha: ea962b1e59432bd6b003836cf0c93bed73732abf
|
||||
window-ordinal: 1
|
||||
jsonl-hint: D--Dropbox-CONG-VIEC-SOLUTION-SOLUTION-ERP/6753d44e-d12a-4af8-8a55-a02b8215d159
|
||||
account-label: none
|
||||
18
.claude/sessions/session-8/_snapshot-1.md
Normal file
18
.claude/sessions/session-8/_snapshot-1.md
Normal file
@ -0,0 +1,18 @@
|
||||
# _snapshot-1 — SESSION-MODEL checkpoint (sID 8)
|
||||
|
||||
ts: 2026-07-29T14:40:10+07:00
|
||||
head-sha: ea962b1e59432bd6b003836cf0c93bed73732abf
|
||||
head-subject: [CLAUDE] Contract+FE-User: dot-5 menu 6-leaf y-chang-Duyet-NCC + GET /contracts/deleted + bo hero + "Tong quan quy trinh"
|
||||
head-ts: 2026-07-29T14:40:10+07:00
|
||||
label: auto-snap @PAUSE-1 — arc-2 dong tron 5 dot, cicd ea962b1 PASS 9/9 ve kip truoc chot
|
||||
snapshot-ordinal: 1
|
||||
|
||||
## essence (≤3 dòng — chốt cái CHƯA nằm file)
|
||||
- cicd đợt-5 PASS 9/9 VỀ KỊP trước chốt (arc-2 = 5/5 verdict: PASS·PARTIAL-bắt-revoker·PASS·PASS·PASS) — false-fail được cứu bằng luật control-âm-độc-quyền.
|
||||
- ctx-curator @PAUSE-1 = SUA 7 điểm, áp trọn vào MIND-2; mind-check full đạt=11/TRƯỢT=0 (phép (10) thứ-tự mới cũng xanh).
|
||||
- Dư-lượng governance 6 file dirty vào commit `wal: pause` cùng lượt; closeout arc-2 sổ bền = việc /tiep.
|
||||
|
||||
## machine-block
|
||||
```json
|
||||
{ "session": 8, "anchor": { "subject": "[CLAUDE] Contract+FE-User: dot-5 menu 6-leaf y-chang-Duyet-NCC + GET /contracts/deleted + bo hero + \"Tong quan quy trinh\"", "ts": "2026-07-29T14:40:10+07:00" }, "anchor_from_fallback_since": false, "changed_files": [], "changed_count": 0, "run_id": null }
|
||||
```
|
||||
@ -0,0 +1,265 @@
|
||||
# cicd-verify `ea962b1` — S159 ĐỢT-5 "menu 6-leaf y-chang-Duyệt-NCC + GET /contracts/deleted + bỏ hero"
|
||||
|
||||
> Ghi-TRỌN-RA-ĐĨA (chống #53). Lead /pause — **file này là sản phẩm chính, đọc ở phiên sau qua `/tiep`**.
|
||||
> Đợt-1 `b5799fc` PASS 9/9 · đợt-2 `1a47a61` PARTIAL 8/9 · đợt-3 `2a72695` PASS 9/9 · đợt-4 `a2bbcb9` PASS 9/9 — 4 file cũ KHÔNG chạm.
|
||||
> Bắt đầu: 2026-07-29 ~14:42+07 · cicd-monitor (READ-only)
|
||||
|
||||
---
|
||||
|
||||
## M1 — Push + phạm vi · ✅ PASS
|
||||
|
||||
```
|
||||
git log -1 → ea962b1e59432bd6b003836cf0c93bed73732abf
|
||||
[CLAUDE] Contract+FE-User: dot-5 menu 6-leaf y-chang-Duyet-NCC
|
||||
+ GET /contracts/deleted + bo hero + "Tong quan quy trinh"
|
||||
2026-07-29 14:40:10 +0700
|
||||
git log origin/main..HEAD → EMPTY (đã push)
|
||||
```
|
||||
Range `ceeebf3..ea962b1` = **10 file, 173+/371−** (net âm vì gỡ hero + gỡ code cũ). Khớp brief.
|
||||
|
||||
| File | vai trò |
|
||||
|---|---|
|
||||
| `src/Backend/…/Api/Controllers/ContractsController.cs` | **+`GET deleted`** |
|
||||
| `src/Backend/…/Application/Contracts/ContractFeatures.cs` | `ListDeletedContractsQuery` |
|
||||
| `src/Backend/…/Application/PurchaseEvaluations/PeSoftDeleteFeatures.cs` | comment |
|
||||
| `src/Backend/…/Persistence/DbInitializer.cs` | 🔴 **seeder**: Ct +3 leaf ×7 · Khkk 6 · labelBackfill · reviewKeys |
|
||||
| `fe-user/src/pages/UserDashboardPage.tsx` | **BỎ HERO** + đổi title |
|
||||
| `fe-user/src/pages/contracts/MyContractsPage.tsx` | phase/deleted + guard |
|
||||
| `fe-user/src/pages/pe/WorkflowMatrixViewPage.tsx` | matrix + `type=3` |
|
||||
| `fe-user/src/App.tsx` · `fe-user/src/components/Layout.tsx` | route alias · Ct-regex + staticMap + MenuLeaf-caps |
|
||||
| `fe-admin/src/components/Layout.tsx` | staticMap + 2 |
|
||||
|
||||
**Cả 2 app FE đều sửa ⇒ bundle 2 app đều phải rotate.**
|
||||
|
||||
## M2 — Migration VẮNG là ĐÚNG · ✅ PASS
|
||||
```
|
||||
git diff --name-only ceeebf3..ea962b1 -- '*Migrations*' → 0
|
||||
```
|
||||
BE thêm **query + endpoint + seeder**, không schema. Mig giữ **68** / 89 bảng.
|
||||
⇒ Toàn bộ thay đổi menu đi qua **SEEDER lúc app khởi động** → rủi ro thật nằm ở M6 (seeder-run).
|
||||
|
||||
## 📌 BASELINE TRƯỚC DEPLOY (đo 14:41–14:42+07, run chưa land — pool CHƯA recycle)
|
||||
|
||||
Đây là "vế TRƯỚC" để chứng minh seeder thật sự chạy (delta 0→7 rất sắc):
|
||||
|
||||
| Phép đo | TRƯỚC |
|
||||
|---|---|
|
||||
| `Ct_%_WfView` (MenuItems) | **0** |
|
||||
| `Ct_%_Approved` | **0** |
|
||||
| `Ct_%_Deleted` | **0** |
|
||||
| `Khkk_%` | **5** |
|
||||
| `Ct_%_Pending` có `Label LIKE N'Đang%'` | **0** (trên tổng 7 key Pending) |
|
||||
| `Dashboard` có `Label LIKE N'%quy tr%'` | **0** |
|
||||
| **MenuItems TỔNG** | **119** |
|
||||
| 38-key set | **min=13 max=13 keys=38** |
|
||||
|
||||
Bundle trước deploy: admin js `CC1pGE1F` css `DWDbm5As` · user js `CLfAftby` css `B6bUSFBt`
|
||||
— khớp baseline hậu-đợt-4 ⇒ hợp lệ.
|
||||
|
||||
## 🔎 M3-pre — Marker kỳ vọng (dự đoán TRƯỚC khi đo, khuôn đợt-4)
|
||||
|
||||
`UserDashboardPage.tsx` (50.142 B — đợt-4 là 63.124 B, giảm đúng với "bỏ hero"):
|
||||
|
||||
| Chuỗi | trong UserDashboardPage | vai trò |
|
||||
|---|---|---|
|
||||
| `Tổng quan quy trình` | **1** | dương |
|
||||
| `Toàn trình 4 giai đoạn` | **0** | (hero h2 đã gỡ) |
|
||||
| `Quy trình mua sắm` | **0** | (eyebrow hero đã gỡ) |
|
||||
|
||||
### ⚠️⚠️ CẢNH BÁO: 1 trong 2 control ÂM của brief **KHÔNG HỢP LỆ** — đã soi TRƯỚC khi đo
|
||||
|
||||
`grep -rn` toàn `fe-user/src` + `fe-admin/src`:
|
||||
```
|
||||
fe-user/src/components/pe/PePipelineStrip.tsx:24: aria-label="Toàn trình 4 giai đoạn"
|
||||
fe-admin/src/components/pe/PePipelineStrip.tsx:24: aria-label="Toàn trình 4 giai đoạn"
|
||||
```
|
||||
⇒ Chuỗi `Toàn trình 4 giai đoạn` **VẪN SỐNG** trong `PePipelineStrip` (aria-label, thêm từ đợt-2).
|
||||
Nó **KHÔNG độc quyền** cho hero ⇒ **dự đoán bundle USER = 1, KHÔNG phải 0**.
|
||||
**Nếu máy móc áp "phải VẮNG" sẽ báo FAIL OAN** — trong khi hero thật sự đã gỡ.
|
||||
→ Xử lý: coi đây là control **KHÔNG dùng được**, thay bằng phép đúng ở dưới.
|
||||
|
||||
`Quy trình mua sắm`: `grep -rc` toàn 2 app = **0 hit ở mọi file** ⇒ **control ÂM HỢP LỆ**,
|
||||
dự đoán bundle = **0**. Đây là phép chứng "hero đã gỡ" đáng tin của lượt này.
|
||||
|
||||
(Đúng bài đợt-4: marker phổ-thông/dùng-chung chỉ kết luận "≥1", muốn phép ÂM phải chọn chuỗi ĐỘC QUYỀN.)
|
||||
|
||||
## 🔎 M7-pre — Endpoint mới (đọc code, chốt kỳ vọng)
|
||||
|
||||
`ContractsController.cs`:
|
||||
```csharp
|
||||
[ApiController]
|
||||
[Route("api/contracts")] // L12
|
||||
[Authorize] // L13 (class-level)
|
||||
…
|
||||
[HttpGet("deleted")] // L28
|
||||
[Authorize(Policy = "Contracts.Read")] // L29 (chặt hơn [Authorize] trần)
|
||||
public async Task<ActionResult<PagedResult<ContractListItemDto>>> ListDeleted(…)
|
||||
```
|
||||
⇒ URL thật = **`GET /api/contracts/deleted`** (brief ghi `/contracts/deleted`, thiếu prefix `api`).
|
||||
Kỳ vọng **401** khi không token (route tồn tại) · **404 = FAIL** (route không tồn tại).
|
||||
👍 Ghi nhận: có policy method-level chứ không dựa `[Authorize]` trần — đúng bài 2-tầng authz (#82).
|
||||
|
||||
## M4 — Poll terminal · ✅ PASS
|
||||
- **id=539 · run_number=426 · head_sha `ea962b1e`** · created 14:40:15 → **`status=success` 14:45:53**
|
||||
- **Duration 5m38s** (đợt 1-4: 5m59s / 5m44s / 5m56s / 5m40s) — ổn định.
|
||||
|
||||
### M4b — Test gate · ✅ 562 EXACT
|
||||
```
|
||||
Passed! - Failed: 0, Passed: 45, Skipped: 0, Total: 45, 151 ms - SolutionErp.Domain.Tests.dll
|
||||
Passed! - Failed: 0, Passed: 517, Skipped: 0, Total: 517, 1m23s - SolutionErp.Infrastructure.Tests.dll
|
||||
```
|
||||
`Failed!` ×0 ⇒ **562**. Guard đợt-3 (`StillHiddenKeys`/`ReopenedKeys`) nằm trong 517 này, vẫn xanh.
|
||||
|
||||
## M5 — Bundle · ✅ PASS (đo 14:46:14, sau status=success)
|
||||
|
||||
| App | asset | TRƯỚC | SAU | KQ |
|
||||
|---|---|---|---|---|
|
||||
| **admin** | js | `CC1pGE1F` | **`D0sXA0fe`** | ✅ ROTATE |
|
||||
| admin | css | `DWDbm5As` | `DWDbm5As` | FROZEN — lành tính (chứng dưới) |
|
||||
| **user** | js | `CLfAftby` | **`Y6dW_5CM`** | ✅ ROTATE |
|
||||
| **user** | css | `B6bUSFBt` | **`6YIAufJR`** | ✅ ROTATE |
|
||||
|
||||
"Bundle ×2 rotate (cả 2 app đổi)" — **đạt qua JS cả 2 app** ✅.
|
||||
```
|
||||
USER index-Y6dW_5CM.js ct=application/javascript size=1.595.577 LM=14:45:25+07 (in-window)
|
||||
ADMIN index-D0sXA0fe.js ct=application/javascript size=1.678.823 LM=14:44:29+07 (in-window)
|
||||
ADMIN index-DWDbm5As.css ct=text/css size= 83.705 LM=14:44:29+07 (in-window) ← FROZEN
|
||||
```
|
||||
🔑 **admin-css FROZEN nhưng `Last-Modified` 14:44:29 NẰM TRONG cửa sổ deploy** (14:40:15→14:45:53)
|
||||
⇒ file **đã được ghi lại**, chỉ là thay đổi `fe-admin` lần này là **staticMap trong Layout.tsx (JS thuần,
|
||||
không thêm class Tailwind mới)** nên CSS ra byte y hệt. Đúng khuôn #69 mode-B đã dùng ở đợt-4:
|
||||
**frozen + LM-in-window = re-ship giống hệt** (khác "bundle cũ kẹt lại" — cái đó LM sẽ cũ).
|
||||
|
||||
Control âm 2 tầng: hash CŨ `CLfAftby` / `CC1pGE1F` đều → `ct=text/html` 876/900 B ⇒ bundle đợt-4 đã
|
||||
biến mất khỏi đĩa = chứng ghi-đè thật.
|
||||
|
||||
## M3 — Byte-marker · ✅ PASS (dự đoán TRƯỚC khớp ĐO 100%)
|
||||
|
||||
| Chuỗi | USER | ADMIN | Kỳ vọng đã chốt trước | KQ |
|
||||
|---|---|---|---|---|
|
||||
| `Tổng quan quy trình` | **1** | 0 | dương ở user (chỉ fe-user có) | ✅ |
|
||||
| `Quy trình mua sắm` | **0** | **0** | **ÂM (control HỢP LỆ)** | ✅ |
|
||||
| `Toàn trình 4 giai đoạn` | **1** | **1** | **dự đoán 1, KHÔNG phải 0** | ✅ đúng dự đoán |
|
||||
|
||||
🔑 **CỨU MỘT FALSE-FAIL — điểm quan trọng nhất lượt này:**
|
||||
Brief yêu cầu control âm `Toàn trình 4 giai đoạn` phải **VẮNG**. Nếu áp máy móc thì lượt này **FAIL OAN**,
|
||||
vì đo ra **1**. Nhưng tao đã soi TRƯỚC KHI ĐO và biết lý do:
|
||||
```
|
||||
fe-user/src/components/pe/PePipelineStrip.tsx:24: aria-label="Toàn trình 4 giai đoạn"
|
||||
fe-admin/src/components/pe/PePipelineStrip.tsx:24: aria-label="Toàn trình 4 giai đoạn"
|
||||
```
|
||||
Chuỗi này **không độc quyền cho hero** — nó còn sống trong `PePipelineStrip` (thêm từ đợt-2) dưới dạng
|
||||
`aria-label`. Hero **thật sự đã gỡ** (`UserDashboardPage` chứa 0 lần; file giảm 63.124 → **50.142 B**).
|
||||
⟹ Kết luận: control này **KHÔNG DÙNG ĐƯỢC**, thay bằng `Quy trình mua sắm` (eyebrow hero) —
|
||||
`grep -rc` toàn 2 app = **0 hit ở mọi file** ⇒ độc quyền ⇒ bundle = **0** ⇒ **chứng hero đã gỡ**.
|
||||
|
||||
**Luật rút ra (nối tiếp đợt-4):** trước khi nhận một chuỗi làm **control ÂM**, phải grep repo-wide
|
||||
xác nhận nó **ĐỘC QUYỀN** cho thứ mình muốn chứng vắng. Chuỗi dùng-chung chỉ làm marker DƯƠNG.
|
||||
|
||||
## M6 — 🔴 SEEDER-RUN sau restart · ✅ PASS (delta khớp từng đơn vị)
|
||||
|
||||
**Bước 0 — chứng minh restart THẬT (khuôn 3 chân kiềng, đợt-3):**
|
||||
|
||||
| Bằng chứng | Giá trị |
|
||||
|---|---|
|
||||
| Dòng ĐẦU log `solution-erp-20260729.log` | **`14:45:37.283 +07:00 [INF] Applying migrations...`** |
|
||||
| `w3wp` StartTime mới nhất | **14:45:32** (và 14:45:31) |
|
||||
|
||||
Deploy xoá `logs/` ⇒ log mở lại 14:45:37 = **boot mới**; DbInitializer đã chạy. Đo SAU mốc này.
|
||||
|
||||
| Phép đo | TRƯỚC (14:41) | **SAU (14:46)** | Δ | KQ |
|
||||
|---|---|---|---|---|
|
||||
| `Ct_%_WfView` | 0 | **7** | +7 | ✅ |
|
||||
| `Ct_%_Approved` | 0 | **7** | +7 | ✅ |
|
||||
| `Ct_%_Deleted` | 0 | **7** | +7 | ✅ |
|
||||
| `Khkk_%` | 5 | **7** | +2 | ✅ (6 leaf + G1) |
|
||||
| `Ct_%_Pending` nhãn `N'Đang%'` | 0/7 | **7**/7 | +7 | ✅ labelBackfill ăn |
|
||||
| `Dashboard` nhãn `N'%quy tr%'` | 0 | **1** | +1 | ✅ |
|
||||
| **MenuItems TỔNG** | **119** | **142** | **+23** | ✅ |
|
||||
| root | — | **12** | 0 | ✅ không double-insert |
|
||||
| `DUPKEYS` | — | **0** | — | ✅ |
|
||||
|
||||
🔑 **Toán khớp từng đồng:** +23 = 7 (`WfView`) + 7 (`Approved`) + 7 (`Deleted`) + 2 (`Khkk`) = **23** ✅.
|
||||
Không có row thừa nào ⇒ seeder insert ĐÚNG tập, không nhân đôi.
|
||||
|
||||
**Permission cho key MỚI (nhánh insert của reviewKeys — key chưa có row nên insert ăn):**
|
||||
```
|
||||
F_NEWPERM min=13 max=13 keys=21 (21 = 7 WfView + 7 Approved + 7 Deleted)
|
||||
```
|
||||
✅ Mỗi key mới đủ **13/13 role canread**. Đây đúng như ghi chú code đợt-3: nhánh grant là
|
||||
**skip-existing** nên chỉ ăn với key MỚI — lần này key hoàn toàn mới ⇒ grant thành công (khác
|
||||
tình huống đợt-2/3 nơi row `false` sẵn có không được nâng).
|
||||
|
||||
## M7 — Endpoint mới `GET /api/contracts/deleted` · ✅ PASS (401, KHÔNG 404)
|
||||
|
||||
| Probe | HTTP | Đọc |
|
||||
|---|---|---|
|
||||
| **`GET /api/contracts/deleted`** (no token) | **401** | ✅ **route TỒN TẠI**, auth chặn đúng |
|
||||
| `GET /api/contracts/zzznope` (route bịa) | **404** | ✅ **control** — 404 vẫn hoạt động ⇒ 401 ở trên CÓ NGHĨA |
|
||||
| `GET /api/contracts` (route cũ đã biết) | 401 | ✅ nhất quán |
|
||||
|
||||
🔑 Có **control 404** nên 401 không thể là "server trả 401 cho mọi thứ" — discriminator sạch.
|
||||
(Nếu chỉ đo mỗi 401 thì không loại trừ được khả năng catch-all.)
|
||||
|
||||
## M8 — Restart-durability lần 3 · ✅ PASS
|
||||
|
||||
| Phép đo | Kết quả SAU restart | KQ |
|
||||
|---|---|---|
|
||||
| **38-key set** | **min=13 max=13 keys=38 · totcr=494/494** | ✅ GIỮ (lần 3) |
|
||||
| Control ÂM `Hrm%`/`Off%`/`Personal` non-Admin canread | **0** | ✅ revoker CÒN SỐNG |
|
||||
| `Pe%` | **197 / 185** | ✅ |
|
||||
| `Hdc_*` nhãn `- Bảng cứng` (`%-%`) | **7** | ✅ nguyên |
|
||||
| 3 root ẩn — `SUM(IsVisible)` | **0** | ✅ cả 3 vẫn ẩn |
|
||||
|
||||
⚠️ **Lưu ý hình-dạng-truy-vấn (quan trọng cho lần sau):** đợt này `Ct_*` đẻ thêm 21 key mới
|
||||
(`_WfView`/`_Approved`/`_Deleted`). Nếu vẫn dùng `LEFT(MenuKey,3)='Ct_'` như các đợt trước thì
|
||||
"38-key set" sẽ thành **59 key** và **không so sánh được với 4 đợt trước**. Nên tao **cố ý loại
|
||||
3 hậu tố mới** khỏi mệnh đề để giữ đúng tập 38 gốc:
|
||||
```sql
|
||||
WHERE MenuKey IN (10 key Master-set + 'Contracts')
|
||||
OR (LEFT(MenuKey,3)='Ct_'
|
||||
AND MenuKey NOT LIKE 'Ct[_]%[_]WfView'
|
||||
AND MenuKey NOT LIKE 'Ct[_]%[_]Approved'
|
||||
AND MenuKey NOT LIKE 'Ct[_]%[_]Deleted')
|
||||
```
|
||||
⇒ ra đúng **38 key**, so được apples-to-apples với đợt 2/3/4. (Bài học query-shape từ đợt-1: khi tập
|
||||
dữ liệu nở ra, phải neo lại phạm vi truy vấn, nếu không con số "tăng" sẽ bị đọc nhầm thành drift.)
|
||||
|
||||
## M9 — Smoke · ✅ PASS 4/4
|
||||
```
|
||||
/health/live 200 Healthy · /health/ready 200 Healthy · admin 200 · eoffice 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## VERDICT: **PASS — 9/9**
|
||||
|
||||
| # | Mục | KQ |
|
||||
|---|---|---|
|
||||
| 1 | Push + range 10 file (173+/371−, net âm do bỏ hero) | ✅ |
|
||||
| 2 | Migration VẮNG là ĐÚNG | ✅ |
|
||||
| 3 | Run #426 success 5m38s | ✅ |
|
||||
| 4 | Test gate **562** (45+517, 0 fail) | ✅ |
|
||||
| 5 | Bundle: JS rotate **cả 2 app**; admin-css frozen chứng minh lành tính bằng LM-in-window | ✅ |
|
||||
| 6 | Marker `Tổng quan quy trình`=1 · control ÂM HỢP LỆ `Quy trình mua sắm`=0 | ✅ |
|
||||
| 7 | **SEEDER-RUN**: 0→7 ×3 · Khkk 5→7 · Pending-label 7 · Dashboard-label 1 · tổng 119→142 (+23 khớp) · root 12 · DUPKEYS 0 · 21 key mới 13/13 | ✅ |
|
||||
| 8 | **Endpoint `/api/contracts/deleted` = 401** (route sống) + control bịa **404** | ✅ |
|
||||
| 9 | **Durability lần 3**: 38-key 494/494 · control âm 0 · Pe 197/185 · Hdc nhãn 7 · 3 root ẩn | ✅ |
|
||||
|
||||
**Không có cờ đỏ. Không khuyến nghị rollback.**
|
||||
|
||||
### ⚠️ 1 điểm cần owner biết (KHÔNG phải lỗi deploy)
|
||||
Control âm `Toàn trình 4 giai đoạn` trong brief **không dùng được** — chuỗi vẫn sống hợp lệ ở
|
||||
`PePipelineStrip.tsx` (`aria-label`, cả 2 app). Hero đã gỡ đúng (chứng bằng `Quy trình mua sắm`=0 +
|
||||
`UserDashboardPage` 63.124→50.142 B). Lần sau nếu muốn phép ÂM cho hero, dùng `Quy trình mua sắm`.
|
||||
|
||||
Baseline mới: **admin js `D0sXA0fe` / css `DWDbm5As`(frozen hợp lệ) · user js `Y6dW_5CM` / css `6YIAufJR`** ·
|
||||
gate 562 · Mig 68 / 89 bảng · MenuItems **142** · root 12 · 38-key 13/13 · `Ct_` leaf mới 21 key × 13.
|
||||
|
||||
### Arc S159 — 5 đợt
|
||||
đợt-1 PASS 9/9 → đợt-2 **PARTIAL 8/9** (bắt regression revoker ăn 447/494 row) → đợt-3 PASS 9/9
|
||||
(đóng regression, 3 chân kiềng) → đợt-4 PASS 9/9 (durability lần 2) → **đợt-5 PASS 9/9
|
||||
(durability lần 3 + seeder-insert delta khớp toán + endpoint mới sống)**.
|
||||
|
||||
<!-- END cicd-verify-ea962b1 · VERDICT=PASS 9/9 -->
|
||||
|
||||
@ -89,7 +89,25 @@
|
||||
- [x] **Đ4-d — build PASS 495ms · Infra 517 PASS · commit `a2bbcb9` push** (2 file, 1052+/338−)
|
||||
- [!] **Đ4-e — cicd verify `a2bbcb9`** đang chạy (marker bản mới + control âm "chưa tới"/"HĐ ĐANG SOẠN" vắng + restart-durability lần 2 của 38-key)
|
||||
|
||||
<!-- ARC-2: 4 đợt · 4 commit (`b5799fc`·`1a47a61`·`2a72695`·`a2bbcb9`) · prod-data 7 lượt SQL -->
|
||||
## ĐỢT-5 — "y chang Duyệt NCC" 6-leaf + endpoint deleted + bỏ hero + "Tổng quan quy trình"
|
||||
|
||||
**Owner:** ảnh khoanh 6 leaf PE + *"Những cái dưới nó cũng y chang thế này luôn nhé, trừ chỗ hợp đồng cứng ra, vì bảng cứng thì ko cần"* · AskUser "Chạy cả 2" (endpoint deleted READ-only + matrix HĐ) · *"Ồ đẹp đấy, vậy bỏ cái trên đi nhé, cái dưới OK đấy. Tổng quan → Viết hoa → Đổi thành gì...? Tổng quan quy trình?"* (lead lấy tên anh đề) · *"xong thì /pause nhé"*.
|
||||
|
||||
- [x] BE: `ListDeletedContractsQuery` + GET `/contracts/deleted` [Contracts.Read] (khuôn PE S155; Contract CÓ soft-delete filter `:44`) · phát hiện rẻ: **ApplicableType.Contract=3 — HĐ chung 1 bộ V2** ⇒ matrix = mở rộng trang PE +type=3, 7 leaf WfView trỏ chung `?type=3&ct=N`
|
||||
- [x] Seeder: Ct ×7 +3 leaf (khớp regex FE) + "Đang duyệt" ×7 backfill + Khkk 6-leaf + Dashboard→"Tổng quan quy trình" + reviewKeys +23 — **KHÔNG SQL tay** (bài revoker: code-first, deploy tự insert/reorder qua upsert)
|
||||
- [x] FE: BỎ HERO (note verbatim owner trong code) + title + MyContractsPage phase/deleted + MenuLeaf depth-0 caps
|
||||
- [x] reviewer **PASS_WITH_FLAGS 10 FLAG (0H/4M/6L)** (`sub-reviewer-diff-dot5.md` 22.369B; garble #53 ×2 — lần 2 43-tool/1.3KB = làm-nhiều-ghi-ít, resume "đổ-đĩa-trước-đọc-thêm" ăn). **Disposition:**
|
||||
· F1[M] fe-admin thiếu 2 route Khkk → **FIX** (+2 staticMap; Ct_ mới bên admin ẩn `isAdminHidden` — reviewer đã kiểm CLEAN)
|
||||
· F2[M] màn Đã-xóa click row = panel trắng/404 (detail KHÔNG IgnoreQueryFilters — nó truy tới `GetContractQuery:471` chứng thật) → **FIX** guard `if (showDeleted) return` tại `selectContract` (chặn cả desktop lẫn mobile, khuôn PE `disabled={deletedView}`)
|
||||
· F3[M] chú thích khai "Hộp thư viết HOA" nhưng Hộp-thư đi đường `StaticLeaf` → **FIX** khai đúng phạm vi (đồng bộ StaticLeaf = quyết owner, PARK)
|
||||
· F10[M] ⭐ comment "chỗ THỨ HAI" = **số-tự-lan** (grep thật: 3 tệp/17 vị trí — `ApprovalWorkflowV2AdminFeatures` 15 chỗ lớp-khác; "DUY NHẤT" S155 vốn đã sai) → **FIX cả 2 site: BỎ SỐ, nêu LUẬT + lệnh grep tự-kiểm**. 🔴 Lead lặp đúng lỗi errata-của-errata sáng nay — thay số sai cũ bằng số sai mới; cùng lớp meta-count
|
||||
· F4[L] **PARK→NEXT-anh**: định nghĩa "Đã duyệt" HĐ (phase=9 Đã-phát-hành bỏ sót phase-8 đã-qua-BOD; nhãn menu ↔ chip đang lệch) — cần owner chốt
|
||||
· F5-F9[L] **PARK có ghi** (F5 active-state đôi · F6 asymmetry authz endpoint-mới-chặt-hơn-list-sống [đúng hướng, ghi nhận] · F7 chuỗi trần vs factory · F8 thiếu cột mốc-xóa · F9 client-filter-type trên 100 dòng)
|
||||
- [x] build fe ×2 + BE PASS · test **562 PASS** · commit **`ea962b1`** push (10 file, 173+/371−)
|
||||
· 🔧 bẫy mới ghi sổ: PS 5.1 here-string commit-message vỡ → **`git commit -F <file>`**; lần vỡ đầu chỉ đẩy 2 `wal:` commit (noise, code chưa mất)
|
||||
- [!] cicd verify `ea962b1` **CHẠY NỀN** (`cicd-verify-ea962b1.md`) — đọc ở phiên sau qua `/tiep` (mục nặng: seeder-run insert menu-delta + endpoint 401-not-404 + durability lần 3)
|
||||
|
||||
<!-- ARC-2: 5 đợt · 5 commit (`b5799fc`·`1a47a61`·`2a72695`·`a2bbcb9`·`ea962b1`) · prod-data 7 lượt SQL + đợt-5 code-first 0-SQL -->
|
||||
|
||||
- [x] **T2 — Prod menu data** ✅ **LIVE + VERIFY TRỌN** (`prod-backup-pre-apply.txt` → apply `hide=3 upd=99 ins=376` → `prod-verify-post-apply.txt`):
|
||||
· Inspect lộ thực-trạng ≠ giả định: Master subtree có matrix 13-row/key (canread 2/13) nhưng **`Ct_*` 28 key = 0 ROW** (chưa từng seed), `Contracts` root 1 row ⇒ INSERT 376 + UPDATE 99, số khớp toán từng đồng (9×11=99 · 28×13+12=376)
|
||||
|
||||
@ -26,3 +26,129 @@ Ngoài scope lead khai (KHÔNG soi, chỉ ghi nhận có mặt trong working-tre
|
||||
|
||||
## FLAG (ghi dần)
|
||||
|
||||
### FLAG-1 [M] — `fe-admin` thiếu 2 route ⇒ 2 lá menu mới rơi IM LẶNG (gotcha #50)
|
||||
**File:** `fe-admin/src/components/Layout.tsx:31-44` (`staticMap`)
|
||||
**Đo:** staticMap fe-admin có `Khkk_List` / `Khkk_Create` / `Khkk_Pending` / `Khkk_Approved` nhưng **KHÔNG có** `Khkk_WfView`, `Khkk_Deleted` — trong khi seeder (`DbInitializer.cs:1780-1786`) đã thêm 2 key này vào cây menu dùng CHUNG cho cả 2 app.
|
||||
`MenuLeaf` của fe-admin: `resolvePath` trả `null` ⇒ `return null` ⇒ lá biến mất, KHÔNG có cảnh báo. Đúng lớp bẫy mà chính comment `Layout.tsx:33` cảnh báo ("thiếu route = sidebar drop SILENT").
|
||||
**Hệ quả:** nhóm "1. Kế hoạch ký kết HĐ (NCC-TP)" bên fe-admin hiện 4/6 lá; lệch với fe-user 6/6. Vi phạm `docs/rules.md §3.9` (mirror 2 app).
|
||||
**Không phải lỗi cho `Ct_*_WfView/Approved/Deleted`:** fe-admin ẩn TOÀN BỘ `Ct_*` (`isAdminHidden` :176-178 `key.startsWith('Ct_')`) ⇒ 21 lá HĐ mới không cần route bên admin. Đã kiểm, CLEAN.
|
||||
**Tiêu chí nhận:** thêm 2 dòng `Khkk_WfView` + `Khkk_Deleted` vào staticMap fe-admin (trỏ `/coming-soon?stage=2&m=...` như fe-user), rồi `grep -c "Khkk_" ` ở 2 app phải BẰNG NHAU (=6).
|
||||
|
||||
### FLAG-2 [M] — Màn "Đã xóa" HĐ: bấm vào dòng ⇒ 2 panel TRẮNG (không thông báo), khác hẳn khuôn PE
|
||||
**File:** `fe-user/src/pages/contracts/MyContractsPage.tsx:163-190` (`onClick={() => selectContract(c.id)}`) + `:52-56` (`detail` query) + `:196-222` (Panel 2/3)
|
||||
**Đo (tĩnh, 2 vế):**
|
||||
- Khuôn PE (S155) CHẶN hẳn: `fe-user/src/pages/pe/PurchaseEvaluationsListPage.tsx:524-527` — `disabled={deletedView}` + `cursor-default`.
|
||||
- Bản HĐ đợt-5 **không có nhánh `showDeleted`** ở nút dòng ⇒ vẫn `selectContract(id)` → set `?id=` → `detail` query `GET /contracts/{id}`.
|
||||
- `Contract` CÓ global query filter `!IsDeleted` (`ContractConfiguration.cs:44`) và endpoint chi tiết KHÔNG dùng `IgnoreQueryFilters` ⇒ HĐ đã xóa **404 NotFound**.
|
||||
- Render: Panel 2 chỉ có 3 nhánh `!selectedId` / `isLoading` / `detail.data` — nhánh `isError` KHÔNG có ⇒ lỗi 404 rơi vào khoảng trống ⇒ **panel trắng trơn**, người dùng không biết vì sao. Panel 3 (`:221`) cũng trắng.
|
||||
- Trên mobile (< lg) còn nặng hơn: `selectContract` (`:75-81`) `navigate('/contracts/{id}')` ⇒ **rời khỏi màn** sang trang chi tiết 404.
|
||||
**Tiêu chí nhận:** hoặc `disabled={showDeleted}` cho nút dòng (rẻ nhất, đúng khuôn PE), hoặc thêm nhánh `detail.isError` hiển thị "Phiếu đã xóa — không xem được chi tiết". Nếu chọn cách 2 phải chặn thêm nhánh mobile-navigate.
|
||||
|
||||
### FLAG-3 [M] — Chú thích khai "Hộp thư viết HOA" nhưng mã KHÔNG chạm "Hộp thư"
|
||||
**File:** `fe-user/src/components/Layout.tsx:354-357` (chú thích + nhánh `depth === 0`) vs `:385-408` (`StaticLeaf`)
|
||||
**Đo:** "Hộp thư" KHÔNG render bằng `MenuLeaf` — nó là `USER_FIXED_TOP` (`:376-378`) render qua **`StaticLeaf`** (`:385`), lớp CSS hard-code `px-3 py-1.5 text-[12px] font-medium` và **không** có `uppercase`. Sửa `MenuLeaf` không với tới nó.
|
||||
**Hệ quả:** ngay đầu sidebar sẽ là "Hộp thư" (thường, 12px) nằm cạnh "TỔNG QUAN QUY TRÌNH" (HOA, 11px, semibold) — đúng thứ owner muốn đồng bộ thì lại lệch; chú thích trong mã mô tả sai việc mã làm (bẫy cho người đọc sau).
|
||||
**Ghi nhận CLEAN kèm theo:** nhánh 3 ngả `depth===0 ? … : isDeep ? … : …` **không** phá lá sâu — depth 1 vẫn ăn nhánh cũ, depth ≥2 vẫn `isDeep`; khối `isActive` nằm NGOÀI ternary nên style active nguyên vẹn (trục 6 PASS phần này).
|
||||
**Tiêu chí nhận:** hoặc bê cùng bộ lớp sang `StaticLeaf`, hoặc sửa chú thích bỏ chữ "Hộp thư" (khai đúng phạm vi).
|
||||
|
||||
### FLAG-4 [L] — "Đã duyệt" của HĐ lọc `phase=9` (Đã phát hành), bỏ sót HĐ đã được duyệt nhưng chưa phát hành
|
||||
**File:** `fe-user/src/components/Layout.tsx:141` (`?type=N&phase=9`) + `MyContractsPage.tsx:117-121` (chip "Đã phát hành")
|
||||
**Đo:** `ContractPhase` 9 = `DaPhatHanh`. HĐ ở phase 8 `DangDongDau` **đã qua chữ ký BOD** (`DangTrinhKy → DangDongDau` do Director/AuthorizedSigner) ⇒ về nghiệp vụ đã "được duyệt" nhưng KHÔNG lọt vào màn "Đã duyệt". Param `phase` của BE là so-khớp BẰNG (`ContractFeatures.cs:305`), không diễn tả được `>= 8`.
|
||||
Chính FE cũng lệch tên: nhãn menu "Đã duyệt" ↔ chip trên trang "Đã phát hành".
|
||||
**Tiêu chí nhận:** owner chốt định nghĩa; nếu giữ 9 thì đổi nhãn lá thành "Đã phát hành" cho khớp chip, nếu muốn "đã duyệt" thì cần BE nhận `phaseFrom`/danh sách phase.
|
||||
|
||||
### FLAG-5 [L] — Ở `?type=N&phase=9`, lá "Danh sách" SÁNG cùng lúc với "Đã duyệt"
|
||||
**File:** `fe-user/src/components/Layout.tsx:304-329` (`TRANSIENT_QUERY_KEYS` + `queryMatches`)
|
||||
**Đo (chạy tay luật ở :322-328):** URL hiện tại `type=1&phase=9`, đích "Danh sách" = `type=1` ⇒ `pinnedByTarget={type}` ⇒ `phase` là transient & không được đích ghim ⇒ **bị bỏ qua** ⇒ `aKeys=[type]` = `bKeys=[type]` ⇒ MATCH. Đích "Đã duyệt" = `type=1&phase=9` ⇒ cũng MATCH. ⇒ 2 lá cùng sáng.
|
||||
Bản vá S155 chỉ đóng chiều ngược (ở `?type=1` không cho "Đã duyệt" sáng). **Là tật kế thừa từ PE, không phải do đợt-5 gây ra** — nhưng đợt-5 nhân nó ra 7 nhóm HĐ nên ghi để owner biết.
|
||||
**Không dính lá "Đã xóa":** `deleted` không nằm trong `TRANSIENT_QUERY_KEYS` ⇒ so khớp nghiêm, phân biệt đúng. Đã kiểm.
|
||||
**Tiêu chí nhận:** (tùy chọn) đối xứng hóa: nếu URL có key mà MỘT đích anh-em ghim thì đích không-ghim phải trượt.
|
||||
|
||||
### FLAG-6 [L] — Endpoint mới siết `Contracts.Read` trong khi list sống không siết ⇒ mở đúng hình bẫy #44
|
||||
**File:** `src/Backend/SolutionErp.Api/Controllers/ContractsController.cs:26-35` vs `:16-24`
|
||||
**Đo:** `MenuPermissionHandler.cs:40-49` so khớp **ĐÚNG KHÓA** `MenuKey == "Contracts"`, **không** kế thừa cha-con (kế thừa chỉ có ở tầng hiển thị `GetMyMenuTreeQuery`, gotcha #35). ⇒ vai nào được cấp lẻ `Ct_X_Deleted` mà **không** có dòng `Contracts` CanRead=1 sẽ thấy lá menu nhưng ăn **403 câm** (TanStack nuốt lỗi → `isError` → trang hiện "Không có HĐ"/trắng).
|
||||
Rủi ro hiện thấp vì: (a) `Contracts` CÓ trong `MenuKeys.All` (`MenuKeys.cs:168`) ⇒ policy `Contracts.Read` tồn tại thật — **trục 1 câu hỏi này PASS**; (b) `SeedAllRolesReviewReadPermissionsAsync` (`DbInitializer.cs:2157-2214`) đã đưa `Contracts` vào bộ cấp-cho-mọi-vai; (c) PE S155 dùng y hệt khuôn này và đã chạy thật.
|
||||
**Nhưng:** nhánh cấp cho khóa thường là **bỏ-qua-nếu-đã-có** (chú thích :2211-2213 tự khai), nên trên prod dòng `Contracts` CanRead=0 sẽ KHÔNG được nâng bởi seeder. Đây là rủi ro triển khai, không phải lỗi mã.
|
||||
**Tiêu chí nhận (rẻ):** 1 câu `SELECT r.Name, p.CanRead FROM Permissions p JOIN Roles r ON r.Id=p.RoleId WHERE p.MenuKey='Contracts'` trên prod — mọi vai đang dùng phải CanRead=1; hoặc curl `/api/contracts/deleted` bằng `nv.test@` kỳ vọng 200.
|
||||
|
||||
### FLAG-7 [L] — Khóa menu mới viết chuỗi trần, không đi qua `MenuKeys` factory (lệch chuẩn PE)
|
||||
**File:** `DbInitializer.cs:1871,1875,1876` + `:2163,2167,2168` + `:2179-2180` (`"Ct_" + code + "_WfView"` …)
|
||||
**Đo:** PE khi thêm đúng 2 mục tương ứng (S155) đã bổ sung factory `MenuKeys.PurchaseEvaluationApproved/Deleted` + `PurchaseEvaluationWorkflowView` (`MenuKeys.cs:140-156`). Bản HĐ **không** thêm `ContractTypeWfView/Approved/Deleted`, mà nối chuỗi tay ở **2 chỗ khác nhau** (cây menu + danh sách cấp quyền). `MenuKeys.cs:3` tự nhận là "nguồn duy nhất".
|
||||
**Hệ quả:** gõ sai 1 ký tự ở 1 trong 2 chỗ = lệch câm, trình biên dịch không bắt.
|
||||
**Tiêu chí nhận:** thêm 3 factory vào `MenuKeys.cs` (KHÔNG đưa vào `MenuKeys.All` — giữ số canonical `Menu keys`/`Policies` không trôi, đúng như ghi chú PE `MenuKeys.cs:145-148`), rồi thay 8 chỗ nối chuỗi.
|
||||
|
||||
### FLAG-8 [L] — Danh sách "Đã xóa" sắp theo `DeletedAt` nhưng không trả về cột nào cho người dùng thấy mốc xóa
|
||||
**File:** `ContractFeatures.cs:387` (`OrderByDescending(x => x.c.DeletedAt)`) + `:391-397` (DTO)
|
||||
**Đo:** `ContractListItemDto` không có `deletedAt` (trường thời gian duy nhất là `CreatedAt`). Người dùng thấy thứ tự "mới xóa trước" nhưng trên dòng chỉ có ngày tạo ⇒ trông như sắp lộn xộn. PE gặp đúng vấn đề này và đã xử bằng cách sắp theo `UpdatedAt` **và hiển thị** nó (`PurchaseEvaluationsListPage.tsx:557-562`).
|
||||
`DeletedAt` chắc chắn có số liệu: `DeleteContractCommandHandler` (`ContractFeatures.cs:632`) gọi `.Remove()` → `AuditingInterceptor.cs:54-63` đặt `IsDeleted/DeletedAt/DeletedBy`. Nên đây là vấn đề trình bày, không phải sai dữ liệu.
|
||||
**Tiêu chí nhận:** hiện `deletedAt` (thêm trường vào DTO hoặc mượn `updatedAt` như PE), hoặc đổi sang sắp theo trường đang hiển thị.
|
||||
|
||||
### FLAG-9 [L] — Chế độ "Đã duyệt": lọc loại HĐ làm ở phía trình duyệt trên 100 dòng đầu ⇒ có thể ra rỗng oan
|
||||
**File:** `MyContractsPage.tsx:41-49` (chỉ gửi `phase`, KHÔNG gửi `type`) + `:58-60` (`items.filter(c => c.type === typeFilter)`)
|
||||
**Đo:** ở chế độ **không**-xóa, `type` cố ý không gửi lên máy chủ (giữ nguyên nếp cũ), nhưng nay cộng thêm `phase=9`: máy chủ trả 100 HĐ phase 9 của **mọi loại**, rồi trình duyệt mới lọc loại. Nếu số HĐ đã phát hành > 100 thì lá "Đã duyệt" của một loại có thể hiện 0 dù thực tế có.
|
||||
Chế độ "Đã xóa" thì ngược lại — có gửi `type` lên máy chủ (`:45`), nên không dính. Lệch nhau giữa 2 nhánh trong cùng một hàm.
|
||||
**Tiêu chí nhận:** gửi `type` lên máy chủ cho cả 2 nhánh (BE `ListContractsQuery` hiện **chưa** có tham số `type` — cần thêm), hoặc chấp nhận và ghi rõ giới hạn 100.
|
||||
|
||||
---
|
||||
|
||||
## Ghi nhận PASS (đã kiểm, không thành FLAG)
|
||||
|
||||
- **Trục 1 — `Where(IsDeleted)`:** `ContractFeatures.cs:369-370` có `.IgnoreQueryFilters().Where(x => x.IsDeleted)` — đúng bài 8a S155 (Ignore GỠ chứ không ĐẢO). CLEAN.
|
||||
- **Trục 1 — IDOR:** `ContractFeatures.cs:375-380` sao đúng khuôn list sống `:298-303` (`DrafterUserId == userId || eligiblePhases.Contains(Phase)`), gọi lại chính `ListContractsQueryHandler.GetEligiblePhases` (internal static, cùng assembly — biên dịch được). Không nới rộng hơn list sống. CLEAN.
|
||||
- **Trục 1 — policy có thật:** `Contracts` ∈ `MenuKeys.All` (`MenuKeys.cs:168`) ⇒ `Contracts.Read` được đăng ký (`|All| × |Actions|`). Không phải tên policy ma. CLEAN.
|
||||
- **Trục 1 — Contract CÓ query filter:** `ContractConfiguration.cs:44` `HasQueryFilter(x => !x.IsDeleted)` ⇒ list sống không rò HĐ đã xóa; endpoint mới là đường DUY NHẤT thấy chúng. CLEAN.
|
||||
- **Trục 2 — định dạng khóa khớp lưới FE:** seeder sinh `Ct_<Code>_{WfView,List,Create,Pending,Approved,Deleted}`; lưới FE `Layout.tsx:130` `/^Ct_([^_]+)_(List|Create|Pending|WfView|Approved|Deleted)$/`. 7 mã loại (`ThauPhu`…`NguyenTacDv`) **không chứa dấu gạch dưới** ⇒ `[^_]+` bắt trọn. 6/6 nhánh có nhánh trả đường dẫn (`:136-142`). CLEAN.
|
||||
- **Trục 2 — `order++` chèn giữa không rối nhóm khác:** biến `order` (`DbInitializer.cs:1860`) chỉ phục vụ nhóm `Ct_*`; `Order` chỉ so trong cùng cấp anh em; nhóm loại HĐ nay cách nhau 7 thay vì 4 nhưng **thứ tự tương đối giữ nguyên**; `Hdc_*` dùng biến riêng `hdcOrder`, `Wf_*` dùng `wfOrder=95` (cha khác). Upsert chỉ đặt lại `Order` (`:1933-1937`), không đụng Label/ParentKey/Icon. CLEAN.
|
||||
- **Trục 2 — labelBackfill KHÔNG đè tên admin đặt:** backfill ghi `item.Label` (`:1999-2001`); tên admin đổi qua Mig 27 nằm ở cột **khác** là `DisplayLabel` (`MenuItem.cs:16`), và FE ưu tiên `displayLabel` (`Layout.tsx:202-203` `effectiveLabel`). ⇒ ép `Label` mỗi lần khởi động là **enforce có chủ đích** và **không** giẫm lên tuỳ biến của admin. CLEAN (đúng như lead khai).
|
||||
- **Trục 3 — `Khkk_WfView`/`Khkk_Deleted` bên fe-user:** đã có trong staticMap (`Layout.tsx:78-80`). Bên fe-admin thiếu → xem FLAG-1.
|
||||
- **Trục 3 — trạng thái sáng của 6 lá Khkk:** phân biệt nhau nhờ tham số `m` (`m=wfview|list|create|pending|approved|deleted`), `m` không nằm trong danh sách transient ⇒ so khớp nghiêm. CLEAN. (`ComingSoonPage.tsx:27-28` chỉ đọc `stage`, bỏ qua `m` — đúng ý đồ, `m` chỉ để tách trạng-thái-sáng.)
|
||||
- **Trục 4 — nút "Tạo HĐ mới" đã ẩn ở chế độ xóa:** `MyContractsPage.tsx:122-127` bọc `{!showDeleted && …}`. Kèm chip "Đã xóa — chỉ xem" (`:112-116`) và câu rỗng riêng (`:158`). CLEAN.
|
||||
- **Trục 5 — cắt hero không để lại tham chiếu mồ côi (kiểm ĐỘC LẬP, 2 phép):**
|
||||
1. Đếm từng ký hiệu đã gỡ trong file hiện tại: `STAGE_TONE`, `StageCard`, `StageArrow`, `StageStatRow`, `StageLink`, `StageSoon`, `MyDashboard`, `peApproved`, `hasError`, `retryFailed`, `stats`, `Workflow`, `ChevronDown`, `PurchaseEvaluationPhase` = **0/0 hết**.
|
||||
2. `npx tsc -p tsconfig.app.json --noEmit` ⇒ **EXIT=0, không một dòng lỗi**. `tsconfig.app.json` có `noUnusedLocals: true` + `noUnusedParameters: true` ⇒ nếu còn import/biến mồ côi thì trình biên dịch đã đỏ. Đây là chứng mạnh hơn grep.
|
||||
⚠️ Đo đúng phạm vi: tsconfig này **không bật** `strict`/`strictNullChecks`, nên "tsc xanh" ở đây chỉ chứng "không mồ côi + không sai kiểu thô", KHÔNG chứng an toàn null.
|
||||
- **Trục 5 — phần danh sách còn nguyên:** tab 4 giai đoạn (`UserDashboardPage.tsx:1030-1034` `STAGES.map` + `active={stage === st.n}`), 2 bảng (`:536`, `:735`), cột "Hành trình" + `JourneyDots` (`:635`, `:682`, `:847`), nạp lười theo tab (`:974`, `:986`). Dữ liệu `soonDesc` vẫn có người đọc (`:881`), `tone` vẫn dùng (`:311`) ⇒ không thành rác. CLEAN.
|
||||
- **Trục 5 — tiêu đề mới:** `PageHeader title="Tổng quan quy trình"` khớp nhãn menu backfill `MenuKeys.Dashboard = "Tổng quan quy trình"`. CLEAN.
|
||||
- **`?type=3` của trang ma trận:** `ApprovalWorkflowApplicableType.Contract = 3` có thật (`ApprovalWorkflow.cs:56`); endpoint `GET /api/approval-workflows-v2` **chỉ cần đăng nhập**, không gắn policy (`ApprovalWorkflowsV2Controller.cs:16-26`) ⇒ **không** dính bẫy #44. Trang có sẵn nhánh rỗng lịch sự nếu admin chưa ghim quy trình nào cho loại 3 (`WorkflowMatrixViewPage.tsx:99-103`). CLEAN. *(Còn 1 điều KHÔNG kiểm được bằng đọc mã: prod đã có `ApprovalWorkflow` nào `ApplicableType=3` và `IsUserSelectable=true` chưa — nếu chưa, 7 lá "Luồng duyệt" đều hiện khung rỗng "Liên hệ Admin".)*
|
||||
- ~~**Sửa chú thích D3 ở PE:** … đúng 2 chỗ …~~ 🔴 **TÔI TỰ ĐÍNH CHÍNH — xem FLAG-10.** (Tôi suýt duyệt con số này bằng mắt; chỉ khi chạy `grep -rn "IgnoreQueryFilters" src/Backend` mới lòi ra. Bài học tự ghi: **con số trong chú thích cũng là một khẳng định đo được — phải chạy phép đếm, không đọc lướt.**) Phần CÒN LẠI của chú thích vẫn đúng và giữ nguyên giá trị: cả 2 handler list-deleted đều kèm `.Where(x => x.IsDeleted)`, và lệnh cấm gộp thành helper dùng chung vẫn nguyên.
|
||||
|
||||
### FLAG-10 [M] — Chú thích vừa sửa trong chính diff này khai SAI con số, và nó là loại sai tự lan
|
||||
**File:** `PeSoftDeleteFeatures.cs:184` (dòng ĐƯỢC SỬA trong diff) + `ContractFeatures.cs:356` (dòng MỚI trong diff)
|
||||
**Khẳng định trong mã:** "NAY CÓ **2** chỗ dùng `IgnoreQueryFilters`" và "Chỗ **THỨ HAI** trong `src/Backend`".
|
||||
**Phép phản chứng (1 lệnh, chạy thật):** `grep -rn "IgnoreQueryFilters" src/Backend --include=*.cs` ⇒ ngoài 2 chỗ nói trên còn **`ApprovalWorkflowV2AdminFeatures.cs`** dùng ở **15 vị trí** (`:957, 964, 971, 978, 985, 992, 999` — 7 bảng `*LevelOpinions`; và `:1038, 1042, 1046, 1050, 1054, 1058, 1062` — 7 phép dò "phiếu cha còn sống không"). Tổng thực tế ≈ **3 tệp / 17 vị trí**, không phải 2.
|
||||
**Vì sao không chỉ là lỗi chính tả:** câu này được viết dưới dạng **luật** ("chỗ DUY NHẤT/THỨ HAI ⇒ CẤM gộp helper"). Người đọc sau tin vào con số để kết luận "mình đang sửa chỗ duy nhất còn lại" trong khi thực tế có cả một cụm 15 chỗ ở module khác. Con số sai từ S155 ("DUY NHẤT") đã có cơ hội được sửa trong đúng lượt này, nhưng bị viết lại thành một con số sai mới.
|
||||
**Tiêu chí nhận:** bỏ hẳn con số đếm ra khỏi chú thích (nêu **luật** thay vì **số**: "mọi nơi dùng `IgnoreQueryFilters` để liệt kê bản ghi đã xoá BẮT BUỘC kèm `.Where(x => x.IsDeleted)`"), hoặc giữ số nhưng nói rõ phạm vi đếm ("2 chỗ **thuộc lớp list-deleted**"; các chỗ trong `ApprovalWorkflowV2AdminFeatures` thuộc lớp khác — dò mồ côi). Kèm 1 lệnh grep trong chú thích để người sau kiểm lại được.
|
||||
- **Không có dấu vết giả-lập:** diff không chứa `// Mock` / `alert(` / `TODO.*wire` / `placeholder`. Endpoint mới là truy vấn thật, FE gọi thật.
|
||||
- **Chứng cứ cho FLAG-2 (đã truy tới đĩa):** truy vấn chi tiết HĐ nằm ở `ContractFeatures.cs:462` (`GetContractQuery`) → `:471` `db.Contracts.AsNoTracking()` — **không** `IgnoreQueryFilters` ⇒ HĐ đã xoá chắc chắn rơi vào `NotFoundException` (404). Không phải suy đoán.
|
||||
|
||||
---
|
||||
|
||||
## Phạm vi KHÔNG kiểm (khai thật)
|
||||
|
||||
- **Không chạy `dotnet build` / `dotnet test`.** Phần BE chỉ soi tĩnh. Rủi ro biên dịch thấp (chỉ dùng lại `internal static` cùng assembly, DTO có sẵn) nhưng **tôi chưa có chứng**.
|
||||
- **Không kiểm chứng dữ liệu prod:** (a) dòng `Permissions` khoá `Contracts` của 13 vai (FLAG-6); (b) có `ApprovalWorkflow` nào `ApplicableType=3, IsUserSelectable=1` chưa. Cả hai cần 1 câu `sqlcmd` hoặc 1 lần curl — lead chốt soi tĩnh nên tôi dừng đúng ranh.
|
||||
- **Không kiểm 7 tệp ngoài phạm vi lead khai** (`docs/*`, `scripts/*`, `.claude/*`) — chúng có mặt trong cây làm việc và sẽ đi cùng lượt commit nếu dùng `commit -a`.
|
||||
- **Không kiểm `git status` cho `Migrations/`** — đợt này không đụng migration nào (0 tệp trong `Migrations/`), nên bẫy "migration chưa được git theo dõi" (S155) không áp dụng.
|
||||
- **Chưa chạy `npm run build` cho fe-admin** — FLAG-1 là thiếu dữ liệu trong bản đồ tuyến, không phải lỗi kiểu, nên trình biên dịch sẽ không bắt được; chạy build cũng không đổi kết luận.
|
||||
|
||||
---
|
||||
|
||||
## PHÁN QUYẾT
|
||||
|
||||
**PASS_WITH_FLAGS — 10 FLAG (0 High / 4 Medium / 6 Low).**
|
||||
|
||||
Không có lỗi nào đủ nặng để chặn commit: đường vào dữ liệu của endpoint mới **đúng** (có `.Where(IsDeleted)`, có rào IDOR sao đúng khuôn list sống, policy có thật, không rò dữ liệu). Việc cắt hero **sạch**, có chứng độc lập (`tsc` EXIT=0 với `noUnusedLocals`).
|
||||
|
||||
Bốn việc nên xử **trước khi bàn giao cho người dùng thử** (không nhất thiết trước khi commit):
|
||||
1. **FLAG-2** — bấm dòng ở màn "Đã xóa" cho ra 2 panel trắng; một dòng `disabled={showDeleted}` là xong, đúng khuôn PE đã có.
|
||||
2. **FLAG-1** — thêm 2 dòng vào `staticMap` của fe-admin, nếu không 2 lá menu biến mất im lặng bên app quản trị.
|
||||
3. **FLAG-10** — con số trong chú thích sai và nó được viết dưới dạng luật, dễ dẫn người sau đi sai.
|
||||
4. **FLAG-3** — hoặc làm nốt "Hộp thư", hoặc sửa chú thích cho khớp việc mã thực sự làm.
|
||||
|
||||
Sáu FLAG mức thấp còn lại là chuyện định nghĩa nghiệp vụ (FLAG-4), tật kế thừa từ PE (FLAG-5), rủi ro triển khai cần một câu truy vấn để yên tâm (FLAG-6), nợ quy ước (FLAG-7), trình bày (FLAG-8) và giới hạn phân trang (FLAG-9).
|
||||
|
||||
---
|
||||
|
||||
**END — TOTAL: 10 FLAG (0 High / 4 Medium / 6 Low) · VERDICT: PASS_WITH_FLAGS**
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user