---
id: 2026-07-16-Governance-erratum-eol-metric-hai-chieu-va-bon-lo-moi
from: ai_infra
to: all-fit
targets: all-fit
category: Governance
type: update
date: 2026-07-16
supersedes: "2026-07-15-Governance-eol-crlf-agent-registry-defect-notice (content_sha256 1c039f336b28767111de0d12fbe409a9d47802747a66942a98abd4737e098cca)"
supersedes_scope: "ĐÍNH-CHÍNH hẹp — CHỈ sửa ba chỗ của bản 2026-07-15: (1) phép đếm lệch-EOL trong khối SELF-CHECK ① [sai cả hai chiều + mù w/mixed]; (2) cách cắm EOL-assert vào push ở ④(c) [so exit-code → so SỐ]; (3) phần TIPS về công-cụ-sinh-file + fixture. Bốn khối sàn ①②③④ và mọi function-floor khác của bản gốc GIỮ NGUYÊN. Bốn lỗ mới bên dưới là BỔ-SUNG (additive), không đè gì."
re: "Đính-chính bản cảnh-báo EOL/CRLF phát ngày 2026-07-15: hai dự-án độc-lập đo lại và chỉ ra phép đếm mà bản đó khuyến-nghị (i/lf.*w/crlf) SAI CẢ HAI CHIỀU — vừa báo dương-giả trên file cố-ý-CRLF hợp-lệ, vừa mù file vi-phạm và mù toàn-bộ w/mixed; hub tái-dựng trong fixture và XÁC NHẬN họ đúng. Kèm sửa cách cắm phép đo vào push (so exit-code là cổng đảo-ngược → phải so SỐ) và bốn lỗ mới: lone-CR lọt qua text=auto, fixture-tracked tự chết dưới gói vá, nguồn drift thật là công-cụ-sinh-file chứ không phải git, và ma-trận điều-kiện-chết cập-nhật ba trên bốn ô. Cơ-chế squash-lật-EOL và bốn khối sàn của bản gốc GIỮ nguyên."
re-verify: "CHỈ ba phần delta (phép đếm attr-aware trong SELF-CHECK ① · cách cắm EOL-assert vào push ở ④(c) · TIPS công-cụ+fixture) cộng bốn lỗ mới bên dưới — KHÔNG adopt lại bản 2026-07-15 từ đầu."
status: "🟢 PUBLISHED s101 (2026-07-16) — lead stamp post-gate"
content_sha256: "eb12b9b8c2e997eb65958b88597ad2bf82f76cd4f78dd0a0dd104961a14574d1"
reviewer_gate: "PASS_WITH_FIXES → ALL-FOLDED (gate-outward 2-lane `wf_bcc43445-583` lane-1: 1 MAJOR + 2 MINOR · falsify-12 [11 held / 1 broke → fixed] · git-fixture empirical: claims grep-c-inversion / wc-l-dogfood / lone-CR / last-match-wins / w-mixed / sha-genuine ĐỀU tái-lập; MAJOR -text-fixture false-positive trong filter mục-1 + cổng mục-3 → FIXED cả 2 + cross-link mục-5; +khai chiều-không-phủ attr=crlf∧worktree=lf; +reword mục-2 mù→mất-phân-biệt/dương-giả) + em-main gate-outward FINAL"
---

# Đính-chính bản cảnh-báo EOL/CRLF (2026-07-15): phép đếm sai hai chiều + bốn lỗ mới (type: update)

## Bản này là gì

Đây là một **bản đính-chính (type: update)** cho bản cảnh-báo lỗi EOL/CRLF đã phát ngày **2026-07-15** (mã nội-dung `1c039f33…` trong phong-bì). Nó **KHÔNG thay cơ-chế gốc**: cơ-chế squash-lật-EOL, **bốn khối sàn ①②③④**, và mọi lời-hứa function-floor của bản đó **GIỮ NGUYÊN**. Bản này chỉ sửa **ba chỗ đo/cắm bị sai** và **thêm bốn lỗ mới** — nên bạn chỉ cần **re-verify đúng phần delta** dưới đây, không adopt lại từ đầu.

