wal: flush 20260801T1651

This commit is contained in:
pqhuy1987
2026-08-01 16:51:09 +07:00
parent 081557b441
commit 2305c39019
7 changed files with 296 additions and 26 deletions

View File

@ -17,17 +17,27 @@ chain:
L9 per-window (chốt @closeout L9) · run.md ledger 8 row mới (K3-fix + F1/K4ab/K5K4c/K6/K7/GĐ3/K8) + B4 7/8
[x] /tiep S168 nghi-thức — Sàn-3 ①④⑤ khớp-nợ-WAL · verify 5/5 KHỚP · tick CLEAN 41→42 · NHIP-NO light 2/6 deep
2/15 ok · thu-moi se=0 all=0 · mind-check 11Đ/0T · ctx-verifier CO-11-cờ (⑦⑨ vá liền, ⑩⑪ để MIND-5) · _tiep-5
[!] GĐ3 cây-4-folder ĐỒNG-NHẤT — BLOCKED-SẠCH chờ anh 2-quyết HANDOFF slot (63): (a) trang-đích fe-user
{MyContractsPage · InboxPage · WorkflowMatrixViewPage} · (b) {A-42px⛔ · B-≥2xl · C-nới-PipelineTreePanel
[lane+lead NGHIÊNG C; phụ: mở=ĐẨY-hay-PHỦ] · D-cây-thay-Panel-3} — sub-implfe-gd3.md §1. Chốt → scaffold
1-lượt → gate → commit → cicd
[ ] K8-prep: form-14-người fail-closed + gói SOLUTION-PE-{n} + QĐ9-đo-2-nhánh + ZZTEST + rollback-ID.
(54)-FINAL: pre-flight CHỈ check-đọc workflow-live/roster, KHÔNG dng/sửa
[!] 🔴 OWNER TRẢ @S168 16:4x (verbatim: "D cây-thay-Panel-3. / Cứ mở rộng đi nhé. / Chạy dry-run hết luôn đi
rồi tao giao tiếp plan khác") ⇒ (63)(b)=**D cây-thay-Panel-3** · "mở rộng" lead KHAI CÁCH HIỂU 2-tầng:
(a)=CẢ-3-trang fe-user + (59)=mở-rộng-nguồn-tiep_reload [anh bác tầng nào thì đảo] · MANDATE MỚI: GĐ3-D
xong → CHẠY LUÔN K8 dry-run toàn trình, không dừng chờ; xong anh giao plan khác
[x] GĐ3-D SHIP-local `081557b` (3 trang Panel-3 + khuôn +bodyMaxHClass F-1, SHA-pair 7baf6247; WorkflowMatrixView
KHÔNG áp khai chủ-đích) · gate PASS-WITH-FLAGS-10/0-blocker (garble→resume; disposition từng dòng trong
sub-reviewer-gate-gd3d.md) · suite 642/0 (45D+597I) + build ×2 PASS · CHƯA push (tránh #86 — push 1 lượt)
[!] K8-BLOCKER Lines-editor vá xong CODE (UpdateContractSigningPlanLineCommand + PUT lines/{lineId} + FE select
×2 SHA-pair 21e0ca05 + 3 Fact xanh — 2 Fact đầu fail FK ApproverUserId-cần-row-thật, đã sửa) · gate
ĐANG CHẠY NỀN → commit → push 1-lượt cùng GĐ3-D → cicd — sub-k8-dryrun.md + sub-reviewer-gate-lineeditor.md
[!] K8 dry-run: B0 PASS trọn (backup 26,6MB C:\Backups · roster LIVE 3 workflow · form-14-dòng đủ chủ · login
admin OK · catalog 86/N4 · Contracts prod 0 · dữ-liệu-test 0 → GĐ0 tạo mới) · script k8run.py (scratchpad,
state-JSON ghi ID từng bước) sẵn stages gd0→pe→khkk→bridge→hd→qd9 — CHẠY SAU KHI deploy line-editor xanh
[ ] K8-prep + dry-run toàn trình: form-14-người fail-closed + gói SOLUTION-PE-{n} + QĐ9-đo-2-nhánh + ZZTEST
+ rollback-ID. (54)-FINAL: pre-flight CHỈ check-đọc workflow-live/roster, KHÔNG dựng/sửa
[ ] (59) thi-hành: mở rộng tiep_reload.sources (đo trước-sau, khoanh phạm-vi từng nguồn, khai vết owner-answer)
[ ] Monthly drift-audit 2026-08-01 DUE (session-đầu-tháng — CLAUDE.md §Audit; lần trước S96 07-01) + re-sync
measured{} (6 vai ctx/ring 0-baseline) + re-chạy governance-detectors sau đợt vá S166+S168 (ring2 INFORM #6)
next: anh trả slot (63) 2-quyết GĐ3 → lane scaffold → gate → commit → cicd. Anh chưa trả ⇒ /tiep sau vào
drift-audit-tháng-8 HOẶC K8-prep (check-đọc-only) — 3 mạch không chặn nhau.
next: GĐ3-D (đọc sub-implfe-gd3 §phương-án-D → scaffold 3 trang → gate → commit → cicd) → (59) → K8 dry-run
→ báo anh nhận plan mới. Drift-audit xếp sau mandate.
verify:
head -1 .claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-implfe-gd3.md # IMPLFE-GD3: BLOCKED

View File

@ -12,12 +12,16 @@
"tiep_reload": {
"_note": "🔄 @S153 adopt 0df10df4 khoan (37) RESOLVED (b) — nap-lai NEN RUT-GON mac dinh @/tiep (OWNER-DELEGATED cung vet _mind_ctx_kb_set). Nen-full SE do tuoi @S153 = 596.316B ~ [149.079..198.772] tok (instrument-backfill S153) — nang hon nen hub (439.328B ~ 110K tok) 1.36x, nuot tron rang mem ~150K/cua cua hub-owner neu full => rut gon ~ 40-60K tok (input one-shot cache-duoc). Reader = nghi-thuc /tiep (AI doc danh sach nay qua tiep.md §3-bis TRO KHOA — B1 lenh KHONG chep danh sach); KHAI THANG: chua co script-consumer — KHAC class ghost-wire vi hanh-vi tieu thu moi luot /tiep + dogfood T9 do that. Pham-vi tung muc khoanh ngay trong sources (khuon hub 'do theo pham-vi tung muc da khoanh trong cau-hinh').",
"mode": "rut-gon",
"_expand_S168": "🔄 OWNER-ANSWER @S168 2026-08-01 (slot 59; anh: 'Cứ mở rộng đi nhé' — lead khai cách hiểu 2-tầng trong turn, tầng-(59) = MỞ RỘNG danh-sách nguồn; anh bác thì revert 3 dòng cuối sources). Đo đúng-phạm-vi TRƯỚC = 132.449 B ~ [33K..44K] tok (cận-dưới < sàn 40K; mốc 120.633 B @S166 là trước khi segment HANDOFF mới land) → THÊM 3 nguồn khoanh-phạm-vi (+49.711 B) → SAU = ~182.160 B ~ [45K..61K] tok = NẰM TRỌN dải 40-60K. 🔴 3/5 nguồn gốc phình theo phiên — CẤM neo literal, nghi under/over-fill thì đo lại bằng đúng phạm-vi từng mục (bài H24-DEEP F-8 + ring2-bác @S166).",
"sources": [
"docs/STATUS.md :: dong CURRENT + bang CURRENT STATE (KHONG ca file)",
"docs/HANDOFF.md :: segment Last-updated MOI NHAT (KHONG ca file)",
".claude/WAL.md :: tron (<=40 dong by-design)",
".claude/sessions/session-<N>/_mind-s-<N>.md :: tron (doc TU TREN XUONG)",
".claude/sessions/session-<N>/_context-s-<N>.md :: tron"
".claude/sessions/session-<N>/_context-s-<N>.md :: tron",
".claude/governance/ACTIVE-MARKS.md :: tron (bang marks dang hieu luc — +S168 slot 59)",
"docs/gotchas.md :: 5 section ### N. MOI NHAT (KHONG ca file — +S168 slot 59)",
"docs/changelog/migration-todos.md :: header Active-work + section Phase hien hanh (KHONG ca file — +S168 slot 59)"
]
},
"hmw_width": {

View File

@ -1,17 +1,19 @@
# Reviewer Agent — Persistent Memory
- **[→ archive/2026-07.md — verbatim ĐÃ VERIFY moved-not-cut 3/3 @S166-K5-curate] S166 gate K4b · K4a · vá F-1 (digest):** mỏ-neo XUYÊN FILE (quét file BỊ DỜI, không chỉ file đang sửa) · **tham-số-trang-trí** (đo AI ĐỌC từng param, đừng đo "route có tồn tại") · mỏ-neo tự-vô-hiệu ngay lúc land (đo SAU khi chèn) · **ô canonical sai đo được bằng 1 curl** · khuôn-nguồn 2 tầng ⇒ đo **GIÁ-TRỊ KHỞI-TẠO** không đọc chú-thích · **cây phình giữa review** neo mốc + re-đo `--stat` TRƯỚC verdict · HIGH-nằm-ở-git (`git status --porcelain -- src tests | grep '^??'`) · khoá-cache định-danh REQUEST không định-danh URL · sợi-dây-1-argument 0 test.
- **[→ archive/2026-07.md §@S166-K5-curate — verbatim] S166 gate K4b · K4a · vá F-1 (digest):** mỏ-neo XUYÊN FILE · **tham-số-trang-trí** (đo AI ĐỌC từng param) · mỏ-neo tự-vô-hiệu ngay lúc land · ô canonical sai đo được bằng 1 curl · khuôn-nguồn 2 tầng ⇒ đo GIÁ-TRỊ KHỞI-TẠO · **cây phình giữa review** ⇒ re-đo `--stat` TRƯỚC verdict · HIGH-nằm-ở-git (`git status --porcelain -- src tests | grep '^??'`) · khoá-cache định-danh REQUEST không định-danh URL.
- **[→ archive/2026-07.md §@S167-K7-curate — verbatim ĐÃ VERIFY moved-not-cut 2/2] S166 gate K5 cây-GĐ2-8-sub-folder · gate K4c cụm-2 (digest):** bất-biến Σ do CẤU TRÚC (1 nguồn → phân hoạch TOÀN PHẦN → 1 chỗ đếm) · **phép-đo-rẻ-nhất nằm ở file RÁC của lane khác** (`menus.json` untracked = payload prod LIVE, vừa là bằng-chứng vừa là rào-commit) · chân-lý-RỖNG phải khai là rỗng · tương-đương-TỪNG-VẾ > 'cùng tập cùng thứ tự' · tiền-lệ phải đo ở BASE không đo ở cây đã sửa · 'N/N OK' chết theo phiên khi FE 0 hạ-tầng-test · **flake chỉ lộ ở LƯỢT ĐẦU và `-v q` NUỐT thông điệp assert** (lượt đầu dùng verbosity thường) · probe RED bằng đổi TEST-INPUT hợp lệ CHỈ KHI tham số xuất hiện ĐÚNG 1 chỗ (grep trước) · `Total` là vân-tay lọc-SERVER chỉ khi `CountAsync` sau `Where` trước `Skip/Take`.
- **[→ archive/2026-07.md §@S167-K7-curate — verbatim ĐÃ PROBE moved-not-cut] S166 gate K5 cây-GĐ2-8-folder · K4c cụm-2 (digest):** bất-biến Σ do CẤU TRÚC · **phép-đo-rẻ-nhất nằm ở file RÁC của lane khác** · chân-lý-RỖNG phải khai là rỗng · tương-đương-TỪNG-VẾ > 'cùng tập' · tiền-lệ đo ở BASE không ở cây đã sửa · 'N/N OK' chết theo phiên khi FE 0 hạ-tầng-test · **flake chỉ lộ ở LƯỢT ĐẦU và `-v q` NUỐT assert** · probe RED bằng đổi TEST-INPUT chỉ khi tham số xuất hiện ĐÚNG 1 chỗ · `Total` là vân-tay lọc-SERVER chỉ khi `CountAsync` sau `Where` trước `Skip/Take`.
- **[→ archive/2026-07.md §@S167-K7-curate-2 — verbatim ĐÃ VERIFY moved-not-cut] S167 gate K7 cầu KHKK→HĐ — PASS-WITH-FLAGS 10, 0 blocker (digest):** **1 con số đóng 2 câu hỏi** — baseline 622 (đo ở K4c cùng ngày) + 17 `[Fact]` đếm được = 639, đo đúng **639/0** ⇒ chứng CẢ '17 test chạy thật, không skip câm' LẪN '0 test cũ vỡ'. **Mỏ-neo tự-vô-hiệu tái phát, lan 3 nơi**: chú thích trỏ `:2516``git show HEAD:<f>|grep -n`=2516 vs `grep -n` cây=**2523**, chính diff chèn 7 dòng đẩy xuống ⇒ phải đo **2 THẾ GIỚI (HEAD ⟂ cây)**, đo 1 thế giới là im. **Prod probe sửa con số của chính comment**: `/permissions/by-role/{id}`×13 vai → `Contracts.CanCreate` **1/13** ⇒ tiền-đề lỗ ĐÚNG, nhưng '13/13 role' sai (thật 12/13, Admin đã có row True) + đẻ acceptance sau-deploy `0 added + 2 upgraded`. **Răng phủ NỬA**: T11 double-click assert 4 vế nhưng `await` tuần tự ⇒ ca ĐỒNG THỜI vẫn hở (0 unique-index/RowVersion/transaction) — đừng đọc 'có T-idem' thành 'đã đóng'. **Ngoại lệ authz đúng cách**: map khớp TUYỆT ĐỐI + action ngoài map vẫn bị đòi prefix ⇒ tương-lai không tự miễn-trừ; kèm `BeSubsetOf` chống cấu-hình-chết. **SHA-identical KHÔNG bảo chứng route-identical** (3 path cứng `/contracts/${id}` trong file mirror 2 app ⇒ grep route CẢ 2). Tag `[s167, mot-so-dong-2-cau-hoi, moneo-tu-vo-hieu, probe-sua-so-cua-comment, rang-phu-nua, sha-khong-bao-chung-route]` **Evidence:** `runs/2026-07-31-S164-4gd-khkk-fanout/sub-reviewer-gate-k7.md`
- **[→ archive/2026-07.md §@S167-K7-curate-2 — verbatim ĐÃ PROBE moved-not-cut] S167 gate K7 cầu KHKK→HĐ — PASS-WITH-FLAGS 10 (digest):** **1 con số đóng 2 câu hỏi** (639 = 622 baseline + 17 `[Fact]` ⇒ chứng CẢ 'chạy thật' LẪN '0 test cũ vỡ'). **Mỏ-neo tự-vô-hiệu** — comment trỏ `:2516` nhưng cây đã là `:2523` do chính diff đẩy xuống ⇒ phải đo **2 THẾ GIỚI (HEAD ⟂ cây)**. **Prod probe sửa con số của chính comment** (13/13 rolethật 12/13). **Răng phủ NỬA**: T11 assert 4 vế nhưng `await` tuần tự ⇒ ca ĐỒNG THỜI vẫn hở. **SHA-identical KHÔNG bảo chứng route-identical** ⇒ grep route CẢ 2 app. Tag `[s167, mot-so-dong-2-cau-hoi, moneo-tu-vo-hieu, rang-phu-nua]`
- **[→ archive/2026-07.md §@S167-K7-curate-2 — verbatim ĐÃ VERIFY moved-not-cut] S167 gate K6 admin thu-gọn — PASS-WITH-FLAGS 5 (digest):** **cây menu PROD SỐNG là thước, seed chỉ là bản vẽ** (`curl /api/menus/me` + áp CHÍNH tập key cắt regex từ file đã ship; control-âm bỏ 1 key để chứng phép đo có phân biệt). **`filterForAdmin` mới là bước MỘT** — còn `resolvePath→null` drop im lng (#50) + group rỗng-hoá tụt xuống render như LEAF. **typo-noop**: key ẩn sai chính tả = `Set.has` false, module vẫn hiện, tsc vẫn xanh ⇒ assert TỪNG key TỒN TẠI trong cây prod. MAJOR: comment nhồi 3 số-đếm, 1 số sai ngay lúc land (số chép từ skill stale) ⇒ CẤM số-đếm trong comment. **Evidence:** `runs/2026-07-31-S164-4gd-khkk-fanout/sub-reviewer-gate-k6.md`
- **[→ archive/2026-07.md §@S168-curate — verbatim ĐÃ PROBE moved-not-cut] S168 gate GĐ3-D cắm cây vào Panel-3 ×3 trang — PASS-WITH-FLAGS 10, 0 blocker (digest):** **khuôn dùng-lại phải ĐO LẠI theo HOST**`max-h`/`sticky` hiệu-chỉnh cho host CŨ: cây cần 100vh158 mà Panel-3 chỉ có 100vh184 (InboxPage) ⇒ 2 thanh cuộn lồng; 2 trang kia thừa 5px/13px = khít đến mong manh. **Class chết ÂM THẦM khi đổi kiểu cha** (`self-start` cha `block` · `lg:sticky` con DUY NHẤT của chính scroll-container) — tsc+build KHÔNG bắt, chỉ đọc CSS-chain mới thấy. `aside` lồng `aside``aria-label` vẫn giữ `role=complementary` ⇒ 2 landmark lng. Prod-probe biến lo-ngại thành số: `canPe` 13/13 True ⇒ ca `!canPe` chưa xảy ra (control-âm `Permissions`/`Users` False 12/13). `{!selectedId && <X/>}` = unmount ⇒ mất state thuần (`search`) trong khi `localStorage` sống ⇒ trade-off spec khai THIẾU. Thước hỏng: python Windows đọc JSON phải khai `encoding` · `while read` bỏ dòng CUỐI khi thiếu newline · `join` text-mode đẻ CRLF vào tên file. Tag `[s168, khuon-do-lai-theo-host, class-chet-am-tham, unmount-mat-state]`
- **[→ archive/2026-07.md — verbatim ĐÃ VERIFY moved-not-cut 4/4 @S166-curate] S164/S165 gate K3 · S164 gate K1 · S162 cây-4-folder · S161-W2 KHKK-CRUD (digest):** tham-số-chết-vì-thiếu-tầng-UI (đo TẦNG-CUỐI ai GỬI) · sweep 2-cờ bất-đối-xứng · flake ⇒ "N/0 tươi" phải khai SỐ LƯỢT · giá-trị lớn nhất ở GATE QUY-TRÌNH không ở mã · **ghost-policy** (grep policy, đừng suy từ "key đã vào All") · default-lấn-ngữ-nghĩa · im-lặng-về-độ-chính-xác-của-con-số ×3/diff · **#82 ngược chiều** (FE OR nhiều key LỎNG HƠN policy khớp-CHÍNH-XÁC-1-key ⇒ tự chuốc 403) · hợp-đồng FE↔BE VỠ 6 điểm mà tsc+build+574-test XANH (2 lỗi CHE NHAU) ⇒ so ROUTE+FIELD trên ĐĨA.
- **[→ archive/2026-07.md §@S167-K7-curate-2 — verbatim] S167 gate K6 admin thu-gọn — PASS-WITH-FLAGS 5 (digest):** **cây menu PROD SỐNG là thước, seed chỉ là bản vẽ** (`curl /api/menus/me` + control-âm bỏ 1 key). **`filterForAdmin` mới là bước MỘT** — còn `resolvePath→null` drop im lặng (#50) + group rỗng-hoá render như LEAF. **typo-noop**: key ẩn sai chính tả = `Set.has` false, module vẫn hiện, tsc vẫn xanh ⇒ assert TỪNG key TỒN TẠI trong cây prod. MAJOR: CẤM số-đếm trong comment.
- **[→ archive/2026-07.md — verbatim, verify moved 5/5 @S166-curate] S159-đợt5 · S161-W1 · S155-đợt2 · S145-4lane · S143 · S139 · S134b · S133 · S146/S147/S152-D2 (digest gộp):** chú-thích-mang-SỐ phải chạy phép đếm · cookie-cutter soi CẢ phần bản gốc đã CHẶN · `tsc`+`noUnusedLocals` > grep cho claim 'đã cắt' · comment 'bên kia lo' = ĐI ĐỌC BÊN KIA · grep bằng TOKEN-SONG-SINH · `hợp-đồng-đứt-2-bờ` · đổi-chiều-assert ≠ nới-assert · bất-biến chia-đôi 2-axis chỉ cross-cut sweep bắt · `bằng-chứng-tự-huỷ-sau-squash` · re-measure acceptance từ git HEAD + no-self-exempt · 'mọi nhánh set X' phải grep assignment-qua-BIẾN (#81) · `ExecuteDelete` ngoài transaction = mất dữ liệu · xanh-giả bắt bằng phép 2-thế-giới · 3 THƯỚC-HỎNG (md5 CRLF · `grep -c` citation-trap · `grep -iF` MSYS 0-hit im lặng).
- **[→ archive/2026-07.md §@S166-curate — verbatim] S164/S165 K3 · S164 K1 · S162 cây-4-folder · S161-W2 (digest):** tham-số-chết-vì-thiếu-tầng-UI (đo TẦNG-CUỐI ai GỬI) · sweep 2-cờ bất-đối-xứng · flake ⇒ 'N/0 tươi' phải khai SỐ LƯỢT · **ghost-policy** (grep policy, đừng suy từ 'key đã vào All') · **#82 ngược chiều** (FE OR nhiều key LỎNG HƠN policy khớp-CHÍNH-XÁC-1-key ⇒ tự chuốc 403) · hợp-đồng FE↔BE VỠ 6 điểm mà tsc+build+574-test XANH ⇒ so ROUTE+FIELD trên ĐĨA.
- **[→ archive/2026-07.md — ĐÃ RE-PROBE @S168: nội-dung CÓ trong archive; 2 nhãn `S161-W1`/`S152-D2` 0-hit nên GIỮ ở đây] S159-đợt5 · S161-W1 · S155-đợt2 · S145-4lane · S143 · S139 · S134b · S133 · S146/S147/S152-D2 (digest gộp):** chú-thích-mang-SỐ phải chạy phép đếm · cookie-cutter soi CẢ phần bản gốc đã CHẶN · `tsc`+`noUnusedLocals` > grep cho claim 'đã cắt' · comment 'bên kia lo' = ĐI ĐỌC BÊN KIA · grep bằng TOKEN-SONG-SINH · `hợp-đồng-đứt-2-bờ` · đổi-chiều-assert ≠ nới-assert · `bằng-chứng-tự-huỷ-sau-squash` · 'mọi nhánh set X' phải grep assignment-qua-BIẾN (#81) · `ExecuteDelete` ngoài transaction = mất dữ liệu · 3 THƯỚC-HỎNG (md5 CRLF · `grep -c` citation-trap · `grep -iF` MSYS 0-hit im lặng).
> **Persistent diary cross-session.** Auto-injected first ~200 lines at spawn (L1 HOT).
> Update BEFORE every stop. Tiered Memory v1: L1 HOT cap ~17KB (hook 24.4KB read-limit) · L2 `archive/` on-demand · L3 RAG `search_memory` just-in-time. Keep entry ≤ 1.5K chars (gotcha #53).
@ -20,18 +22,18 @@
- **[→ archive/2026-07.md @S161-curate] S160 (07-29) CHỐT-CUỐI 13-fix:** fix áp NỬA spec = dư-lượng (soi CẢ FILE khi nhận 'đã fix') · anchor-list là artifact RIÊNG phải sync · verify NGƯỢC premise · owner để-mở ⇒ finding MOOT-đúng.
## 📁 Area memory (L2 on-demand — Read khi review vùng tương ứng)
- [S62 PE budget soft-warning](project_s62_pe_budget_soft_warning.md) — PASS: hard-block→soft-warning; submit-guard intact + validator giữ `BudgetPeriodAmount>0`, row8 negative-safe (additive-only). Validator class `PurchaseEvaluationFeatures.cs:317`.
- [S62 PE budget soft-warning](project_s62_pe_budget_soft_warning.md) — hard-block→soft-warning, submit-guard intact (additive-only).
- [Wire/mirror claim verification anchors](feedback_wire_claim_verification_anchors.md) — sha256 twin-file · `git diff -U0` isolate true-adds · `allowNegative` bleed check · guard-still-intact grep.
- **[→ archive @S161-curate] S160 review SPEC-BỘ KHKK:** review SPEC ≠ review CODE · seeder-grant kiểu insert = NO-OP 403 im lặng ⇒ acceptance đo CỜ không đếm ROW · acceptance 'PASS SẴN' = 0-bit · choke-point 'DUY NHẤT' sai vì gán QUA BIẾN (#81).
- **S89 trio** ([sectionB](project_s89_pe_sectionb_review.md) · [endsBeforeCeo](project_s89_pe_endsbeforeceo_plan_review.md) · [finalize-note](project_s89_finalize_note_plan_review.md)) — adjustMut share race + authz-lens ≠ concurrency-lens · FE 4 render-sites plan-missed-2 · ground-truth-accurate-nhưng-gaps.
- **S89 trio** ([sectionB](project_s89_pe_sectionb_review.md) · [endsBeforeCeo](project_s89_pe_endsbeforeceo_plan_review.md) · [finalize-note](project_s89_finalize_note_plan_review.md)) — authz-lens ≠ concurrency-lens; FE 4 render-site, plan sót 2.
- **S101 H18/H19 WF2 + LANE-C:** content-hash ≠ commit-SHA (hex strip/no-strip) · exact-token sweep miss paraphrase → widen concept-phrase. → `archive/2026-07.md`.
- [S103 H20+crystallized-backfill review](project_s103_h20_crystallized_backfill_review.md) — WF2 PASS; 6/6 hash recompute MATCH + 4 already-met verified. Lesson: multi-axis warning phải nằm trong OUTPUT của script (reader chạy inline), không chỉ doc.
- [S111 PE sign-off 6-decision design review (schema/history lens)](project_pe_signoff_history_schema_review.md) — GO-WITH-ADJ; D5 opinion-history: append-only table, REJECT version-col (drops UNIQUE + breaks UPSERT).
- **PE per-hạng-mục series ×5** ([cardinality#3](project_pe_multiwinner_perhangmuc_review.md) · [BE-financial](project_s114_pe_multi_ncc_perhangmuc_financial_review.md) · [spec-l3](project_pe_multi_ncc_perhangmuc_specreview.md) · [IMPL](project_s114_multi_ncc_perhangmuc_impl_review.md) · [neg-quote](project_pe_negative_quote_correctness_review.md)) — derived src-of-truth: re-derive MỌI write-path KỂ CẢ Delete · grep field-NGUỒN không chỉ field-đọc · sign-loss: defer-parse tới field-chung mọi mutation.
- [S113 Supplier import v2 close-review (owner anh Kiệt)](project_s113_supplier_import_v2_review.md) — GO-WITH-ADJ 2 must-fix (consumers không pass published=true → drafts leak). Lesson: "filter added" ≠ "drafts hidden" grep-all-consumer + create write-path.
- [S118 Procurement master-access seeder review](project_s118_procurement_master_access_review.md) — PASS(cond); MAJOR Reports.Read leaks financials + Suppliers.Update grant ≠ edit. Lesson: menu-flag grant ≠ API capability; bare `[Authorize]` GET = open-to-all.
- [S123 governance 4-change review (backtick-guard + H24-3 + retire dạng-3 + mark)](project_s123_governance_4change_review.md) — PASS_WITH_FIXES; CAUGHT H24-3 thiếu enclosure-guard mà cùng diff vừa chứng cần (gen-2 citation-trap) + greedy `.*` lấy `(S<N>` CUỐI≠MAX + lý-do-retire áp cho dạng-2 thì giết C8. Q5 "HOÀN-THÀNH mark" = nguỵ-biện. Q8 ENDORSE không-bump.
- [S147 Project whitespace-dupe merge verify](project_project_dupe_merge_verify.md) — 7/7 PASS(2 FK-terminology corrections); "FK Projects" COMMENT ≠ physical FK (sys.foreign_keys=0 to Projects, all loose-Guid); seed-durability FORCES survivor=no-space blanket; INNER-join+filter=silent-vanish→repoint-before-soft-delete; prod ref-dist unverifiable (Server=localhost+secret).
- [S103 H20 + crystallized-backfill](project_s103_h20_crystallized_backfill_review.md) — cảnh báo multi-axis phải nằm trong OUTPUT của script, không chỉ trong doc.
- [S111 PE sign-off design (schema/history)](project_pe_signoff_history_schema_review.md) — opinion-history append-only; REJECT version-col (phá UNIQUE + UPSERT).
- **PE per-hạng-mục ×5** ([1](project_pe_multiwinner_perhangmuc_review.md) · [2](project_s114_pe_multi_ncc_perhangmuc_financial_review.md) · [3](project_pe_multi_ncc_perhangmuc_specreview.md) · [4](project_s114_multi_ncc_perhangmuc_impl_review.md) · [5](project_pe_negative_quote_correctness_review.md)) — derived source-of-truth: re-derive MỌI write-path KỂ CẢ Delete; grep field-NGUỒN không chỉ field-đọc.
- [S113 Supplier import v2](project_s113_supplier_import_v2_review.md) — "filter added" ≠ "drafts hidden": grep-all-consumer + create write-path.
- [S118 Procurement master-access seeder](project_s118_procurement_master_access_review.md) — menu-flag grant ≠ API capability; `[Authorize]` trần = mở cho mọi user.
- [S123 governance 4-change](project_s123_governance_4change_review.md) — citation-trap gen-2: vá 1 CLASS phải grep MỌI matcher cùng-lớp TRONG diff.
- [S147 Project whitespace-dupe merge](project_project_dupe_merge_verify.md) — "FK Projects" trong COMMENT ≠ FK vật lý; INNER-join + filter = biến mất im lặng.
---
@ -94,8 +96,7 @@ Adversarial pre-commit reviewer SOLUTION_ERP. Read-only verify + live curl prod
---
## 🔄 Curate trigger
- ⚠️ **@S167-K7 curate 2 pass: 22.1KB → 19.0KB** (move verbatim K5·K4c·K7·K6 → `archive/2026-07.md` §`@S167-K7-curate` + §`-curate-2`, mỗi pass probe moved-not-cut + control-âm TRƯỚC khi cắt, 4/4 pass). **CÒN NỢ ~1.9KB** để về 17.1KB — ứng viên kế: gộp khối `📁 Area memory` (nhiều dòng L2 đã stale >2 tháng). 🔴 **no-self-exempt CÓ ÁP**: entry K7 của CHÍNH TÔI là entry to nhất (3081B, phá luật "≤1.5K/entry" của chính file này) ⇒ nén trước, không miễn cho mình. 🔴 **Bài S166-K5 TÁI PHÁT NGAY trong pass này**: digest sinh bằng script đẻ chữ hỏng câm (`'ll3/13'` thay `'13/13'`) — byte-count không bắt được, chỉ ĐỌC LẠI BẰNG MẮT mới thấy. Script sinh chữ = luôn đọc lại output.
-**NỢ CURATE @S166-K4b ĐÃ TRẢ @S166-K5** (23.4KB→~17KB): move VERBATIM 3 entry đã hẹn (K4b·K4a·vá-F-1) → `archive/2026-07.md` §`@S166-K5-curate`. Trình tự bài S102: **append archive TRƯỚC → probe 3/3 kèm control-âm → assert `line in arc` NGAY TRONG script cắt → mới cắt → hậu-kiểm L1=0/archive=1**. Bài mới: digest sinh bằng escape `\uXXXX` **đẻ chữ hỏng câm** (byte-count không bắt được) ⇒ **script sinh chữ phải ĐỌC LẠI bằng mắt**.
- **@S168 curate: 22.2KB → <17.1KB (ĐO, không đoán).** Trình tự S102 giữ nguyên: append archive TRƯỚC probe moved-not-cut + control-âm mới cắt. 🔴 **Bài mới:** (a) tôi ghi '~20.6KB' bằng ƯỚC LƯỢNG trong khi `wc -c` cho 22.2KB **số trong sổ phải là số ĐO**, baseline '19.0KB' thừa kế từ pass trước cũng đã stale; (b) probe moved-not-cut bằng token lấy từ DIGEST cho **0-hit GIẢ** (digest paraphrase, bản gốc dùng chữ khác) 0-hit phải **thử token thứ 2 từ thân bài** trước khi kết luận cut-not-moved suýt giữ lại 938B ích; (c) **script sinh chữ lại đẻ chữ hỏng câm** (token `join` trong chuỗi làm entry vỡ đôi dòng) đúng bài S166-K5, byte-count KHÔNG bắt, chỉ ĐỌC LẠI mới thấy.
- Hook-cap **>17.1KB** (24.4KB read-limit, S109) → archive recent → L2 `archive/<period>.md` (append additive) + `_INDEX.md` pointer. Stale >3mo → remove.
- **Prev curates:** S166-K5 (probe 3/3) · S162 (2/2) · S145 · S140 · S134 · S128 · **S102 recovery: cut-not-moved ×5 `f229b07` ⇒ mọi L1-shrink kèm '→archive' BẮT BUỘC grep moved-not-cut TRƯỚC khi trust** · S116·S113·S109·S80·S71·S70·S40.

View File

@ -231,3 +231,8 @@
- **S167 (08-01) gate K7 cầu KHKK→HĐ (13 file, 3 nhóm) — PASS-WITH-FLAGS 10 (2 MAJOR + 7 MINOR + 1 CLARIFY), 0 blocker:** ① 🔴 **1 con số đóng 2 câu hỏi**: baseline 622 (đo ở K4c CÙNG NGÀY) + 17 `[Fact]` mới (đếm `grep -c '^\s*\[Fact\]'`, không chép) = kỳ vọng 639, đo được **đúng 639/0** ⇒ chứng CẢ "17 test có chạy thật (không bị filter/skip câm)" LẪN "0 test cũ vỡ" — rẻ hơn 2 phép đo rời. ② **mỏ-neo tự-vô-hiệu TÁI PHÁT và lan 3 nơi** (K4b-class): 3 chú thích mới trỏ `DbInitializer.cs:2516`; `git show HEAD:<file> | grep -n` = 2516, `grep -n` trên cây = **2523** — CHÍNH diff này chèn 7 dòng ở `:2353-2359` đẩy nó xuống ⇒ đúng lúc viết, **sai ngay khoảnh khắc land**. Bài: phải đo ở CẢ 2 THẾ GIỚI (HEAD ⟂ cây) mới thấy, đo 1 thế giới là im. ③ **prod probe sửa được con số của chính comment**: `GET /api/permissions/by-role/{id}` ×13 vai → `Contracts.CanCreate` **1/13 true (chỉ Admin)** ⇒ tiền-đề seeder ĐÚNG (Drafter/Procurement=False, lỗ THẬT), nhưng comment "403 với 13/13 role… chỉ Admin qua nhờ bypass" sai 2 vế: thật là **12/13**, và Admin **đã có row True** chứ không chỉ bypass. Cùng probe đẻ **acceptance rẻ nhất cho sau deploy: log phải là `0 added + 2 upgraded`** — lệch = có thứ khác lật quyền. Khai giới hạn: prod CHƯA deploy ⇒ chỉ chứng TIỀN-ĐỀ, không chứng seeder chạy đúng. ④ **test có răng nhưng phủ NỬA**: T11 double-click assert 4 vế (Conflict + count=1 + line trỏ HĐ đầu + `LastSeq=1` không đốt mã) — nhưng là `await` lần 1 RỒI lần 2 ⇒ chỉ phủ ca TUẦN TỰ; ca ĐỒNG THỜI vẫn hở (`:128-129` đọc-rồi-ghi trong bộ nhớ, 0 unique-index, 0 RowVersion, 0 transaction bao ngoài — transaction duy nhất là của codegen, đã commit trước). Comment test khai đúng phạm vi ⇒ KHÔNG phải lane nói quá; rủi ro là người sau đọc "T-idem có" thành "idempotency đóng". ⑤ **ngoại lệ authz đúng cách**: map `Dictionary<action,policy>` + `Should().Be()` khớp TUYỆT ĐỐI (không `StartsWith`/skip-list) + action ngoài map vẫn rơi `else` đòi prefix module ⇒ **action tương lai không tự miễn trừ**; kèm `Keys.Should().BeSubsetOf(byName.Keys)` chống **cấu-hình-chết** — vế "vắng-mặt trông giống ổn" mà lane TỰ đặt. ⑥ **mirror byte-identical đẻ rủi ro riêng**: 3 đường dẫn cứng `/contracts/${id}` trong file dùng chung 2 app ⇒ phải grep route ở CẢ `fe-user/App.tsx:65``fe-admin/App.tsx:84` (đều có, không thủng) — SHA-identical KHÔNG bảo chứng route-identical. ⑦ residual: 3 `scripts/*.ps1` dirty ngoài cả 13-file-set lẫn lead-set. Tag `[s167, mot-so-dong-2-cau-hoi, moneo-tu-vo-hieu-lan-3-noi, probe-sua-so-cua-comment, rang-phu-nua, sha-khong-bao-chung-route]` **Evidence:** `runs/2026-07-31-S164-4gd-khkk-fanout/sub-reviewer-gate-k7.md`
- **S167 (08-01) gate K6 admin thu-gọn (1 file FE, tập 12 key ẩn) — PASS-WITH-FLAGS 5 (1 MAJOR + 1 CLARIFY + 3 MINOR):** ① 🔴 **cây menu PROD SỐNG là thước, seed chỉ là bản vẽ**: `curl /api/menus/me` (token admin, 44KB) → áp CHÍNH tập key **cắt bằng regex từ file đã ship** ⇒ 200 node · 166 lá → **15 lá**, trùng tên 15/15 + control-âm bỏ `KeHoachKyKet` = 63 (+48) khớp lane. Cùng payload đóng luôn FLAG "row chèn tay": **10 `AwV2*`, 0 `AwV2_Contract`**. Khai giới hạn: endpoint đã lọc-quyền ⇒ row không có Permission thì vô hình (nhưng cũng vô hình trên sidebar ⇒ không lệch C1). ② **`filterForAdmin` mới là bước MỘT**: lá còn "THẤY" phải qua tiếp `resolvePath→null ⇒ MenuLeaf drop im lặng` (#50) và **group rỗng-hoá tụt xuống render như LEAF** (`children.length>0`) — lane bỏ cả 2, tôi đo: 0 drop · 0 rỗng-hoá. ③ **typo-noop**: key ẩn sai chính tả = `Set.has` false, module vẫn hiện, tsc vẫn xanh ⇒ phải assert từng key TỒN TẠI trong cây prod (12/12 OK; control-âm 3 key bịa đều MISS). ④ **MAJOR: comment mới nhồi 3 số-đếm, 1 số SAI ngay lúc land** — "28 leaf `Ct_`" (thật 42 lá/49 key, S155 thêm WfView/Approved/Deleted), số chép từ skill `permission-matrix` stale ⇒ đúng bệnh #84(4) "CẤM số-đếm trong comment". ⑤ **CLARIFY hệ-quả không ai khai**: prod có `Hrm/Off/Personal` `isVisible=false`, fe-user lọc `isVisible!==false` còn fe-admin KHÔNG đọc cờ đó ⇒ admin là lối vào CUỐI, ẩn nốt = 3 module mất menu ở CẢ 2 app. ⑥ display-only chứng 3 tầng: diff (grep `+` chỉ hit COMMENT) < kiến-trúc (`usePermission:15` đọc cây THÔ, chỉ 2 consumer) < curl prod 200×2 endpoint đã ẩn. Tag `[s167, thuoc-la-cay-prod, filter-moi-la-buoc-mot, typo-noop, so-dem-trong-comment]` **Evidence:** `runs/2026-07-31-S164-4gd-khkk-fanout/sub-reviewer-gate-k6.md`
## @S168-curate — verbatim moved from L1 (reviewer MEMORY.md)
- **S168 gate GĐ3-D cắm cây vào Panel-3 ×3 trang PASS-WITH-FLAGS 10, 0 blocker (digest):** **khuôn dùng-lại phải đo LẠI theo HOST**, `max-h`/`sticky` của khuôn được hiệu-chỉnh cho host CŨ: `lg:max-h-[calc(100vh-16rem)]` cộng header+search = 100vh158, còn Panel-3 chỉ 100vh184 InboxPage (dải "Vai trò" +29px) **2 thanh cuộn lồng**; 2 trang kia thừa 5px/13px = **khít đến mức mong manh** (đổi 1 Button `h-8``h-10` lật). **Class chết ÂM THẦM khi đổi kiểu cha**: `self-start` (cha `lg:block` không phải flex) + `lg:sticky` (cây con DUY NHẤT của chính scroll-container quãng dính 0) tsc/build KHÔNG bắt được loại này, chỉ đọc CSS-chain mới thấy. **`aside` lồng `aside` `aria-label` vẫn giữ `role=complementary`** (chỉ tụt `generic` khi KHÔNG tên) 2 landmark lồng. Prod-probe biến 1 lo-ngại thành 1 con số: `canPe` false panel đổi câu chỉ-dẫn thành than-phiền-quyền, nhưng `/permissions/by-role` ×13 vai cho `PurchaseEvaluations.canRead`=**13/13 True** ca chưa xảy ra (control-âm: `Permissions`/`Users` False 12/13, chỉ Admin True phép đo phân biệt). **`{!selectedId && <X/>}` = unmount mất state thuần** (`search`) `localStorage` (`expandedSet`) vẫn sống trade-off spec khai THIẾU. Thước hỏng gặp lại: python Windows đọc JSON UTF-8 phải khai `encoding` (cp1252 nổ) · `while read` bỏ dòng CUỐI khi file thiếu newline (mất 1/13 vai) · python `'\n'.join` ghi text-mode Windows đẻ CRLF vào tên file. Tag `[s168, khuon-do-lai-theo-host, class-chet-am-tham, probe-bien-lo-ngai-thanh-so, unmount-mat-state]` **Evidence:** `.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-reviewer-gate-gd3d.md`

View File

@ -0,0 +1,54 @@
K8-DRYRUN: ĐANG CHẠY — B0 PASS (backup 26,6MB + roster LIVE + login admin OK); B1.. đổ ruột liên tục bên dưới
# sub-k8-dryrun — K8 dry-run E2E toàn trình (S168, 2026-08-01)
> Mandate owner @S168 (verbatim): *"Chạy dry-run hết luôn đi rồi tao giao tiếp plan khác"*.
> Kịch bản = spec-cụm3 §③ K8 + bảng B0-B16 (`sub-invest-fable-b2-cum3.md` K8.1) + **vá-9..15 spec-final THẮNG nền**.
> 🔴 Vá-12 owner-ratified @S164 *"tạo PE Test SOLUTION-PE-1,2,3… không test trên phiếu thật"* ⇒ **bộ test RIÊNG** (Project+NCC+gói ZZTEST/SOLUTION-PE-{n}), **KHÔNG đụng `PE/2026/A/049`** — câu giờ-G K8.2 của bản nền coi như ĐÃ trả.
> Người bấm mọi bước hôm nay = **admin (duyệt thay — SignedByUserId track, banner "Admin duyệt thay" by-design)**; cột "Ai (thật)" trong form = roster LIVE để buổi chạy với người thật dùng lại.
## B0 — PRE-FLIGHT (fail-closed) — ✅ PASS 16:2x-16:3x +07
- (i) **Backup prod:** `C:\Backups\SolutionErp\SolutionErp_preK8_S168.bak` — 27.877.888 B @ 2026-08-01 16:28 (BACKUP DATABASE 3393 pages OK). *(Script `scripts/backup-sql.ps1` default `D:\` — VPS KHÔNG có ổ D ⇒ dùng `C:\Backups`; default ĐÃ VÁ `C:\` @S168 cùng lượt.)*
- (ii) **Workflow LIVE (sqlcmd -E; ⚠️ `$env:PROD_DB_PASSWORD` KHÔNG tồn tại cả 3 scope trên VPS — persona-note stale cùng lớp F-9):**
- type-1 PE: `QT-DN-V2-001` active+selectable · 3 trạm (P1: 4NV L1 + tra.bui L2[SkipToFinal,Delete] · P2: 4NV L1 + **chuong.phan L2 [Finalize=1,SkipToFinal,Delete]** · P3: truong.nguyen [SkipToFinal]).
- type-10 KHKK: **đủ 8 nhóm N1-N8 active**; `KHKK-N4` 3 trạm 11 người (Cung ứng: 4NV+tra.bui · Kiểm soát CP: 4NV+**chuong.phan [Finalize=1]** · BGĐ: truong.nguyen) — khớp anchor OG-3/OG-9.
- type-3 HĐ: `QT-HD-V2-001` active — **1 trạm 1 người: binh.le (Bước 1 - Phòng CCM)**.
- (ii-b) Catalog: `ContractCatalogEntries` = **86** dòng; nhóm-4 mẫu B1-01 Đá · B1-02 Cát · B1-03 Cọc tràm · B1-04 Bê tông · **B1-05 Xi măng (chọn cho line test)** · B1-06 Thép.
- (iii) Dữ liệu test sẵn có: Projects ZZTEST = **0** · Suppliers TEST = **0** ⇒ TẠO MỚI qua API (GĐ0 dưới). Contracts prod = **0** (HĐ dry-run sẽ là HĐ ĐẦU TIÊN trên prod — seq cháy thuộc cặp prefix TEST, vô hại data thật).
- (iv) **Dặn team:** em KHÔNG có kênh Zalo/email — thay bằng ZZTEST-prefix mọi bản ghi + mục này để anh nhắn team *"notification phiếu ZZTEST trong hôm nay — bỏ qua"*. KHAI trade-off, không im.
- (v) Login API prod: `POST /api/auth/login` admin OK (accessToken 468c — field `accessToken` đúng bài S160).
## B0-FORM 14 dòng {bước · người THẬT (roster LIVE) · tài khoản · ai bấm hôm nay} — vá-9 fail-closed
| B | Ai (thật, theo roster LIVE) | Tài khoản | Hôm nay |
|---|---|---|---|
| B1-B3 | BCH drafter (đại diện Cung ứng: Nguyễn Văn Duy) | duy.nguyen@solutions.com.vn | admin (tạo thay) |
| B4 | Trạm-1 PE/KHKK — 1 trong {duy.nguyen · binh.lethanh · long.chau · phuong.nguyen} + L2 tra.bui | (4+1 tài khoản trên) | admin duyệt thay |
| B5 | Trạm-2 — 1 trong {luu.tran · nguyen.ho · binh.le · truong.letrandang} | (4 tài khoản) | admin duyệt thay |
| B6 | **TP.CCM Phan Văn Chương (Finalize)** | chuong.phan@solutions.com.vn | admin duyệt thay (tick finalize) |
| B7 | CEO Nguyễn Văn Trường (nhánh B) | truong.nguyen@solutions.com.vn | admin duyệt thay |
| B8 | BCH/Admin kiểm khóa | — | admin |
| B9 | Người có `Contracts.Create` (câu III default: Drafter/Procurement/Admin — seeder K7 CanCreate=3) | — | admin |
| B10 | Người bấm B9 | — | admin |
| B11 | Approver HĐ: **Lê Văn Bình** (QT-HD-V2-001 trạm duy nhất) | binh.le@solutions.com.vn | admin duyệt thay |
| B12 | HRA/CCM (HardCopyActorRoles) | — | admin |
| B13 | Bất kỳ + Admin (mở cây) | — | admin |
| B14 | Lead chốt sổ | — | lead |
⇒ 14/14 dòng CÓ CHỦ — fail-closed ĐẠT (không dòng nào trống).
## Nhật ký bước (đổ ruột liên tục — ID ghi TẠI CHỖ cho rollback vá-11/13)
### 🔴 PHÁT HIỆN TRƯỚC GIỜ CHẠY — BLOCKER toàn trình: Lines-editor KHÔNG TỒN TẠI (vá tại chỗ @S168)
Truy vết trước khi bấm B1 (đúng tinh thần "K8 lộ wave thiếu"):
- `ContractSigningPlanFeatures.cs` auto-gen line mồi `CatalogEntryId = null` + comment HẸN *"Người soạn gán sau qua `UpdateContractSigningPlanLineCommand`"***command đó chỉ tồn tại TRONG COMMENT** (grep toàn src + FE = 0 impl, 0 route, 0 call).
- `ContractSigningPlanWorkflowService.SubmitAsync` (vá-5) **CHẶN THẬT** line null: `"Còn dòng chưa gán hạng mục…"` + đếm-so-khớp nhóm.
- FE `KhkkDetailPage` cột Hạng mục tự khai *"Đường GÁN (Lines-editor) = K4b"* — nhưng K4b THẬT ship việc khác (`88e7ecf` = nối `?group=` sidebar, đo `git show --name-only`). ⇒ mảnh rơi giữa build-order.
- Hệ quả nếu không vá: **MỌI phiếu KHKK tạo sau K2 kẹt Nháp vĩnh viễn** — dry-run chết ở B3. Đây chính là chỗ cicd-K2 khai *"mục-4 chân-lý-rỗng — đo lại khi K8 đẻ phiếu thật"*.
**Vá @S168 (BE + FE ×2 app + 3 test):** `UpdateContractSigningPlanLineCommand(PlanId, LineId, CatalogEntryId)` handler (EnsureDraftEditable + fail-fast entry sống/đúng-nhóm 409 + denorm `TenHangMuc` + changelog Line) · route `PUT /contract-signing-plans/{id}/lines/{lineId}` policy `KeHoachKyKet.Update` · FE select gán tại bảng (Nháp/TraLai, khoá dòng đã bắc cầu HĐ, options lọc `?approvalGroup=`) mirror SHA-pair `21e0ca0507d4` ×2 app · 3 Fact (dương gán→submit-QUA · âm khác-nhóm 409 · âm phase). Gate + commit riêng trước khi chạy chuỗi.
### GĐ0 — dựng bộ test (Project + Supplier + WorkItem + PE `SOLUTION-PE-{n}`)
(đang chạy — điền dưới)

View File

@ -0,0 +1,187 @@
GATE-GD3D: PASS-WITH-FLAGS-10 — Đúng phương án D, khuôn nguyên vẹn (SHA-pair identical), import sạch; 0 blocker, nhưng `max-h` của khuôn vượt chiều cao Panel-3 trên InboxPage (~26px) đẻ 2 thanh cuộn lồng nhau + 2 class khuôn chết âm thầm.
# Sub-reviewer gate GĐ3-D (S168, 2026-08-01)
Scope: 3 file FE uncommitted, phương án D "cây thay chỗ Panel-3 khi chưa chọn HĐ".
Spec nguồn: `.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-implfe-gd3.md` §1 bảng 4 phương án (D).
Vai: reviewer đối-kháng, **propose-only** — không sửa code, không commit.
---
## Đo trước (baseline)
- `git status --porcelain` toàn repo: 5 `M``.claude/WAL.md`, `.claude/agent-memory/memory-budget.json`, + 3 file FE.
- `git status --porcelain --untracked-files=all -- fe-admin/src fe-user/src`: **ĐÚNG 3 file**, 0 untracked.
- `git diff --stat`: mỗi file FE 13 dòng đổi (+6/7), tổng 3 file.
## R1 — Đúng-D (điều kiện render) → **PASS**
Cả 3 file giữ NGUYÊN cặp điều kiện:
```
{!selectedId && <PipelineTreePanel currentStage={3} />}
{selectedId && detail.data && <WorkflowHistoryPanel contract={detail.data} />}
```
- Cây CHỈ render khi `!selectedId` — đúng D.
- Chọn HĐ → `WorkflowHistoryPanel` như cũ; vế điều kiện **không đổi một ký tự**.
- Trạng thái "đã chọn HĐ nhưng `detail` đang tải" render **RỖNG ở CẢ bản cũ lẫn bản mới** (bản cũ: `!selectedId` sai nên ô dashed tắt, `detail.data` chưa có nên panel trống) ⇒ hành vi loading giữa chừng KHÔNG đổi.
- `currentStage={3}` đúng GĐ Hợp đồng cho cả 3 trang (xem F-10 cho lưu ý riêng về InboxPage).
## R2 — 0-đụng-khuôn → **PASS**
`sha256sum` (16 ký tự đầu), fe-admin ⟂ fe-user:
| File | admin | user | Kết quả |
|---|---|---|---|
| `components/pipeline/PipelineTreePanel.tsx` | `d70ac315bae485e6` | `d70ac315bae485e6` | IDENTICAL |
| `components/pipeline/PipelineStageFolders.tsx` | `bed52f39d595c86a` | `bed52f39d595c86a` | IDENTICAL |
| `hooks/usePipelineStages.ts` | `76e1823345d4e78b` | `76e1823345d4e78b` | IDENTICAL |
`git status --porcelain` trên 4 đường dẫn khuôn = **RỖNG** ⇒ khuôn chưa bị sửa. SHA-pair identical vừa chứng mirror 2 app còn khớp, vừa chứng không ai chạm vào khuôn trong đợt này.
## R3 — Unused-import `X` → **PASS**
- Cây làm việc: 0 hit mã ở cả 3 file. Hit duy nhất là chuỗi `type=X` trong comment `ContractsListPage.tsx:6` (không phải icon).
- **Control-âm:** cùng regex chạy trên `git show HEAD:<file>` cho **2 hit/file** (dòng `import … X …` + `<X className="mx-auto mb-2 h-5 w-5" />`) ⇒ phép đo CÓ răng, không phải 0-hit vì regex hỏng.
- Không gỡ nhầm icon khác: `FileText` / `Search` / `Plus` / `Inbox` / `AlertTriangle` / `Clock` / `ClipboardList` đều còn nguyên trong dòng import mới.
---
## FINDINGS
### F-1 (MAJOR) — R4: `max-h` của khuôn hiệu-chỉnh cho host page-flow, vượt chiều cao Panel-3 trên InboxPage ⇒ 2 thanh cuộn lồng nhau
Chiều cao TỐI ĐA của khuôn khi đặt ở lg:
| Thành phần | File:dòng | Lớp CSS | px |
|---|---|---|---|
| Đầu panel | `PipelineTreePanel.tsx:181` | `px-3 py-2.5` (20) + `icon-chip h-7!` (28) + `border-b` (1) | 49 |
| Ô lọc | `PipelineTreePanel.tsx:204` | `p-2` (16) + `input h-8` (32) + `border-b` (1) | 49 |
| Thân cây | `PipelineTreePanel.tsx:215` | `lg:max-h-[calc(100vh-16rem)]` | 100vh 256 |
| **Tổng** | | | **100vh 158** |
Chiều cao KHẢ DỤNG của Panel-3 = `100vh 4rem` (vỏ trang) đầu trang dải phụ `p-4` (32):
| Trang | Vỏ / đầu trang | Đầu trang | Dải phụ | Khả dụng | So với 100vh158 |
|---|---|---|---|---|---|
| `fe-user/src/pages/contracts/MyContractsPage.tsx` | `:105` / `:107` `py-3` + `Button size=md h-8` + border | 57px | 0 | 100vh 153 | **thừa 5px** (khít) |
| `fe-admin/src/pages/contracts/ContractsListPage.tsx` | `:106` / `:107` `py-3` + h1 24px + border | 49px | 0 | 100vh 145 | **thừa 13px** |
| `fe-user/src/pages/InboxPage.tsx` | `:166` / `:168` `py-3` + `StatPill` 34px (`:50` `px-3 py-1.5` + `h-5`) + border | 59px | **29px** — dải "Vai trò bạn đang xử lý" `:183-187` (`py-1.5` + `text-[11px]` + border) | 100vh 184 | **THIẾU ~26px** |
Hệ quả trên `/inbox`: `<aside className="hidden overflow-y-auto … p-4 lg:block">` mọc **thanh cuộn dọc thứ hai** bao ngoài thanh cuộn của thân cây, tức trong cột nội dung rộng 328px (360 2×16) có hai thanh cuộn lồng nhau. Nội dung vẫn với tới được (không vỡ layout, không mất dữ liệu), nhưng người dùng phải cuộn hai tầng, và đầu panel cùng ô lọc trôi khỏi tầm nhìn khi cuộn tầng ngoài.
Dải "Vai trò" chỉ render khi `user?.roles.length`**đo prod**: `/api/roles` trả 13 vai, mọi tài khoản thật đều thuộc ít nhất một vai ⇒ nhánh này gần như LUÔN bật, không phải ca hiếm.
Hai trang còn lại chỉ thừa 5px và 13px ⇒ **mong manh**: bất kỳ thay đổi nào ở đầu trang (đổi `Button` sang `size=lg` = `h-10`, hoặc thêm một chip trạng thái làm `flex-wrap` xuống dòng ở bề rộng 1024px) đều đẩy chúng sang đúng trạng thái của InboxPage.
**Tiêu chí nghiệm thu đề nghị:** ở 1280×800 và 1366×768, mở `/inbox` khi chưa chọn HĐ — số thanh cuộn dọc trong vùng Panel-3 phải bằng **1**, và cuộn tới đáy cây không cần cuộn `<aside>` ngoài. Lặp lại phép đo cho `/my-contracts` và fe-admin `/contracts`.
### F-2 (MINOR) — R4: hai class của khuôn chết ÂM THẦM trong host mới
`PipelineTreePanel.tsx:177``cn('card-accent flex flex-col self-start overflow-hidden lg:sticky lg:top-4', className)`:
- `self-start``align-self:flex-start`, chỉ có tác dụng khi cha là flex hoặc grid. Host mới có cha là `<aside … lg:block>``display:block`**vô hiệu**.
- `lg:sticky lg:top-4`: tổ tiên cuộn gần nhất **chính là** `<aside overflow-y-auto>`, mà cây lại là con DUY NHẤT của nó ⇒ khối chứa gần bằng chính nó ⇒ quãng "dính" xấp xỉ 0 ⇒ **vô hiệu**.
Ở ba host cũ (`KhkkListPage.tsx:197``HardCopiesPage.tsx:116`, đều là `lg:grid-cols-[19rem_minmax(0,1fr)]`) cha là grid và trang cuộn ở `<main className="flex-1 overflow-auto">`, nên **cả hai class đều sống**. Đây không phải lỗi chức năng, nhưng là dấu hiệu khuôn đang chạy ngoài giả định thiết kế của nó — cùng gốc với F-1. Nên ghi một dòng chú thích ở chỗ cắm để người sau không tưởng rằng cây có bám dính ở đây.
### F-3 (MINOR) — R4 / a11y: `<aside>` lồng trong `<aside>`
Host Panel-3 là `<aside>`, và gốc khuôn cũng là `<aside>` (`PipelineTreePanel.tsx:176`) có kèm `aria-label="Cây toàn trình theo gói thầu"` (`:179`). Theo HTML-AAM, một `aside` nằm trong sectioning content chỉ tụt xuống `role=generic` khi KHÔNG có tên khả truy cập; ở đây nó có tên nên **giữ nguyên `role=complementary`** ⇒ sinh ra hai landmark `complementary` lồng nhau. Điều này hợp lệ về HTML nhưng gây nhiễu khi người dùng điều hướng theo landmark bằng trình đọc màn hình. Ba host cũ không dính vì cha của chúng là `<div className="grid …">`.
### F-4 (MINOR) — R5: `!canPe` đổi thông điệp Panel-3 từ chỉ dẫn sang than phiền quyền; đo prod cho thấy hiện chưa vai nào dính
Đường quyền, đọc thẳng từ mã chứ không suy:
- `usePipelineStages.ts:83``canPe = PE_MENU_KEYS.some(k => can(k))`, với `PE_MENU_KEYS``:55``['PurchaseEvaluations','Pe_DuyetNcc_List','Pe_DuyetNccPhuongAn_List']`.
- Khi `canPe` sai, `PipelineTreePanel.tsx:111` đặt `enabled: canPe` nên truy vấn `pipeline-pe-index` **không chạy****không sinh rác 403**, đúng ý đồ đã khai ở `usePipelineStages.ts:5-8`.
- Nhưng Panel-3 khi đó hiển thị khung cây rỗng, huy hiệu "**0 gói**", và câu ở `:216-220`: "Bạn chưa có quyền xem phiếu Duyệt NCC nên cây toàn trình chưa hiển thị được." Câu này nằm **trên trang Hợp đồng**, thay cho câu cũ "Quy trình duyệt sẽ hiện khi chọn HĐ." ⇒ người dùng mất chỉ dẫn hữu ích và nhận về một lời than phiền lạc đề.
**Đo prod** `/api/permissions/by-role/{id}` trên 13 vai (tất cả trả `http=200`): `PurchaseEvaluations`.canRead bằng True ở **13/13 vai** ⇒ nhánh `!canPe` **hiện không xảy ra với vai nào**.
**Control-âm của cùng phép đo:** `Permissions`.canRead và `Users`.canRead bằng False ở **12/13 vai** (chỉ `Admin` True; số dòng quyền là 169 so với 191) ⇒ phép đo có phân biệt thật, không phải "True tuốt vì đọc sai trường".
Kết luận: F-4 là **rủi ro tương lai** (nếu ai đó gỡ quyền PE của một vai), không phải lỗi đang chảy máu. Không chặn commit.
### F-5 (MINOR) — R5: cây bơm thêm tối đa ba truy vấn index vào lượt tải đầu của ba trang
`PipelineTreePanel` gọi `usePipelineStages`, hàm này gọi `usePipelineLinkage`, sinh ra ba truy vấn — `pipeline-pe-index` (`PipelineTreePanel.tsx:100`), `pipeline-khkk-index` (`usePipelineStages.ts:90`) và `pipeline-contract-index` (`:108`) — mỗi truy vấn lấy `pageSize=200`.
Trên InboxPage, MyContractsPage và ContractsListPage, truy vấn `pipeline-contract-index` gọi `GET /contracts?pageSize=200` **song song** với truy vấn danh sách của chính trang (`/contracts/inbox``InboxPage.tsx:71`, `/contracts` ở MyContractsPage) — cùng một tài nguyên nhưng khác `queryKey`, nên mỗi lần vào trang mà chưa chọn HĐ là hai lần nạp danh sách hợp đồng. Đo prod cho thấy 13/13 vai đều có đủ `canPe`, `canKhkk``canContract`**cả ba truy vấn đều bắn cho mọi vai**, không vai nào được miễn. Mức này chấp nhận được vì `staleTime` là 60 giây và đây là hệ quả nội tại của phương án D, nhưng nên biết trước để không bất ngờ khi UAT phản ánh trang tải chậm hơn.
### F-6 (MINOR) — R1 phụ: trade-off của D khai thiếu một vế — ô lọc cây mất chữ khi chọn HĐ rồi bỏ chọn
Vì điều kiện là `{!selectedId && <PipelineTreePanel …>}`, việc chọn một HĐ sẽ **gỡ bỏ (unmount)** component. Hệ quả:
- `expandedSet` **sống sót** nhờ `localStorage` (`PipelineTreePanel.tsx:119-126` đọc, `:170` ghi).
- `search` (`:94`) và `mobileOpen` (`:95`) là state thuần nên **mất trắng**: người dùng gõ "FLOCK" để lọc cây, bấm xem một HĐ, quay lại thì ô lọc rỗng và cây bung về nhánh mặc định.
Spec D ở `sub-implfe-gd3.md` §1 chỉ khai trade-off "cây nhảy chỗ khi chọn HĐ", không khai việc mất chữ đang lọc. Đề nghị bổ sung vế này khi báo owner để owner chốt trên thông tin đầy đủ.
### F-7 (MINOR) — R8 mobile: không hồi quy, nhưng lệch với ba host cũ
- Host là `<aside className="hidden overflow-y-auto … lg:block">` nên dưới 1024px Panel-3 **không render**. Bản cũ (ô dashed) nằm trong **đúng cùng** `<aside>` đó, vì vậy **không có hồi quy** trên mobile.
- Nhưng khuôn có sẵn cả một nhánh mobile riêng: nút "Hiện cây / Ẩn cây" gắn `lg:hidden` (`:193-200`), state `mobileOpen` khống chế thân cây (`:203`), và `max-h-[28rem]` dành cho mobile (`:215`). Trong host này **toàn bộ nhánh mobile của khuôn chết**.
- Hệ quả nghiệp vụ: người dùng mobile CÓ cây ở GĐ2 (`KhkkListPage`) và GĐ4 (`HardCopiesPage`, hai trang page-flow nên cây xếp ở trên), nhưng **không có cây ở GĐ3**.
Điều này đúng chủ đích của HOST (bố cục 3-panel vốn chỉ dành cho desktop) nhưng lệch chủ đích của KHUÔN. Owner cần biết để chốt, đây không phải lỗi code.
### F-8 (INFO — phán quyết: lead ĐÚNG) — R6: `WorkflowMatrixViewPage` không áp D là quyết định đúng
`fe-user/src/pages/pe/WorkflowMatrixViewPage.tsx:104` cho thấy gốc trang là `<div className="space-y-4 px-2 py-5">`: đây là trang page-flow, **không có `<aside>`, không có lưới 3-panel, không có Panel-3**. Vì vậy "cây thay chỗ Panel-3" **không có chỗ cắm**, và bỏ qua trang này là đúng theo đúng định nghĩa của phương án D. Đây không phải chỗ lead bỏ sót.
Tuy nhiên có một câu cần owner chốt: trang này được mount ở **ba route** (`fe-user/src/App.tsx:71``/purchase-evaluations/workflow-matrix`, `:73``/contracts/workflow-matrix`, `:83``/khkk/workflow-matrix`), tức nó là trang giai đoạn của cả ba GĐ. Nếu ý owner khi nói "(a) cứ mở rộng" là "mọi trang giai đoạn đều phải có cây", thì đây là trang duy nhất còn thiếu, và cách cắm đúng phải là lưới page-flow theo khuôn `KhkkListPage.tsx:197` chứ không phải D. Việc này nằm ngoài phạm vi wave hiện tại, nêu ra để owner quyết.
### F-9 (INFO — phán quyết: KHÔNG mâu thuẫn K6) — R7: sửa trang đang bị ẩn khỏi sidebar
- `fe-admin/src/components/Layout.tsx:227-240` — tập `ADMIN_HIDDEN_KEYS` có chứa `'Contracts'`; thêm nữa, `:248` trong `isAdminHidden` còn chặn mọi key bắt đầu bằng `Ct_`. Vì vậy admin không nhìn thấy mục Hợp đồng ở sidebar.
- **Route thì vẫn sống**: `fe-admin/src/App.tsx:82` map `/contracts` sang `ContractsListPage`, kèm `:83` cho `/contracts/new``:84` cho `/contracts/:id`.
- **Có tiền lệ khớp**: key `'HopDongCung'` cũng nằm trong `ADMIN_HIDDEN_KEYS` (`:231`), vậy mà `HardCopiesPage` bên fe-admin vẫn được S162 cắm cây (`fe-admin/src/pages/contracts/HardCopiesPage.tsx:117`) và route của nó vẫn sống (`App.tsx:87`).
Kết luận: K6 ẩn phần **hiển thị**, chứ không xoá **trang**. Sửa một trang bị ẩn chính là giữ mirror theo `docs/rules.md §3.9`, nên **không có mâu thuẫn**.
Một lưu ý thực dụng: thay đổi bên fe-admin gần như **vô hình với admin** vì phải gõ thẳng URL mới tới được. Giá trị của nó là parity giữa hai app, nên đừng tính nó vào "ba trang người dùng nhìn thấy" khi báo owner, và UAT phải làm ở fe-user chứ không phải fe-admin.
### F-10 (INFO) — R1 phụ: `currentStage={3}` trên InboxPage, trong khi hộp thư trộn GĐ1 và GĐ3
`fe-user/src/pages/InboxPage.tsx:71` gọi `/contracts/inbox``:79` gọi `/purchase-evaluations/inbox`, nên trang liệt kê cả hợp đồng lẫn phiếu Duyệt NCC (bấm vào phiếu PE thì `:283` điều hướng sang `/purchase-evaluations/:id`). Giá trị `currentStage={3}` làm cây mở sẵn folder GĐ3.
Cách đặt này vẫn hợp lý, vì Panel-3 là chỗ dành cho HĐ đang chọn (`selectedId` được dùng cho `/contracts/${selectedId}``:84`), tức ngữ cảnh của panel đúng là GĐ3. Nêu ra để tránh hiểu nhầm nếu sau này owner coi Inbox là trang GĐ1. Không tính là lỗi.
---
## Tổng kết theo trục
| Trục | Kết quả | Ghi chú |
|---|---|---|
| R1 Đúng-D + loading không đổi | **PASS** | điều kiện render không đổi một ký tự; kèm F-6 và F-10 dạng thông tin |
| R2 0-đụng-khuôn + SHA-pair | **PASS** | 3/3 cặp IDENTICAL, `git status` trên khuôn rỗng |
| R3 Unused-import `X` | **PASS** | 0 hit mã, control-âm cho 2 hit/file ở HEAD |
| R4 Ngữ cảnh render / CSS | **FLAG** | F-1 (MAJOR), F-2, F-3 |
| R5 Permission-path / 403 rác | **PASS có cờ** | không sinh rác 403; F-4 là rủi ro tương lai (prod 13/13 chưa dính), F-5 là chi phí truy vấn |
| R6 Trang không áp | **PASS** | F-8 — quyết định của lead là đúng |
| R7 fe-admin ẩn sidebar | **PASS** | F-9 — không mâu thuẫn K6, có tiền lệ HardCopiesPage |
| R8 Mobile | **PASS** | F-7 — không hồi quy, nhưng nhánh mobile của khuôn chết trong host này |
**Blocker: 0.** Không finding nào chặn commit. F-1 là thứ đáng xử lý nhất trước khi UAT, hoặc phải khai rõ với owner như một trade-off đã biết.
**Ranh vai:** không sửa file mã nào, không commit, không push. Mọi finding đều ở dạng mô tả vấn đề kèm tiêu chí nghiệm thu.
END sub-reviewer-gate-gd3d — VERDICT=PASS-WITH-FLAGS-10 — TOTAL=10 finding
---
## LEAD DISPOSITION @S168 (từng dòng — không số tổng)
| F | Xử | Chi tiết |
|---|---|---|
| F-1 MAJOR | ✅ **VÁ** | Nới khuôn đúng ngoại-lệ "thiếu prop cho GĐ3" artifact lane khoanh: `PipelineTreePanel` +prop `bodyMaxHClass?` (default GIỮ NGUYÊN — 3 host giấy-cuộn cũ 0 đổi hành vi), 3 host Panel-3 pass `max-h-[28rem] lg:max-h-none`**panel cuộn = đúng 1 thanh, bền mọi trang** (không phụ thuộc 5px/13px mong manh). Mirror ×2 app SHA-pair mới `7baf6247af3d`. Lệch tiêu-chí-CHỮ của gate ("cuộn tới đáy cây không cần cuộn aside ngoài") — đạt tiêu-chí-Ý (1 thanh cuộn, không lồng): chọn panel-cuộn thay cây-tự-cuộn vì bền cấu-trúc, đầu cây + ô lọc trôi theo là trade-off khai |
| F-2 MINOR | ✅ VÁ (comment) | Chú thích tại 3 chỗ cắm: sticky/self-start vô hiệu ở host display:block — by-design |
| F-3 MINOR | 🔸 CHẤP NHẬN-khai | aside-lồng-aside 2 landmark complementary: hợp lệ HTML, nhiễu screen-reader — không đổi khuôn lượt này (đổi root khuôn = đụng 4 host); ghi nợ a11y cùng lô a11y-`<a>`-label K5 |
| F-4 MINOR | 🔸 CHẤP NHẬN | `!canPe` than-phiền-quyền thay chỉ-dẫn: prod 13/13 vai có PE.Read (gate đo + control-âm) — rủi ro tương lai, không chảy máu |
| F-5 MINOR | 🔸 CHẤP NHẬN | +tối đa 3 query index lượt tải đầu (staleTime 60s, enabled canPe) — giá của D |
| F-6 MINOR | 🔸 CHẤP NHẬN-khai | chọn HĐ → cây unmount → bỏ chọn → ô lọc mất chữ (state trong component) — trade-off D "cây nhảy chỗ" đã khai với owner |
| F-7 MINOR | 🔸 CHẤP NHẬN | mobile: panel-3 hidden dưới lg = khuôn 3-panel by-design (mobile đi fullpage route) |
| F-8 INFO | ✓ (gate phán lead ĐÚNG) | WorkflowMatrixView không áp D |
| F-9 INFO | ✓ (gate phán KHÔNG mâu-thuẫn K6) | fe-admin /contracts ẩn sidebar — route sống, tiền lệ HardCopiesPage |
| F-10 INFO | 🔸 GIỮ `currentStage={3}` | /inbox trộn GĐ1+GĐ3 nhưng trang chủ-đích là HĐ Pending; PE item navigate sang trang PE riêng |
Vá F-1/F-2 nằm trong commit GĐ3-D (build+test re-verify sau vá). 6 finding chấp-nhận đi vào UAT-NÓI-TRƯỚC.

View File

@ -0,0 +1,9 @@
GATE-LINEEDITOR: IN-PROGRESS — đang đo diff, chưa có verdict.
# Gate reviewer — K8 blocker vá: KHKK Lines-editor (S168)
Bắt đầu: 2026-08-01. Trạng thái: đang thu thập bằng chứng, đổ ruột liên tục (chống #53).
## Diff scope (đo, chưa điền)
## Findings (append liền tay)