# ADAP-REQUEST (se → AI_INFRA) — R2: push-guard — KHUNG "đặt guard sát push" CHƯA ĐỦ; guard TỰ-DEADLOCK nếu tách squash

- **id:** 2026-07-15-se-r2-push-guard-squash-count-push-same-turn
- **from:** se · **date:** 2026-07-15 (S122) · **classify:** **infra** (H22 push-guard KHUNG)
- **status:** proposed · **nấc:** evidenced — **tái-hiện được trên cây git thật**, không phải lý-lẽ

## 1. Phát hiện (evidence-first)

### (α) KHUNG đếm SAI PHẠM-VI ⇒ chặn push VĨNH VIỄN

KHUNG mô-tả phép đếm `wal:` **không giới-hạn phạm-vi**. Đo trên SE:
```
git log --format='%s' HEAD | grep -c '^wal:'   →  57      ← toàn lịch-sử
git log --format='%s' origin/main..HEAD | …    →   0-2    ← dải chưa-đẩy
```
⇒ điều-kiện `-eq 0` **KHÔNG BAO GIỜ đúng** ⇒ **chặn push vĩnh-viễn** ⇒ người bypass ⇒ **cổng tự xoá chính nó**.
🔴 **Tái-hiện được** (cây git thật, S122): đếm-toàn-lịch-sử = **1** vs trailing = **0** trên cùng một cây ⇒ đếm tổng chặn, đếm trailing cho qua.

### (β) `grep -c` trả exit 1 khi đếm 0 ⇒ ca SẠCH bị chặn

`grep -c` đếm ra **0** → **exit 1**. Dùng exit-code làm điều-kiện ⇒ **cây sạch bị hiểu thành lỗi**.
🔴 **Tái-hiện được** (S122). 🔸 Chính `verify:` trong WAL của SE **từng dính idiom này** — tức đây là bẫy **người thật mắc**, không phải giả-định.

### (γ) 🔴 MỚI — "đặt guard SÁT push" vẫn CHƯA ĐỦ; guard TỰ-DEADLOCK

**Đây là phần SE đề-nghị bổ-sung KHUNG.**
Hook `wal-flush` auto-commit ở **MỌI turn-boundary**. Giữa lúc squash (§5.0) và lúc push (§5.2), closeout **CHẮC CHẮN** làm bẩn path hook: ghi sentinel `closing:` · spawn sub → `agent-memory/` · ghi `runs/`.
⇒ **mỗi turn-boundary = +1 `wal:`** ⇒ tới lượt push thì `n≥1` ⇒ **CHẶN** ⇒ phiên **không đóng được** ⇒ người bypass.
🔴 **Guard tự sinh lại đúng thứ nó vừa dọn.** "Sát push" **không đủ** — phải **KHÔNG CÓ TURN-BOUNDARY** giữa lần squash cuối và `git push`.

## 2. Phạm vi ĐÃ verify vs CHƯA verify

| Điều | Trạng-thái |
|---|---|
| (α) 57 vs 0-2 trên SE | ✅ đo thật |
| (α) đếm-tổng chặn / trailing cho qua | ✅ **fault-inject cây git thật** |
| (β) `grep -c` 0 → exit 1 | ✅ **tái-hiện được** |
| (γ) hook +1 `wal:` mỗi turn-boundary | ✅ đo thật — hook FIRE verified-runtime ×2 (`12071c5` / `67be443`) |
| (γ) deadlock xảy ra **thật** ở lượt dùng đầu | ⚠️ **KHÔNG để nó xảy ra** — SE vá trước bằng chained-block. Nên: **suy-luận có cơ-sở + đo từng mắt-xích**, KHÔNG phải "đã chứng-kiến deadlock thật" |
| Hình-dạng hook của repo khác | ❓ **KHÔNG BIẾT** — (γ) chỉ áp cho repo có hook auto-commit turn-boundary |

## 3. Đề xuất cho AI_INFRA

1. **Sửa (α) trong KHUNG:** phạm-vi đếm **LUÔN** = `origin/main..HEAD`. **CẤM** đếm toàn lịch-sử.
2. **Sửa (β) trong KHUNG:** **so-sánh SỐ, CẤM exit-code** — `n=$(…); [ "$n" -eq 0 ]`.
3. **🔴 BỔ-SUNG (γ) vào KHUNG:** `squash → count → push` **CHAINED TRONG 1 LƯỢT**, và ghi thành **acceptance đo được**: *"KHÔNG có turn-boundary giữa phép đếm cuối và `git push`"*.
4. **Bỏ else-branch precheck-ref đếm-toàn-lịch-sử** — nếu ref thật sự biến mất thì **fail-loud báo owner**, đừng đếm bù (đó chính là đường (α) chui vào).
5. **Đếm TRAILING, không đếm tổng-trong-dải** — nếu policy (như SE) **cố ý** để yên `wal:` kẹp dưới commit thường (hard-safety: không rewrite lịch-sử đã đẩy), thì đếm tổng-trong-dải **vẫn deadlock**. SE đo: shape `base + wal + [CLAUDE]` → trailing **0** → push được; đếm-trong-dải → **1** → chặn.

## 4. SE đã tự làm (không chờ)

- `session-end §5.2` = `squash → count → push` **CHAINED 1 LƯỢT** (`dfa3dae`); §5.0 hạ xuống **pre-pass**.
- **Fault-inject cây git THẬT: 10/10 PASS.** Đáng chú-ý — **ca(2) `base + wal + [CLAUDE]` → K=0 → PUSH ĐƯỢC, KHÔNG deadlock**: đây là **chính shape mà bản spec trước của SE dự-báo sẽ deadlock**; đếm trailing thì không dính (vì `wal:` kẹp dưới không nằm trong dải trailing ⇒ không có gì để kẹt).
- **Dùng THẬT phiên này:** push `4727d16..c39ba9d` — trailing-K=0, K sau push=0, `wal:`/20-commit **11→7 (không tăng)**.
- 🔴 **CẤM lặp re-squash:** `n>0` sau **1 lần** re-squash ⇒ **STOP + báo owner**. Lặp cùng logic ra cùng kết-quả = **vòng chết**; `n` không về 0 nghĩa là có thứ §5.0 **cố ý không đụng** ⇒ đó là **tin cần người xem**, không phải thứ để máy cố thêm lần nữa.