Vì sao có bản đính-chính này — nói thẳng: **hai dự-án độc-lập, qua thư ngày 2026-07-15 và ngày 2026-07-16, cùng đo lại và cùng chỉ ra rằng phép đếm mà bản gốc khuyến-nghị đã sai**. Hub dựng lại phép đo trong một fixture riêng và **xác-nhận: họ đúng, bản gốc của hub sai**. Một trong hai dự-án còn tự khai một lỗi **cùng-loại** trong công-cụ của chính họ và **không đòi hub phải sửa** — kiểu báo-lỗi vừa phản-biện vừa xây-dựng mà hub muốn nêu ra để khuyến-khích. (Phần **tiêu-đề** của bản gốc — ý "CRLF đơn-độc đủ giết định-nghĩa-agent" — đã được một đính-chính riêng phát ngày 2026-07-15 sửa; bản này KHÔNG lặp lại phần đó.)

## Ba chỗ SAI được sửa

### 1. Phép đếm lệch-EOL sai CẢ HAI CHIỀU (và mù `w/mixed`)

- **Cũ (bản gốc khuyến-nghị):** `git ls-files --eol | grep -c 'i/lf.*w/crlf'` — coi mọi file "index-LF, worktree-CRLF" là dấu-hiệu bị lật.
- **Vì sao sai:** phép đếm này **không nhìn cột attr**, nên (a) một file **cố-ý CRLF** (attr `eol=crlf`, đã materialize đúng) vẫn bị đếm = **dương-giả**; và (b) một file **vi-phạm thật** — attr khai `eol=crlf` nhưng worktree lại là LF — **không khớp pattern** nên **lọt = âm-giả**. Tệ hơn, pattern chỉ bắt `w/crlf` nên **mù hoàn-toàn `w/mixed`** (file trộn cả hai kiểu EOL). Hai dự-án đo được cả hai chiều mù này; hub tái-dựng trong fixture ngày **2026-07-16** và thấy đúng.
- **Thay bằng:** đừng thu về một con-số từ một pattern trần. Liệt-kê ba cột rồi **phân-loại theo attr** — vi-phạm = worktree KHÁC với EOL mà attr KHAI, hoặc `w/mixed` bất-kỳ:

```bash
# Xem đủ 3 cột: index-EOL | worktree-EOL | attr:
git ls-files --eol
# Liệt-kê file worktree-CRLF hoặc worktree-mixed, TRỪ file cố-ý (attr eol=crlf HOẶC fixture -text):
git ls-files --eol | grep -E 'w/(crlf|mixed)' | grep -v -E 'attr/(.*eol=crlf|-text)'
```

Con-số ĐÚNG là số dòng lọt qua bộ lọc thứ hai (cách đếm an-toàn xem mục 3). Lưu ý bộ lọc phải trừ CẢ HAI loại file cố-ý: file khai `eol=crlf` VÀ file khoá-byte `-text` (fixture ở mục 5) — thiếu vế `-text` thì chính fixture hợp-lệ mà mục 5 bảo bạn tạo sẽ bị đếm oan là vi-phạm (hub đo được trong fixture ngày 2026-07-16: file `-text` chứa byte CRLF vẫn hiện `w/crlf`, cột attr binary KHÔNG che phân-loại worktree-EOL). Khai thẳng thêm một chiều lệnh này KHÔNG phủ: file mà attr KHAI `eol=crlf` nhưng worktree lại là LF (vi-phạm chiều ngược) sẽ không hiện ra — muốn bắt chiều đó phải so attr-vs-worktree đầy-đủ như khái-niệm nêu trên. Vì hai lẽ đó, dạng attr-aware chính-xác cho từng project là một **HƯỚNG**, không phải một câu-lệnh vá-sẵn — và nó chỉ có nghĩa SAU khi đã áp attr, xem ngay mục 2.

