diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index e2f7649..88e7ea5 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -6,7 +6,7 @@ ## 🔴 CHỜ ANH — ĐÁNH SỐ (trả lời bằng số) -**(54) 🔴🔴 BLOCKER MẠCH CHÍNH — chốt NGƯỜI DUYỆT 3 trạm PRO/CCM/CEO** (chọn trong 18 user prod; em KHÔNG tự chọn người thật). Có 3 tên/email ⇒ dựng 2 workflow type-3 + type-10 qua Designer (KHÔNG SQL tay #84) → W5 cầu KHKK→HĐ → E2E toàn trình PE `PE/2026/A/045` → KHKK → HĐ → bản-cứng. 🔴 *`lead-gap-auditor` FLAG-2 HIGH @S162: món này đã im **3 cửa-sổ / 2 ngày** vì chỉ sống trong `.claude/WAL.md` — đúng file bị reset mỗi closeout; **0 hit** trên cả 4 bề mặt owner. Nay đưa lên đây, có số.* +**(54) 🔴🔴 BLOCKER MẠCH CHÍNH — chốt NGƯỜI DUYỆT 3 trạm PRO/CCM/CEO** (chọn trong 18 user prod; em KHÔNG tự chọn người thật). Có 3 tên/email ⇒ dựng 2 workflow type-3 + type-10 qua Designer (KHÔNG SQL tay #84) → W5 cầu KHKK→HĐ → E2E toàn trình PE `PE/2026/A/045` → KHKK → HĐ → bản-cứng. 🔴 *`lead-gap-auditor` FLAG-2 HIGH @S162: món này đã im **3 cửa-sổ / 2 ngày** vì chỉ sống trong `.claude/WAL.md` — đúng file bị reset mỗi closeout; **0 hit** trên cả 4 bề mặt owner. Nay đưa lên đây, có số.* 🔄 **@S164 2026-07-31 anh note hướng (verbatim):** *"PRO/CCM/CEO -> vẫn là cách danh sách cũ trong duyệt NCC. Nhưng note ý thôi, tao giao việc tiếp."* ⇒ người duyệt 3 trạm = **lấy đúng bộ người duyệt đang cấu hình ở workflow Duyệt NCC (PE) prod**, KHÔNG cần anh chọn 3 tên mới. 🔴 Đây là **NOTE Ý, chưa phải lệnh triển khai** — chờ anh giao việc; khi triển khai vẫn qua Designer (KHÔNG SQL tay #84), đọc danh sách từ workflow PE live trước khi dựng. **(55) Trang GĐ3 của cây 4-folder** — anh chốt AskUser *"áp **CẢ** các trang GĐ khác"*, em giao **3/4** (Duyệt NCC · KHKK · Hợp đồng cứng) và **tự hoãn cụm Duyệt hợp đồng** (7 loại HĐ × 6 leaf) sang đợt-2 để anh nhìn prod trước. Làm tiếp ngay, hay giữ nguyên 3 trang? *(lead-gap FLAG-3)* @@ -14,10 +14,12 @@ **(57) 🔄 (51-bis) MỞ LẠI BẰNG THƯỚC TƯƠI** — `cicd-monitor/archive/_INDEX.md` đo lại @S162 = **99,18% / headroom 167 B** (KHÔNG phải 97,45% / 522 B như slot (51-bis) cũ ghi). Vế (c) "chấp nhận vỡ cap ~1-2 phiên nữa" thực chất **còn đúng 1 dòng append**. Chọn: **(a)** split file · **(b)** nâng cap · **(c)** để vỡ. 🔴 *`lead-stale` FLAG-7 HIGH: anh đang chọn trên thước cũ.* -**(58) 🔄 SLOT (45) SỐNG LẠI — trần `_mind` là THẬT, không còn báo-động-giả.** S159 rút slot này vì số 92,5% đo trên `_mind-s-7` **đã đóng băng**; file SỐNG `_mind-s-8.md` nay **89,06% (29.184/32.768 B)** và mỗi cửa `/pause` phải nén 1 block mới đủ chỗ. Chọn: nâng khoá `mind_ctx_kb` · đổi luật nén · hay giữ nguyên (chấp nhận mỗi cửa nén 1 block). *(lead-stale FLAG-8 HIGH)* +**(58) 🔄 Trần **`_mind` CỦA PHIÊN-LOGIC HIỆN HÀNH** — câu hỏi thiết kế còn mở; số SỐNG đọc từ máy, CẤM neo literal/tên file ở đây.** 🔴 Số hiện-hành = chạy `python scripts/session_ctx.py mind-check --session ` **phép (7)** — đo @S165 SAU khi chèn MIND-2: **84,8% (27.787/32.768 B), dư 4.981 B < 1 block ⇒ cửa `/pause` kế PHẢI nén MIND-1** kèm cấp nơi-đi cho 6 ý chỉ-sống-ở-đó (4 nhánh-loại §B + 2 ý §C — `ctx-verifier` K1/K1-bis @S165; số 65,7% đời trước chết ngay khi MIND-2 chèn — đúng bẫy "sạch lúc HH:MM"). Câu hỏi cho anh GIỮ NGUYÊN nhưng hết khẩn: khi file SỐNG chạm ~90% thì **nâng khoá `mind_ctx_kb`** · **đổi luật nén** · hay **giữ nguyên** (mỗi cửa nén 1 block)? 🧊 *Lineage 2 đời số chết:* 92,5% đo trên `_mind-s-7` đóng băng (báo-động-giả, RÚT @S159) → 89,06% đo trên `_mind-s-8` cuối L8 (thật lúc đó, nhưng L8 đã `_end` ⇒ số lại chết theo file). *(lead-stale FLAG-6/8 @S163: neo tên file = class **tham-chiếu trượt khi phiên-logic sang trang** — chu kỳ 3 của cùng bệnh, cùng họ slot (45).)* **(59) `tiep_reload` — mục tiêu và danh-sách nguồn KHÔNG khớp.** Khoá hẹn nạp nền **40-60K tok**, nhưng đo tươi ĐÚNG 5 nguồn đã khoanh = 71.492 B ⇒ **~20-24K tok ≈ ½ sàn**; đọc rộng nhất có thể vẫn chỉ ~35-41K. Tức **tuân 100% danh sách vẫn dưới hạn**, và `/tiep` không có bước %-print nên không máy nào thấy. Sửa mục tiêu xuống, hay mở rộng danh-sách nguồn? *(lead-gap FLAG-4)* +**(62) 🆕 @S164 — nửa-còn-lại fail-LOUD của luật `refresh @closeout` (FLAG-1 `ctx-audit`, HIGH DUY NHẤT của vai, đời-3).** Luật "chèn 1 block refresh vào `_mind` khi closeout" hiện KHÔNG có máy nào kêu nếu quên — đề xuất: ghi thêm dòng `mind-refresh: ` vào `_end` khi closeout đã chèn block + máy `closed-check` soi thiếu ⇒ KÊU. 🔴 Chạm **form `_end` anh đã chốt "giống hub" @S146** nên em không tự wire — anh gật thì em thêm 1 trường + 1 phép soi; không thì luật kia mãi chạy bằng trí-nhớ. *(Bằng-chứng nó cần răng: chính phiên này máy `mind-check` phép (10) đã phải kêu hộ vì `_mind` tụt sau artifact 28,9′ — cờ đúng nội dung. `ctx-verifier` K2 @S164: FLAG-1 từng 0-hit trên cả WAL/HANDOFF/STATUS = ý chỉ sống ở lớp mềm — nay có nhà tại slot này.)* + **Slot cũ còn treo:** (42)(43)(44) canonRows / carry-con-trỏ / END-line-thành-luật · (46) thước "17,1K" ma · (47) `_INDEX` cap · (48) `harvest-curator/MEMORY.md` 0-byte-đổi *(nay vai tự khai self-compact DUE, L1 21.277 B)* · (39) RAG Qdrant · (40) `hmw_width.cap` 20 < roster 23 · (41) `ring5-audit`. **Chờ-anh phụ (không chặn):** F-10 attachment owner-check 2 module · mark-read chuông cũ (mirror PE S96) · tài khoản UAT non-Admin (`nv.test` không tồn tại prod) · thư `ai_infra/all/2026-07-28-day-wake-probe-first-resume` (nấc `agreed`, executed chờ anh). @@ -32,7 +34,7 @@ 6. `CLAUDE.md:62/:69` danh-sách mig thiếu 68+69; `_context-s-8` PAUSE-3 ghi menu "135→142" trong khi cicd đo **142→142 (0 row thêm)** — file này nằm trong `tiep_reload.sources` ⇒ **sai số tái sinh mỗi `/tiep`**. *(FLAG-13/15)* 7. Monthly drift-audit **2026-08-01 (2 ngày nữa)** + re-sync `measured{}` — 🔴 6 vai (3 ctx + 3 ring) **KHÔNG có baseline** ⇒ "delta 0" với chúng = **chưa từng đo**. -**Carry @S162 (RE-STAMP — nghi-thức §L.b(j)(iv), bỏ 2 kỳ liên tiếp S159+S160 nên `gap-carry-dropped` lên streak 8):** `[carry:ctx-t9-dogfood]` · `[carry:ring5-audit-gap]` · `[carry:hmw-width-vs-roster]` · `[carry:hmw-subfile-index-collision]` · `[carry:ctx-verifier-no-self-append]` *(MỚI @S162 — F-01, 3 phiên liên tiếp lead-seed)* · `[carry:orphan-retro-harvest-s159]` *(MỚI)* · `[carry:endline-sub-md]` *(MỚI)* · `[carry:tra-bui-relogin]` · `[carry:mind-tran-nen-moi-cua]` *(MỚI — slot 58)* · `[carry:tiep-reload-underfill]` *(MỚI — slot 59)*. *(26 slug re-stamp @S152 giữ nguyên hiệu lực — con-trỏ segment S152, KHÔNG chép để tránh drift.)* +**Carry @S162 (RE-STAMP — nghi-thức §L.b(j)(iv), bỏ 2 kỳ liên tiếp S159+S160 nên `gap-carry-dropped` lên streak 8):** `[carry:ctx-t9-dogfood]` · `[carry:ring5-audit-gap]` · `[carry:hmw-width-vs-roster]` · `[carry:hmw-subfile-index-collision]` · `[carry:ctx-verifier-no-self-append]` *(MỚI @S162 — F-01, 3 phiên liên tiếp lead-seed)* · `[carry:synthesis-roll-s159-s160]` *(rẻ — đổi-tên/roll file synthesis đã có)* · `[carry:harvest-orphan-s159-tongquan]` *(thật — đọc-gom folder tổng-quan)* *(2 slug này = TÁCH từ `[carry:orphan-retro-harvest-s159]` @S164 theo lead-gap INFORM-G1: chữ "retro-harvest" gộp làm nợ TRÔNG đắt hơn thật → dễ bị hoãn mãi; anh đã chốt @S163 "bỏ mạch" nên đây là nợ ngủ, tách để phần rẻ khỏi bị phần đắt kéo)* · `[carry:endline-sub-md]` *(MỚI)* · `[carry:tra-bui-relogin]` · `[carry:mind-tran-nen-moi-cua]` *(MỚI — slot 58)* · `[carry:tiep-reload-underfill]` *(MỚI — slot 59)*. *(khối re-stamp cũ giữ nguyên hiệu lực — con-trỏ ĐÚNG ĐỐI-TƯỢNG: khối gốc **26 slug** nằm ở segment **S151** dòng `Carry MỞ re-stamp @S151`; tại S152 còn **23 giữ / 3 đóng**. Câu cũ ghi "26 slug re-stamp @S152" SAI đối-tượng — lead-gap FLAG-1 @S163 nhường turf, 0 vai nhặt, lead thu-về @S164. KHÔNG chép slug để tránh drift.)* 🧊 *Segment trước (S159→S160) giữ nguyên bên dưới làm lineage:* @@ -62,7 +64,7 @@ **✅ (49)-(53) THI HÀNH XONG @S159** — anh chốt *"đã 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"*: **(49)** vá **6 site** trong 2 file LỆNH ⇒ cặp H24 = **VÔ-ĐIỀU-KIỆN mỗi bookend**, chỉ `deep` còn cổng `deep_every` *(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, và nó trái **hành-vi đo được**: S154/S158 cặp vẫn chạy khi light chưa tới hạn)* · nới bất-biến (6) `session_ctx.py` · **thêm phép (10) `thu-tu bookend vs _mind`** — máy nay tự soi được thứ `ctx-audit` phải đối-chiếu mtime bằng tay; **fault-inject 4/4 hai chiều** (A kêu · B im · **C: 0 run-folder ⇒ `bỏ-qua-CÓ-KHAI`, KHÔNG đọc thành "đạt"** · D biên bằng nhau không kêu oan). Nấc: **INFORM (co), chưa gate** — mtime là proxy, nâng lên TRƯỢT khi có thêm đo thật. **(50)** seed **10/10 diary** on-behalf. **(51)** `_INDEX` header **78→80** + 🔴 **`Run #407` MỒ CÔI** (có trong kho, **0 con-trỏ** ⇒ vô hình với người tra mục-lục) đã thêm; sweep lại **0 mồ côi**. *Bài: **đếm-lệch là triệu-chứng, bản-ghi-mồ-côi là thiệt hại.*** **(52)** slot (45) **RÚT**. **(53)** vá lọc detector. -🔴 **CÒN CẦN SỐ CỦA ANH — (51-bis):** `cicd-monitor/archive/_INDEX.md` nay **19.958/20.480 = 97,45%**, headroom **522 B**. Hai lối thoát `memory-budget.json` kê ra: **gist đã tiêu @S152**; **split** trước bị **header tự-khai lạc hậu chặn** — nay header đã đúng ⇒ **split mở lại được**. Chọn: **(a)** split file · **(b)** nâng cap *(⚠️ `harness-refine` BÁC hướng này: "đổi thước = rời tập-đo")* · **(c)** để nguyên, chấp nhận vỡ cap ~1-2 phiên nữa. +~~🔴 **CÒN CẦN SỐ CỦA ANH — (51-bis):** `cicd-monitor/archive/_INDEX.md` nay **19.958/20.480 = 97,45%**, headroom **522 B**.~~ ✅ **GẠCH @S164 — thước CŨ; câu hỏi DỜI NHÀ sang slot (57) (dòng `:15` trên).** Đo lại @S162 = **99,18% / headroom 167 B** ⇒ vế (c) "vỡ cap ~1-2 phiên" thực chất còn **đúng 1 dòng append**. **Anh chọn ở (57)**, đừng chọn trên số dòng này. 🔸 Giữ phần CÒN ĐÚNG (không vứt cả cụm): **gist đã tiêu @S152** · **split hết bị header-tự-khai chặn ⇒ khả-thi** · `harness-refine` BÁC vế (b) nâng cap (*"đổi thước = rời tập-đo"*). *(lead-stale FLAG-5 @S163: slot này mời anh quyết trên thước cũ trong khi thước tươi nằm ngay (57).)* **🔴 NEXT anh @S158 (đánh số — anh trả lời bằng số):** **(42)(43)(44)** — 🔴 **3 slot này đã cấp ở WAL từ S155 nhưng CHƯA BAO GIỜ xuất hiện ở HANDOFF** (lead-stale FLAG-6 @S158: sổ anh đọc dừng ở slot 41 trong khi sổ lead đã tới 44) ⇒ 3 câu dưới đây anh **chưa từng được hỏi**: **(42)** canonRows · **(43)** carry con-trỏ-vs-slug · **(44)** END-line-thành-luật. @@ -111,7 +113,7 @@ **Last updated (kỳ trước):** 2026-07-22 (S141–S143 — **Wave adap đợt-9/10 ĐÓNG TRỌN (7 thư) + closeout OUTWARD đã push `2757e41`/`3f4310f` — governance-only: 0 prod-code/0 mig, test 528 giữ, bundle FROZEN. Roster 14→17.** S141 vòng-1 `wf_3e1ade40-704` CHẾT giữa chừng → reconcile-từ-ĐĨA + đóng orphan partial-honest 5/13; S142 vòng-2 CẮT-GỌT `wf_1e936b16-30c` 2/2 lane 0-garble land trio `harness-{eval,refine,audit}` + contract-v2 ①a + stamp-decode + annex phép-8 + sweep 13 site; **S143 `/tiep` closeout OUTWARD anh gật**: adap-report 7/7 · email hub `6c94873f72e0` selftest exit 0 · STAGE-2 0-pending cross-check 2 chiều · squash K=8 push 0-`wal:`-lọt (12==12) · vá skill-matrix 15→17 · **+vế-4 `pull-cach`** cho `nhip-no-probe` (fault-inject 7 ca 2 chiều). **Contract v2 chạy runtime LẦN ĐẦU** — và trong CÙNG phiên chạy **cả 2 nhánh lành**: `OK-reachable` (tick 16→17) rồi `squash-benign` (sau closeout). 🔴 **Nấc = `executed-file/verified-pending-restart` — trio CHƯA spawn lần nào.** **Vòng audit anh gật "làm hết": `reviewer` FAIL 1C/4M/4m/1nit + `lead-view` 3 FLAG + `lead-omission` 3 FLAG/3 INFORM → lead re-đo độc-lập từng cái, vá 9/9.** CRITICAL = **E-012/AS-16 `bằng-chứng-tự-huỷ-sau-squash`**: 7 report + email neo sha `wal:` chưa-push, squash phá đúng sha đó ⇒ re-anchor `2757e41` + **errata `e43484cef10f`** gửi hub. Kèm: "TRIPLE" chọn enum DỄ (2 site liệt-kê-TÊN lọt AC4 grep-theo-SỐ) · `TOTAL 46 "==baseline"` sai (ghi=45, live=47) · 4 site khai "NO Write/Edit" như cơ-chế chặn → sửa (nay có **nhân-chứng first-person**: `reviewer` frontmatter 0 Write/Edit mà **đã Write+Edit thật**; **9 vai** không phải 6). **3/3 sub garble #53** cả 3 resume, 0 mất dữ-liệu nhờ ghi-đĩa (sổ đếm ×20/S118 → **×23/S143**). `flag_classes` **10→11** (+`gap-incident-unrecorded` anh duyệt). Test **528 kế-thừa** (0 file `src/`·`fe-*`·`tests/` bị đụng cả 5 commit — verify bằng diff, KHÔNG khai từ trí-nhớ), bundle FROZEN. 5 commit: `2757e41`·`3f4310f`·`36cefcc`·`39cc060`·`c4903dd`.) **Carry MỞ re-stamp (14):** (1) carry-age design-question `[carry:carry-age-vacuous]` — vacuous-GỐC đã chết @S140, anh xem đóng? (2) hook-vs-budget-cap — **S143 đo tươi: `investigator-codebase` 26.624B vượt soft-cap** (`l1_over_30kb=false`, chưa chạm trần auto-inject); probe đếm `tran-bo-nho 1 over-cap` `[carry:hook-vs-budget-cap]` (3) UAT S117 incognito `[carry:uat-s117]` (4) PE phân-quyền số-phiếu+user `[carry:pe-phanquyen]` (5) BVAAU `[carry:bvaau]` (6) quyết-định Bình/Lê `[carry:binh-le-decision]` (7) Trà/Bùi re-login `[carry:tra-bui-relogin]` (8) account trùng `[carry:account-trung]` (9) UAT freeze-NS S133 `[carry:uat-s133-budget-freeze]` (10) UAT lũy-kế tạm-tính S134 `[carry:uat-s134-luyke]` (11) **tools-whitelist KHÔNG chặn ghi runtime** — **15 vai** read-only bị append `Write/Edit` (đo tươi @S148; "6" là số S143-era **đã stale** — roster phình 3 vai kiểm @S145, KHÔNG phải xấu đi); ✅ **anh QUYẾT @S148: (a) giữ nguyên, KHÔNG hardening** ⇒ carry **THU HẸP** còn DUY-NHẤT vế chờ hub xác nhận cơ-chế §3(b), **KHÔNG còn vế chờ anh** `[carry:tools-whitelist-no-teeth]` (12) 🆕 **ngưỡng `pull_warn_days`** — vế-4 đang INFORM trần, em CỐ Ý không tự đặt số (quyền anh) `[carry:pull-warn-threshold]` (13) 🔁 **session-model adopt** — draft `docs/governance/session-model-se-draft.md`; (a) hub phát mẫu ✅ + (b) khuôn bộ-ba SUB ✅ **đã mở khoá trong wave này**, chỉ còn **(c) anh duyệt bảng VC-1/2/3**; CẤM wire trước (c) `[carry:session-model-adopt]` — 🔴 **carry này BỊ RƠI khỏi khối S143 bản đầu** (khai 13 trong khi đúng là 14; lead-omission-auditor bắt @S143 class `gap-carry-dropped`), nay trả về (14) chờ-HUB reply: `b9ef5ede` (session-model **5 câu**) + `6c94873f` (báo-nấc wave 9/10) + errata `[carry:hub-review-wave-1807]`. ✅ **ĐÓNG @S143 (1):** `[carry:dangling-head]` — hub APPROVE khuôn ①a (`58e28bae`), SE land contract v2 + chạy runtime 1 lần nhánh `OK-reachable`; 3 ca lịch-sử S127/S133/S139 reclassify đúng `squash-benign`. -**🔴 NEXT anh (đánh số — trả lời bằng số):** (1) ~~⚑ trio first-run~~ ✅ **ĐÓNG — đã chạy @S144 (2026-07-22)**: restart ĐÃ xảy ra + bộ-ba chạy THẬT 3/3 TUẦN TỰ, nấc `executed-file`→**`verified-runtime`**; bằng-chứng đĩa `runs/2026-07-22-S144-trio-first-run/` (eval 10.907 B · refine 14.321 B · audit 11.251 B + `trio-synthesis.md`), commit `8b6df0a`. 🔴 **Dòng cũ SỐNG SAI 4 phiên (S144→S147)** vì bảng số này thuộc khối S141–S143 mà các phiên sau bồi thêm ở TRÊN **không re-stamp bảng** ⇒ @S148 lead đọc bảng thay vì đọc đĩa, **trình lại cho anh một việc đã xong** (class `view-stale-status`, lọt vì chưa tới nhịp H24 — light 5/6). Bài: bảng chờ-anh là **view dẫn-xuất**, khẳng-định trạng-thái = phải chạm đĩa 1 lệnh (S121). 🔒 **Số 1 GIỮ CHỖ, CẤM tái-dụng cho việc khác** — anh trả lời bằng số, tái-dụng slot = câu trả lời trúng nhầm việc (vắng-mặt-trông-giống-ổn S146). Vế CÒN MỞ của trio = **#12** (thể-thức consent dài hạn), KHÁC first-run; (2) ~~⚑ ngưỡng `pull_warn_days`~~ ✅ **ANH ĐỔI TRỤC @S148 (2026-07-24)** — *"mỗi session end/start luôn, vì 1 session giờ có thể kéo dài 2-3 phiên qua 2-3 ngày"* ⇒ **KHÔNG đặt ngưỡng-theo-NGÀY**, nhịp neo vào **SỰ-KIỆN PHIÊN**. Đã wire `/check-email all` vào **4 CỬA** (`session-start` BƯỚC 0.9 · `tiep` §2.5b · `session-end` BƯỚC 0.5 · `pause` §2.7), luật canonical 1 nhà ở `check-email.md` §"Nhịp chạy — 4 CỬA PHIÊN" + 4 con-trỏ (B1). 🔴 **Trước đó `check-email` wire 0/4 cửa** — sống hoàn-toàn nhờ lead nhớ chạy tay; đó là gốc ca hub-gửi-giục `2026-07-18-nudge-pull`. **Dogfood @S148: chạy thật, 0 thư mới, fail-soft kích-hoạt THẬT** (repo `dyd` không có trên máy → bỏ qua, không nổ). 🔒 `pull_warn_days` **CỐ Ý để TRỐNG** — đổi vai thành **chỉ-báo sức-khoẻ nghi-thức** (số leo trong khi phiên vẫn mở/đóng đều ⇒ nghi-thức bị chạy tắt). Khai giới-hạn: vế-4 mạnh với im-DÀI, yếu với trễ-kéo-NGẮN (ca 07-18 chỉ N=2 bắt được, mà N=2 = kêu liên-tục) `[carry:pull-warn-threshold]`; (3) ~~⚑ tools-whitelist no-teeth~~ ✅ **ANH CHỐT (a) GIỮ NGUYÊN @S148 (2026-07-24)** — **KHÔNG** hardening (không thêm hook `PreToolUse` chặn sub-write vào path tracked). ⇒ whitelist `tools:` trong frontmatter = **tài-liệu mô-tả ý-định, KHÔNG phải hàng-rào**; backstop THẬT vẫn là **git-diff commit-gate** (bắt SAU khi ghi, KHÔNG chặn TRƯỚC — khai thẳng, đừng đọc thành "đã kín"). 🔴 **Số đo tươi @S148 = 15 vai** (KHÔNG phải "6" của dòng carry cũ, cũng không phải "9" @S143): 20 vai trên đĩa − 5 write-specialist = **15 vai khai `tools:` KHÔNG Write/Edit mà runtime cấp Write+Edit cả 15** (chứng 2 chiều: `reviewer.md:7` frontmatter 0 Write/Edit ⟂ registry phiên S148 có `Write, Edit`; `harness-eval` mô-tả tự viết "KHÔNG Write/Edit" vẫn bị cấp). Số phình **theo roster lớn lên**, KHÔNG phải tình-hình xấu đi. **CÒN MỞ 1 vế:** chờ hub xác nhận cơ-chế này có cố-ý không (đã báo §3(b)) `[carry:tools-whitelist-no-teeth]`; (4) UAT lũy-kế tạm-tính S134 `[carry:uat-s134-luyke]`; (5) UAT freeze-NS S133 `[carry:uat-s133-budget-freeze]`; (6) ⚑ D1 nháp-không-cộng-tiền — gật giữ hay đổi (1 predicate); (7) hook-vs-budget-cap — chốt hướng (S143: inv-cb 26.6K over-cap) `[carry:hook-vs-budget-cap]`; (8) session-model — duyệt bảng VC-1/2/3 `[carry:session-model-adopt]`; (9) close-sweep cụm 7-carry đồng-tuổi S117/S118; **⬇️ 3 mục dưới CHÌM từ `spec:68` suốt S141→S143 (0 hit trên cả 4 bề mặt owner — `gap-decision-sunk` @S143), nay kéo lên:** (10) ⚑ **ratify ranh `refine ⟂ lead-omission` (nay `lead-gap-auditor` @rename S149 — file `harness-refine.md:22-30` đã dùng tên mới)** — 🔴 ranh này **đã wire** TRƯỚC khi được anh/hub ratify; anh gật giữ hay sửa ranh; (11) ⚑ **5 câu session-model** (mới mang 1/5 lên bảng): `session_ctx_kb`=64? · map N↔L + folder-form (`sessions/session-` hub vs `S` SE) · VC từng dòng · hook 3→4-path? · prereq unblock — 🔴 `session_ctx_kb` **0 hit** mọi bề mặt dù em khai "❌ treo"; (12) ⚑ **consent-form trio dài hạn** — mỗi phiên gật hay thành luật đứng (hub `58e28bae` Câu-3 đá về SE, câu hỏi còn nguyên cho anh). **⬇️ 4 mục MỚI thêm @S148 — (13) từ UAT S147, (14)–(16) lòi ra khi đọc 10 thư fan-out tồn đọng:** (13) **UAT nghiệm-thu ① S147** — bạn kia mở phiếu **Trả lại** → màn "Danh sách" → "Bảng so sánh giá" → nút X + tải lên hiện lại cho người soạn; **chưa có tin**; (14) ⚑ **nhãn model lead (O-5) — RE-STAMP @S150, câu hỏi VIẾT LẠI (GIỮ MỞ theo lead-gap I-3, chống đóng-nhầm-bằng-sự-kiện-kề):** đĩa tươi 07-25: settings = **`claude-opus-5[1m]`** (thư GẤP `59ce1d0f` default fleet → Opus 5 MAX đã áp) · lead PHIÊN NÀY = **Fable 5** (anh `/model`, hợp-lệ Khoản 5) · ô canonical §Sub-agents flip-chain dừng flip-8/S107 — ring1 đo stale **2 flip** (flip-9 nằm ở STATUS:8 frozen ⇒ vá-theo-chữ-H1 sẽ sót flip-9). **Câu hỏi THẬT cho anh: ô canonical hiển thị DEFAULT-fleet hay THỰC-TẾ-PHIÊN?** — nhãn = quyền anh, em không tự đoán; (15) ⚑ **ngưỡng "điểm-dừng phải RẺ" (thư `goi-chot` khoản 3 — ràng-buộc BINDING)** — hub chốt luật nhưng **số là của anh**; em đã đo hộ @S148: **dò ~5ms · kéo ~200–500ms/thư** ⇒ anh đặt mốc bao nhiêu thì gọi là "đắt"; (16) ⚑ **khuôn nhãn spawn 4 thành-phần — ĐẾN HẠN** (`C14` xếp 1.3/1.4 `DEFER — áp từ phiên kế`; S144 là phiên trước ⇒ **S148 là phiên đến hạn**), hiện mới phủ **1/4** loại spawn (mới cổng review; còn nhịp-2-đầu-phiên · việc-thường · workflow-task). **⬇️ 3 mục kéo lên @S148 từ bảng owner-gated `O-1…O-7` — CÂM 5 nhãn phiên (S144→S148), xem khối 🔴 ở đầu file:** (17) ~~⚑ O-2 posture trục-SÂU~~ ✅ **DISPOSITION @S149 (OWNER-DELEGATED → lead-decided): posture-A — KHÔNG DỰNG trục sâu**, trần tổng-lượt không có gì để assert (depth-cost: mode thật thắng ~1/10 giá); **EXECUTE K2 ✅ land @S150** (1 dòng posture @`harness-11-engine.md` PHẦN K); anh veto = 1 edit (xem #25); (18) ~~⚑ O-3 vai-KIỂM vòng còn thiếu~~ ✅ **DISPOSITION @S149 (OWNER-DELEGATED → lead-decided): tập-rỗng** — 3 vòng đầu đã TÁCH @S145, KHÔNG còn vòng-thiếu hiện-hành; **EXECUTE K5 ✅ land @S150** (luật 【1】 mặc-định TÁCH cho vòng-đo MỚI, home duy-nhất @`session-end.md §L.b(g-bis)`); anh veto = 1 edit (xem #25); (19) ~~⚑ O-7 trần WIDTH~~ ✅ **ĐÓNG @S149 (OWNER-DELEGATED → lead-decided, uỷ-quyền "Trả lời 4 câu"; anh veto = 1 edit — xem #25):** `hmw_width{cap:20, mechanism:loud-skip}` ratified 2026-07-24 @`memory-budget.json:11-14` + `hmw.js:148` `WIDTH_CAP = 20` (con-trỏ cũ `:115` đã chết — S-1 HIGH @S150 bắt cả 3 tầng lệch); (20) 🔴 **`_end` ĐÓNG BĂNG SAI ĐƠN-VỊ — mâu-thuẫn với chính mô-hình anh vừa phát-biểu** (`lead-omission-auditor` FLAG-3 `gap-owner-specifics` @S148): anh nói *"1 session giờ có thể kéo dài **2-3 phiên** qua 2-3 ngày"* = định-nghĩa **đơn-vị LOGIC**. Nhưng [`session-end.md §6.3-bis`](../.claude/commands/session-end.md) ghi `_end` **FROZEN cho `` đang mở ở MỌI closeout**, **không kèm điều-kiện *"phiên-LOGIC thực sự khép"*** — dù header của chính nó trích đúng câu ấy từ manifest. ⇒ **mỗi closeout cửa-sổ sẽ đóng băng phiên-logic** ⇒ mô-hình 1-logic-nhiều-window **không thể thành hiện-thực**. 🔸 Lead **đã ghi `_end` cho session-2 ở closeout S148 này** — chấp-nhận được vì arc S147+S148 thật sự khép, **nhưng luật thì vẫn sai** cho ca đóng giữa arc. Kèm: `NEXT anh #11` vẫn liệt "map N↔L" như **câu hỏi MỞ** ⇒ hai chỗ nói ngược nhau. **Anh chốt điều-kiện đóng `_end`** thì em sửa 1 chỗ. **⬇️ 6 slot MỚI @S150 (bookend-lượt-đầu — 5 khoản 🔒 từng CÂM vết-chỉ-ở-WAL [lead-gap FLAG-1: 5/7 câm, WAL sắp reset ⇒ bốc hơi] + 1 mẫu-số hỏng):** (21) 🔴 **dải JUMP FROZEN** — `class_repeat` sau bookend @S150: **gap-owner-specifics 5 · gap-decision-sunk 4 · gap-carry-dropped 4 · view-stale-status 3** (đều ≥ jump=3) + `view-residual-asym`∈**{1,4}** bất-định (CẤM chốt số trần) + mới-fire {stale-count 2 · header 1 · role-desc 1 · incident 1}; khoá `_frozen_until_owner` ĐÃ CẮM (máy GIỮ counts, hành-vi in thật) — **anh quyết: kéo deep-audit sớm / reset class nào / giữ frozen** `[carry:jump-dai-frozen]`; (22) ⚑ **2 cột thẩm-quyền eval 🔒** (owner-SỐ ⟂ owner-LUẬT — bảng eval-của-eval `runs/2026-07-25-S149-adap-backlog/sub-apply-L5-evalcuaeval.md:37,41,59,115`) `[carry:eval-thamquyen]`; (23) ⚑ **thẩm-SÂU per-pointer 6 ca** — gate adap-backlog chỉ re-do CƠ-KHÍ; 6 ca C/D chưa thẩm sâu = **nợ khai với hub** `[carry:tham-sau-6-ca]`; (24) ⚑ **G-011 token-fork** — đã trình hub (email `3a05a6cc8509`), chờ hub trả lời `[carry:g011-token-fork]`; (25) ⚑ **veto-window 4 mục OWNER-DELEGATED @S149** — {**hình B** bookend vô-điều-kiện 2 đầu · **cap 20** hmw_width · **posture A** không-dựng trục-sâu · **light→stats-only**} — 4 quyết lead đã áp theo uỷ-quyền *"Trả lời 4 câu"*; **anh veto = 1 edit từng mục, không veto = giữ** `[carry:veto-window-s149]`; (26) ⚑ **`crystallized_backfill.target` 210000 > headroom đo tươi 190.437 tok** ⇒ chỉ-báo underfill **hỏng mẫu-số** (mọi lần nạp "dưới hạn-mức" by-definition) — anh **hạ target / tuyên ASPIRATIONAL / giữ** (số = quyền anh) `[carry:underfill-mau-so]`. +**🔴 NEXT anh (đánh số — trả lời bằng số):** (1) ~~⚑ trio first-run~~ ✅ **ĐÓNG — đã chạy @S144 (2026-07-22)**: restart ĐÃ xảy ra + bộ-ba chạy THẬT 3/3 TUẦN TỰ, nấc `executed-file`→**`verified-runtime`**; bằng-chứng đĩa `runs/2026-07-22-S144-trio-first-run/` (eval 10.907 B · refine 14.321 B · audit 11.251 B + `trio-synthesis.md`), commit `8b6df0a`. 🔴 **Dòng cũ SỐNG SAI 4 phiên (S144→S147)** vì bảng số này thuộc khối S141–S143 mà các phiên sau bồi thêm ở TRÊN **không re-stamp bảng** ⇒ @S148 lead đọc bảng thay vì đọc đĩa, **trình lại cho anh một việc đã xong** (class `view-stale-status`, lọt vì chưa tới nhịp H24 — light 5/6). Bài: bảng chờ-anh là **view dẫn-xuất**, khẳng-định trạng-thái = phải chạm đĩa 1 lệnh (S121). 🔒 **Số 1 GIỮ CHỖ, CẤM tái-dụng cho việc khác** — anh trả lời bằng số, tái-dụng slot = câu trả lời trúng nhầm việc (vắng-mặt-trông-giống-ổn S146). Vế CÒN MỞ của trio = **#12** (thể-thức consent dài hạn), KHÁC first-run; (2) ~~⚑ ngưỡng `pull_warn_days`~~ ✅ **ANH ĐỔI TRỤC @S148 (2026-07-24)** — *"mỗi session end/start luôn, vì 1 session giờ có thể kéo dài 2-3 phiên qua 2-3 ngày"* ⇒ **KHÔNG đặt ngưỡng-theo-NGÀY**, nhịp neo vào **SỰ-KIỆN PHIÊN**. Đã wire `/check-email all` vào **4 CỬA** (`session-start` BƯỚC 0.9 · `tiep` §2.5b · `session-end` BƯỚC 0.5 · `pause` §2.7), luật canonical 1 nhà ở `check-email.md` §"Nhịp chạy — 4 CỬA PHIÊN" + 4 con-trỏ (B1). 🔴 **Trước đó `check-email` wire 0/4 cửa** — sống hoàn-toàn nhờ lead nhớ chạy tay; đó là gốc ca hub-gửi-giục `2026-07-18-nudge-pull`. **Dogfood @S148: chạy thật, 0 thư mới, fail-soft kích-hoạt THẬT** (repo `dyd` không có trên máy → bỏ qua, không nổ). 🔒 `pull_warn_days` **CỐ Ý để TRỐNG** — đổi vai thành **chỉ-báo sức-khoẻ nghi-thức** (số leo trong khi phiên vẫn mở/đóng đều ⇒ nghi-thức bị chạy tắt). Khai giới-hạn: vế-4 mạnh với im-DÀI, yếu với trễ-kéo-NGẮN (ca 07-18 chỉ N=2 bắt được, mà N=2 = kêu liên-tục) `[carry:pull-warn-threshold]`; (3) ~~⚑ tools-whitelist no-teeth~~ ✅ **ANH CHỐT (a) GIỮ NGUYÊN @S148 (2026-07-24)** — **KHÔNG** hardening (không thêm hook `PreToolUse` chặn sub-write vào path tracked). ⇒ whitelist `tools:` trong frontmatter = **tài-liệu mô-tả ý-định, KHÔNG phải hàng-rào**; backstop THẬT vẫn là **git-diff commit-gate** (bắt SAU khi ghi, KHÔNG chặn TRƯỚC — khai thẳng, đừng đọc thành "đã kín"). 🔴 **Số đo tươi @S148 = 15 vai** (KHÔNG phải "6" của dòng carry cũ, cũng không phải "9" @S143): 20 vai trên đĩa − 5 write-specialist = **15 vai khai `tools:` KHÔNG Write/Edit mà runtime cấp Write+Edit cả 15** (chứng 2 chiều: `reviewer.md:7` frontmatter 0 Write/Edit ⟂ registry phiên S148 có `Write, Edit`; `harness-eval` mô-tả tự viết "KHÔNG Write/Edit" vẫn bị cấp). Số phình **theo roster lớn lên**, KHÔNG phải tình-hình xấu đi. **CÒN MỞ 1 vế:** chờ hub xác nhận cơ-chế này có cố-ý không (đã báo §3(b)) `[carry:tools-whitelist-no-teeth]`; (4) UAT lũy-kế tạm-tính S134 `[carry:uat-s134-luyke]`; (5) UAT freeze-NS S133 `[carry:uat-s133-budget-freeze]`; (6) ⚑ D1 nháp-không-cộng-tiền — gật giữ hay đổi (1 predicate); (7) hook-vs-budget-cap — chốt hướng (S143: inv-cb 26.6K over-cap) `[carry:hook-vs-budget-cap]`; (8) session-model — duyệt bảng VC-1/2/3 `[carry:session-model-adopt]`; (9) close-sweep cụm 7-carry đồng-tuổi S117/S118; **⬇️ 3 mục dưới CHÌM từ `spec:68` suốt S141→S143 (0 hit trên cả 4 bề mặt owner — `gap-decision-sunk` @S143), nay kéo lên:** (10) ⚑ **ratify ranh `refine ⟂ lead-omission` (nay `lead-gap-auditor` @rename S149 — file `harness-refine.md:22-30` đã dùng tên mới)** — 🔴 ranh này **đã wire** TRƯỚC khi được anh/hub ratify; anh gật giữ hay sửa ranh; (11) ⚑ **5 câu session-model** (mới mang 1/5 lên bảng): `session_ctx_kb`=64? · map N↔L + folder-form (`sessions/session-` hub vs `S` SE) · VC từng dòng · hook 3→4-path? · prereq unblock — 🔴 `session_ctx_kb` **0 hit** mọi bề mặt dù em khai "❌ treo"; (12) ⚑ **consent-form trio dài hạn** — mỗi phiên gật hay thành luật đứng (hub `58e28bae` Câu-3 đá về SE, câu hỏi còn nguyên cho anh). **⬇️ 4 mục MỚI thêm @S148 — (13) từ UAT S147, (14)–(16) lòi ra khi đọc 10 thư fan-out tồn đọng:** (13) **UAT nghiệm-thu ① S147** — bạn kia mở phiếu **Trả lại** → màn "Danh sách" → "Bảng so sánh giá" → nút X + tải lên hiện lại cho người soạn; **chưa có tin**; (14) ⚑ **nhãn model lead (O-5) — RE-STAMP @S150, câu hỏi VIẾT LẠI (GIỮ MỞ theo lead-gap I-3, chống đóng-nhầm-bằng-sự-kiện-kề):** đĩa tươi 07-25: settings = **`claude-opus-5[1m]`** (thư GẤP `59ce1d0f` default fleet → Opus 5 MAX đã áp) · lead PHIÊN NÀY = **Fable 5** (anh `/model`, hợp-lệ Khoản 5) · ô canonical §Sub-agents flip-chain dừng flip-8/S107 — ring1 đo stale **2 flip** (flip-9 nằm ở STATUS:8 frozen ⇒ vá-theo-chữ-H1 sẽ sót flip-9). **Câu hỏi THẬT cho anh: ô canonical hiển thị DEFAULT-fleet hay THỰC-TẾ-PHIÊN?** — nhãn = quyền anh, em không tự đoán; (15) ⚑ **ngưỡng "điểm-dừng phải RẺ" (thư `goi-chot` khoản 3 — ràng-buộc BINDING)** — hub chốt luật nhưng **số là của anh**; em đã đo hộ @S148: **dò ~5ms · kéo ~200–500ms/thư** ⇒ anh đặt mốc bao nhiêu thì gọi là "đắt"; (16) ⚑ **khuôn nhãn spawn 4 thành-phần — ĐẾN HẠN** (`C14` xếp 1.3/1.4 `DEFER — áp từ phiên kế`; S144 là phiên trước ⇒ **S148 là phiên đến hạn**), hiện mới phủ **1/4** loại spawn (mới cổng review; còn nhịp-2-đầu-phiên · việc-thường · workflow-task). **⬇️ 3 mục kéo lên @S148 từ bảng owner-gated `O-1…O-7` — CÂM 5 nhãn phiên (S144→S148), xem khối 🔴 ở đầu file:** (17) ~~⚑ O-2 posture trục-SÂU~~ ✅ **DISPOSITION @S149 (OWNER-DELEGATED → lead-decided): posture-A — KHÔNG DỰNG trục sâu**, trần tổng-lượt không có gì để assert (depth-cost: mode thật thắng ~1/10 giá); **EXECUTE K2 ✅ land @S150** (1 dòng posture @`harness-11-engine.md` PHẦN K); anh veto = 1 edit (xem #25); (18) ~~⚑ O-3 vai-KIỂM vòng còn thiếu~~ ✅ **DISPOSITION @S149 (OWNER-DELEGATED → lead-decided): tập-rỗng** — 3 vòng đầu đã TÁCH @S145, KHÔNG còn vòng-thiếu hiện-hành; **EXECUTE K5 ✅ land @S150** (luật 【1】 mặc-định TÁCH cho vòng-đo MỚI, home duy-nhất @`session-end.md §L.b(g-bis)`); anh veto = 1 edit (xem #25); (19) ~~⚑ O-7 trần WIDTH~~ ✅ **ĐÓNG @S149 (OWNER-DELEGATED → lead-decided, uỷ-quyền "Trả lời 4 câu"; anh veto = 1 edit — xem #25):** `hmw_width{cap:20, mechanism:loud-skip}` ratified 2026-07-24 @`memory-budget.json:11-14` + `hmw.js:148` `WIDTH_CAP = 20` (con-trỏ cũ `:115` đã chết — S-1 HIGH @S150 bắt cả 3 tầng lệch); (20) 🔴 **`_end` ĐÓNG BĂNG SAI ĐƠN-VỊ — mâu-thuẫn với chính mô-hình anh vừa phát-biểu** (`lead-omission-auditor` FLAG-3 `gap-owner-specifics` @S148): anh nói *"1 session giờ có thể kéo dài **2-3 phiên** qua 2-3 ngày"* = định-nghĩa **đơn-vị LOGIC**. Nhưng [`session-end.md §6.3-bis`](../.claude/commands/session-end.md) ghi `_end` **FROZEN cho `` đang mở ở MỌI closeout**, **không kèm điều-kiện *"phiên-LOGIC thực sự khép"*** — dù header của chính nó trích đúng câu ấy từ manifest. ⇒ **mỗi closeout cửa-sổ sẽ đóng băng phiên-logic** ⇒ mô-hình 1-logic-nhiều-window **không thể thành hiện-thực**. 🔸 Lead **đã ghi `_end` cho session-2 ở closeout S148 này** — chấp-nhận được vì arc S147+S148 thật sự khép, **nhưng luật thì vẫn sai** cho ca đóng giữa arc. Kèm: `NEXT anh #11` vẫn liệt "map N↔L" như **câu hỏi MỞ** ⇒ hai chỗ nói ngược nhau. **Anh chốt điều-kiện đóng `_end`** thì em sửa 1 chỗ. **⬇️ 6 slot MỚI @S150 (bookend-lượt-đầu — 5 khoản 🔒 từng CÂM vết-chỉ-ở-WAL [lead-gap FLAG-1: 5/7 câm, WAL sắp reset ⇒ bốc hơi] + 1 mẫu-số hỏng):** (21) 🔴 **dải JUMP FROZEN** — `class_repeat` sau bookend @S150: **gap-owner-specifics 5 · gap-decision-sunk 4 · gap-carry-dropped 4 · view-stale-status 3** (đều ≥ jump=3) + `view-residual-asym`∈**{1,4}** bất-định (CẤM chốt số trần) + mới-fire {stale-count 2 · header 1 · role-desc 1 · incident 1}; khoá `_frozen_until_owner` ĐÃ CẮM (máy GIỮ counts, hành-vi in thật) — **anh quyết: kéo deep-audit sớm / reset class nào / giữ frozen** `[carry:jump-dai-frozen]`; (22) ⚑ **2 cột thẩm-quyền eval 🔒** (owner-SỐ ⟂ owner-LUẬT — bảng eval-của-eval `runs/2026-07-25-S149-adap-backlog/sub-apply-L5-evalcuaeval.md:37,41,59,115`) `[carry:eval-thamquyen]`; (23) ⚑ **thẩm-SÂU per-pointer 6 ca** — gate adap-backlog chỉ re-do CƠ-KHÍ; 6 ca C/D chưa thẩm sâu = **nợ khai với hub** `[carry:tham-sau-6-ca]`; (24) ⚑ **G-011 token-fork** — đã trình hub (email `3a05a6cc8509`), chờ hub trả lời `[carry:g011-token-fork]`; (25) ⚑ **veto-window 4 mục OWNER-DELEGATED @S149** — {**hình B** bookend vô-điều-kiện 2 đầu · **cap 20** hmw_width · **posture A** không-dựng trục-sâu · **light→stats-only**} — 4 quyết lead đã áp theo uỷ-quyền *"Trả lời 4 câu"*; **anh veto = 1 edit từng mục, không veto = giữ** `[carry:veto-window-s149]`; (26) ⚑ **`crystallized_backfill.target` 210000 > headroom** ⇒ chỉ-báo underfill **hỏng mẫu-số** *(headroom = số TRÔI: 190.437 tok lúc viết @S144 → **160.924 tok** đo tươi @S163 — khoảng cách còn RỘNG hơn, lập-luận nguyên vẹn; lead-gap FLAG-4 nhường turf 0 vai nhặt, thu-về @S164 — số sống đọc máy `crystallized-backfill` LIVE, đừng neo literal ở slot)* (mọi lần nạp "dưới hạn-mức" by-definition) — anh **hạ target / tuyên ASPIRATIONAL / giữ** (số = quyền anh) `[carry:underfill-mau-so]`. **~~NEXT em (kỳ S150)~~ — 🧊 SUPERSEDED @S151 (stale-deep FLAG-1: khối này giao 3 việc đã đóng/bất-khả — thư `59ce1d0f` vắng-cả-hub → chuyển hỏi hub · AS-17 đã promote · trio nay AUTO; NEXT em SỐNG = segment S151 trên):** 🔴 **KHÔNG watch `broadcasts/inbox`** — đọc vế-4 `pull-cach` dòng-nợ rồi `/check-email` khi tăng bất thường. **Bookend @open lượt kế** (hình B đầu-vào — cặp soi-lead + ring2 nếu OVERDUE; ~~trio first-run~~ ✅ đã chạy @S144, dòng cũ sống-sai 4 phiên; trio nay = consent #12) · **H24 deep CHƯA tới hạn** (counter 25 − deep_at 17 = **8 < 15** [re-stamp S151 — số 24/7 viết @S150 đã stale sau tick], mốc ~32; JUMP-kéo-sớm = quyền anh #21) · **copy thư GẤP `59ce1d0f` vào `broadcasts/inbox/ai_infra/` + stamp-verify rồi sửa mục 1 report** (to-do sống trong caveat `adap-report…opus-5-max §5(b)` — kéo lên đây khỏi chìm) · monthly combined audit **2026-08-01** (kèm full re-sync measured-block) · MFE strikes theo dõi · **sub-class #53 mới (`ngược-#53` · `skeleton-ruột-rỗng`) theo dõi thêm 1 phiên → xét error-ledger row (2-strike)** · nếu anh đổi D1 → predicate `ComputePendingAsync` + test C3 flip + deploy. **Prev S140:** 2026-07-18 (S140 — **Direction owner "đóng đủ 5 vòng + session-model" → 5 vòng ĐÓNG phần SE tự chủ TRONG 1 PHIÊN — governance-only: 0 prod-code/0 mig, test 528 giữ ×2, bundle FROZEN `BVCukFP5`/`DoxyEOxh`, lead Fable 5 owner-choice.** Vòng-2 trả nợ force-fire (cặp H24 light#3 @counter14) · Vòng-4 sleep 5-gist 183KB→69KB `aa88d01` (Fidelity-gate CÓ RĂNG 4P+1F→re-distill; sleep-INFORM chuỗi-3 GIẢI) · Vòng-5 `624c378d` executed-runtime (probe 5-nhãn) · Phép-8 đo LẦN ĐẦU fleet (FAIL-as-written/PASS-with-tailoring-DP-3) · Session-model draft VC-1/2/3 + email hub `b9ef5ede5679` (gate GO-WITH-FIXES; 4 câu hỏi + nhắc wave-1807) · Hở duy nhất = khuôn bộ-ba SUB vòng-3 chờ hub. H2 GATE-PASS-0 + H1 PASS_WITH_FLAGS/3-đều-xử + §L.a 0-RCA. → log `2026-07-18-1300-S140-nam-vong-dong-session-model.md`.) **Carry MỞ re-stamp (13):** (1) carry-age design-question `[carry:carry-age-vacuous]` — 🎯 S140: carry-age FIRE THẬT lần đầu (hook streak 9) ⇒ vacuous-GỐC chết hẳn, anh xem đóng carry này? (2) hook-cap cụm D6 — S140: harvest+tooling TỰ-compact về dưới target (19.7→14.9K · 18.1→15.9K md5-verified); còn inv-cb/cicd/test-spec theo dõi `[carry:hook-vs-budget-cap]` (3) UAT S117 incognito `[carry:uat-s117]` (4) PE phân-quyền số-phiếu+user `[carry:pe-phanquyen]` (5) BVAAU `[carry:bvaau]` (6) quyết-định Bình/Lê `[carry:binh-le-decision]` (7) Trà/Bùi re-login `[carry:tra-bui-relogin]` (8) account trùng `[carry:account-trung]` (9) UAT freeze-NS S133 `[carry:uat-s133-budget-freeze]` (10) UAT lũy-kế tạm-tính S134 `[carry:uat-s134-luyke]` (11) dangling-head — anh chọn 1a @S139, chờ HUB khuôn chung `[carry:dangling-head]` (12) chờ-HUB reply wave 18-07 — NAY 2 email chờ: 3-khoản cũ (`21b71a1e`+`fc2b218d`) + **5-vòng/session-model MỚI (`b9ef5ede` — 4 câu hỏi mẫu áp dụng + phép-8 data-point)** `[carry:hub-review-wave-1807]` (13) 🆕 **session-model adopt** — draft `docs/governance/session-model-se-draft.md` (nhãn 2-tầng + bảng VC-1/2/3) chờ: (a) hub phát mẫu, (b) khuôn bộ-ba SUB, (c) anh duyệt bảng VC; CẤM wire trước đủ 3 điều kiện `[carry:session-model-adopt]`. diff --git a/docs/STATUS.md b/docs/STATUS.md index 72f1864..fa8312a 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -3,7 +3,7 @@ > **Update rule:** trước khi bắt đầu 1 task → ghi row `🔥 In Progress`. Xong → `✅ Recently Done`. > **Tiering rule (S40):** chỉ giữ **state hiện tại + 3 session gần nhất** ở file này. Session cũ hơn → `docs/changelog/sessions/`. Full history pre-S40 → `docs/_archive/STATUS-preS40-fullhistory.md`. (Tránh over-context — xóa double, không cắt nội dung.) -**🔥 CURRENT (S161→S162, 2026-07-29→30 — phiên-LOGIC **L8** window-3+4: **5/5 wave KHKK build SHIP** rồi `/tiep` nối tiếp = **cây 4-folder GIAI ĐOẠN** ship prod + bookend-close 5 vòng):** Mig **69** · **96 bảng** · **590 test PASS** (45D+545I, đo tươi @S162) · gotcha **84** · counter **36** · roster 23 · **MenuItems prod 142 GIỮ** (key `KeHoachKyKet` sinh **policy** không sinh menu-row — H1 bác nghi-vấn stale) · Policies **220** · Menu keys **55** · bundle prod sau Run #432: admin `index-DXoYDy7R.js`/`index-BZ5F-g1w.css` · user `index-o8KDluD1.js`/`index-yNZDmVNz.css` (4/4 rotate). Contracts prod **0**. +**🔥 CURRENT (S161→S162 + S164/S165 2026-07-31 — phiên-LOGIC **L9**: chuỗi SP-002 B0→B3 spec ĐÓNG BĂNG + **B4 K1 danh mục loại HĐ SHIP** gate PASS-WITH-FLAGS; lineage L8 giữ dưới):** Mig **70** (K1 `AddContractCatalogEntries` — bảng danh mục 86 dòng × 8 nhóm duyệt) · **97 bảng** · **598 test PASS** (45D+553I, đo tươi @S165 gate-K1) · gotcha **85** · counter **39** · roster 23 · **MenuItems prod 142 GIỮ** (key `KeHoachKyKet` sinh **policy** không sinh menu-row — H1 bác nghi-vấn stale) · Policies **224** · Menu keys **56** (+`ContractCatalog` K1 — 4 policy sinh ra, giới-hạn khai: chưa endpoint nào tiêu thụ, controller mirror khuôn Catalogs `Roles=Admin` #82) · bundle prod → **canonical ở `:479`, KHÔNG chép số ở đây** (B1). 🔴 *Cặp `DXoYDy7R`/`o8KDluD1` từng ghi ở dòng này **chưa bao giờ là trạng-thái cuối** — `e5123ff` ship thêm 6 file FE-src SAU snapshot; đo live @S164: admin `B43R6Y17` · user `NLI5umBg`.* Contracts prod **0**. ① **5 wave KHKK SHIP (window-3, S161):** W1 `a8027c0` (Mig 69, 7 bảng, 89→96) · W2 `6cbc6ad` (CRUD+Dossier 15 policy) · W3 `df52fa0` (duyệt V2 3-trạm, test-before RED→GREEN) · W6 `761d818` (đường-ống HĐ V2) · W7 `3119cb9` (bản-cứng **1-MỐC** theo owner: form điền đủ → duyệt nếu có → upload bản cứng = xong). Mỗi wave: gate 5-anchor → hmw → test → reviewer → F-0 → cicd 3-chân. 3 quyết owner: mã `KHKK/{YYYY}/{Seq:D3}` · **bỏ "MB"** · GĐ4 1-mốc. ② **Cây 4-folder GĐ (window-4, S162) — `bfc7b79`, cicd PASS 5/5 Run #432:** lệnh owner *"Tạo folder: Duyệt NCC / Kế hoạch Hợp đồng. / Duyệt Hợp đồng / Hợp đồng cứng."* + AskUser chốt **DƯỚI TỪNG GÓI THẦU** + **áp CẢ trang GĐ khác**. 3 file MỚI ×2 app (`PipelineStageFolders` · `PipelineTreePanel` · `usePipelineStages`) + 3 trang ×2 app, **SHA-pair 6/6**, thuần FE 0-mig. designer SHIP-READY (tự soi ảnh bắt 4 lỗi build-không-báo) → reviewer PASS-WITH-FLAGS 8 → lead vá F-1/3/4/6/7 + F-8. 🔴 **CÒN NỢ: trang GĐ3 chưa làm** (owner nói "CẢ", lead giao 3/4 — lead-gap FLAG-3) · F-2 liên-danh N-HĐ chờ W5 · F-5 layout 19rem chờ owner nghiệm thu prod. ③ **Bookend-close 5 vòng, 10 vai:** H1 `PASS_WITH_FLAGS` 10 finding · H2 **`GATE-FAIL` 4** (⭐ `ctx-verifier` chạy thật mà **chưa bao giờ tự APPEND** diary — S153+S158 đều lead-seed; `cicd-monitor` khai "verbatim→archive" thứ `git log -S` chứng **chưa từng vào archive**) · cặp H24 **20 FLAG** (stale 16 = **SÀN** + gap 4, `gap-carry-dropped` **streak 8**) · trio memory **MIXED** (headroom 165.767 tok đơn-điệu giảm 3 mốc · light 9/6 OVERDUE · baseline `measured` 17/23) · orphan run-folder **3 xác nhận** (2 thiếu THẬT + 1 dương-giả TÊN). @@ -470,13 +470,13 @@ monthly re-tier mega-line + ef-core SKILL Mig→59 + 3 vocab-fork (2026-07-01 du | Policies | **220** | 🔄 **220 @S161** (W1 KHKK: `All` +`KeHoachKyKet` ⇒ 55 × 4 — reviewer F-2 bắt cặp-row-đôi đúng luật dòng dưới) · 🆕 **row MỚI S121** (wave adap-6-broadcast W2 — H24 GAP-3: `governance-detectors.ps1` `Get-StatusValue` resolve canonical TỪ bảng này; **KHÔNG có row ⇒ token `policy` UNRESOLVED ⇒ nửa `policy` của GAP-3 không thể fire** ⇒ detector khai FAIL-LOUD + SKIP, KHÔNG im-lặng-giả-vờ-xanh). 🔴 **DERIVED, KHÔNG độc-lập:** `Policies = |MenuKeys.All| × |MenuKeys.Actions| = 55 × 4 = 220 (@S161; trước W1 = 54 × 4 = 216)` — sinh runtime ở `src/Backend/SolutionErp.Api/Program.cs:82-89` (`foreach menu → foreach action → opts.AddPolicy($"{menu}.{action}")`), `Actions = ["Read","Create","Update","Delete"]` (`MenuKeys.cs:167`). ⇒ **row này BUỘC đổi CÙNG row `Menu keys`**: thêm 1 menu key ⇒ 55 × 4 = 220; sửa `Menu keys` mà quên `Policies` ⇒ detector flag derived-doc thay vì flag chính chỗ sai. Ground S121 bằng đếm mã-nguồn (`MenuKeys.All` = 54 entry, `public const string` = 54, khớp) — KHÔNG chép số từ spec. ⚠️ `.claude/skills/permission-matrix/SKILL.md` ghi **~240 = STALE** ⇒ W4 vá (🔴 chuỗi H24 §2(4) BẮT BUỘC: detector fire → cổng chứng-kiến → RỒI MỚI vá; vá trước = giết chứng-nhân sống) | | Menu keys | **55** | **+1 S161 `KeHoachKyKet` vào `All` (W1 KHKK — policy `KeHoachKyKet.*` sinh runtime; menu-row prod KHÔNG đổi, 142 giữ)** · +1 S69 `Off_Dashboard` (landing Văn phòng số). Prev re-ground S61 (`MenuKeys.cs` const — Mig 50 gỡ 4 `Bg_*` Budget menu cũ). Prev 57 (S58) | | Tests | **590 PASS** | 45 Domain + 545 Infra · 🔄 **+28 @S161** (5 wave KHKK: W1 566 → W2 574 → W3 580 → W6 587 → W7 590; đo tươi @S162 `dotnet test` 45+545, gate CI Run #432 khớp). ~~45 Domain + 517 Infra~~ · 0 fail / 0 skip · **S155 +30** (PE xóa-by-approver: `PeDeleteByApproverTests.cs` + T26/T27 — 🔴 **T27 khoá CẢ LỚP lỗi** *entity-có-cờ-mà-DTO-không-có* bằng reflection [mọi cờ `Allow*` trên entity phải có mặt trên DTO], **fault-inject xác nhận có răng**: tiêm bug ⇒ ĐỎ, khôi phục ⇒ xanh — không phải guard tuyên-bố suông; T26 giữ ĐỎ có chủ ý vì bug production thật, lead vá bằng đảo thứ tự chứ KHÔNG nới assert. Số 562 cross-check 2 nguồn độc-lập: log CI run #417 + `dotnet test` local @S156) · **S147 +4** (`ProjectCodeNormalizationTests` — Trim khi lưu + kiểm-trùng bỏ-dấu-cách; 🔴 **ô canonical này STALE 528/483I suốt S147→S148**, header `:6` và session-log S147 đều ghi 532/487I từ đầu — closeout S147 vá header mà quên ô canonical; H24 `lead-view-auditor` bắt @S148, lead verify 3-nguồn rồi vá) · **S134 +8** (`PePendingAccumulationTests` C1-C8 lũy-kế-TẠM-TÍNH: C1 ChoDuyet+winner vào delta-selected + PriorPes · C2 TuChoi/DaDuyet loại sạch · C3 Nháp list-only KHÔNG cộng tiền [D1] + TraLai vào delta-submitted [D2] · C4 peer-window CreatedAt · C5 delta âm signed · **C6 complementary-no-double-count exact-numbers [ComputeAsync bất động khi thêm peer non-terminal]** · C7 frozen→pending 0/null · C8 D4 changelog `[S134 pending-prior]` positive+contrast) · **S133 +11** (`PeBudgetFreezeTests` freeze-NS-at-DaDuyet: **T0 repro test-before RED→GREEN** [phiếu DaDuyet phải giữ số cũ sau `UpdatePeBudgetPro` — RED bắt 999tr @:184 pre-fix, phiếu đưa tới DaDuyet QUA service finalize mirror `PeApproverFinalizeTests` KHÔNG seed thẳng Phase] + **T1-T9** spec-coverage [snapshot các nhánh finalize + display-gate + 2 terminal-guard chặn sửa NS phiếu đã kết thúc (cả Admin) + backfill] + **site-5 AdminOverride** [reviewer catch — override thẳng → DaDuyet cũng chốt snapshot]) · **S118 +8** (PRO-perm scope-isolation **+4** `ProcurementMasterAccessSeedTests` [reflection no-prod-touch: upgrade-path Suppliers R+C+U + Master-Read · **scope-isolation** DeptManager-riêng vẫn revoke=false · insert-path no-Delete · idempotent] · ReportsController authz **+4** `AuthorizePolicyRegressionTests` [dashboard/export require Reports.Read · my-dashboard KHÔNG action-policy · class-level [Authorize] intact]) · **S117 +6** (PE endsBeforeCeo pointer-aware **+1** `PeFinalizeProjectionTests.ListPe_EndsBeforeCeo_ChoDuyet_PointerAware_PastFinalizeIsFalse` [ChoDuyet con-trỏ QUA cấp finalize → endsBeforeCeo=false, test-before RED→GREEN + EF-subquery translate smoke] · duyệt-theo-khoảng ProMinMax **+5** `PeApprovedPriceFinalizeTests` [happy set Min+Max · min==max boundary lock strict-`>` · thiếu-Max→Conflict not-finalized · min>max→Conflict · single-source+Max→Conflict wire-bug + regression Max=null] · harness `InvokeApply` 4→5 args fix TargetParameterCountException) · **S116 +9** (PE nhập giá âm: T1 ngân-sách-bất-biến khi quote-âm · T2 winnerQuoteTotal signed-sum · T3 submit-guard all-zero-blocked/pure-neg+net-zero-submittable · T4 CCM-net-threshold) · **S114 +9** (PE multi-NCC per hạng mục: `PeSelectWinnerClearTests` delete-invariant +4 [DeleteQuote/DeleteDetail re-derive IsWinner **test-before RED→GREEN** + guard-no-over-clear + UpsertQuote-update-branch coverage] + impl-lane +5 [per-hạng-mục-total · giaTri-single-partial · threshold-crossing · derive-invariant]) · **S113 +19** (Supplier import v2: `SupplierImportV2DedupTests` 13 [dedup MST-primary+Code-backstop T1-T7 · draft-on-import IsPublic · all-or-nothing · LayoutValid re-bake real-token · **null-Code→nháp FIXED** flipped-from-characterization] + `SupplierPublishAndListTests` 6 [publish-guard Code-required→409 · unpublish · list-filter published · DTO IsPublic]) · **S112 +18** (Supplier `SupplierExcelImportServiceTests` +10 [case-collation dedup có-RĂNG SQLite-BINARY-collation-nên-chứng-dedup-ở-SERVICE · fill-nulls-safe · all-or-nothing-abort · parser abs-cell-index/#REF!-null/header-fingerprint · Type-lạ→NCC · Status-emoji-strip] · PE `PeSubmitGuardAndBypassTests` 4-update→A1 [minOwn subordinate-only, pointer-stop] + 8-new [A1-happy · non-contiguous · EDGE-5 non-admin-throw/admin-pass/system-pass · opinion-history log/none]) · Prev 440: **S96 +6** (PE `PeApproverFinalizeTests` +2 [#6 finalize-opt-out→advance-CEO · #7 stale-noti-clear ExecuteUpdate] · `PeSuggestedPriceSetterAuthzTests` +4 [price-lock phase-guard after-submit — PRO/CCM ChoDuyet/DaDuyet→Conflict, Admin-override; +sửa SeedPeAsync default ChoDuyet→DangSoanThao spec-change]) · **S92 +3** (`AdminOnlyModulesRevokeTests` ẩn 5 nhóm menu chỉ Admin — non-Admin revoked-4-cờ / Admin untouched / Pe_* preserved) · **S91 +9** (ChangePassword self-service BE+test) · **S89 +3** (`PeDraftVisibilityTests` IDOR nháp riêng-tư) · **S88 +17** (D1 financials/CEO-notify/HoSoLink null-safe +11 · D2 create-contract multi-winner +6) · **S87 +25** (Mig 58 net +18: `PeMultiWinnerTests` sum + `PeSelectWinnerClearTests` multi-update + `PeSubmitGuardAndBypass` budget=0 + `PeApproverFinalizeTests` 5 auto-finalize [từng 7, −2 khi opt-in→auto rework] + `PeCcmThresholdFinalize`/`PeWorkItemBudget` collateral; Mig 59 +7 `PeCcmBudgetPeriodSetterAuthzTests` role-gate Admin|CCM/budget=0/null-clear) · **S86 +3** (`PeApproverNotifyExcludesDrafterTests` — notify "cần duyệt" loại NGƯỜI TẠO phiếu: drafter-excluded · co-approver-vẫn-nhận · drafter-vẫn-nhận-chuông-kết-quả) · **S85 +18** (PE CV PRO C5 hủy/đổi NCC winner +10 · urgent drafter-self-unset +5 · workflow-preserve bug-class S42 +3 `PeUpdateDraftWorkflowPreserveTests`) · **S83 +2** (PE require-workflow: `PeWorkItemGuardTests` ApprovalWorkflowId `NotEmpty` ×2 validator + submit-guard rewrite test 7/13 `PeSubmitGuardAndBypass` V1-submit-deprecated, no-count-change) · **S77 +10** (+7 `PeSuggestedPriceSetterAuthzTests` ghi chú PRO/CCM Mig 57 absolute-set/role-gate/null-clear/max-1000 · +3 `PeUrgentToggleAuthzTests` rewrite cờ-gấp **BẤT ĐỐI XỨNG** SET=function role / UNSET=function+DeptManager, plain-PRO-unset Forbidden) · **S76 +5** (`PeWorkItemBudgetTests` PRO split: set ProInitial+ProAdjust gồm âm · validator ProInitial≥0/ProAdjust-cho-âm · full=proFull khi CCM empty · full=CCM khi CCM present) · **S74 +5** (`PeWorkItemBudgetTests` §4b CcmNote: set CCM/Admin · null-clear absolute-set · non-priv Forbidden+no-mutate · all-3-persist) · **S73 +28** (PE opt-in finalize spec 6→11 + 10 giá-chốt `PeApprovedPriceFinalizeTests` + 13 setter-authz `PeSuggestedPriceSetterAuthzTests`) · **S69 +20** (Office-golive OfficeModulePermissionSeed 6 + PE PeCcmThresholdFinalize 5 + PeUrgentToggleAuthz 9) · **S67 +23 HRM test-after** (DepartmentTreeTests 8 cycle-guard/rollup + PeHoSoLinkTests 9 absolute-set + HrmProfilePermissionSeedTests 6 reflection-seed) → 286 · S61 +22 `PeWorkItemBudgetTests` −14 `BudgetPolicyTests` −1 → 263 · S60 +14 `PeSubmitGuardAndBypassTests` +2 spec → 256 (Domain 58→45 do drop Budget module tests) | -| Gotchas | **84** | **+S159 #84** (revoker-2-chiều: sửa data tay trên prod bị code-path ungated @restart ĐẢO NGƯỢC im lặng — grant menu SQL tay bị revoker [S92] lật 447/494 row; fix = code-first seeder + gỡ nhánh + guard 2-vế + khuôn 3-chân-kiềng; liên-quan #75/#76 chiều seeder). **+S133 #81-EXT** (instance 2 indirect-assignment, KHÔNG số mới: snapshot-NS-at-DaDuyet hook đặt ở finalize choke-point NHƯNG 2 site gán Phase TRỰC TIẾP bypass — admin-override `PurchaseEvaluationWorkflowService.cs:308` `= targetPhase` + seeder `DbInitializer:1323` `= current` → phiếu vào DaDuyet qua 2 đường này KHÔNG snapshot [frozen=0, display rơi về live = đúng bug đang vá]; reviewer adversarial bắt pre-commit, em-main + 3 invocation implement đều sót — cùng bài enumeration-writer #81; meta: field-đích có hook/derive/snapshot ⇒ grep MỌI assignment-site kể cả override/seeder). +1 S126 (**#83** số AGGREGATE session-cumulative tự-lão-hoá — audit COUNTS đổi bởi CHÍNH spawn review sau đó [2/2/0→4/4/0]; outward mời-reproduce = bảng per-lane theo run-id, KHÔNG số tổng; reviewer-gate bắt, em-main-solo sót). +1 S118 (**#82** menu-flag grant ≠ quyền API thật: display-layer [Permission `CanRead`→`GetMyMenuTree` FE-menu] ⟂ API-authz-layer [controller `[Authorize(Policy=)]`] = 2 tầng ĐỘC-LẬP; grant `Suppliers` R+C+U cho PRO nhưng `PUT/DELETE /suppliers` gate `[Authorize(Roles="Admin,CatalogManager")]` → PRO vẫn 403 sửa/xóa NCC [chỉ Publish/Import theo `Suppliers.Update` là ăn]; ngược lại `ReportsController` `[Authorize]` trần → mọi authed-user đọc `dashboard` tài-chính-HĐ ngược S92; fix gate `[Authorize(Policy="Reports.Read")]` per-method + grep controller authz TRƯỚC khi tin hiệu-lực grant; reviewer adversarial bắt, em-main-solo tin menu-flag=quyền thì sót; liên-quan #44). +1 S114 (**#81** derived-invariant re-derive ở write/select NHƯNG SÓT DELETE writer [DeleteQuote/DeleteDetail cascade] → xóa quote-selected cuối của NCC → `Supplier.IsWinner` kẹt stale [Any(IsSelected)=false] → `CreateContract Where(IsWinner)` gom NCC phantom → **HĐ giaTri=0**; fix helper `PeWinnerInvariant.ReSyncAfterQuoteRemovalAsync` gọi sau SaveChanges 2 handler; **enumeration-writer-trong-spec KHÔNG đáng tin** → grep MỌI `.Remove(`/`.Add(`/`=field` chạm entity chứa field-nguồn; chỉ **verify-adversarial-refute** bắt [invest/review/implement đều sót]; cardinality S87/S88 lên tầng mutation-path). +1 S113 (**#80** null-forgiving `!` che NRE trên external-parsed data: `SupplierExcelImportService.NewSupplier` gán `Code = r.Code!.Trim()` → NullReferenceException khi parser trả `null` cho ô cột-4 rỗng [ĐÚNG path R4 "thiếu Mã NCC → nháp"] → confirm nổ 500 CẢ batch dù preview classify New-draft KHÔNG lỗi; sibling `IsPublic=!IsNullOrWhiteSpace(row.Code)` null-safe nhưng NewSupplier thì KHÔNG; empty-string `""` chạy OK nhưng FE round-trip blank→JSON `null` → nổ. Fix `(r.Code ?? "").Trim()`. **Build 0/0 KHÔNG bắt** [`!` tắt nullable-warning]; chỉ test-before-critical bắt. Lesson: `!` null-forgiving trên dữ-liệu-parse-ngoài = mìn, dùng `?? default`). +1 S97 (**#79** backfill soi NHẦM bảng — transition-marker "[Duyệt KẾT THÚC]" nằm ở `PurchaseEvaluationChangelogs.ContextNote` KHÔNG phải `PurchaseEvaluationApprovals` → Mig 60 backfill match 0, phiếu finalize-CCM giữ flag=0 hiện sai "Đã được CEO duyệt"; fix Mig 61 query đúng bảng. PE 2 bảng lịch-sử tách vai [Approvals=ai-duyệt record :745 · Changelogs=chuyển-phase+marker LogTransition :1073]; verify marker-count prod trước khi tin backfill). +1 S97 (**#78** display-flag suy CONFIG thay RUNTIME → nói dối khi instance lệch config: `endsBeforeCeo` từ "workflow CÓ cấp finalize" thay "phiếu NÀY finalize" → phiếu lên CEO báo sai "không qua CEO"; nặng sau S97 default-flip opt-in; fix field runtime `EndedByLevelFinalize` Mig 60. Flag = "instance ĐÃ làm-gì" KHÔNG "config CÓ THỂ"). +1 S96 (**#77** SPA deploy bundle cache-lag: team thấy UI CŨ dù đã deploy + Ctrl+F5 — index.html no-cache ĐÚNG-chuẩn + JS hash-immutable nhưng browser giữ tab-SPA-mở/cache-local → verify bundle prod **BYTE-LEVEL** (curl + grep string-fix trong `/assets/index-.js`) phân-biệt cache-vs-deploy; **incognito = golden-test**; no-service-worker nên clear-site-data đủ). +2 S91 (**#76** deploy render config từ `.example` KHÔNG phải file-gitignored → prod thiếu setting tới khi điền tay · **#75** seed ungated per-code RESURRECT data đã-wipe mỗi restart → gate sau env-flag `DemoSeed:Disabled` + verify-by-restart-thật). +1 S90 (**#74** Chrome-MCP screenshot kẹt SPA-SignalR `document_idle` → PowerShell `CopyFromScreen` + Win32 lọc-process-`chrome.exe`). +2 S85 (**#72** lệch giờ +7h — DateTime lưu UTC nhưng serialize thiếu 'Z' → FE `new Date()`=local → `UtcDateTimeJsonConverter` global ép UTC-with-Z · **#73** UpdateDraft absolute-set CLEAR field SÓT khỏi pattern null-safe = bug-class S42 → guard `if (request.X is not null)`). +1 S78 **#71** (thêm enum value vào entity DÙNG-CHUNG → pollute UI/guard phân-loại theo PROXY-predicate `supplierId===null` thay vì enum tường minh; file-khi-duyệt purpose=5 lẫn "Bảng so sánh" + false-pass submit-guard → grep mọi field-proxy predicate loại value mới + build-verify TỪNG app; em-main grep no-leak bắt trước deploy). +1 S76 **#70** (FE absolute-set echo field anh-em từ server-snapshot + `invalidate()` fire-and-forget → cửa-sổ stale-echo mất-dữ-liệu khi lưu 2 ô liên-tiếp; guard `useIsFetching` gate nút Lưu tới khi refetch land; reviewer workflow bắt, impl self-review sót). +1 S72 **#69** FE bundle-hash non-deterministic + `deploy.yml` rebuild-FE-unconditional → bundle rotate kể cả commit governance (SPA-fallback-200 trap; cicd Run #312). +2 S69: **#67** Tailwind v4 accent palette thiếu stop (teal/violet/amberx/greenx chỉ 50/100/500/600/700; dùng -300 → teal/violet rơi DEFAULT Tailwind sai-hệ, amberx/greenx drop hẳn — build PASS, phải soi dist CSS) · **#68** IDE TS diagnostic giữa background-agent/workflow = snapshot dở-dang (TS server bắn mỗi save), chỉ tin build SẠCH sau-cùng (2× session này: re-skin + PE-FE đều false-alarm). Prev +1 S68 **#66** Tailwind v4 rule element thô `h1-h4{color:#0b1220}` viết NGOÀI `@layer` thắng utility `text-white` → heading render đen; fix ĐIỂM `text-white!` (important, grep dist confirm), KHÔNG move `@layer` (load-bearing ~30+ heading toàn app). Prev +1 S65 **#65** build csproj con (vd `SolutionErp.Api.csproj`) ≠ `dotnet build SolutionErp.slnx` (gồm tests) → miss test-compile khi đổi chữ ký record command (CreateDepartmentCommand +ParentId) → CI CS7036 FAIL-gated Run #291 (deploy chặn, prod nguyên — test-gate làm đúng việc). Fix: build full slnx trước push BE signature-change. Prev +2 S61: **#63** EF scaffold tự sinh `RenameColumn` SAI-semantics khi drop+add cùng type (test xanh không bắt — SQLite EnsureCreated không replay migration) · **#64** `dotnet ef database update` áp Design-DB 0-rows ≠ Dev-DB → data-migrate `Sql()` chạy thật lần đầu trên prod. Prev +2 S59 (#61 sqlcmd `-f 65001` · #62 rename natural-key UPDATE trước deploy) | +| Gotchas | **85** | **+S162 #85** (menu-key OR-gate FE ≠ policy per-action BE: cây 4-folder GĐ gate `['Khkk_List','Khkk_G1','KeHoachKyKet'].some(can)` NHƯNG endpoint gắn `[Authorize(Policy="KeHoachKyKet.Read")]` [`ContractSigningPlansController.cs:36`] + `MenuPermissionHandler.cs:40` so khớp **CHÍNH XÁC 1 key** — KHÔNG kế-thừa cha, KHÔNG OR key con ⇒ role `Khkk_List=1 ∧ KeHoachKyKet=0`: FE mở cổng → API **403** → folder GĐ2 hiện "—" dưới **MỌI** gói thầu, claim "gate trước fetch ⇒ 0 rác 403" **vỡ**. Bẫy: luật "OR nhiều key" **ĐÚNG ở tầng HIỂN-THỊ** [`GetMyMenuTreeQuery.cs:61-96` trả node khi con `CanRead`] nhưng **SAI ở tầng API-authz** — mặt thứ 2 của #82, lần này **menu-hiện RỘNG hơn API-cho** [#82 là chiều ngược]. Fix = gate đúng key policy CỦA endpoint; **mở controller đọc attribute TRƯỚC khi wire gate**, đừng suy từ cây menu. Reviewer bắt **trước ship**; Dev 13/13 role đều `Contracts=1 ∧ KeHoachKyKet=1` nên **chưa lộ** — lớp "chưa vỡ vì dữ-liệu hiện tại may mắn", vá ngay không đợi triệu chứng). **+S159 #84** (revoker-2-chiều: sửa data tay trên prod bị code-path ungated @restart ĐẢO NGƯỢC im lặng — grant menu SQL tay bị revoker [S92] lật 447/494 row; fix = code-first seeder + gỡ nhánh + guard 2-vế + khuôn 3-chân-kiềng; liên-quan #75/#76 chiều seeder). **+S133 #81-EXT** (instance 2 indirect-assignment, KHÔNG số mới: snapshot-NS-at-DaDuyet hook đặt ở finalize choke-point NHƯNG 2 site gán Phase TRỰC TIẾP bypass — admin-override `PurchaseEvaluationWorkflowService.cs:308` `= targetPhase` + seeder `DbInitializer:1323` `= current` → phiếu vào DaDuyet qua 2 đường này KHÔNG snapshot [frozen=0, display rơi về live = đúng bug đang vá]; reviewer adversarial bắt pre-commit, em-main + 3 invocation implement đều sót — cùng bài enumeration-writer #81; meta: field-đích có hook/derive/snapshot ⇒ grep MỌI assignment-site kể cả override/seeder). +1 S126 (**#83** số AGGREGATE session-cumulative tự-lão-hoá — audit COUNTS đổi bởi CHÍNH spawn review sau đó [2/2/0→4/4/0]; outward mời-reproduce = bảng per-lane theo run-id, KHÔNG số tổng; reviewer-gate bắt, em-main-solo sót). +1 S118 (**#82** menu-flag grant ≠ quyền API thật: display-layer [Permission `CanRead`→`GetMyMenuTree` FE-menu] ⟂ API-authz-layer [controller `[Authorize(Policy=)]`] = 2 tầng ĐỘC-LẬP; grant `Suppliers` R+C+U cho PRO nhưng `PUT/DELETE /suppliers` gate `[Authorize(Roles="Admin,CatalogManager")]` → PRO vẫn 403 sửa/xóa NCC [chỉ Publish/Import theo `Suppliers.Update` là ăn]; ngược lại `ReportsController` `[Authorize]` trần → mọi authed-user đọc `dashboard` tài-chính-HĐ ngược S92; fix gate `[Authorize(Policy="Reports.Read")]` per-method + grep controller authz TRƯỚC khi tin hiệu-lực grant; reviewer adversarial bắt, em-main-solo tin menu-flag=quyền thì sót; liên-quan #44). +1 S114 (**#81** derived-invariant re-derive ở write/select NHƯNG SÓT DELETE writer [DeleteQuote/DeleteDetail cascade] → xóa quote-selected cuối của NCC → `Supplier.IsWinner` kẹt stale [Any(IsSelected)=false] → `CreateContract Where(IsWinner)` gom NCC phantom → **HĐ giaTri=0**; fix helper `PeWinnerInvariant.ReSyncAfterQuoteRemovalAsync` gọi sau SaveChanges 2 handler; **enumeration-writer-trong-spec KHÔNG đáng tin** → grep MỌI `.Remove(`/`.Add(`/`=field` chạm entity chứa field-nguồn; chỉ **verify-adversarial-refute** bắt [invest/review/implement đều sót]; cardinality S87/S88 lên tầng mutation-path). +1 S113 (**#80** null-forgiving `!` che NRE trên external-parsed data: `SupplierExcelImportService.NewSupplier` gán `Code = r.Code!.Trim()` → NullReferenceException khi parser trả `null` cho ô cột-4 rỗng [ĐÚNG path R4 "thiếu Mã NCC → nháp"] → confirm nổ 500 CẢ batch dù preview classify New-draft KHÔNG lỗi; sibling `IsPublic=!IsNullOrWhiteSpace(row.Code)` null-safe nhưng NewSupplier thì KHÔNG; empty-string `""` chạy OK nhưng FE round-trip blank→JSON `null` → nổ. Fix `(r.Code ?? "").Trim()`. **Build 0/0 KHÔNG bắt** [`!` tắt nullable-warning]; chỉ test-before-critical bắt. Lesson: `!` null-forgiving trên dữ-liệu-parse-ngoài = mìn, dùng `?? default`). +1 S97 (**#79** backfill soi NHẦM bảng — transition-marker "[Duyệt KẾT THÚC]" nằm ở `PurchaseEvaluationChangelogs.ContextNote` KHÔNG phải `PurchaseEvaluationApprovals` → Mig 60 backfill match 0, phiếu finalize-CCM giữ flag=0 hiện sai "Đã được CEO duyệt"; fix Mig 61 query đúng bảng. PE 2 bảng lịch-sử tách vai [Approvals=ai-duyệt record :745 · Changelogs=chuyển-phase+marker LogTransition :1073]; verify marker-count prod trước khi tin backfill). +1 S97 (**#78** display-flag suy CONFIG thay RUNTIME → nói dối khi instance lệch config: `endsBeforeCeo` từ "workflow CÓ cấp finalize" thay "phiếu NÀY finalize" → phiếu lên CEO báo sai "không qua CEO"; nặng sau S97 default-flip opt-in; fix field runtime `EndedByLevelFinalize` Mig 60. Flag = "instance ĐÃ làm-gì" KHÔNG "config CÓ THỂ"). +1 S96 (**#77** SPA deploy bundle cache-lag: team thấy UI CŨ dù đã deploy + Ctrl+F5 — index.html no-cache ĐÚNG-chuẩn + JS hash-immutable nhưng browser giữ tab-SPA-mở/cache-local → verify bundle prod **BYTE-LEVEL** (curl + grep string-fix trong `/assets/index-.js`) phân-biệt cache-vs-deploy; **incognito = golden-test**; no-service-worker nên clear-site-data đủ). +2 S91 (**#76** deploy render config từ `.example` KHÔNG phải file-gitignored → prod thiếu setting tới khi điền tay · **#75** seed ungated per-code RESURRECT data đã-wipe mỗi restart → gate sau env-flag `DemoSeed:Disabled` + verify-by-restart-thật). +1 S90 (**#74** Chrome-MCP screenshot kẹt SPA-SignalR `document_idle` → PowerShell `CopyFromScreen` + Win32 lọc-process-`chrome.exe`). +2 S85 (**#72** lệch giờ +7h — DateTime lưu UTC nhưng serialize thiếu 'Z' → FE `new Date()`=local → `UtcDateTimeJsonConverter` global ép UTC-with-Z · **#73** UpdateDraft absolute-set CLEAR field SÓT khỏi pattern null-safe = bug-class S42 → guard `if (request.X is not null)`). +1 S78 **#71** (thêm enum value vào entity DÙNG-CHUNG → pollute UI/guard phân-loại theo PROXY-predicate `supplierId===null` thay vì enum tường minh; file-khi-duyệt purpose=5 lẫn "Bảng so sánh" + false-pass submit-guard → grep mọi field-proxy predicate loại value mới + build-verify TỪNG app; em-main grep no-leak bắt trước deploy). +1 S76 **#70** (FE absolute-set echo field anh-em từ server-snapshot + `invalidate()` fire-and-forget → cửa-sổ stale-echo mất-dữ-liệu khi lưu 2 ô liên-tiếp; guard `useIsFetching` gate nút Lưu tới khi refetch land; reviewer workflow bắt, impl self-review sót). +1 S72 **#69** FE bundle-hash non-deterministic + `deploy.yml` rebuild-FE-unconditional → bundle rotate kể cả commit governance (SPA-fallback-200 trap; cicd Run #312). +2 S69: **#67** Tailwind v4 accent palette thiếu stop (teal/violet/amberx/greenx chỉ 50/100/500/600/700; dùng -300 → teal/violet rơi DEFAULT Tailwind sai-hệ, amberx/greenx drop hẳn — build PASS, phải soi dist CSS) · **#68** IDE TS diagnostic giữa background-agent/workflow = snapshot dở-dang (TS server bắn mỗi save), chỉ tin build SẠCH sau-cùng (2× session này: re-skin + PE-FE đều false-alarm). Prev +1 S68 **#66** Tailwind v4 rule element thô `h1-h4{color:#0b1220}` viết NGOÀI `@layer` thắng utility `text-white` → heading render đen; fix ĐIỂM `text-white!` (important, grep dist confirm), KHÔNG move `@layer` (load-bearing ~30+ heading toàn app). Prev +1 S65 **#65** build csproj con (vd `SolutionErp.Api.csproj`) ≠ `dotnet build SolutionErp.slnx` (gồm tests) → miss test-compile khi đổi chữ ký record command (CreateDepartmentCommand +ParentId) → CI CS7036 FAIL-gated Run #291 (deploy chặn, prod nguyên — test-gate làm đúng việc). Fix: build full slnx trước push BE signature-change. Prev +2 S61: **#63** EF scaffold tự sinh `RenameColumn` SAI-semantics khi drop+add cùng type (test xanh không bắt — SQLite EnsureCreated không replay migration) · **#64** `dotnet ef database update` áp Design-DB 0-rows ≠ Dev-DB → data-migrate `Sql()` chạy thật lần đầu trên prod. Prev +2 S59 (#61 sqlcmd `-f 65001` · #62 rename natural-key UPDATE trước deploy) | | User memory | **31** | +S84 `feedback_derived_flush_execution` (derived→canonical flush = em-main execution KHÔNG gate user-confirm; monitor INFORM-only ≠ cần-anh-duyệt; chỉ ĐỔI-con-số/policy = quyền anh). +S79 `feedback_canonical_spec_over_broadcast` (adap pull canonical-command/§P đừng phỏng-theo broadcast + report-before-stamp dogfood + residual own-diary OK). +S75 `feedback_harness11_engine` (engine governance tự-bảo-trì + gotcha #30-via-Edit + multi-workflow-verify + git-add-specific lesson). re-ground S71 (H1 disk-count: 25 `feedback_*` + 3 `project_*`; STATUS "21" stale từ S64). +S71 `feedback_harness10_run_trace` (run-trace + 3 lesson). Prev +1 S64 `feedback_harness7_outward_writing_quality`; re-grounded S54 base 19 | | Skills | 6 | 3 domain + 3 ops | | Sub-agents | **23** | **+S153 bộ-ba vòng Ctx** (adopt thư `0df10df4` — 🔄 **OWNER-VETO @S153 hub-form 3-vai**: bản đầu (38)(a) 1-vai OWNER-DELEGATED→LEAD-DECIDED, **anh bác cùng ngày** chốt trực tiếp *"Build đủ 3 vai như hub"* qua AskUser = owner chốt THẬT cho quyết này; 🔄 **RENAME cùng phiên theo lệnh anh** *"đặt tên giống hub và sisters để dễ audit"* — tên khai-sinh `ctx-pause-check`/`ctx-reload` → **hub-form `ctx-curator`/`ctx-verifier`** [đo đĩa 3 nhà: hub = curator/verifier/audit · VIPIX = check/reload/chain · hub≠sister ⇒ SE theo HUB-form, tiền lệ rename S149 lead-*]): **`ctx-curator`** vai-1 KIỂM nháp @mỗi-`/pause` (carve-out `pause.md §1` mirror hub O-1(a)) · **`ctx-verifier`** vai-2 ĐỐI-CHIẾU+NẠP-LẠI @mỗi-`/tiep` (dòng `LEAD-PHAI-NHAC-LAI:` = bằng-chứng nội-dung) · **`ctx-audit`** vai-3 SOI-CHUỖI @2-bookend (kiểm-vết vai-1/2, hồi-tố = fallback phiên 1-cửa). Nấc: ctx-audit `file-land + registry-surfaced @S153` cho CẢ 3 vai ctx [🔄 vá @S153 FLAG-6: bản đầu chỉ cấp nấc này cho `ctx-audit` trong khi `hmw.js:42` + writeGuard + README `:27`/`:199`/`:200` chứng 2 vai mới ĐẠT CÙNG nấc — khai THIẾU vẫn là lệch] — spawn-probe ALIVE cả 3 = phiên sau restart T9; hmw VALID_ROLES 20→**23** + writeGuard :228/:235; ⚠️ **roster 23 > `hmw_width.cap` 20** ⇒ full-roster-sweep LOUD-skip 3 lane cuối — số cap = quyền anh, O-7 (câu (40) đang treo). **🔄 RENAME @S149 (anh ratify "OK đồng ý"): 5 vai đổi tên đồng-bộ sister — `tooling-harvest-audit`→`ring1-audit` · `h24-audit`→`ring2-audit` · `sleep-audit`→`ring4-audit` · `lead-view-auditor`→`lead-stale-auditor` · `lead-omission-auditor`→`lead-gap-auditor` (ring* = 2 sister TRÙNG tuyệt đối; lead-* = hub-form, VIPIX-form là prefix). Nấc **NÂNG @cùng-phiên: `registry-listed-in-session`** — harness list 5 tên MỚI + gỡ 5 tên CŨ NGAY TRONG PHIÊN S149 (không restart), **TRÁI phép đo S120/S121 no-hot-reload** (môi trường có thể đã đổi — đây là data-point ĐO ĐƯỢC, không suy). Spawn-probe vẫn CẦN để chứng instantiate (list ≠ spawn được — đúng 0.6b). Tên cũ trong narrative/runs/history = vết lịch-sử, CỐ Ý giữ.** **+S145 🔬 `ring1-audit` (18th sub — round-KIỂM vòng tooling/harvest, C4/C4b adap dot-11 khoản ① `harness-dong-du-vong:51`, owner-decision [2] TÁCH mỗi-vòng-1-con-KHÔNG-gộp). Vai-KIỂM ĐỘC-LẬP đọc OUTPUT H1 `tooling-auditor` (4-mặt freshness) + H2 `harvest-curator` (5-trục integrity), **KHÔNG re-chạy đo**: verdict per-claim ĐẠT/TRƯỢT + falsify ≥2 + **tự tái-dựng ≥1 số CÓ-THỂ-SAI** (đếm `.claude/agents/*.md` vs roster-count H1 · N-harvested/M-spawned) + **thách-CLEAN** (H1 PASS/H2 GATE-0 → mở 1 skill "fresh"+1 sub "harvested" xem thật). 🔴 no-self-exempt ≠ H1/H2 (m-3 S139). **AUTO fire-after-measure @session-end §L.b(g-bis)** — owner-chốt S145 KHÔNG consent-gate (KHÁC trio/H24), fail-safe NO-OP nếu H1/H2 không chạy phiên đó (CẤM ĐẠT-ảo); tuần-tự C4b (invocation SAU con-đo, mtime chứng). Propose-only, backstop = git-diff commit-gate (KHÔNG dựa thiếu-tool `[carry:tools-whitelist-no-teeth]`). `model: opus`+`effort: max`+`memory: project`+`maxTurns: 16`. **Vai #1/3 round-KIỂM** — `ring2-audit` (vòng soi-lead) + `ring4-audit` (vòng nén-ngủ) land dần incremental (§6 broadcast báo-nấc-per-vòng, owner S145). 🔴 **Nấc = executed-file / verified-pending-restart** — registry Agent-tool KHÔNG hot-reload (`agents/README` §Memory discipline — anchor không-số-dòng, vá cite-dòng-trống @S151 per H1 F-3), spawn-probe alive cần **restart CLI**; run-thử-thật @session-end phiên sau. Design-doc: `runs/2026-07-22-S144-adap-dot-11/C4-design-3-vai-kiem.md`.** **+S145 🔬 `ring2-audit` (19th sub — round-KIỂM vòng soi-lead H24, vai #2/3 C4/C4b). Đọc OUTPUT `lead-stale-auditor` + `lead-gap-auditor` (🔴 KIỂM 2 con-đo H24, **KHÔNG soi lead trực-tiếp**) → verdict per-FLAG + tự tái-dựng ≥1 số (flag-count · mở 1 FLAG đối-chứng source · `class_repeat` `.session-counter.json`) + **thách-CLEAN** (H24 báo ít/0 FLAG ⇒ tự soi 1 surface tìm FLAG sót — CLEAN "lead ổn" là ca nguy nhất, khớp H24 §2(1) phép-thử-nhanh). Verify vai H24 KHÔNG tự chế class ngoài enum ĐÓNG `flag_classes`. no-self-exempt ≠ lead-stale/lead-gap (m-3; tên mới @rename S149). AUTO wired `session-end §L.b(j)(ii-bis)` sau cặp H24; fail-safe NO-OP nếu H24 không chạy (nhịp/consent). `maxTurns: 16`.** **+S145 🔬 `ring4-audit` (20th sub — round-KIỂM vòng nén-ngủ, vai #3/3 C4/C4b). Đọc OUTPUT `/sleep-recovery-memory-l2` (`.gist.md` mới + Fidelity verdict + claim giảm-byte) → verdict per-claim + **tự ĐO LẠI byte** archive gốc vs gist (đối-chứng claim) + **thách-CLEAN** (Fidelity 5P/0F ⇒ mở 1 gist đối-chứng `.md` gốc xem có giữ 4-field load-bearing hay nén quá tay) + kiểm bất-biến gist-ADDITIVE (gốc byte-identical). no-self-exempt độc-lập lượt nén (m-3). AUTO wired `sleep-recovery-memory-l2.md Phase 4.5` sau nén; fail-safe NO-OP nếu 0 gist tươi. `maxTurns: 16`.** **+S141/S142 🧪🧭🧮 `harness-eval` + `harness-refine` + `harness-audit` (15th–17th sub — bộ-ba đồng-kiểm HỆ MEMORY, adopt AI_INFRA khuôn 5-câu `58e28bae` khối B, wave adap đợt-9/10; land S141 persona+seed+`hmw.js` VALID_ROLES 14→17, wiring nghi-thức S142 vòng-2 sau khi vòng-1 chết giữa chừng). Read-only INFORM-only propose-only, frontmatter **không khai Write/Edit** — 🔴 nhưng đo S143: **runtime VẪN cấp** (append cuối tool-list, 9/9 vai read-only; nhân-chứng `reviewer` đã Write+Edit thật) ⇒ đây là **tuyên-bố ý-định**, KHÔNG phải chặn-bằng-thiếu-tool; containment thật = single-writer + git-diff commit-gate · `model: opus`+`effort: max`+`memory: project`+`maxTurns: 18`. Trục = **CHÍNH HỆ MEMORY**, chạy **TUẦN TỰ** eval→refine→audit (🔴 CẤM `parallel()` — con KIỂM chạy song song sẽ chấm bản CŨ ⇒ verdict vô-nghĩa): `harness-eval` = con **ĐO** (chỉ trả số nguyên-văn từ instrument, CẤM số-từ-trí-nhớ) ⟂ `harness-refine` = con **ĐỀ-XUẤT** (4 lớp reinject/promote/archive/distill, bắt buộc có mục "đã cân-nhắc→BÁC") ⟂ `harness-audit` = con **KIỂM** (verdict ĐẠT/TRƯỢT per-claim + re-chạy ≥2 số load-bearing; PIN run-folder đích-danh, CẤM tự tìm "run mới nhất"). 🔴 **KHÔNG gộp** với H1 tooling-freshness / H2 harvest-integrity / H24 lead-surface / reviewer diff-code — 4 trục khác nhau. Nghi-thức 2-đầu-phiên **AUTO 1 vòng/phiên GIỮA 2 bookend** (`session-start §2.1.9(a)` canonical + `session-end §L.b(k)(i)` trỏ): owner chốt @S151 — em-main IN 1 dòng trạng-thái rồi CHẠY, KHÔNG chờ gật [🔄 vá @S153 FLAG-4 HIGH: ô này còn khai "consent-gated + anh gật mới chạy" sau khi owner (32) đã lật AUTO, và ngược chính `:466` cùng file; trio đã chạy THẬT 3 vòng S151/S152/S153]; skip → ghi vết WAL `trio: skipped S` (vết-2-lớp, chuỗi skip nhìn thấy được). Budget seed đo TƯƠI `measure-agent-memory.ps1`: eval 1547B · refine 1616B · audit 1513B (l2_*=0 = SEED-STATE đúng, chưa chạy lần nào). 🔸 **Nấc trung-thực = executed-file / verified-pending-restart** — 3 vai CHƯA spawn-probe lần nào (cần restart CLI + anh consent; KHÔNG được đọc thành verified-runtime).** **+S121 👁 `lead-stale-auditor` + `lead-gap-auditor` (13th+14th sub — H24 lead-self-audit, adopt broadcast `2026-07-14-Governance-harness-24`, anh chốt S119 `owner-decisions-15-07-2026`, land S121 wave-W2 `hmw.js` VALID_ROLES 12→14 CÙNG COMMIT roster+STATUS [R3-M2: lệch landing ⇒ C2/B3 flag mọi derived ghi "14" là stale-vs-12 ⇒ detector BÃO trên chính thay đổi của H24]). 2 monitor INFORM-only propose-only, cookie-cutter frontmatter monitor sẵn (`model: inherit` lúc-land [S124 re-pin → `opus`+`effort:max` cùng toàn roster] · `memory: project` · `maxTurns: 18` · 8 tool read-only, **NO Write/Edit**). Trục: `lead-stale-auditor` = **view LỆCH source** (5 class `view-*`) ⟂ `lead-gap-auditor` = **cái BỊ THIẾU** (6 class `gap-*` — +`gap-incident-unrecorded` @S143 owner-duyệt) — 🔴 **KHÔNG gộp** vào tooling-auditor(H1 tooling-freshness)/harvest-curator(H2 harvest-integrity): H24 §2(1) *"gộp là mất tiêu-điểm; cái ta cần là cặp mắt CHỈ dành cho lead"*. Class-flag = **enum ĐÓNG** `lead_self_audit.flag_classes` ở `.claude/agent-memory/memory-budget.json` (11 class = 5+6, vai CẤM tự chế — mở enum là quyền owner). Nhịp = `h24_cadence` cùng file (single-source H24 §2(3) — doc TRỎ key, KHÔNG chép số ⇒ 🔄 **đã GỠ digits `{6/15/3}` @S153** theo `ring2-audit` R-3: chép số ngay cạnh câu cấm-chép-số là **B1-copy** [số vẫn KHỚP canonical nên KHÔNG phải stale — F-6 chống vu-oan]; đọc số sống ở `memory-budget.json → h24_cadence`); 🔄 **HÌNH B @S149**: cặp soi-lead spawn **VÔ-ĐIỀU-KIỆN 2 bookend mỗi phiên** — `light_every` hạ thành **stats-only** (KHÔNG còn cổng bắn), `deep_every` GIỮ cổng nhịp [vá @S153 FLAG-5: ô này còn mô-tả bắn thuần-cadence; đối-chứng hành-vi = cặp chạy @counter 27 với light 1/6 CHƯA tới hạn]; counter `.claude/governance/.session-counter.json` (content-based `last_ticked_head`, KHÔNG blind-increment — hook `wal-flush.ps1` auto-commit `agent-memory/` mỗi turn sẽ làm hỏng đếm mù; đặt ngoài **4 path** hook CÓ CHỦ-ĐÍCH — *hook nới từ 3 lên 4 @S146 khi thêm `.claude/sessions/`; mệnh-đề "governance/ nằm ngoài" đã RE-VERIFY sau khi nới, vẫn đúng*). ✅ **verified-runtime S122** (spawn-probe toàn-roster-KHI-ĐÓ SỐNG post-restart — **N=14 tại thời-điểm S122**, chứng runtime, §Recently-Done S122; 🔸 +3 trio S141 CHƯA nằm trong phép đo này) **+ chạy THẬT S124** (dogfood 2-vai đầu-tiên, owner-triggered @counter 3) **+ S137** (light-audit consent OVERDUE 8/6 → 2 FLAG view-stale-status + 1 FLAG gap-decision-sunk). 🔸 Residual THẬT giữ: đường `hmw.js→agentType` cho 2 vai H24 chưa exercise (Agent-tool path đã chứng; workflow-path chưa) [S137 view-audit lật ⚠️→✅ khớp source, giữ residual]. 🔸 Từ-vựng "view"/"omission" chọn có chủ-đích tránh va-chạm: `governance-detectors.ps1:15` đã sở-hữu `derived-staleness`, và "GAP" quá-tải (`session-end` build-gap · `memory-selfimprove-audit.ps1` CRITERIA+GAP).** **+S107 📄 `office-document` (12th sub — Office document WRITE specialist: docx·xlsx·pptx·pdf + form-engine; floor OD1–OD10; VALID_ROLES hmw.js 9→10; anh giao trực tiếp S107 "Sub chính" Office; ✅ **verified-runtime S107 CÙNG PHIÊN** — registry hot-reload agent MỚI giữa phiên [S27 lesson thu hẹp còn EDIT-agent/command] + spawn-test PASS OD1/OD4/OD5 template thật, model self-report `claude-fable-5` [data-point #5 flip-8]; finding: 2 template `wwwroot/templates` 0-placeholder mẫu-blank).** **worker-tier-pin (owner-decision S124 2026-07-15 — thay all-inherit H8 S66, trước nữa two-tier H4; sub TOÀN-ROSTER pin cứng KHÔNG theo lead; H23-precedence ĐO S126: spawn-param THẮNG hard-pin ⇒ `/fable-real` SỐNG). Lead = **DEFAULT Opus 5 MAX** (thư `9c909007` @S149 (re-stamp 07-25 G-024a; pre `59ce1d0f`); **anh chốt ô canonical hiển-thị DEFAULT-fleet @S151** — *"cần gọi Fable tao chọn đầu mỗi phiên hoặc gọi bằng command fable-real"* ⇒ Fable = anh `/model` đầu phiên HOẶC per-invocation `/fable-real`; runtime thật = BƯỚC 0.6 probe mỗi phiên, KHÔNG ghi sổ mỗi flip — hết class stale-2-flip ring1 bắt @S150). 🧊 Lineage owner-choice/flip-chain FROZEN tới flip-8 (flip-9 S119 Opus-4.8 owner-choice + các lượt /model sau = per-session không enumerate, anti-churn S106):** em main Fable 5 (1M) Max (✅ **Fable RESTORED S98 2026-07-02** — lead self-report `claude-fable-5` + settings `claude-fable-5[1m]`; fallback-Opus H5 S62→S97 kết thúc, H5.6 hoàn tất) ⚠️ **S101 2026-07-03: runtime lead = Opus 4.8** (lead + 2 monitor + WF2 reviewer inherit đều self-report `claude-opus-4-8`; settings vẫn pin `claude-fable-5[1m]` → **Fable unavailable phiên này → H5-fallback tự-kích, revert-free, KHÔNG sửa frontmatter**; "Fable RESTORED S98" = point-in-time KHÔNG phải steady-state — inherit-chain resolve theo availability runtime; ✅ **S102 2026-07-06: Fable VỀ LẠI runtime** — lead + 2 monitor inherit self-report `claude-fable-5` → H5-fallback S101 kết thúc, pattern availability per-session xác nhận [S98 Fable → S101 Opus → S102 Fable → **S103 Opus** → **S104 Opus** (2026-07-06 Fable outage tiếp — S104 lead + 2 monitor spawn self-report `claude-opus-4-8` ×3, H5-fallback revert-free) → ✅ **S105 Fable** (2026-07-07 Fable VỀ lần 3 — lead + 2 monitor self-report `claude-fable-5` ×3, runtime-verified) → ⚠️ **S106 Opus** (2026-07-07 Fable outage lại — lead runtime self-report `claude-opus-4-8`, H5-fallback revert-free; flip-7) → ✅ **S107 Fable** (2026-07-10 Fable VỀ lần 4 — lead + 2 monitor self-report `claude-fable-5` ×3, runtime-verified; flip-8)], BƯỚC 0.6 probe mỗi phiên; per-session KHÔNG steady-state. 🔴 **[§Sub-agents = CANONICAL single-owner flip-chain — S106 gộp per anh-confirm; derived `agents/README §Architecture` · `session-start BƯỚC 0.6` · `session-end §commit-format` trỏ về đây [🔄 vá @S153 FLAG-1: 3 cite số-dòng cũ `:3`/`:37`/`:113` đo lại SAI cả 3 (đúng là `:5`/`:48`/`:315`) — vá bằng BỎ số theo luật đã áp @S151, không ĐỔI số], KHÔNG tự enumerate mỗi flip → chống churn 4-file/flip]**) · **TOÀN roster `model: opus` + `effort: max`** (worker-tier-pin S124; **A2 đo S126:** lane no-tier resolve `claude-opus-4-8` DÙ lead=Fable ⇒ pin ăn runtime, hết inherit-lead; số vai = cột giữa của chính row này — KHÔNG chép lại vào narrative, đó là cách row-14 và narrative-12 lệch nhau từ S121 tới S122; lineage: 7 demoted flip S66 → all-inherit S66–S123 [office-document S107 inherit-born] → pin S124) · effort Max. 10 product/quality + monitor INFORM-only (H1 · H2 · 2×H24 S121 · 3×trio-memory S141 · 3×round-KIỂM `ring1-audit`/`ring2-audit`/`ring4-audit` S145) — **liệt-kê NHÓM, số vai = cột giữa của chính row này**. Nấc H8 = **runtime-verified S98 (2026-07-02)** — spawn-test inherit-chain ×2 monitor self-report `claude-fable-5` (cả đội tự lên Fable khi lead restore, 0 sửa frontmatter — đúng thiết kế all-inherit). Prev two-tier H4 runtime-verified S57bis (spawn-test 2 chiều). | | RAG chunks | 🟢 **service UP** · 🔴 **SE index STALE** | 🔴 **TÁCH 2 TRỤC @S159 — 1 nhãn nhị-phân DOWN/alive che mất trục thứ hai.** ① **service-liveness = UP**: đo LIVE 2026-07-29 `list_projects` → **7/7 project chunk > 0** (`solution_erp` **2449** · vipix_multisite 7090 · dh_y_duoc 15440 · namgroup 6106 · ashico 4742 · vipix_ai_infra 4176 · benhvien_aau 3024; chỉ `shared_global` = 0); **query THẬT** `search_code` → 1 hit **có ruột** score 7.156 ⇒ collection đọc được, **không phải vỏ rỗng** (metadata một mình KHÔNG đủ — có thể là registry-cache). ② **SE index-freshness = KÉM**: `last_indexed_at` = **2026-05-29** trong khi **5 sibling re-index 2026-07-29 08:21** cùng một lượt. **Lật thẳng DOWN→alive sẽ CHÔN trục ②.** 🧊 **Nhãn "DOWN từ S153" ĐÚNG LÚC GHI** (`33e7f68`, 07-28 20:50, 3 phép đo khớp, `chunk_count=0` cả 7 project) — service **hồi phục sau ~12h** ⇒ class `view-stale-status`, **KHÔNG phải lead ghi ẩu**; đừng quy lỗi cho lượt viết vốn có bằng chứng đúng. 🔸 **Caveat giữ (H1 F-4 → ring1 T-2):** H1 tuyên `last_indexed_at` "không đáng tin" (2447→2449 nhúc nhích), `ring1` **BÁC** bằng nội-dung — coi là **đáng tin**; muốn chắc tuyệt đối thì re-index rồi xem field có nhảy không. Re-index = AI_INFRA op (cần VOYAGE_API_KEY). *(FLAG-2 `view-stale-status` HIGH — `lead-stale-auditor` @S159, `ring2` tái-dựng 8/8 số. Lead trước đó chỉ vá `:6`, sót đúng ô canonical này = **canonical-poison**, cùng lớp khác site.)* | -**Bundle hash live (prod):** admin **`8-QYcMOg`** · user **`DptYR4wL`** (đo LIVE @S157 2026-07-28, **Run #419** `6250f39` — cicd PASS 9/9 + lead tự re-curl độc lập khớp từng byte, marker 2/2 mỗi app; **control-âm**: 2 hash CŨ `index-BK8w3QxU.js`/`index-DYnxSI0T.js` nay trả `text/html` **900 B admin / 876 B eoffice** ⇒ bundle cũ đã chết trên prod. 3 phép đo độc-lập: cicd-verify · lead re-curl · ring2-audit). 🔴 **Ô này từng đứng ở cặp `BK8w3QxU`/`DYnxSI0T` sau khi prod đã rotate** — `git show 3b6fabc -- docs/STATUS.md` chỉ có 2 hunk, **0 hunk chạm dòng này**; hại đo được: `cicd-verify-s157-diadiem.md:58` dùng chính ô này làm baseline ⇒ lượt deploy kế hoặc báo "ĐỔI" giả hoặc che một lần không-rotate (lead-stale-auditor FLAG-1 HIGH @S158). 🧊 *Mốc trước:* admin `BK8w3QxU` · user `DYnxSI0T` (S156 CI run 531 `f4494cf`; `Last-Modified` 14:41:15/14:42:10 +07 **nằm trong cửa-sổ deploy** — ship-proof, không phải hash-delta suông. Byte-verify 4/4 marker: `allowApproverDelete` 8/2 · `by-approver` 1/1 · `deleted=1` 1/1 · `view=approved` 1/1. ⚠️ **Không chứng minh hồi-tố** được bundle của riêng `4464a46` đã rotate lúc 14:12 — `f4494cf` đè lên sau 30 phút, không ai chụp snapshot khoảng giữa). 🧊 *Mốc trước:* admin `B2j1LQ-v` · user `C3wbJegk` (S147 CI #407 — re-stamp @S152: FLAG-2 lead-stale bắt ô này đứng S134 qua 5 nhãn trong khi `:6`/`:465`/HANDOFF đều S147; ring2 verify 2 lệnh độc-lập). 🧊 *Chuỗi rotate lịch-sử giữ nguyên:* (S134 — **`6664a61`** PE lũy kế TẠM TÍNH vs CHÍNH XÁC [0 mig — `ComputePendingAsync` complementary + DTO 5-field + FE sub-dòng amber + banner màn duyệt + D4 audit-note]; **Run #399** GO 5/5 5m27s gate-**528**-EXACT-từ-log rotate ×2 admin js `3NCdyODe`→**`BVCukFP5`** css `B--dUHcm`→**`CBzUlgOA`** / eoffice js `CKsoiNCI`→**`DoxyEOxh`** css `BuntB63r`→**`BHsBUA8e`** [FE-src đổi thật 6 file ⇒ rotate expected; CSS rotate = amber badge utilities, benign #69]; real-size 1.659.037/1.565.367B + LM 14:31:34/14:32:28+07 in-window; **byte-verify #77** `pendingPriorPes`=2 + `Tạm tính`=3 + `phiếu trước cùng gói thầu CHƯA duyệt`=2 PRESENT cả 2 js [control old-hash 900B text/html 0-hit — ct=text/html là discriminator sạch hơn size-919b]; Mig 67 top FROZEN + 89 bảng [0-mig by-construction]; smoke 4×200 + PE 401; lead spot-check độc-lập post-verdict: 4/4 hash khớp + marker PRESENT). **Prev S133** — admin **`3NCdyODe`** · user **`CKsoiNCI`** (S133 — **`d4fbced`** PE freeze NS phiếu DaDuyet [Mig 67 snapshot 11 cột + backfill 18/18 + FE 2-app badge 🔒 SHA-mirror `5534addb` + display-gate `budgetFrozen`]; **Run #398** GO 5/5 gate-520 rotate ×2 admin →**`3NCdyODe`** [CSS `B--dUHcm`] / user →**`CKsoiNCI`** [CSS `BuntB63r`] — prev `yCkX-2Bp`/`DKSD4p9k` @Run #397 S131-S132 [dòng-live lag S117→S133, hoist per precedent S104/S107]; **CSS cũng rotate** = badge sinh utility-class mới, benign #69; **byte-verify #77** `budgetFrozen`=1 + VN-string 🔒=1 PRESENT [old-control 0]; Mig 67 prod history-top + 11 cột + backfill 18/18 chua=0 + sys.tables **89**; smoke 4×200 + PE 401; `/tiep` S134 re-verify độc-lập 4/4: mig-top + health-200 + bundle-hash ×2 + filter 11/11). **Prev S117** — admin **`cFI7ih4a`** · user **`MxgooVZw`** (S117 — **`316a82f`** PE endsBeforeCeo pointer-aware [display-fix BE-only, phiếu chờ CEO hết báo "Kết thúc" sai] + duyệt-theo-khoảng Min–Max [Mig 66 + FE 2-app ProMinMax radio thứ 5]; **Run #387** PASS 5m36s rotate ×2 admin `Dd55jBpj`→`cFI7ih4a` / user `DVDr1UQ4`→`MxgooVZw` CSS-frozen [logic-only fingerprint]; **byte-verify #77** `ProMinMax`/`duyệt theo khoảng`/`cả Min và Max`/`Min–Max` PRESENT [bundle-cũ=0]; Mig 66 prod col decimal(18,2) + `__EFMigrationsHistory`-top + sys.tables **89**; smoke 200×3 · PE 401-secured; **#53-garble ×2** [invest-lane-1 + cicd-monitor] → SendMessage-resume + em-main curl cross-verify khớp). **Prev S116** — admin **`Dd55jBpj`** · user **`DVDr1UQ4`** (S116 — **`88368fd`** PE cho nhập giá ÂM ô báo giá [FE 2-app nút ± toggle + submit-gate `>0`→`!==0` + helper `hasRealSelectedQuote` 2-mirror `:201`/`:235`; BE submit-guard `:206` `Sum()<=0`→`All(==0)`; +9 test 486→495; **NO migration**]; **Run 386** PASS 5m28s rotate ×2 admin `D51OYyGV`→`Dd55jBpj` / user `BVdssm5S`→`DVDr1UQ4` CSS-frozen [logic-only fingerprint]; **byte-verify #77** `Đã nhập giá chào thầu`/`hoàn tiền NCC`/`phát sinh giảm`/`Đơn vị được chọn chưa có giá chào thầu` PRESENT [bundle-cũ=0]; smoke 200×3 · PE 401-secured · 89 bảng; cicd bắt bẫy snapshot-while-CI-running→poll-success TRƯỚC re-snapshot). **Prev S114** — admin **`D51OYyGV`** · user **`BVdssm5S`** (S114 — **`d436af2`** PE multi-NCC per hạng mục [Mig 65 backfill 2-chiều + BE re-key 5 financial→IsSelected + FE per-cell grid Detail×NCC 2-app SHA `ae3788e9`]; bundle rotate ×2 admin `acprMqfB`→`D51OYyGV` / user `Ck3BTDdA`→`BVdssm5S`; **byte-verify** `detailId`+`select-winner`+`hạng mục` PRESENT [bundle 1.65MB real-size]; smoke health/admin/eoffice 3/3 200 + endpoint POST select-winner bogus-401-live; **cicd garble #53 ×5 → em-main-solo curl-verify**; mig applied [API-healthy=migrations-ran-startup]). **Prev S113** — admin **`acprMqfB`** · user **`Ck3BTDdA`** (S113 — **Run #497** `5fa11b5` Supplier import v2 [Mig 64 publish/draft + dedup-MST + template + file mẫu] FE 2-app real-src rotate; JS admin `Cixlt_MA`→`acprMqfB` / user `BhBOG1dn`→`Ck3BTDdA` + CSS admin `Ds-E5neu`→`CfunQHbd` / user `Beb0Bb1h`→`DGqqHQXS`; ship-proof real-size 1.66/1.56MB + LM-in-window + **byte-verify** `Tải file mẫu`×1/`Đã công bố`×3/`Nháp (ẩn)`×4/`import/template`×1/`published`×5 PRESENT; Mig 64 prod 37/37-backfill sys.tables **89**; smoke health/admin/eoffice 200 + `import/template`-401-live + `suppliers?published=true`-401-live bogus-404-control). **Prev S112** — admin **`Cixlt_MA`** · user **`BhBOG1dn`** (S112 — **Run #496** `e100ef0` Supplier Excel-import Phase B FE 2-app [`SupplierImportDialog` NEW cả fe-admin+fe-user] + PE sign-off code-only; **real fe-src rotate — kết thúc #489→#495 non-determinism-freeze**; JS admin `BVyH-FY7`→`Cixlt_MA` / user `EDsZebvn`→`BhBOG1dn` + CSS admin `Bbtesa6B`→`Ds-E5neu` / user `BmuPz_Dy`→`Beb0Bb1h`; ship-proof real-size + Last-Modified-in-window + `"Database NCC"` marker PRESENT ×1; Mig 63 prod 2/2-col sys.tables **89**; smoke health/admin/eoffice 200 + PE-endpoint 200 + import-route preview-415/confirm-401-live bogus-404). **Prev S107** — admin **`BVyH-FY7`** · user **`EDsZebvn`** (S107 — **Run #489** `21387cd` +office-document 12th sub governance-only; **rotate benign #69-two-mode** [0 fe-src — Vite non-determinism], CSS frozen `Bbtesa6B`/`BmuPz_Dy`, real-size 1.64/1.55MB + Last-Modified-in-window 09:32/09:33 = ship-proof, Mig 62/89 frozen, smoke 200×3; hoist @S107-closeout per H1 flag D-BUNDLE-lặp — ⚠️ closeout-push kế có non-md sẽ rotate tiếp → Run #490 record nằm ở cicd MEMORY [authoritative], hoist ở bundle đầu phiên sau per precedent S99-#485). **Prev S104** — admin **`ChSNAJBK`** · user **`Bn2w1znH`** (S104 — **Run #488** `778fc98` Mig 62 Supplier expand 30-field; real-src-rotate FE 2-app SHA-mirror [form 4-section + Status select + `PathLink` 3 NAS-link], **sys.tables 89 pre==post no-new-table**, prod-verified marker grep-HIT, smoke 200×3; S104-shipped nhưng dòng-live `:27` lag tới S106 → hoist per H1 flag D-BUNDLE). **Prev S97** — admin **`mEGqlNWc`** · user **`Dnrp4PU1`** (S97 — **Run #484** `fb2dfa9` Mig 61 data-fix backfill; bundle rebuild **#69-benign** [0 FE-src change, review-workflow cicd re-snapshot LIVE → `CeTA7Z82`/`CGaAyXen` #483 đã bị #484 rebuild rotate]; backfill A/005/A/008 flag 0→1 prod-verified). **Prev #483** — admin **`CeTA7Z82`** · user **`CGaAyXen`** (S97 — **Run #483** `9e12e80` badge "✅ Đã được CEO duyệt" phiếu đi-hết-lên-CEO [PeFinalizeChip single-source + 6 call-site 2-app, đối xứng chip "⚑ Kết thúc·X"]; FE-only tận-dụng endsBeforeCeo runtime, no BE/mig; JS rotate CSS-frozen #69; byte-#77 "Đã được CEO duyệt" PRESENT cả 2 app + prev-badge additive no-regression; Mig 60 frozen; smoke 200×3 + PE-list 200). **Prev #482** — admin **`DR9q2VUy`** · user **`Bgz8u4E5`** (S97 — **Run #482** `8037fa0` endsBeforeCeo config→RUNTIME [Mig 60 `EndedByLevelFinalize`]: **Mig 60 applied prod** (history-top + col-default-0 EXISTS + backfill=0 UAT-no-marker + sys.tables **89** pre==post); **EF-ternary SQL-Server LIVE-confirmed** GET /purchase-evaluations 200 items=9 + PE-detail `31faef0d` 200 **endsBeforeCeo=False phase=7** = runtime-path chạy = **bug fixed LIVE** (NOT 500); JS rotate 2-app PeWorkflowPanel gate-diff, CSS frozen #69-REFINE; byte-#77 `endsBeforeCeo` PRESENT in-bundle; smoke 200×3. reviewer PASS 0-issue + test 440. **Prev #481** — admin **`Cf0o8dx8`** · user **`x8rppRAK`** (S97 — **Run #481** `1c8fb6a` PE finalize 2-fix [badge "không trình tới"→"Kết thúc tại {cấp}" + đảo default checkbox opt-in]; JS rotate real-FE 2-app `PeWorkflowPanel` diff, CSS frozen `Bbtesa6B`/`BmuPz_Dy` #69-REFINE logic-only; **byte-level #77** NEW `Kết thúc tại`+helper PRESENT / OLD `không trình tới`+`Đã BỎ tick` ABSENT; real-size 1.64/1.54MB; Mig 59/88-table frozen; smoke 200×3. FE-only mirror SHA `bcf812fd`, 0 BE/mig/test). **Prev S96** — admin **`COYAX-yQ`** · user **`0MU-kb4N`** (S96 — **Run #480** `0c2f9f6` price-lock; chuỗi 3 PE deploy #478 `e657faa` checkbox-opt-out → #479 `36cbf8b` chuông-stale-clear (BE-only) → #480 `0c2f9f6` khóa-giá-sau-duyệt, cicd PASS each ~5m; **FE JS rotate THẬT** (PeWorkflowPanel #478 + PeDetailTabs #480, 2-app) real-size 1.64/1.54MB vs 919b SPA-trap, CSS frozen #69-REFINE; Mig 59 frozen 0-mig; +Run #477 `ba4b8e0` NCC seed no-mig prod 21 NCC. **Verify byte-level:** prod `0MU-kb4N` CHỨA checkbox-fix ("BỎ tick"/"Đã BỎ tick") + "✅-static" cũ MẤT → checkbox "ko-được" = browser-cache KHÔNG deploy-fail). **Prev S95** — admin **`0T6vVJST`** · user **`BGXEVZO6`** (S95 — **Run #476** `18a17f5` H17 governance-only deploy; bundle rotate benign #69 CSS-frozen, prod 200×3, Mig 59 unchanged; cicd re-snapshot LIVE — pre-deploy `CkZ1MjC1`/`_-gpX7_Y`, mega-line S93 `CDn40vpV`/`CoPmPBg7` đã bị deploy trung-gian rotate từ S94). **Prev S85** — admin **`aKuNip19`** · user **`BUsL0HeQ`** (S85 — **Run #341** `49ac086` lệch-giờ TZ serialize UTC-with-Z; FE frozen #339+#341 BE-only. Chuỗi ~10 deploy #334→#341 buổi UAT anh Kiệt CV PRO: #334 `5a61254` batch CV PRO 6-fix → #335-#338 6 vòng UAT → #339 `1263c0d` workflow-preserve bug-class S42 → #341 `49ac086` TZ; #342 `d4f6021` docs+cicd BE-frozen, bundle giữ-nguyên gotcha #69-REFINE). **Prev S78** — admin **`CsJetgZH`** · user **`BVS0ApIm`** (S78 — **Run #330** `7886fd0` PE attach-file khi duyệt; bundle ROTATE từ S77 `BqKD3Y23`/`Cn-i349D` dù NO migration enum-only — gotcha #69 non-determ rebuild; cicd ship-proof byte-stable + fake-hash control). **Prev S77** — admin **`BqKD3Y23`** · user **`Cn-i349D`** (S77 — **Run #329** `e823694` banner Trả-lại; cuối chuỗi 10 deploy #320→#329 cùng ngày, cicd PASS each: #320 pill `DsSg6RRz`-era → #321 focus → #322 revert → #323 Mig57+notes `CPm4LTqm`/`BWJUAqEI` → #324 số-âm-đỏ `C6fx-0ea`/`N3sW4Div` → #325 indent `BhnNMucS`/`B1VebpXc` → #326 cờ-gấp-asymmetric `DwXqn37C`/`COXMCv7E` → #327 decouple `B0gboSAg`/`DbDg7pM-` → #328 notify (BE-only, FE frozen) → #329 banner **`BqKD3Y23`**/**`Cn-i349D`**). **Prev S76** — admin **`jOqxW4-p`** · user **`DbsznVvR`** (S76 — **Run #319** `21d1f4e` bảng lưới `` ngân sách viền-ô; chuỗi S76 cùng ngày: #318 `e33481e` feature ma-trận-3-cột + badge quyền-NS `BhFDF9IJ`/`BAkuRl3C` → #319 **`jOqxW4-p`**/**`DbsznVvR`**, cicd PASS each, smoke 3×200, Mig 56 applied prod #318). **Prev S74** — admin **`BYF5vIMJ`** · user **`CB-tiRxd`** (S74 — **Run #315** `8655ebf` Mig 55 CcmNote, cicd PASS ~4m54s: Mig 55 applied prod CcmNote nvarchar(1000) nullable · sys.tables 88 · smoke 4×200 · 0 regression). **Prev S73** — admin **`Bv3jUCNo`** · user **`BWlMBQz6`** (Run #314 `6aa4dcb` FE empty-candidates guard fix; chuỗi 2 deploy: #313 `1d86abc` feature `OlNyG9OD`/`DSzSLVtL` → #314 **`Bv3jUCNo`**/**`BWlMBQz6`**, cicd PASS each). **Prev S72** — admin **`fc_xkNpJ`** · user **`DP-tBcg0`** (Run #312 governance commit `18fced6`; bundle ROTATE do **non-deterministic hash + deploy rebuild-FE-unconditional** (gotcha #69) — 0 FE source change, prod state/DB GIỮ NGUYÊN). **Prev S69** — admin **`BgNCjwsG`** · user **`CBvh0vtf`** (Run #308 PE cờ gấp + ngưỡng CCM; chuỗi 4 deploy session: #305 Văn phòng số foundation → #306 re-skin 10 page → #307 Office golive (BE-only) `Wt54PHYl`/`B99fMU6X` → #308 **`BgNCjwsG`**/**`CBvh0vtf`**). **Prev S68** — admin **`CNUv1jxY`** · user **`CpOskeS1`** (S68 — Run #304 `37752eb` fix tên đen→trắng; chuỗi #303 `6983609` `D532XZKG`/`CuFaBoWt` → #304 **`CNUv1jxY`**/**`CpOskeS1`**, cicd PASS each). **Prev S67** — admin `CcrZqfht`/user `DniDFUB_` (S67 — 6 deploy Run #297→#302; admin rotate chuỗi #298`xkSz9BfE`→#300`PxiZQkaw`→#301`I1fpLeYw`→#302**`CcrZqfht`** · user #297`BumgrwCJ`→#300`B36hGoKd`→#301`DrQYkzh0`→#302**`DniDFUB_`**; #299 tests BE-only cả 2 frozen, cicd PASS each). **Prev S65** — admin `BDwV5d0X`/user `DbVv6rsf` (Run #293-295). Prev S62 admin `0xKYGhhf`/user `C81ZdG9G` (Run #286). **Prev S59:** admin `B1DtNT9C` · user `D6uF3Mln` (Run **#282** `792c030` FINAL — bỏ nút Thêm hạng mục; ships kèm `80b64dd` gỡ Điều khoản TT, #281 cancelled-benign ancestor-verified). Chuỗi S59 cùng ngày: #280 `69997da` (`BKy_8OO9`/`XcZ6PRyA`, ships kèm `f21c55d` table-fixed #279-cancelled) · #278 `9c330d2` self-approve+quick-add-NCC (`BSh2fG2X`/`D22KfpPc`, authz probe 4/4) · #277 `faed59f` SearchableSelect (`ex7Tc92G`/`DzUeSk96`) · #276 `c869d26` rename 71 PMH (`BBA0KSWu`/`DzdTI18G`) · #275 `bbd1554` dọn demo WorkItems (FROZEN BE-only) · #274 `0eafcd3` tree 4 tầng (`DuU7OTym`/`DWyeTzf3`) · #273 `56882ac` wipe + tree v1 (`R9uGRxvw`/`DikfX1RD`). Prev S58: Run #386 `3ebaf84` admin `DMm9rtNA`/user `BUkOMn_Y` (chi tiết session log S58). +**Bundle hash live (prod):** admin **`B43R6Y17`** · user **`NLI5umBg`** — CSS **frozen** `BZ5F-g1w`/`yNZDmVNz` (logic-only fingerprint #69). **Đo LIVE bằng curl 2026-07-31 @S164** (`/tiep` window-2, vá mục 3). **Nguồn rotate = push `e5123ff`**, commit gắn nhãn `Docs: S162 closeout` **nhưng ship 6 file FE-src THẬT** (`PipelineStageFolders.tsx` · `PipelineTreePanel.tsx` · `usePipelineStages.ts` ×2 app) ⇒ rotate **real-src, KHÔNG phải #69-benign**; scope commit sai convention (đáng lẽ `FE-Admin`+`FE-User`, không phải `Docs`). 🔴 **Hại đo được — LỆCH 2 BỀ MẶT, không phải 1:** ô này đứng ở cặp S157 qua **2 đời deploy**, VÀ dòng `:6` ghi cặp S162 `DXoYDy7R`/`o8KDluD1` **chưa bao giờ là trạng-thái cuối** (snapshot chụp sau `bfc7b79`, trong khi `e5123ff` còn ship FE tiếp) ⇒ **cả 2 bề mặt owner đều lệch prod cùng lúc**. Đúng ca *"closeout-push kế có non-md sẽ rotate tiếp"* mà chính ô này đã tiên-liệu (precedent S99-#485 / S107) — tiên-liệu đúng, cơ-chế bắt vẫn không có. **Control-âm:** cặp S157 `8-QYcMOg`/`DptYR4wL` nay trả `text/html` **900 B** admin / **876 B** eoffice ⇒ bundle cũ đã chết. ⚠️ **CHƯA neo run-số CI** — lead chỉ curl 3 phép (2 hash live + control-âm), **không đọc Gitea Actions**; cần `cicd-monitor` xác nhận Run # + test-gate trước khi coi ô này đủ bằng-chứng. 🧊 *Mốc trước S157:* admin **`8-QYcMOg`** · user **`DptYR4wL`** (đo LIVE @S157 2026-07-28, **Run #419** `6250f39` — cicd PASS 9/9 + lead tự re-curl độc lập khớp từng byte, marker 2/2 mỗi app; **control-âm**: 2 hash CŨ `index-BK8w3QxU.js`/`index-DYnxSI0T.js` nay trả `text/html` **900 B admin / 876 B eoffice** ⇒ bundle cũ đã chết trên prod. 3 phép đo độc-lập: cicd-verify · lead re-curl · ring2-audit). 🔴 **Ô này từng đứng ở cặp `BK8w3QxU`/`DYnxSI0T` sau khi prod đã rotate** — `git show 3b6fabc -- docs/STATUS.md` chỉ có 2 hunk, **0 hunk chạm dòng này**; hại đo được: `cicd-verify-s157-diadiem.md:58` dùng chính ô này làm baseline ⇒ lượt deploy kế hoặc báo "ĐỔI" giả hoặc che một lần không-rotate (lead-stale-auditor FLAG-1 HIGH @S158). 🧊 *Mốc trước:* admin `BK8w3QxU` · user `DYnxSI0T` (S156 CI run 531 `f4494cf`; `Last-Modified` 14:41:15/14:42:10 +07 **nằm trong cửa-sổ deploy** — ship-proof, không phải hash-delta suông. Byte-verify 4/4 marker: `allowApproverDelete` 8/2 · `by-approver` 1/1 · `deleted=1` 1/1 · `view=approved` 1/1. ⚠️ **Không chứng minh hồi-tố** được bundle của riêng `4464a46` đã rotate lúc 14:12 — `f4494cf` đè lên sau 30 phút, không ai chụp snapshot khoảng giữa). 🧊 *Mốc trước:* admin `B2j1LQ-v` · user `C3wbJegk` (S147 CI #407 — re-stamp @S152: FLAG-2 lead-stale bắt ô này đứng S134 qua 5 nhãn trong khi `:6`/`:465`/HANDOFF đều S147; ring2 verify 2 lệnh độc-lập). 🧊 *Chuỗi rotate lịch-sử giữ nguyên:* (S134 — **`6664a61`** PE lũy kế TẠM TÍNH vs CHÍNH XÁC [0 mig — `ComputePendingAsync` complementary + DTO 5-field + FE sub-dòng amber + banner màn duyệt + D4 audit-note]; **Run #399** GO 5/5 5m27s gate-**528**-EXACT-từ-log rotate ×2 admin js `3NCdyODe`→**`BVCukFP5`** css `B--dUHcm`→**`CBzUlgOA`** / eoffice js `CKsoiNCI`→**`DoxyEOxh`** css `BuntB63r`→**`BHsBUA8e`** [FE-src đổi thật 6 file ⇒ rotate expected; CSS rotate = amber badge utilities, benign #69]; real-size 1.659.037/1.565.367B + LM 14:31:34/14:32:28+07 in-window; **byte-verify #77** `pendingPriorPes`=2 + `Tạm tính`=3 + `phiếu trước cùng gói thầu CHƯA duyệt`=2 PRESENT cả 2 js [control old-hash 900B text/html 0-hit — ct=text/html là discriminator sạch hơn size-919b]; Mig 67 top FROZEN + 89 bảng [0-mig by-construction]; smoke 4×200 + PE 401; lead spot-check độc-lập post-verdict: 4/4 hash khớp + marker PRESENT). **Prev S133** — admin **`3NCdyODe`** · user **`CKsoiNCI`** (S133 — **`d4fbced`** PE freeze NS phiếu DaDuyet [Mig 67 snapshot 11 cột + backfill 18/18 + FE 2-app badge 🔒 SHA-mirror `5534addb` + display-gate `budgetFrozen`]; **Run #398** GO 5/5 gate-520 rotate ×2 admin →**`3NCdyODe`** [CSS `B--dUHcm`] / user →**`CKsoiNCI`** [CSS `BuntB63r`] — prev `yCkX-2Bp`/`DKSD4p9k` @Run #397 S131-S132 [dòng-live lag S117→S133, hoist per precedent S104/S107]; **CSS cũng rotate** = badge sinh utility-class mới, benign #69; **byte-verify #77** `budgetFrozen`=1 + VN-string 🔒=1 PRESENT [old-control 0]; Mig 67 prod history-top + 11 cột + backfill 18/18 chua=0 + sys.tables **89**; smoke 4×200 + PE 401; `/tiep` S134 re-verify độc-lập 4/4: mig-top + health-200 + bundle-hash ×2 + filter 11/11). **Prev S117** — admin **`cFI7ih4a`** · user **`MxgooVZw`** (S117 — **`316a82f`** PE endsBeforeCeo pointer-aware [display-fix BE-only, phiếu chờ CEO hết báo "Kết thúc" sai] + duyệt-theo-khoảng Min–Max [Mig 66 + FE 2-app ProMinMax radio thứ 5]; **Run #387** PASS 5m36s rotate ×2 admin `Dd55jBpj`→`cFI7ih4a` / user `DVDr1UQ4`→`MxgooVZw` CSS-frozen [logic-only fingerprint]; **byte-verify #77** `ProMinMax`/`duyệt theo khoảng`/`cả Min và Max`/`Min–Max` PRESENT [bundle-cũ=0]; Mig 66 prod col decimal(18,2) + `__EFMigrationsHistory`-top + sys.tables **89**; smoke 200×3 · PE 401-secured; **#53-garble ×2** [invest-lane-1 + cicd-monitor] → SendMessage-resume + em-main curl cross-verify khớp). **Prev S116** — admin **`Dd55jBpj`** · user **`DVDr1UQ4`** (S116 — **`88368fd`** PE cho nhập giá ÂM ô báo giá [FE 2-app nút ± toggle + submit-gate `>0`→`!==0` + helper `hasRealSelectedQuote` 2-mirror `:201`/`:235`; BE submit-guard `:206` `Sum()<=0`→`All(==0)`; +9 test 486→495; **NO migration**]; **Run 386** PASS 5m28s rotate ×2 admin `D51OYyGV`→`Dd55jBpj` / user `BVdssm5S`→`DVDr1UQ4` CSS-frozen [logic-only fingerprint]; **byte-verify #77** `Đã nhập giá chào thầu`/`hoàn tiền NCC`/`phát sinh giảm`/`Đơn vị được chọn chưa có giá chào thầu` PRESENT [bundle-cũ=0]; smoke 200×3 · PE 401-secured · 89 bảng; cicd bắt bẫy snapshot-while-CI-running→poll-success TRƯỚC re-snapshot). **Prev S114** — admin **`D51OYyGV`** · user **`BVdssm5S`** (S114 — **`d436af2`** PE multi-NCC per hạng mục [Mig 65 backfill 2-chiều + BE re-key 5 financial→IsSelected + FE per-cell grid Detail×NCC 2-app SHA `ae3788e9`]; bundle rotate ×2 admin `acprMqfB`→`D51OYyGV` / user `Ck3BTDdA`→`BVdssm5S`; **byte-verify** `detailId`+`select-winner`+`hạng mục` PRESENT [bundle 1.65MB real-size]; smoke health/admin/eoffice 3/3 200 + endpoint POST select-winner bogus-401-live; **cicd garble #53 ×5 → em-main-solo curl-verify**; mig applied [API-healthy=migrations-ran-startup]). **Prev S113** — admin **`acprMqfB`** · user **`Ck3BTDdA`** (S113 — **Run #497** `5fa11b5` Supplier import v2 [Mig 64 publish/draft + dedup-MST + template + file mẫu] FE 2-app real-src rotate; JS admin `Cixlt_MA`→`acprMqfB` / user `BhBOG1dn`→`Ck3BTDdA` + CSS admin `Ds-E5neu`→`CfunQHbd` / user `Beb0Bb1h`→`DGqqHQXS`; ship-proof real-size 1.66/1.56MB + LM-in-window + **byte-verify** `Tải file mẫu`×1/`Đã công bố`×3/`Nháp (ẩn)`×4/`import/template`×1/`published`×5 PRESENT; Mig 64 prod 37/37-backfill sys.tables **89**; smoke health/admin/eoffice 200 + `import/template`-401-live + `suppliers?published=true`-401-live bogus-404-control). **Prev S112** — admin **`Cixlt_MA`** · user **`BhBOG1dn`** (S112 — **Run #496** `e100ef0` Supplier Excel-import Phase B FE 2-app [`SupplierImportDialog` NEW cả fe-admin+fe-user] + PE sign-off code-only; **real fe-src rotate — kết thúc #489→#495 non-determinism-freeze**; JS admin `BVyH-FY7`→`Cixlt_MA` / user `EDsZebvn`→`BhBOG1dn` + CSS admin `Bbtesa6B`→`Ds-E5neu` / user `BmuPz_Dy`→`Beb0Bb1h`; ship-proof real-size + Last-Modified-in-window + `"Database NCC"` marker PRESENT ×1; Mig 63 prod 2/2-col sys.tables **89**; smoke health/admin/eoffice 200 + PE-endpoint 200 + import-route preview-415/confirm-401-live bogus-404). **Prev S107** — admin **`BVyH-FY7`** · user **`EDsZebvn`** (S107 — **Run #489** `21387cd` +office-document 12th sub governance-only; **rotate benign #69-two-mode** [0 fe-src — Vite non-determinism], CSS frozen `Bbtesa6B`/`BmuPz_Dy`, real-size 1.64/1.55MB + Last-Modified-in-window 09:32/09:33 = ship-proof, Mig 62/89 frozen, smoke 200×3; hoist @S107-closeout per H1 flag D-BUNDLE-lặp — ⚠️ closeout-push kế có non-md sẽ rotate tiếp → Run #490 record nằm ở cicd MEMORY [authoritative], hoist ở bundle đầu phiên sau per precedent S99-#485). **Prev S104** — admin **`ChSNAJBK`** · user **`Bn2w1znH`** (S104 — **Run #488** `778fc98` Mig 62 Supplier expand 30-field; real-src-rotate FE 2-app SHA-mirror [form 4-section + Status select + `PathLink` 3 NAS-link], **sys.tables 89 pre==post no-new-table**, prod-verified marker grep-HIT, smoke 200×3; S104-shipped nhưng dòng-live `:27` lag tới S106 → hoist per H1 flag D-BUNDLE). **Prev S97** — admin **`mEGqlNWc`** · user **`Dnrp4PU1`** (S97 — **Run #484** `fb2dfa9` Mig 61 data-fix backfill; bundle rebuild **#69-benign** [0 FE-src change, review-workflow cicd re-snapshot LIVE → `CeTA7Z82`/`CGaAyXen` #483 đã bị #484 rebuild rotate]; backfill A/005/A/008 flag 0→1 prod-verified). **Prev #483** — admin **`CeTA7Z82`** · user **`CGaAyXen`** (S97 — **Run #483** `9e12e80` badge "✅ Đã được CEO duyệt" phiếu đi-hết-lên-CEO [PeFinalizeChip single-source + 6 call-site 2-app, đối xứng chip "⚑ Kết thúc·X"]; FE-only tận-dụng endsBeforeCeo runtime, no BE/mig; JS rotate CSS-frozen #69; byte-#77 "Đã được CEO duyệt" PRESENT cả 2 app + prev-badge additive no-regression; Mig 60 frozen; smoke 200×3 + PE-list 200). **Prev #482** — admin **`DR9q2VUy`** · user **`Bgz8u4E5`** (S97 — **Run #482** `8037fa0` endsBeforeCeo config→RUNTIME [Mig 60 `EndedByLevelFinalize`]: **Mig 60 applied prod** (history-top + col-default-0 EXISTS + backfill=0 UAT-no-marker + sys.tables **89** pre==post); **EF-ternary SQL-Server LIVE-confirmed** GET /purchase-evaluations 200 items=9 + PE-detail `31faef0d` 200 **endsBeforeCeo=False phase=7** = runtime-path chạy = **bug fixed LIVE** (NOT 500); JS rotate 2-app PeWorkflowPanel gate-diff, CSS frozen #69-REFINE; byte-#77 `endsBeforeCeo` PRESENT in-bundle; smoke 200×3. reviewer PASS 0-issue + test 440. **Prev #481** — admin **`Cf0o8dx8`** · user **`x8rppRAK`** (S97 — **Run #481** `1c8fb6a` PE finalize 2-fix [badge "không trình tới"→"Kết thúc tại {cấp}" + đảo default checkbox opt-in]; JS rotate real-FE 2-app `PeWorkflowPanel` diff, CSS frozen `Bbtesa6B`/`BmuPz_Dy` #69-REFINE logic-only; **byte-level #77** NEW `Kết thúc tại`+helper PRESENT / OLD `không trình tới`+`Đã BỎ tick` ABSENT; real-size 1.64/1.54MB; Mig 59/88-table frozen; smoke 200×3. FE-only mirror SHA `bcf812fd`, 0 BE/mig/test). **Prev S96** — admin **`COYAX-yQ`** · user **`0MU-kb4N`** (S96 — **Run #480** `0c2f9f6` price-lock; chuỗi 3 PE deploy #478 `e657faa` checkbox-opt-out → #479 `36cbf8b` chuông-stale-clear (BE-only) → #480 `0c2f9f6` khóa-giá-sau-duyệt, cicd PASS each ~5m; **FE JS rotate THẬT** (PeWorkflowPanel #478 + PeDetailTabs #480, 2-app) real-size 1.64/1.54MB vs 919b SPA-trap, CSS frozen #69-REFINE; Mig 59 frozen 0-mig; +Run #477 `ba4b8e0` NCC seed no-mig prod 21 NCC. **Verify byte-level:** prod `0MU-kb4N` CHỨA checkbox-fix ("BỎ tick"/"Đã BỎ tick") + "✅-static" cũ MẤT → checkbox "ko-được" = browser-cache KHÔNG deploy-fail). **Prev S95** — admin **`0T6vVJST`** · user **`BGXEVZO6`** (S95 — **Run #476** `18a17f5` H17 governance-only deploy; bundle rotate benign #69 CSS-frozen, prod 200×3, Mig 59 unchanged; cicd re-snapshot LIVE — pre-deploy `CkZ1MjC1`/`_-gpX7_Y`, mega-line S93 `CDn40vpV`/`CoPmPBg7` đã bị deploy trung-gian rotate từ S94). **Prev S85** — admin **`aKuNip19`** · user **`BUsL0HeQ`** (S85 — **Run #341** `49ac086` lệch-giờ TZ serialize UTC-with-Z; FE frozen #339+#341 BE-only. Chuỗi ~10 deploy #334→#341 buổi UAT anh Kiệt CV PRO: #334 `5a61254` batch CV PRO 6-fix → #335-#338 6 vòng UAT → #339 `1263c0d` workflow-preserve bug-class S42 → #341 `49ac086` TZ; #342 `d4f6021` docs+cicd BE-frozen, bundle giữ-nguyên gotcha #69-REFINE). **Prev S78** — admin **`CsJetgZH`** · user **`BVS0ApIm`** (S78 — **Run #330** `7886fd0` PE attach-file khi duyệt; bundle ROTATE từ S77 `BqKD3Y23`/`Cn-i349D` dù NO migration enum-only — gotcha #69 non-determ rebuild; cicd ship-proof byte-stable + fake-hash control). **Prev S77** — admin **`BqKD3Y23`** · user **`Cn-i349D`** (S77 — **Run #329** `e823694` banner Trả-lại; cuối chuỗi 10 deploy #320→#329 cùng ngày, cicd PASS each: #320 pill `DsSg6RRz`-era → #321 focus → #322 revert → #323 Mig57+notes `CPm4LTqm`/`BWJUAqEI` → #324 số-âm-đỏ `C6fx-0ea`/`N3sW4Div` → #325 indent `BhnNMucS`/`B1VebpXc` → #326 cờ-gấp-asymmetric `DwXqn37C`/`COXMCv7E` → #327 decouple `B0gboSAg`/`DbDg7pM-` → #328 notify (BE-only, FE frozen) → #329 banner **`BqKD3Y23`**/**`Cn-i349D`**). **Prev S76** — admin **`jOqxW4-p`** · user **`DbsznVvR`** (S76 — **Run #319** `21d1f4e` bảng lưới `
` ngân sách viền-ô; chuỗi S76 cùng ngày: #318 `e33481e` feature ma-trận-3-cột + badge quyền-NS `BhFDF9IJ`/`BAkuRl3C` → #319 **`jOqxW4-p`**/**`DbsznVvR`**, cicd PASS each, smoke 3×200, Mig 56 applied prod #318). **Prev S74** — admin **`BYF5vIMJ`** · user **`CB-tiRxd`** (S74 — **Run #315** `8655ebf` Mig 55 CcmNote, cicd PASS ~4m54s: Mig 55 applied prod CcmNote nvarchar(1000) nullable · sys.tables 88 · smoke 4×200 · 0 regression). **Prev S73** — admin **`Bv3jUCNo`** · user **`BWlMBQz6`** (Run #314 `6aa4dcb` FE empty-candidates guard fix; chuỗi 2 deploy: #313 `1d86abc` feature `OlNyG9OD`/`DSzSLVtL` → #314 **`Bv3jUCNo`**/**`BWlMBQz6`**, cicd PASS each). **Prev S72** — admin **`fc_xkNpJ`** · user **`DP-tBcg0`** (Run #312 governance commit `18fced6`; bundle ROTATE do **non-deterministic hash + deploy rebuild-FE-unconditional** (gotcha #69) — 0 FE source change, prod state/DB GIỮ NGUYÊN). **Prev S69** — admin **`BgNCjwsG`** · user **`CBvh0vtf`** (Run #308 PE cờ gấp + ngưỡng CCM; chuỗi 4 deploy session: #305 Văn phòng số foundation → #306 re-skin 10 page → #307 Office golive (BE-only) `Wt54PHYl`/`B99fMU6X` → #308 **`BgNCjwsG`**/**`CBvh0vtf`**). **Prev S68** — admin **`CNUv1jxY`** · user **`CpOskeS1`** (S68 — Run #304 `37752eb` fix tên đen→trắng; chuỗi #303 `6983609` `D532XZKG`/`CuFaBoWt` → #304 **`CNUv1jxY`**/**`CpOskeS1`**, cicd PASS each). **Prev S67** — admin `CcrZqfht`/user `DniDFUB_` (S67 — 6 deploy Run #297→#302; admin rotate chuỗi #298`xkSz9BfE`→#300`PxiZQkaw`→#301`I1fpLeYw`→#302**`CcrZqfht`** · user #297`BumgrwCJ`→#300`B36hGoKd`→#301`DrQYkzh0`→#302**`DniDFUB_`**; #299 tests BE-only cả 2 frozen, cicd PASS each). **Prev S65** — admin `BDwV5d0X`/user `DbVv6rsf` (Run #293-295). Prev S62 admin `0xKYGhhf`/user `C81ZdG9G` (Run #286). **Prev S59:** admin `B1DtNT9C` · user `D6uF3Mln` (Run **#282** `792c030` FINAL — bỏ nút Thêm hạng mục; ships kèm `80b64dd` gỡ Điều khoản TT, #281 cancelled-benign ancestor-verified). Chuỗi S59 cùng ngày: #280 `69997da` (`BKy_8OO9`/`XcZ6PRyA`, ships kèm `f21c55d` table-fixed #279-cancelled) · #278 `9c330d2` self-approve+quick-add-NCC (`BSh2fG2X`/`D22KfpPc`, authz probe 4/4) · #277 `faed59f` SearchableSelect (`ex7Tc92G`/`DzUeSk96`) · #276 `c869d26` rename 71 PMH (`BBA0KSWu`/`DzdTI18G`) · #275 `bbd1554` dọn demo WorkItems (FROZEN BE-only) · #274 `0eafcd3` tree 4 tầng (`DuU7OTym`/`DWyeTzf3`) · #273 `56882ac` wipe + tree v1 (`R9uGRxvw`/`DikfX1RD`). Prev S58: Run #386 `3ebaf84` admin `DMm9rtNA`/user `BUkOMn_Y` (chi tiết session log S58). **Phase:** ✅ Phase 10 COMPLETE · ✅ **Phase 11 product backlog ĐÓNG TRỌN** · ✅ **Văn phòng số (E-Office) golive S69** (foundation PURO + re-skin 10 page + public read+create 16-key allow-list mọi role) prod-verified · ✅ **PE cờ gấp PRO/CCM + CCM duyệt-final theo ngưỡng (Mig 53 S69)** prod-verified · ✅ **PE ngân sách MA TRẬN 3 cột Dự án/PRO/CCM (bảng lưới `
`) + badge quyền-NS theo role trong Workflow Designer & flow Duyệt NCC (Mig 56 S76)** prod-verified · ✅ **PE UX batch S77 (10 deploy)** — cờ GẤP pill mọi danh sách+inbox · focus/inline+overlay decouple+"Xem mở rộng" · ô ghi chú giá đề xuất PRO/CCM (Mig 57) · số phân cách + số-âm-đỏ-ngoặc + indent mục con · cờ gấp GẮN=NV/GỠ=Trưởng phòng · chuông báo người duyệt · banner Trả-lại — prod-verified · ✅ **PE đính kèm file khi DUYỆT (S78, Run #330)** — người duyệt tải file lên lúc duyệt thay vì Trả lại; migration-free reuse-enum `ApprovalAttachment=5` — prod-verified · 🚫 Phase 9 Ops blocked (anh main coordinate — anh dừng). > ⚠️ **Count drift fixed S40:** endpoints ~223→**211**, FE pages 53→**65**, menu keys 85→**~53**. Tables **84 confirmed correct** (DbSet 77 + Identity 7). 3 số "khó fake" (mig/gotcha/git) luôn đúng. Cause: số "incremented mỗi session" over/under-count optimistic — re-ground định kỳ. diff --git a/fe-admin/src/components/Layout.tsx b/fe-admin/src/components/Layout.tsx index bd87865..eb924a5 100644 --- a/fe-admin/src/components/Layout.tsx +++ b/fe-admin/src/components/Layout.tsx @@ -64,6 +64,10 @@ function resolvePath(key: string): string | null { CatalogMaterials: '/master/catalogs/materials', CatalogServices: '/master/catalogs/services', CatalogWorkItems: '/master/catalogs/work-items', + // [K1 — S164, Mig 70] Danh mục Hợp đồng SP-002 — "chỗ thứ 4" Pattern 16-bis: + // BE seed leaf `ContractCatalog` dưới group Catalogs, thiếu dòng này thì + // resolvePath trả null → MenuLeaf DROP IM LẶNG khỏi sidebar (gotcha #50). + ContractCatalog: '/master/catalogs/contract-catalog', PurchaseEvaluations: '/purchase-evaluations', PeWorkflows: '/system/pe-workflows', // [Phase 10.1 G-H1 S33 2026-05-26] Module Hồ sơ Nhân sự (Mig 34). LESSON diff --git a/fe-admin/src/lib/menuKeys.ts b/fe-admin/src/lib/menuKeys.ts index 30ea42e..3714bb6 100644 --- a/fe-admin/src/lib/menuKeys.ts +++ b/fe-admin/src/lib/menuKeys.ts @@ -11,6 +11,10 @@ export const MenuKeys = { CatalogMaterials: 'CatalogMaterials', CatalogServices: 'CatalogServices', CatalogWorkItems: 'CatalogWorkItems', + // [K1 — S164, Mig 70] Danh mục Hợp đồng SOL-CCM-SP-002 (86 dòng × 8 nhóm duyệt). + // Mirror BE MenuKeys.cs:28 — leaf thứ 5 dưới group `Catalogs`, page dùng chung + // CatalogsPage qua `:kind` = `contract-catalog`. + ContractCatalog: 'ContractCatalog', Contracts: 'Contracts', Forms: 'Forms', Reports: 'Reports', diff --git a/fe-admin/src/pages/master/CatalogsPage.tsx b/fe-admin/src/pages/master/CatalogsPage.tsx index 5bcdce2..07338c0 100644 --- a/fe-admin/src/pages/master/CatalogsPage.tsx +++ b/fe-admin/src/pages/master/CatalogsPage.tsx @@ -1,10 +1,15 @@ -// Generic master catalogs CRUD — 1 page handle 4 kind: units / materials / -// services / work-items. URL `/master/catalogs/:kind` driven, mỗi kind có -// fields config riêng. Sub-tabs hiển thị 4 kind trên cùng để chuyển nhanh. +// Generic master catalogs CRUD — 1 page handle 5 kind: units / materials / +// services / work-items / contract-catalog. URL `/master/catalogs/:kind` driven, +// mỗi kind có fields config riêng. Sub-tabs hiển thị 5 kind trên cùng để chuyển nhanh. +// +// [K1 — S164, Mig 70] kind thứ 5 `contract-catalog` = "Danh mục Hợp đồng" +// (SOL-CCM-SP-002, 86 dòng × 8 nhóm duyệt N1..N8). Endpoint BE +// `api/catalogs/contract-catalog` (ContractCatalogController) — cùng tiền tố +// `/catalogs/` nên tái dùng nguyên đường `/catalogs/${kind}` sẵn có, KHÔNG route mới. import { useState, type FormEvent } from 'react' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useParams, useNavigate } from 'react-router-dom' -import { Library, Pencil, Plus, Ruler, Package, Wrench, ListChecks, Trash2, Search } from 'lucide-react' +import { Library, Pencil, Plus, Ruler, Package, Wrench, ListChecks, FileSpreadsheet, Trash2, Search } from 'lucide-react' import { toast } from 'sonner' import { PageHeader } from '@/components/PageHeader' import { Button } from '@/components/ui/Button' @@ -16,9 +21,36 @@ import { api } from '@/lib/api' import { getErrorMessage } from '@/lib/apiError' import { cn } from '@/lib/cn' -type Kind = 'units' | 'materials' | 'services' | 'work-items' +type Kind = 'units' | 'materials' | 'services' | 'work-items' | 'contract-catalog' -type FieldDef = { key: string; label: string; type: 'text' | 'textarea' | 'checkbox'; required?: boolean; placeholder?: string } +type FieldDef = { + key: string + label: string + type: 'text' | 'textarea' | 'checkbox' | 'number' | 'select' + required?: boolean + placeholder?: string + options?: { value: number; label: string }[] + // Ép Number khi build payload — BE nhận `int` (ApprovalGroup / SortOrder), + // gửi chuỗi "3" thì System.Text.Json bind fail. + numeric?: boolean +} + +// 8 nhóm duyệt N1..N8 (SOL-CCM-SP-002 — mục "Nhóm duyệt (8)"). +const APPROVAL_GROUPS = [1, 2, 3, 4, 5, 6, 7, 8] as const +const APPROVAL_GROUP_OPTIONS = APPROVAL_GROUPS.map(n => ({ value: n, label: `N${n}` })) + +// Pattern 14 (Tailwind JIT): KHÔNG interpolate `bg-${x}-50` — class bị purge. +// Full class literal, cycle theo (nhóm - 1) % length. +const APPROVAL_GROUP_BADGE = [ + 'bg-sky-50 text-sky-700 border-sky-200', + 'bg-emerald-50 text-emerald-700 border-emerald-200', + 'bg-amber-50 text-amber-700 border-amber-200', + 'bg-violet-50 text-violet-700 border-violet-200', + 'bg-rose-50 text-rose-700 border-rose-200', + 'bg-teal-50 text-teal-700 border-teal-200', + 'bg-indigo-50 text-indigo-700 border-indigo-200', + 'bg-orange-50 text-orange-700 border-orange-200', +] as const const KIND_CONFIG: Record & { id: string; code: string; name: string } +type CatalogRow = Record & { id: string; code: string; name?: string } + +// Nhãn dòng dùng cho confirm xóa — kind `contract-catalog` không có `name`. +function rowLabel(row: CatalogRow): string { + return (row.name as string) || (row.tenVi as string) || row.code +} export function CatalogsPage() { const navigate = useNavigate() const params = useParams<{ kind?: string }>() const kind = (KINDS.includes(params.kind as Kind) ? params.kind : 'units') as Kind const config = KIND_CONFIG[kind] + const isContractCatalog = kind === 'contract-catalog' + const colCount = (isContractCatalog ? config.columns.length : kind === 'units' ? 2 : 5) + 1 const qc = useQueryClient() const [search, setSearch] = useState('') @@ -96,7 +155,10 @@ export function CatalogsPage() { const list = useQuery({ queryKey: ['catalogs', kind, search], - queryFn: async () => (await api.get(`/catalogs/${kind}`, { params: { q: search || undefined } })).data, + // kind-5: trang quản trị cần thấy CẢ dòng ngừng hiệu lực — BE List mặc định LOẠI (spec K1.c, gate-K1 F4) + queryFn: async () => (await api.get(`/catalogs/${kind}`, { + params: { q: search || undefined, includeInactive: isContractCatalog ? true : undefined }, + })).data, }) const save = useMutation({ @@ -125,7 +187,11 @@ export function CatalogsPage() { function openCreate() { const init: Record = {} - config.fields.forEach(f => { init[f.key] = f.type === 'checkbox' ? true : '' }) + config.fields.forEach(f => { + // select mặc định option đầu (nhóm duyệt N1) — tránh gửi rỗng → BE + // InclusiveBetween(1,8) reject. + init[f.key] = f.type === 'checkbox' ? true : f.type === 'select' ? (f.options?.[0]?.value ?? '') : '' + }) setForm(init) setOpen(true) } @@ -145,10 +211,14 @@ export function CatalogsPage() { Danh mục chi tiết } - description="Master data dùng cho phần Chi tiết HĐ — autocomplete khi user nhập line items." + description={ + isContractCatalog + ? 'Danh mục Hợp đồng (SOL-CCM-SP-002) — tên VN/EN, nhóm mục và nhóm duyệt N1–N8 dùng cho phiếu Kế hoạch ký kết.' + : 'Master data dùng cho phần Chi tiết HĐ — autocomplete khi user nhập line items.' + } /> - {/* Sub-tabs cho 4 kind */} + {/* Sub-tabs cho 5 kind */}
{KINDS.map(k => { const KIcon = KIND_CONFIG[k].icon @@ -191,35 +261,64 @@ export function CatalogsPage() {
- - - {kind !== 'units' && } - {kind !== 'units' && } - {kind !== 'units' && } + {isContractCatalog ? ( + config.columns.map(c => ) + ) : ( + <> + + + {kind !== 'units' && } + {kind !== 'units' && } + {kind !== 'units' && } + + )} {list.isLoading && ( - + )} {!list.isLoading && (list.data?.length ?? 0) === 0 && ( - + )} {list.data?.map(row => ( - - - {kind !== 'units' && } - {kind !== 'units' && } - {kind !== 'units' && ( - + + + + + + + + + ) : ( + <> + + + {kind !== 'units' && } + {kind !== 'units' && } + {kind !== 'units' && ( + )} - + )}
TênNhóm/LoạiĐVTTrạng thái{c}TênNhóm/LoạiĐVTTrạng thái
Đang tải…
Đang tải…
Chưa có dữ liệu — bấm Thêm để tạo mới.
Chưa có dữ liệu — bấm Thêm để tạo mới.
{row.code}{row.name}{(row.category as string) ?? '—'}{(row.defaultUnit as string) ?? '—'} - {(row.isActive as boolean) ? ( - Đang dùng - ) : ( - Tạm tắt + {isContractCatalog ? ( + <> + {row.code}{(row.tenVi as string) || '—'}{(row.tenEn as string) || '—'}{(row.groupCode as string) || '—'} + + {(row.signerRole as string) || '—'}{(row.initialsChain as string) || '—'} + {(row.isActive as boolean) ? ( + Đang dùng + ) : ( + Tạm tắt + )} + {row.code}{row.name}{(row.category as string) ?? '—'}{(row.defaultUnit as string) ?? '—'} + {(row.isActive as boolean) ? ( + Đang dùng + ) : ( + Tạm tắt + )} +
@@ -232,7 +331,7 @@ export function CatalogsPage() {