# SPEC v3 — PATCH-SET trên v2 (S119, 2026-07-15) > 🔴 **CÁCH ĐỌC:** worker đọc **v2** (`spec-…-v2.md`) làm thân + **file này** thay-thế các mục ghi rõ dưới. v3 **KHÔNG** lặp lại v2. **v1 = CẤM CHẠY.** > **Nguồn:** `/fable-real reviewer` cổng-cuối `wf_cb964f83-331` → **GO-WITH-FIXES** (0 tiêu-chí NO-GO; **8 fix BẮT BUỘC** trước wave). Verdict đầy-đủ: `fable-real-final-gate-verdict.md`. > **Nấc phiên S119:** anh chốt **dừng tại spec v3** — **KHÔNG chạy wave phiên này**. Wave = phiên sau qua `/tiep`. --- # 0. 🔴 DISPOSITION 23 FINDING (fix #2 — vá lỗi trung-thực C1) > **Lỗi đã phạm:** v2 ghi *"R2 (4C+**7M**) · R3 (5C+**2M**) … **19 finding** … v2 vá 19"*. **Đếm thật từ file: R2 = 4C+8M · R3 = 5C+6M ⇒ 23 C+M.** Ngay số tự khai cũng không cộng ra (4+7+5+2 = **18 ≠ 19**). > **Vì sao NẶNG:** tập "7M/2M" **khớp chính xác** tập v2 có xử-lý ⇒ con-số "19" **đóng vai "đã phủ hết"** trong khi **3 finding STILL-BROKEN**. Lớp S109 (metadata-count) + S81 (honesty-presentation). Bảng này = **bằng-chứng-chống-tái-phạm**, và **đi nguyên vào adap-report**. | # | Finding | v2 | v3 | |---|---|---|---| | R2-C1 | `ls=14` sai số-học | ✅ FIXED | — | | R2-C2 | anchor số-nhiều | ✅ FIXED (gate tự chạy regex → MATCH `permission-matrix:16` + `form-engine:15`) | — | | R2-C3 | gate⟂cờ tách wave | ✅ FIXED | **fix #6**: `fable-clone.md` 2-chạm vắng §3.2 | | R2-C4 | gate W0.2 xanh-sẵn | ✅ FIXED | **fix #9(d)**: dòng 70 ⟂ §3.9 tự-mâu-thuẫn | | R2-M1 | nửa-(ii) n-a sai | ✅ FIXED | — | | R2-M2 | lỗ chưa-test precedence | ✅ FIXED | — | | R2-M3 | 0-hardcode phình scope | ✅ FIXED | — | | R2-M4 | scheduler mồ-côi + enum | ⚠️ PARTIAL (chủ-sở-hữu ✅ / enum **10/11 SAI**) | **fix #1** | | R2-M5 | ② + ③d rơi | ✅ FIXED | — | | R2-M6 | else "vô-hại" | ✅ FIXED | — | | R2-M7 | thí-nghiệm quyết-định + L3 caveat | ✅ FIXED | — | | **R2-M8** | **sweep roster sai pattern + file không wave** | 🔴 **STILL-BROKEN** (v2 §2.4(5) = "giữ v1") | **fix #6** | | R2-m1 | broadcast #7 presence-not-age | ⬜ chưa xử | **fix #9(e)** 1 dòng | | R2-m3 | — | ✅ FIXED qua PA-2b | — | | R2-m4 | "n-a ĐÃ THOẢ" = under-report | ⬜ chưa xử | **fix #9(b)** | | R2-m5 | lịch stamp-mark | ⬜ chưa xử | **fix #9(f)** → W6 | | R3-C1 | rebase phá thật | ✅ FIXED | **fix #7** (bug shell guard) | | R3-C2 | K stale | ✅ FIXED (K drift **4→5→6→8** = tự chứng minh luật đúng) | — | | **R3-C3** | **push deadlock** | ⚠️ **PARTIAL** — v2 **tự tạo** ca wal:-đáy-range | **fix #3** 🔴 | | R3-C4 | premise stale / renormalize staging | ✅ FIXED | — | | R3-C5 | gate unscoped bất-khả-giữ | ✅ FIXED | — | | R3-M1 | ls=15 | ✅ FIXED (trùng R2-C1) | — | | R3-M2 | STATUS coupling | ✅ FIXED | — | | R3-M3 | TOTAL≥46 bất-khả | ⚠️ PARTIAL (gate đã GỠ; thiếu capture-stdout) | **fix #9(c)** | | **R3-M4** | **Sàn-3 thiếu dual-accept** | 🔴 **STILL-BROKEN** + **REGRESSED** (v2 chọn mtime) | **fix #4** 🔴 | | **R3-M5** | **tripwire vỡ đường sống** | ⚠️ **PARTIAL** (wrapper-list ✅ / điều-kiện ❌) | **fix #5** 🔴 | | **R3-M6** | sweep pattern + `hmw.js:9` | 🔴 **STILL-BROKEN** (trùng R2-M8) | **fix #6** | | R3-m5 | mtime không bền | 🔴 **REGRESSED** — v2 chọn mtime làm phương-án CHÍNH | **fix #4** | | R3-m6 | cấm synthesis giả | ✅ FIXED | — | | R2-m2, R3-m1/m3/m4 | — | ⬜ chưa xử — **không chặn wave**, ghi để không rơi | defer S120 | **Tổng:** 23 C+M ⇒ **16 FIXED · 4 PARTIAL · 3 STILL-BROKEN** (+1 REGRESSED ở lớp m). v3 đóng **3 STILL-BROKEN + 3 PARTIAL + 1 REGRESSED**. --- # FIX #1 — Enum 11 class (thay §2.4 "enum ĐÓNG" của v2) **Lỗi:** v2 chép **10** class rồi tuyên *"khớp H24 §2(1) 5+5"* — **SAI cả 2 chiều**: lane D có **11** (tách `carry-dropped`/`carry-aged` **có chủ-đích, đo được caveat (b)**); broadcast H24 `:48-49` liệt **"một việc rớt khỏi khối work-state so với backlog"** = **dấu-hiệu RIÊNG**. v2 **bỏ hẳn** `workstate-item-dropped` để về 10 rồi tuyên khớp. 🔴 **Hậu-quả:** mất **đúng class bắt ca F1 của chính SE** (S118 chạy 7-spawn mà **0 run-folder** = việc rớt khỏi sổ). Enum **ĐÓNG** ⇒ 2 vai mới **VĨNH VIỄN** không được phát class này. ```json "lead_self_audit": { "flag_classes": [ "view-stale-count", "view-stale-status", "view-stale-header", "view-stale-role-desc", "view-residual-asym", "gap-workstate-drop", // ← KHÔI PHỤC (broadcast :48 — dấu-hiệu RIÊNG; bắt ca F1 của SE) "gap-carry-dropped", "gap-carry-aged", // ← tách-đôi = MỞ-RỘNG lane-D (đo được caveat (b)), KHÔNG phải broadcast "gap-owner-specifics", "gap-decision-sunk", "gap-underfill" ] } ``` 🔴 **XOÁ câu "khớp H24 5+5"** khỏi spec. Nấc đúng: *"5 STALE (khớp broadcast) + **6** GAP = 5 broadcast + **1 mở-rộng lane-D** (carry tách-đôi, lý-do đo-được)"*. **Acceptance:** `flag_classes` ⊇ `{gap-workstate-drop, gap-carry-dropped, gap-carry-aged, gap-owner-specifics, gap-decision-sunk, gap-underfill}` · `grep "khớp 5+5"` = **0**. --- # FIX #3 🔴 — Luật fold leading-wal: (thay v2 W0.3 "commit-chốt ở W6" + §2.1 push-guard) **Lỗi sâu:** v2 **tự tạo** ca chết. W0.3 park nội-dung staged ("commit-chốt ở W6") → `wal-flush.ps1:14-17` commit **pathspec 3 path** ở turn-boundary kế → **toàn bộ nội-dung reset-soft thành 1 `wal:` ĐÁY range** → tại W6 `NONWAL>0` → nhánh B → luật *"todo đầu = pick"* **giữ wal:-đáy sống** → recount=1 → re-squash cùng logic → vẫn 1 → **STOP ngay lần dùng thật ĐẦU TIÊN**. **v3:** - **(a)** W0.3 **BỎ** "commit-chốt ở W6". **Commit-chốt `[CLAUDE]` NGAY CÙNG TURN** với `reset --soft` (gộp luôn `wal-flush.ps1` interlock + `.gitattributes`) ⇒ **đáy range = commit thường**. - **(b)** Mọi push **sau push#1** ở W6 (adap-report…) = **write → commit → squash → count → push CHAINED cùng 1 lượt**. - **(c) Luật tổng-quát:** `wal:` ở **đáy range** ⇒ **`reset --soft origin/main` re-commit**, **KHÔNG rebase**. **Acceptance:** fault-inject cây tạm shape `base + wal + [CLAUDE] + wal×2` ⇒ chained block ra **n=0**, **tree-identical**, **KHÔNG STOP**. --- # FIX #3-bis 🔴 — Điều-kiện rẽ nhánh W0.3 test SAI THỨ (S120 `/tiep` — lead tự bắt @đóng S119, +1 ca mới) **Lỗi:** v2 W0.3 (`:92`) rẽ nhánh bằng `if [ "$NONWAL" -eq 0 ]`. Nhưng luật **fix#3(c)** ở trên nói điều-kiện là *"`wal:` ở **ĐÁY range**"* — **hai thứ khác nhau**. `NONWAL>0` **không** hàm ý đáy là commit-thường: shape `base + wal + [CLAUDE]` có `NONWAL=1` **và** đáy = `wal:` ⇒ v2 đẩy sang **nhánh-B rebase** ⇒ todo đầu = `pick wal:` ⇒ `wal:` **sống sót** ⇒ `n>0` tại §5.2 ⇒ **đúng deadlock mà fix#3 sinh ra để đóng**. **+ ca (0) MỚI (S120 đo thật):** S119 đã commit-chốt `4727d16` **và push** ⇒ `K=0`, range **RỖNG** ⇒ **không có đáy để test**. v2/v3 **không liệt ca này**; `NONWAL=0` sẽ đẩy vào nhánh-A ⇒ `git reset --soft HEAD~0` = **no-op vô hại nhưng vô-nghĩa**, và assert `HEAD~K == origin/main` vẫn PASS (vacuous). Không chết, nhưng **che mất** việc W0.3 lúc đó chỉ còn là *commit thẳng*. **⇒ Điều-kiện đúng = 4 ca, test ĐÁY range (KHÔNG test NONWAL):** ```bash K=$(git rev-list --count origin/main..HEAD) BOTTOM=$(git log --format='%s' origin/main..HEAD | tail -1) # rỗng khi K=0 if [ "$K" -eq 0 ]; then : # ca (0) RỖNG → NO-OP. Không reset, không rebase. Commit thẳng. elif printf '%s' "$BOTTOM" | grep -q '^wal:'; then [ "$(git rev-parse HEAD~$K)" = "$(git rev-parse origin/main)" ] || { echo "ABORT: HEAD~K != origin/main"; exit 1; } git reset --soft "HEAD~$K" # ca (1) NONWAL=0 · ca (2) NONWAL>0 — CÙNG một xử-lý, CẤM rebase else trap 'git rebase --abort' ERR # ca (3) đáy = commit-thường → rebase+fixup an-toàn GIT_SEQUENCE_EDITOR=