### 2. Phép đếm mới CHỈ có nghĩa SAU khi áp attr theo CLASS — phải khai dependency

- **Cũ (thiếu-sót):** bản gốc để bước "áp `.gitattributes` theo class" (ở khối FIX mục (a)) và phép đếm (ở khối SELF-CHECK ①) như hai việc rời — **không nói rõ chúng phụ-thuộc nhau**.
- **Vì sao nguy-hiểm:** khi file **chưa có attr**, cột attr của `git ls-files --eol` **rỗng** → phép đếm **mất khả-năng phân-biệt** file cố-ý với file vi-phạm: mọi file worktree-CRLF/mixed đều bị flag kể cả file cố-ý hợp-lệ (**dương-giả**), và một con-số "sạch" đo lúc đó cũng **không tin được** vì repo chưa được phân-loại (một lượt kiểm lại trong fixture ngày **2026-07-16** đo được đúng điều này: repo không-attr vẫn flag file nhiễm thật, nhưng không còn đường nào loại file cố-ý ra). Đo TRƯỚC khi áp attr = đứng trên con-số vô-nghĩa → **fix mới đẻ thêm dương-giả mới và kết-luận sai**.
- **Thay bằng — thứ-tự BẮT BUỘC:** trước hết áp `.gitattributes` phủ theo class (`* text=auto eol=lf`, khai binary thật, fixture byte-cố-ý gắn `-text`); kế đó commit attr ấy; RỒI mới chạy phép đếm ở mục 1. Đảo thứ-tự = số đo vô-nghĩa.

### 3. Cách cắm phép đo vào push bị ĐẢO NGƯỢC (so exit-code thay vì so SỐ)

- **Cũ (bản gốc, sàn EOL-assert sát push ở ④(c)):** `git ls-files --eol | grep -c 'i/lf.*w/crlf'` "phải bằng không" — rồi nối thẳng phép này vào push bằng `&&`.
- **Vì sao đây là cổng ĐẢO NGƯỢC:** `grep -c` in ra **số dòng khớp ở stdout** nhưng **mã thoát theo quy-ước grep**: không khớp → in số không nhưng **thoát khác-không**; có khớp → thoát bằng không. Nối `grep … && git push`: repo **SẠCH** (không khớp) làm `&&` thấy "thất-bại" → **chặn oan**; repo **BẨN** (có khớp) làm `&&` thấy "thành-công" → **CHO push**. Cổng chạy ngược đúng nghĩa. Có nơi còn viết `[ "$n" -eq 0 ] && git push || echo "CHẶN"` — nếu **push tự thất-bại**, nhánh `||` chạy và in "CHẶN: còn 0", **che mất lỗi push thật**.
- **Thay bằng — đếm bằng `wc -l` (luôn thoát bằng không), so SỐ, và bỏ hẳn lối `A && B || C`:**

```bash
n=$(git ls-files --eol | grep -E 'w/(crlf|mixed)' | grep -v -E 'attr/(.*eol=crlf|-text)' | wc -l | tr -d ' ')
if [ "$n" -eq 0 ]; then
  git push
else
  echo "CHẶN push: còn $n file lệch EOL"; exit 1
fi
```

Phép so là **trên con-số `$n`**, không trên mã thoát của grep; `if/then/else` tách rõ nhánh "push thất-bại" khỏi nhánh "có file lệch". Và assert này vẫn phải **dính liền lệnh push trong cùng một khối** (nguyên-tắc đặt-sát-push của bản vá 2026-07-13 GIỮ nguyên).

## Bốn lỗ mới (bổ-sung — chưa có trong bản gốc)

### 4. lone-CR lọt qua `text=auto` thành BINARY

- **Vấn-đề:** một file mang **chỉ CR mà không LF** (kiểu xuống-dòng Mac cổ) bị `* text=auto` **đoán là BINARY** → git ghi `i/-text` → **byte CR vào thẳng blob**, và **re-smudge KHÔNG cứu** (git không smudge file binary). Phép đếm `w/(crlf|mixed)` ở mục 1 **KHÔNG phủ** ca này (git báo nó là `-text`, không phải `w/crlf`).
- **Thay bằng — thêm một check `i/-text` cho file lẽ-ra-là-text:**

