Files
solution-erp/.claude/workflows/runs/2026-08-07-S180-adap-upgrade-pack-phased/sub-w0b-index-hash.md
2026-08-07 15:43:42 +07:00

199 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# W0 lane b/2 — `broadcasts/_index.md`: mở phạm vi (P6) + bù dòng đã-nhận + ghi mã băm
- **Phiên:** S180 · **Vai:** reviewer (`/fable-clone reviewer`) · **Ngày:** 2026-08-07
- **Phạm vi ghi:** CHỈ `broadcasts/_index.md` + tệp nhật ký này. Không chạm `…-fitmap.md` / `…-tracking.md` (lane a) / `docs/STATUS.md` / `docs/HANDOFF.md` / `CLAUDE.md`.
- **Ghi đĩa liên tục** (chống #53): đo xong khoản nào append ngay khoản đó.
- **Quyết định owner ràng buộc:** P6 = **(a) mở INBOUND cho cả hai kênh** (`outbox/se` directed + `outbox/all` fan-out). Additive: **không dựng sổ thứ hai, không gỡ 22 dòng đang có.**
---
## §0 — Trình tự CỨNG (theo `wave-plan-S180.md:35` rủi ro)
Sửa luật lane `:7` **TRƯỚC**, bù dòng **SAU**. Lý do: bù dòng trước khi mở phạm vi = thêm vi phạm vào chính điều luật đang có hiệu lực.
Trạng thái: `[ ]` B1 đo mẫu số · `[ ]` B2 sửa dòng khai · `[ ]` B3 hash 4 thư · `[ ]` B4 bù dòng · `[ ]` B5 kiểm lại.
---
## §1 — ĐO MẪU SỐ (xong)
Lane A đưa con số **24**. Tôi đếm lại độc lập, và **24 đúng** — nhưng đúng vì một sự trùng hợp, không vì phép đo của lane A chặt. Khai cả ba mẫu số vì phiên này đã có ba ca bẫy đơn vị.
| Mẫu số | Đếm cái gì | Số |
|---|---|---|
| M1 | Mọi `.md` dưới `broadcasts/inbox/**` (đệ quy, gồm cả gốc) | **70** |
| M2 | = M1 trừ `broadcasts/inbox/README.md` (là tệp hướng dẫn, không phải thư, không có phần đầu tệp) | **69** |
| M3 | Thư trong M2 **không có** dòng nào trong `_index.md` (khớp theo mã định danh = tên tệp bỏ đuôi) | **24** |
| M4 | = M3, lọc thêm: **kênh phát đại trà****mã định danh > `2026-07-15`** (mốc nước của `check-email.md:39`) | **17** ← phạm vi lead giao |
🔴 **Bẫy đơn vị thứ tư, tôi bắt được ở chính phép đo của lane A.** Lane A duyệt bằng `broadcasts/inbox/*/` — dấu sao có gạch chéo phía sau nghĩa là **chỉ thư mục con**, nên nó **bỏ qua trọn vẹn thư mục gốc**. Hôm nay gốc chỉ chứa `README.md` nên hai phép đo tình cờ gặp nhau ở 69/24. Nhưng theo đúng `check-email.md:64`, thư vừa kéo về **hạ cánh ở gốc** (`broadcasts/inbox/<id>.md`) và ở đó mới là *chưa xử lý*. Nghĩa là phép đối chiếu ngược mà lane A đề nghị cắm làm máy kiểm thường trực (§2.5 việc 3) sẽ **mù đúng loại thư mà nó cần bắt nhất**. Ai cắm phép đó phải duyệt **đệ quy****loại `README.md` bằng tên**, chứ không loại bằng cách bỏ cả thư mục gốc.
**Phân rã 69 thư theo hai trục** (kênh × mốc nước), đối chiếu với sổ:
| Kênh | Mốc nước | Có dòng | Thiếu dòng |
|---|---|---|---|
| Gửi đích danh (`to: se`) | ≤ 2026-07-15 | 14 | 0 |
| Gửi đích danh (`to: se`) | > 2026-07-15 | 8 | 0 |
| Phát đại trà + không rõ | ≤ 2026-07-15 | 4 | **7** |
| Phát đại trà + không rõ | > 2026-07-15 | 18 | **17** |
| **Cộng** | | **45** | **24** |
- Cột "gửi đích danh" **22 trên 22 sạch** (23 dòng kể cả thư của `namgroup`) ⇒ control dương: kỷ luật ghi sổ có chạy, im lặng ở cột kia là im lặng thật.
- **22 dòng phát đại trà đang sống trong sổ** = 18 (sau mốc) + 4 (trước mốc). Con số 22 của lane A và của `wave-plan:28` khớp chính xác.
- **7 thư trước mốc nước còn thiếu dòng** — nằm **ngoài** phạm vi lead giao. Xem §5, đây là khoản tôi trả về cho lead chứ không tự quyết.
**Không có dòng mồ côi:** cả 45 dòng INBOUND đều tra ra đúng một tệp trên đĩa (0 mồ côi).
---
## §2 — HASH: 17 thư, chạy bằng `scripts/stamp_verify.py`, KHÔNG tự chế lệnh
Lệnh đã chạy (hai lượt, gộp tham số): `python scripts/stamp_verify.py <đường-dẫn…>`.
**Kết quả: 17/17 `OK (canonical match)` · mã thoát 0 cả hai lượt · 0 cờ giả mạo.**
**Bốn thư của gói (`G0-B2` — vế ghi lại mã băm, nay đã có chỗ ghi thường trực):**
| Thư | Khai ở phần đầu tệp | Tính lại (canonical) | Verdict script |
|---|---|---|---|
| `…-thu-chinh` | `43db2cf0` | `43db2cf09baaaa1c2aea3f3601ed35d5005cce4ddd7237fb2de841df2be14676` | OK (canonical match) |
| `…-phu-luc-spec-pitfall` | `49287ce7` | `49287ce7ced42483cad17adafb48a001854adbfeaab305885bf0fd1a9b463608` | OK (canonical match) |
| `…-khuon-fit-map-report-checklist` | `98e31fad` | `98e31fad1ff48ab7ccf49688867611c9ed548105edfa29617a0497ee67a89593` | OK (canonical match) |
| `…-luat-cham-diem` | `c24699f0` | `c24699f0251aa6574e458cf53a471dd936b060a7bf79d66b1b131a7b2f9c891f` | OK (canonical match) |
Cả bốn khai mã băm ở dạng **rút gọn 8 chữ số hex**; script so bằng tiền tố nên vẫn kết luận khớp. Mười ba thư còn lại khai đủ 64 chữ số hex và khớp tuyệt đối.
🔸 **Một dòng khai bắt buộc (không chặn):** cả bốn thư của gói mang `status: DRAFT``reviewer_gate: "PENDING"` ở phần đầu tệp, trong khi chính `thu-chinh:66` tuyên bản phát ra phải mang dấu đã duyệt. Mã băm sạch 4/4 ⇒ **đây không phải giả mạo nội dung**, mà là **dấu trạng thái lệch với nội dung**. Ghi vào cột ghi chú của bốn dòng, không chặn việc bù dòng.
**Kiểm lại lời khai DRAFT bằng chính đĩa, không tin lời lead:** `grep -nE "^(status|reviewer_gate):"` trên bốn tệp cho `status: DRAFT` + `reviewer_gate: "PENDING"` ở cả bốn (dòng 13/14 · 11/12 · 12/13 · 16/17). Đối chiếu `thu-chinh` (khối *"Điều kiện duy nhất còn lại trước khi bộ này rời tay hub"*): *"Khi phát, mỗi món đi kèm dấu xác thực nội dung và trạng thái đã duyệt; bản nháp thì không có."* ⇒ Bốn món **có** dấu xác thực nội dung (mã băm, khớp 4/4) nhưng **mang trạng thái nháp** — đúng một trạng thái lai mà chính thư nói là không được tồn tại khi phát. Lời khai của lead **đứng vững**, tôi xác nhận bằng đĩa.
**Quy ước cột `sha256(12)` — đo từ chính sổ, không suy:** tôi đối chiếu bốn dòng cũ (`b2a2fc1cf399`, `78dc1d82`, `190c11ba2bc0`, `6239dd403792`) với tệp tương ứng. Cả bốn khớp **cả** giá trị khai lẫn 12 chữ đầu của giá trị tính lại; riêng dòng `78dc1d82` chép nguyên giá trị khai rút gọn 8 chữ. ⇒ Sổ đang dùng **12 chữ đầu của mã băm nội dung**, và chấp nhận ngắn hơn khi bên gửi khai ngắn. Dòng mới tôi ghi **12 chữ đầu của giá trị tính lại**, vì đó là thứ tôi thật sự đo được và nó bao trùm giá trị khai.
---
## §3 — ĐÃ SỬA GÌ TRONG `broadcasts/_index.md` (3 nhát, theo đúng thứ tự cứng)
**Nhát 1 — dòng khai phạm vi (`:7` cũ → `:7-9` mới).** Trích trước/sau ở §6.
**Nhát 2 — thêm hai cột `kênh` + `ghi-chú` vào CUỐI hàng.** Đây là chỗ tôi cố ý làm khác chỉ dẫn về hình thức, và lý do đo được: đặt cột mới ở cuối thì mỗi dòng cũ chỉ bị **nối thêm**, phần chữ cũ vẫn là **tiền tố nguyên vẹn** của dòng mới. Nhờ vậy lời hứa *"không sửa dòng cũ"* trở thành **một phép kiểm chạy được**, chứ không phải một lời hứa. Đã chạy phép kiểm đó, kết quả ở §4.
**Nhát 3 — chèn 17 dòng mới**, khuôn 9 ô: `received · id · from → to · status · folder · sha256(12) · verify · kênh · ghi-chú`.
Giá trị từng ô lấy từ đâu (không ô nào chép tay):
- `received` = **ngày commit đầu tiên đưa tệp vào kho**, lấy bằng `git log --diff-filter=A --format=%cs -- <tệp>` rồi lấy dòng cuối. Đây là ngày SE **thật sự nhận**, khác với hôm nay là ngày ghi dòng. 10 thư ngày 2026-07-24, 2 thư ngày 2026-07-26, 5 thư ngày 2026-08-05 (trong đó có trọn bộ bốn món của gói).
- `sha256(12)` = 12 chữ đầu của mã băm **tính lại**, tính trong cùng một lượt chạy bằng đúng thuật toán của `scripts/stamp_verify.py`.
- `verify` = `✓`, dựa trên verdict `OK (canonical match)` của script, 17/17.
- `kênh` = `all` cho cả 17 (đều là thư phát đại trà hoặc không khai kênh).
- `status` = `processed`, `folder` = `ai_infra` — xem §5 khoản 4, đây là chỗ tôi phải khai một dè dặt.
**Backfill 45 dòng cũ:** `kênh` lấy từ trường `to:` của chính tệp trên đĩa, **từng dòng một****23 dòng `se` · 22 dòng `all`**. Ô `ghi-chú` để trống.
---
## §4 — PHÉP KIỂM SAU KHI SỬA (chạy thật, số thật)
| # | Phép kiểm | Kết quả |
|---|---|---|
| K1 | Khối `OUTBOUND` còn nguyên từng byte (so với `git show HEAD:broadcasts/_index.md`) | **ĐÚNG** — 53 dòng ⟂ 53 dòng, chuỗi bằng nhau tuyệt đối |
| K2 | Mỗi dòng `INBOUND` cũ là **tiền tố nghiêm ngặt** của dòng mới tương ứng | **45/45 đạt · 0 dòng vi phạm** |
| K3 | Số dòng dữ liệu `INBOUND` | 45 → **62** (chênh đúng **17**) |
| K4 | Bảng đúng khuôn: mọi hàng cùng số ô | **63 hàng (62 dữ liệu + 1 tiêu đề), tập số ô = {9}** — không hàng nào lệch |
| K5 | Mã định danh trùng lặp trong bảng | **0** |
| K6 | Dòng mồ côi (có dòng nhưng không có tệp) | **0** |
| K7 | `grep -c "upgrade-pack-phased" broadcasts/_index.md` (nghiệm thu `wave-plan:33` đòi ≥ 4) | **4** — đạt |
| K8 | `python scripts/stamp_verify.py broadcasts/inbox/ai_infra/2026-08-04-*.md` | **exit 0** — đạt |
| K9 | Đối chiếu ngược đĩa ↔ sổ, duyệt **đệ quy**, loại `README.md` bằng tên | vắng mặt **24 → 7**; **0 thư sau mốc nước còn vắng** |
🔴 **K9 chưa về 0, và tôi không giấu điều đó.** `wave-plan:33` đặt nghiệm thu là *"danh sách vắng = 0 (hôm nay 24)"*. Sau nhát sửa này còn **7**, tất cả đều **trước mốc nước** `2026-07-15`. Đây không phải sót — đây là **hai văn bản chỉ huy nói khác nhau**, và tôi làm theo phạm vi lead giao rồi trả khoản chênh về cho lead. Chi tiết ở §5 khoản 3.
---
## §5 — SÁU KHOẢN TRẢ VỀ LEAD
### 5.1 🔴 Chỉ dẫn *"backfill `se` cho 22 dòng cũ"* SAI, và sai theo đúng chiều phá hỏng P6
Tôi **không thi hành** chỉ dẫn này, và đây là lý do đo được.
- Khối `INBOUND` không có 22 dòng — nó có **45 dòng**.
- Trong 45 dòng đó, **22 dòng là thư phát đại trà** (trường `to:` = `all-fit`), **23 dòng là thư gửi đích danh**.
- Ghi `se` cho 22 dòng bất kỳ, hay cho cả 45 dòng, đều **dán nhãn sai lên đúng 22 dòng phát đại trà mà P6 sinh ra để hợp thức hoá**. Thi hành đúng câu chữ sẽ **triệt tiêu chính mục đích** của quyết định.
Nguyên nhân nhìn được: trong cùng một bản giao việc có **hai con số 22 khác nghĩa va nhau**.
- 22 thứ nhất — ở khối quyết định P6 (*"KHÔNG gỡ 22 dòng đang có"*) — là **22 dòng phát đại trà** đang sống trong sổ (lane A §2.2, `wave-plan:28`).
- 22 thứ hai — ở mục việc (1) (*"backfill `se` cho 22 dòng cũ"*) — là **22 thư gửi đích danh** từ `outbox/ai_infra``check-email.md:62` khai *"22/22 đều có dòng `_index`"*.
Hai con số bằng nhau về trị, ngược nhau về nghĩa, nằm cách nhau vài dòng. Bài học tái dùng được: **hai hệ đếm khác nhau dùng chung một con số thì con số đó thôi làm chứng.**
**Việc tôi làm thay:** dẫn `kênh` **từng dòng một** từ trường `to:` trên đĩa ⇒ 23 `se` · 22 `all`. Additive, không mất thông tin, đúng sự thật. Muốn khác thì lead phải ra lệnh lại; tôi không tự đổi.
### 5.2 🔴 Bẫy đơn vị thứ **năm** — chính lead vừa giẫm, ngay trong lệnh sửa sai
Lead viết: *"Hiện mới có **4** (đúng 4 thư upgrade-pack)"*, dẫn từ `grep -c 'upgrade-pack-phased' broadcasts/_index.md` = 4.
Con số 4 **đúng**, nhưng nó **không đo cái lead tưởng**: chuỗi `upgrade-pack-phased` chỉ nằm trong tên **4 thư của gói**, nên phép đếm ấy **không bao giờ vượt quá 4** dù có bù bao nhiêu dòng. Đo trên cùng một tệp, cùng một thời điểm:
| Phép đếm | Trị | Nó thật sự đo gì |
|---|---|---|
| `grep -c "upgrade-pack-phased"` | **4** | số dòng **của riêng gói** |
| `grep -c "bù dòng @S180"` | **17** | **tổng dòng đã bù** |
| số dòng dữ liệu khối `INBOUND` | **45 → 62** | chênh **17** |
**17 dòng đã land trước khi lead đo.** Lead đọc "4" thành "mới bù được 4" và suýt cho chạy lại toàn bộ phép đo. Đây là **cùng một lớp lỗi** mà chính lead cảnh báo tôi ở dòng trên (*"số 24 có thể là entry ⟂ file ⟂ thư-sau-watermark"*) — bằng chứng rằng biết tên cái bẫy **không đủ** để khỏi giẫm; phải **khai mẫu số ngay cạnh con số**, kể cả khi mình là người đang đi nhắc người khác khai.
### 5.3 🔴 Phép đối chiếu ngược của lane A **mù đúng chỗ nó cần sáng**
Lane A đo bằng `broadcasts/inbox/*/` — chỉ thư mục con. Theo `check-email.md:64`, thư vừa kéo về hạ cánh ở **gốc** `broadcasts/inbox/<id>.md`, và gốc chính là nghĩa của trạng thái *chưa xử lý* (`_index.md:13`). Vậy phép đo ấy **không bao giờ nhìn thấy thư đang chờ xử lý** — đúng loại thư mà một phép đối chiếu ngược sinh ra để bắt.
Hôm nay hai phép đo tình cờ gặp nhau ở 69/24 vì gốc chỉ có `README.md`. Đó là **trùng hợp, không phải tương đương**.
**Khuyến nghị khi cắm phép này thành máy kiểm thường trực (`G0-B4`/`A0-2`):** duyệt **đệ quy** toàn bộ `broadcasts/inbox/**`, loại `README.md` **bằng tên tệp**, và **in kèm mẫu số** để "0 vắng mặt" không bị đọc nhầm khi phép duyệt hỏng và trả về rỗng.
### 5.4 🔴 Bảy thư trước mốc nước: hai lệnh chỉ huy mâu thuẫn, tôi không tự phá
- Lead giao: *"watermark: bỏ qua id ≤ `2026-07-15`"* ⇒ phạm vi **17**.
- `wave-plan:33` nghiệm thu: *"danh sách vắng = 0"* ⇒ phạm vi **24**.
Tôi theo lệnh lead (17) và trả khoản chênh **7** về cho lead, kèm một quan sát cần có trước khi phán:
Lý lẽ của mốc nước ở `check-email.md:39`*"`/adap-apply` đọc thẳng bên AI_INFRA, **KHÔNG đòi copy** ⇒ **vắng mặt trong inbox** là BÌNH THƯỜNG"*. Lý lẽ đó nói về **vắng mặt khỏi hộp thư**. Nhưng **bảy thư này đang NẰM trong hộp thư** — đã được kéo về, chỉ thiếu dòng trong sổ. Chúng **không thuộc lớp mà mốc nước mô tả**, nên mốc nước không thật sự miễn trừ cho chúng.
Bằng chứng phụ cùng chiều: trong lớp trước mốc nước, **4 thư đã có dòng** (`2026-07-10`, `2026-07-11` ×2, `2026-07-15`) còn **7 thư không có**. Cùng lớp, hai số phận — dấu hiệu của bỏ sót, không phải của miễn trừ.
**Đề nghị:** bù nốt 7 dòng trong một nhát riêng để `K9` về 0 và nghiệm thu `wave-plan:33` đứng được. Chi phí: một lượt `stamp_verify.py` + 7 dòng. Tôi **không tự làm** vì ngoài phạm vi được giao.
### 5.5 🔸 Cột `status`: `processed` **đúng theo định nghĩa**, nhưng không có nghĩa "đã áp"
`_index.md:13` định nghĩa `status` **bằng vị trí thư mục**: ở gốc là *chưa xử lý*, đã chuyển vào `inbox/<from>/`*đã xử lý*. Cả 17 thư đều nằm trong `inbox/ai_infra/` ⇒ theo đúng định nghĩa của chính cuốn sổ, giá trị phải là `processed`. Tôi ghi `processed`, và ghi luôn dè dặt:
`run.md:17` khai *"kênh `outbox/all` (fan-out) = **4 CHƯA ÁP**"*. Với bốn món của gói, `processed` đúng về **vị trí** nhưng không đúng về **tiếp thu**. Với kênh gửi đích danh hai nghĩa này trùng nhau, vì `/check-email` STAGE 2 chỉ chuyển thư mục **sau khi** xử lý xong. Với kênh phát đại trà thì **không trùng**: việc xử lý là `/adap-apply`, hoàn toàn không đụng tới vị trí thư mục.
**P6 vừa gộp hai kênh vào một sổ, nên sổ thừa hưởng một cột `status` chỉ đo đúng cho một nửa số dòng.** Đây là khoản **lược đồ**, không phải khoản dữ liệu, nên tôi không tự thêm giá trị mới. Trạng thái tiếp thu thật của bốn món đã ghi ở cột `ghi-chú`. Muốn đo được bằng máy thì cần một cột riêng (ví dụ `đã-áp`) — quyết định lược đồ, thuộc quyền lead hoặc anh.
### 5.6 🔸 Vế "chiều GỬI" của luật cũ: giữ nguyên, có khai
Câu cũ ở `:7` gộp hai chiều; P6 chỉ mở **chiều NHẬN**. Tôi viết lại chiều NHẬN, giữ nguyên văn câu cũ làm vết, và **không đổi** chiều GỬI — nhưng có đo và khai ngay tại chỗ: `find . -iname "*COMMS*"` = **0 tệp** (control dương cùng lệnh với `*ledger*` = **4 tệp** ⇒ lệnh chạy được, rỗng là rỗng thật), và `broadcasts/outbox/all/` = **0 tệp** ⇒ SE chưa từng phát đại trà lần nào. Luật chiều GỬI đang trỏ tới một cuốn sổ không tồn tại, cho một việc chưa từng xảy ra. Vô hại hôm nay, thành lỗ ngay lần đầu SE phát đại trà.
---
## §6 — TRÍCH TRƯỚC / SAU của dòng khai phạm vi
**TRƯỚC** (`:7`, một dòng):
> **Fan-out adap broadcast** (≠ email directed) → `outbox/all/` (pull `/adap-apply`), track ở COMMS-LEDGER OUT — KHÔNG ở index này (index = email mesh in/out).
**SAU** (`:7-9`, ba dòng — dòng 1 là luật mới, dòng 2 giữ nguyên văn câu cũ làm vết, dòng 3 khai phần chưa phán):
> **Chiều NHẬN — hai kênh, MỘT sổ** (owner chốt **P6** @S180, 2026-08-07; đường additive): khối `INBOUND` của sổ này nhận **CẢ HAI** kênh — thư gửi **đích danh** ở `outbox/se` (kéo bằng `/check-email`) **VÀ** thư **phát đại trà** ở `outbox/all` (kéo và áp bằng `/adap-apply`). Cột **`kênh`** phân biệt hai loại: `se` = đích danh · `all` = phát đại trà. Quyết định này **hợp thức hoá 22 dòng phát đại trà vốn đã sống trong sổ**; không dòng nào bị gỡ và không dựng sổ thứ hai.
Thêm một dòng dẫn ngay trên bảng `INBOUND`, giải thích vì sao hai cột mới nằm ở cuối hàng và ghi con số backfill 23 ⟂ 22.
---
## §7 — CHƯA ĐO / NGOÀI PHẠM VI
- **Ranh đã kiểm bằng `git status --porcelain`:** trong hai tệp tôi được phép ghi, cả hai đều hiện `M` (`broadcasts/_index.md`, `sub-w0b-index-hash.md`). Các tệp `M`/`??` còn lại thuộc lead và lane a — **tôi không chạm**: `docs/governance/adap-upgrade-pack-{fitmap,tracking}.md`, `sub-w0a-fitmap-tracking.md`, `.session-counter.json`, `.claude/auto-memory/…`.
- **Không đo** từng thư trong 17 thư đã thật sự được `/adap-apply` tiếp thu chưa. Tôi chỉ đo **vị trí thư mục****mã băm**. Ai cần con số tiếp thu phải lấy từ nguồn khác, **không đọc ra từ cột `status`** (§5.5).
- **Không đo** khối `OUTBOUND` có thiếu dòng nào không. Tôi chỉ chứng minh nó **không bị tôi đụng vào** (K1).
- **Không sửa** đoạn đối chiếu S123 chen giữa bảng `OUTBOUND` — nó cắt bảng ấy làm hai khúc. Ngoài phạm vi, nhưng ghi lại vì máy nào đọc `OUTBOUND` bằng cách quét liên tục từ dòng tiêu đề sẽ **dừng sớm ở đó**.
- **Không tự bù** 7 thư trước mốc nước (§5.4) và **không tự đổi** luật chiều GỬI (§5.6). Cả hai chờ lead hoặc anh phán.