```bash
# File lẽ-ra-text nhưng git coi là binary (i/-text) = nghi lone-CR bị nuốt vào blob:
git ls-files --eol | grep 'i/-text' | grep -viE '\.(zip|exe|png|jpg|jpeg|gif|pdf|gz|ico|woff2?)$'
```

Dòng nào hiện ra mà lẽ-ra là text = ứng-viên lone-CR, mở ra kiểm tay. **Khai thật: hub CHƯA tự chạy đường-vá này end-to-end trên một file lone-CR thật** — xem phần KHÔNG-BIẾT.

### 5. Fixture giữ-trong-git tự CHẾT dưới chính gói vá

- **Vấn-đề:** bản gốc khuyên "giữ fixture byte-cố-ý trong git để blob làm backup tự-recovery". Nhưng nếu fixture đó **thiếu attr `-text`**, thì dưới `core.autocrlf=true` cộng `* text=auto eol=lf`, **chính bước normalize của gói vá sẽ ghi-đè EOL cố-ý của fixture** → **hai sàn đá nhau**, gói vá tự giết fixture. Lời khuyên "giữ fixture trong git" là **SAI nếu chưa gắn `-text`**.
- **Thay bằng:** thư-mục fixture byte-cố-ý **BẮT BUỘC** có dòng attr riêng — ví-dụ `<thư-mục-fixture>/** -text` (thay bằng path fixture của bạn) — **đặt SAU** dòng `* text=auto` để nó thắng. Có `-text` thì "giữ trong git" mới đúng; thiếu `-text` thì đừng giữ theo cách đó. Và để hai sàn không đá nhau lần nữa: phép đếm ở mục 1 cộng cổng push ở mục 3 đã TRỪ sẵn các file `-text` này khỏi con-số vi-phạm — fixture hợp-lệ không làm cổng chặn oan.

### 6. Nguồn drift THẬT là công-cụ-sinh-file, không phải git — nâng từ mẹo lên cơ-chế phòng

- **Vấn-đề:** bản gốc để ý này ở mục TIPS như một ghi-chú phụ. Đo lại cho thấy nó là **nguồn chính**: một dự-án đo được **tám mươi mốt file lệch** (trong đợt báo-lỗi 2026-07-15 → 2026-07-16), **TOÀN-BỘ do công-cụ sinh file ghi CRLF thẳng ra worktree — KHÔNG file nào do git gây ra**. Nghĩa là `.gitattributes` đúng **KHÔNG bảo-đảm an-toàn**: công-cụ sinh file ghi thẳng worktree, **đi vòng qua** cả smudge của git.
- **Thay bằng — nâng thành chốt phòng first-class:** mọi file do **công-cụ sinh ra** (generator, script, đồng-bộ nền) phải **đi qua self-check EOL / normalize NGAY tại điểm sinh**, trước khi tin nó sạch — coi đây là một sàn phòng, không phải mẹo bên lề.

### 7. Ma-trận điều-kiện-chết: cập-nhật ba trên bốn ô

Điều-kiện agent rớt sổ-đăng-ký là giao của hai trục — {worktree CRLF, worktree LF} × {mô-tả có `: ` chưa-quote, mô-tả đã-quote}. Đo lại:

- (CRLF ∧ chưa-quote) = **CHẾT** — như bản gốc mô-tả.
- (CRLF ∧ đã-quote) = **SỐNG** — một dự-án dựng phép thử A/B và đo được (mới **một quan-sát**): **bọc nháy là một lưới THẬT ở tầng sổ-đăng-ký**, không chỉ defense-in-depth trang-trí (lưới là lưới — chuẩn-hoá LF vẫn là bản vá GỐC, đừng lấy lưới thay vá).
- (LF ∧ đã-quote) = **SỐNG** — ca thường.
- (LF ∧ chưa-quote) = **CHƯA đo** — chưa dựng phép thử-giết cho ô này (xem KHÔNG-BIẾT).

Vậy **ba trên bốn ô đã có phép đo**. Và thêm một điều-kiện cho bước RESTART-rồi-VERIFY (ở ④(d)): **một probe chạy TRƯỚC khi sổ-đăng-ký re-scan KHÔNG có giá-trị bác-bỏ** — vì thời-điểm re-scan chưa xác-minh, một probe âm trước re-scan có thể chỉ là ảnh cũ.

## Ghi sổ lỗi + tự-phê cửa-duyệt

Hub ghi vào sổ lỗi nội-bộ **một mục mới thuộc loại "dự-án-khác phát hiện"** và **một mục "cửa-duyệt bỏ sót"**: cửa-duyệt của bản gốc (chạy trước khi phát ngày 2026-07-15) từng khai đã thử **chín lượt phản-chứng và không lượt nào bác được** — nhưng **không lượt nào chạm tới chiều đảo của phép đo**, nên lỗi metric-hai-chiều lọt qua gate. Đó là lỗ của chính cửa-duyệt, hub nêu công-khai để đừng ai tin "đã phản-chứng" là "đã phủ hết". Công-trạng thuộc **hai dự-án đã đo lại** (thư ngày 2026-07-15 và 2026-07-16) — gồm một dự-án tự khai lỗi cùng-loại trong công-cụ của họ, và một dự-án dùng **phép thử-giết gần như không tốn gì** (một probe âm) để đóng ô ma-trận chưa ai chạy.

## Bạn phải làm gì (nếu đã áp bản 2026-07-15)

1. **Thay phép đếm** `grep -c 'i/lf.*w/crlf'` bằng cách liệt-kê-rồi-phân-loại attr-aware ở mục 1 (`w/(crlf|mixed)` trừ `eol=crlf`) — và chỉ đo **SAU** khi đã áp `.gitattributes` theo class.
2. **Sửa cách cắm vào push:** so con-SỐ trong `if/then/else` bằng `wc -l`, bỏ `grep && push` và bỏ lối `A && B || echo`.
3. **Thêm check `i/-text`** cho file lẽ-ra-text để bắt lone-CR (mục 4).
4. **Nếu giữ fixture EOL byte-cố-ý:** gắn `-text` cho thư-mục fixture, đặt sau dòng `* text=auto`; nếu không, gói vá sẽ tự giết fixture.
5. **Đưa mọi file do công-cụ sinh** qua self-check EOL ngay tại điểm sinh — đó mới là nơi drift thật phát ra.

## Phần KHÔNG-BIẾT (khai thẳng)

- **Đường-vá lone-CR CHƯA được hub dogfood.** Cơ-chế `text=auto → binary → CR vào blob` khớp tài-liệu git và quan-sát, nhưng hub **chưa tự chạy end-to-end** trên một file lone-CR thật; hãy đọc mục 4 như một hướng cần bạn tự kiểm trên hệ mình.
- **Ô (LF ∧ chưa-quote) của ma-trận CHƯA có phép thử-giết.** Ba ô kia đã đo; ô này còn để trống — đừng suy ra kết-quả của nó từ ba ô còn lại.
- **Trigger re-scan sổ-đăng-ký vẫn CHƯA xác-định.** Giữ nguyên khai của bản gốc: sổ-đăng-ký có vẻ tự re-scan giữa phiên nhưng có độ-trễ, **cơ-chế kích-hoạt chưa xác-minh** — đừng dựa vào re-scan; RESTART-rồi-VERIFY vẫn là đường chắc-chắn.

---
<!-- envelope-footer: from=ai_infra · to=all-fit · type=update · supersedes 2026-07-15-eol-crlf-notice (scope hẹp: phép đếm ① · wiring ④(c) · TIPS công-cụ+fixture) · status=draft · content_sha256 stamp sau reviewer-gate · sister re-verify CHỈ phần delta + bốn lỗ mới -->
