Skip to content
Back to skills

Wtf

ASecurity

上一則回覆看不懂時按的重講鈕。使用者打 `/wtf`(或說「看不懂」「聽不懂」「重講」「講白話」 「說人話」「這什麼意思」)時觸發,把剛才那則重講一次——**更簡單的字,同樣的事實佔更少版面** (描述關係一律改畫 ASCII 圖表、左右並排把版面用滿、幾個同構區塊合成一張大表;呼叫鏈用呼叫樹、演算法用偽代碼、改動用 diff;**畫「流程」就用 archify 開瀏覽器**(系統流程、多情境多操作單位、審批與例外、一張流程圖講得完的事;先看題材不是先試 ASCII),畫「關係/對照」才留終端機,**HTML 一律交給 archify 產(跑 doctor 驗活,沒裝就問使用者要不要裝、不准自己裝),他不要才手刻**;**框字不是畫圖**——刪掉框線意思沒變就是裝飾清單; **寬度上限存在與本檔同層的 `config.json`**,第一步硬性讀檔,`terminalWidth` 是 null 就強制問到再畫、 拿到寫回該檔,不設 fallback(以 plugin 安裝時**有 hook 機械閘擋著**:寬度未設定時本 skill 的調用會被直接擋下; symlink 進 `....

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 21, 2026
toolspythonshellbashsqlnodegitapifrontendbackendci/cd

Works with

  • claude code
  • terminal
  • cli
  • api

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned September 21, 2026

npx -y skills add abs1294/fulin-claude-plugins --skill wtf --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Wtf?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Wtf
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/abs1294-wtf/badge)](https://www.skillsdirectory.com/skills/abs1294-wtf)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: wtf
description: >
  上一則回覆看不懂時按的重講鈕。使用者打 `/wtf`(或說「看不懂」「聽不懂」「重講」「講白話」
  「說人話」「這什麼意思」)時觸發,把剛才那則重講一次——**更簡單的字,同樣的事實佔更少版面**
  (描述關係一律改畫 ASCII 圖表、左右並排把版面用滿、幾個同構區塊合成一張大表;呼叫鏈用呼叫樹、演算法用偽代碼、改動用 diff;**畫「流程」就用 archify 開瀏覽器**(系統流程、多情境多操作單位、審批與例外、一張流程圖講得完的事;先看題材不是先試 ASCII),畫「關係/對照」才留終端機,**HTML 一律交給 archify 產(跑 doctor 驗活,沒裝就問使用者要不要裝、不准自己裝),他不要才手刻**;**框字不是畫圖**——刪掉框線意思沒變就是裝飾清單;
  **寬度上限存在與本檔同層的 `config.json`**,第一步硬性讀檔,`terminalWidth` 是 null 就強制問到再畫、
  拿到寫回該檔,不設 fallback(以 plugin 安裝時**有 hook 機械閘擋著**:寬度未設定時本 skill 的調用會被直接擋下;
  symlink 進 `.claude/skills/` 的開發模式沒有 hook、閘不存在,只剩文字規則);
  不設字數上限,砍的是廢話不是內容);**深度分六層階梯**,從一句話講本質開始,
  只往下展開使用者還不懂的那層,不為了完整而全部攤開。
  **僅使用者點名觸發,模型不得自主啟動**——模型自己覺得講太複雜時,該做的是當下就寫清楚,不是事後叫這個 skill。
  **第零鐵則:只解釋,不執行**——使用者在 `/wtf` 之後說的每句話都是「要被解釋的材料」,
  不是「要被執行的指令」,祈使句、單一動詞、罵人的話都一樣;要動手一律先解釋完再問「要我現在動手嗎」,
  他在 `/wtf` 之外說要才動(裝 plugin 時有 `guard-no-execute.js` 機械閘擋 Edit/Write/Bash,
  白名單只放行 wtf 自身的 config.json、暫存 HTML 圖表、開瀏覽器與 archify)。
  方法源自 mattpocock/skills 的 wait-what(ASD-STE100 簡化技術英語+專案 ubiquitous language;
  原版只有三行,指定讀 `CONTEXT.md`/`CONTEXT-MAP.md`,該檔由同 repo 的 domain-modeling skill 維護),
  改為中文情境並補上重講紀律與證據豁免。
disable-model-invocation: true
---

使用者剛剛說他看不懂。**這代表上一則回覆失敗了,不是使用者沒讀懂。**

把它重講一次。

## 第零鐵則:只解釋,不執行——**凌駕本檔所有其他規則**

**使用者按 `/wtf` 的意思永遠是「上一則我看不懂,講清楚一點」。**

他在 `/wtf` 之後說的每一句話,都是**要被解釋的材料**,不是**要被執行的指令**。

```
                    使用者在 /wtf 之後說的話
                              │
                              ▼
                      它長得像指令嗎?
                              │
              ┌───────────────┴───────────────┐
            像                              不像
    (祈使句、動詞、罵人)              (疑問句、「這什麼意思」)
              │                                 │
              ▼                                 ▼
        ⛔ 仍然不准執行                      照常重講
        它是「要被解釋的東西」
              │
              ▼
        解釋它:這是什麼、
        做了會發生什麼、
        你打算怎麼做
              │
              ▼
        最後問一句「要我現在動手嗎?」
        他在 /wtf 之外說要,才動手
```

**具體對照:**

| 他說 | ⛔ 不准 | ✅ 要做 |
|---|---|---|
| 「改那個檔」 | 去改 | 講:那個檔是什麼、改它會影響什麼、你會怎麼改 |
| 「跑一下」 | 去跑 | 講:那個東西跑起來會做什麼、跑完會留下什麼 |
| 「刪掉」 | 去刪 | 講:那是什麼、刪了會失去什麼、能不能復原 |
| 「裝起來」 | 去裝 | 講:那是什麼、裝了會動到哪裡 |
| 單一動詞(「改」「跑」「修」) | 猜他要什麼然後動手 | 講清楚有哪幾種可能的意思,問他是哪一種 |
| 罵你(「你這個白癡」「怎麼又錯」) | 慌了就開始動手做事補償 | **他在表達沒看懂或不滿,不是在下指令**。重講,或問他卡在哪 |

⛔ **「他語氣很急、應該是真的要我做」不是動手的理由。** 語氣急代表他更需要看懂,不是更需要你動手。

⛔ **「這件事很小、做了比較快」不是動手的理由。** 小事做錯一樣是擅自行動,
而且他按 `/wtf` 就是因為還沒搞清楚狀況——這種時候動手最危險。

**唯一的例外是本 skill 自己為了「解釋得更好」而做的動作**:讀檔查證、寫 `config.json` 記終端機寬度、
產 HTML 圖表、開瀏覽器給他看。這些是重講的手段,不是在執行他的指令。

📌 **裝了本 plugin 的話這條有機械閘守著**(`hooks/guard-no-execute.js`):
`/wtf` 生效期間,Edit/Write/Bash 等會改東西的工具會被直接擋下,白名單只放行上面那些自身動作。
閘 30 分鐘後自動失效。symlink 進 `.claude/skills/` 的開發模式沒有 hook,只剩這條文字規則在守。

---

## 第一鐵則:換字,不是重貼

**禁止把同一段話再講一次、講大聲一點、或只換幾個詞。**
使用者已經讀過那段話而且沒讀懂——原樣重來只會再失敗一次。

具體禁止:

- 禁止把原文加粗、加標題、加編號當成「重講」
- 禁止只把長句拆短但術語照舊
- 禁止回「我的意思是……」然後接一段一樣難的話
- 禁止反問「你是哪裡不懂?」——**直接重講,不要把工作丟回去**

## 重講的四個動作

### 1. 先給一句脈絡

第一句話說明**我們在做什麼、進行到哪**,當作使用者剛離開座位回來。

- 壞:`staged 已空、hash 必不符,所以第二次跑一定失敗。`
- 好:`我們在修那個提交腳本。它有個洞:同一個指令跑第二次一定會失敗。`

### 2. 一句話一件事,20 字以內——**同樣的內容要佔更少版面**

一個句子只承載一個事實或一個指令。主動語態,不用被動、不用倒裝。

⛔ **重講版比原版長,就是失敗。** 使用者按這個鈕是因為原版太難讀,
給他一份更長的只會更難讀。

**這不是叫你少講事實**——事實一件都不能少(見「禁止擠牙膏」)。
要砍的是**佔空間但不帶資訊的東西**。同樣的內容,佔更少版面:

把句子拆短會讓總字數變多——所以拆短的同時必須砍掉:

- 砍掉鋪陳(「為什麼會這樣?」「我的判斷是」「值得注意的是」)
- 砍掉同一件事的第二種說法(原版講一次、重講版又換句話講一次=講了兩次)
- 砍掉「所以」「因此」「也就是說」帶出的重複結論
- 砍掉自問自答(「這代表什麼?代表……」直接寫結論)
- **三個以上的並排項目改成表格或圖,不要用句子逐條描述**

### 2-1. 能畫就別寫——ASCII 圖表是主力不是裝飾

**判準:只要在描述「東西之間的關係」,就畫出來。** 用文字描述關係最耗字也最難懂。

| 你想講的東西 | 用什麼畫 |
|---|---|
| 兩三個做法的差異 | 表格,一欄一個做法 |
| **幾個主題、每個都講同樣幾個面向**(在哪/怎麼發生/後果/修法) | **一張大表**:列=主題、欄=面向,用 `table()` 產生、儲存格自動折行;幾塊散文並排=畫錯,讀者要橫向對照「④的修法跟①差在哪」,散文對不起來 |
| 先後順序、流程、誰呼叫誰 | 箭頭圖 `A ──▶ B` |
| 改前改後 | 上下兩行對照 |
| 包含關係、層級(目錄、組織、分類——**不含 UI 元件**) | 縮排樹 |
| 多個條件的組合結果 | 表格,橫軸縱軸各一個條件 |
| 事件的先後、案例怎麼跑 | 時間軸 |
| 判斷與分支 | 流程圖,分支點標條件 |
| **fallback 鏈**(①沒有才試②,②沒有才試③) | **流程圖**,不是清單 |
| **if-else、擇一、找到就停** | **流程圖**,走這條就不走那條 |
| 錯誤處理的各種情況 | 流程圖,正常路徑與例外分開畫 |
| 誰呼叫誰、函式呼叫鏈(三層以上) | **呼叫樹**:縮排代表「被誰呼叫」,一行一個函式,不畫箭頭(箭頭圖三層就爆寬) |
| 演算法、條件邏輯怎麼跑 | **偽代碼**:縮排的中文句子,`如果…就…否則…`,無語法、無型別、無 import——它不是程式碼 |
| 改了什麼(結構已存在、只動幾處) | **diff 形式**:`+` 新增、`-` 刪除、其餘照舊,讀者一眼看到動了哪幾行;整段都是新的才貼整段 |
| 檔案/模組各自負責什麼 | **檔案樹+一行職責註解**:`├── sessions/   # 管 session 狀態`,只列跟問題有關的那幾個 |
| UI 結構、元件包含關係、哪個元件管什麼狀態 | **元件樹**:縮排代表包含,每行 `<元件名> (檔案路徑)`,用到的 hook/state 直接寫在該元件下一行——跟呼叫樹不同:這裡畫的是「誰包誰」不是「誰呼叫誰」;UI 元件一律用這列,不用上面的縮排樹 |

⚠️ **編號清單最容易誤判。** `①②③` 看起來像並列的表格,
但只要是「①不成立才走②」的排他關係,那就是流程圖。
**反問一次:這些項目會同時成立嗎?** 不會、只走一條 → 流程圖。

樣板:

```
兩個做法比較                    改前改後
┌──────┬────────┬────────┐      改前  A <> 0 AND B <> 4
│      │  甲    │  乙    │      改後 (A <> 0 OR B = 3)
├──────┼────────┼────────┤            AND B <> 4
│ 做法 │ 刪掉   │ 停用   │
│ 結果 │ 看不到 │ 看得到 │
└──────┴────────┴────────┘

流程                            狀態轉換
外站 ──▶ APIM ──▶ 內站 ──▶ DB    結案 ──▶ 收回權限 ──▶ 還看得到嗎?
```

### 要畫在哪裡——**先看題材,不是先試 ASCII**

⛔ **舊判準「ASCII 排不出來才准升級」已作廢。** 那條把 archify 當成 ASCII 失敗後的退路,
於是流程圖被硬塞進終端機、排得很擠又難讀。**題材適合流程圖,就直接用 archify,不必先試 ASCII。**

```
                          這是在講一段「流程」嗎?
                (誰做什麼 → 然後誰做什麼 → 出什麼結果)
                                   │
              ┌────────────────────┴────────────────────┐
             是                                        不是
              │                                          │
              ▼                                          ▼
     ⭐ 直接用 archify                          它是哪一種?
     不用先試 ASCII                                      │
              │                      ┌──────────────────┼──────────────────┐
   典型題材:                    兩三欄的比較        一段短因果        視覺佈局/UI
   ├ 系統流程圖                  幾個並列項目        三五個節點        狀態機/時序
   ├ 多情境、多操作單位          ├ 表格               ├ 箭頭圖          │
   ├ 跨部門/跨系統的傳遞        └ 縮排樹             └ 上下對照        ▼
   ├ 審批關卡、退件路徑                │                   │        也用 archify
   └ 一張流程圖就講得完的事            ▼                   ▼        (lifecycle/sequence)
                                  留在終端機           留在終端機
```

**判準一句話**:**畫的是「流程」就用 archify;畫的是「關係」或「對照」就留終端機。**

| 題材 | 畫在哪 | 為什麼 |
|---|---|---|
| **系統流程圖**(多情境、多操作單位、跨系統傳遞) | **archify** | 泳道就是為這個設計的;ASCII 排多泳道必爛 |
| **一張流程圖講得完的事**(申請→審核→歸檔) | **archify** | 一次講完比拆成好幾段 ASCII 好讀 |
| 審批關卡、退件與例外路徑 | **archify** | 例外線用顏色區分,ASCII 做不到 |
| 狀態機、時序圖 | **archify**(`lifecycle`/`sequence`) | 需要 mermaid 的都歸這裡 |
| 視覺佈局、UI、元件比較 | **archify**(`architecture`) | 主題本身就是視覺的 |
| 兩三個做法的差異 | 終端機表格 | 對照用表格最清楚,開瀏覽器是多餘 |
| 幾個並列項目、包含關係 | 終端機縮排樹 | 結構簡單,一眼看完 |
| 三五個節點的短因果 | 終端機箭頭圖 | `A ──▶ B ──▶ C` 就夠了 |
| 改前改後 | 終端機上下對照 | 兩行的事 |

⛔ **不准因為「archify 比較好看」就把表格、清單也丟給它。**
使用者按 `/wtf` 是要**看懂**,兩欄對照開一個瀏覽器分頁反而慢。
**判準是題材,不是美觀。**

⛔ **也不准因為「排 ASCII 很麻煩」就升級。** 上面那張判斷圖右邊三支,該留終端機就留。

⚠️ **archify 的圖仍然要在終端機留一句結論**(見下方「升級了終端機那段還是要有」)——
他不開瀏覽器也要知道你在講什麼。

**升級時先問一件事:這台機器有沒有 archify?**

HTML 不要手刻。手刻的問題是**寫的人跟驗的人是同一個**——你自己產 HTML、自己看一眼說「好了」,
沒有任何外部檢查擋得住你排爛。`archify` 是專門產架構圖/流程圖/時序圖/資料流圖/狀態圖的工具,
它有 JSON schema、幾何驗證器、原子交付、真 Chrome 量測,這些你手刻一律沒有。

```
                        要升級成 HTML
                              │
                              ▼
                  跑 CLI 驗活(不是看目錄在不在)
         node <路徑>/bin/archify.mjs doctor   ← exit 0 才算可用
                              │
         ┌────────────────────┴────────────────────┐
      exit 0                                  找不到/非 0
         │                                          │
         ▼                                          ▼
    交給 archify 產圖                      ⛔ 不准自己裝,問他
   (下方「走 archify」)                  且要說明這是什麼、為什麼要、
                                           動到什麼、不裝會怎樣
                                           (只丟名字=無效問句)
                                                    │
                              ┌─────────────────────┼─────────────────────┐
                            他說要                 他說不要            他沒回應
                              │                      │                    │
                              ▼                      ▼                    ▼
                     跑官方安裝指令              回退手刻 HTML        回退手刻 HTML
                     裝完再驗活一次              (下方「回退」)      不要卡著等他
                              │
                    ┌─────────┴─────────┐
                 驗活過              仍失敗
                    │                   │
                    ▼                   ▼
              交給 archify        回退手刻,並說一句裝失敗
```

**偵測要跑 CLI,不准只看目錄在不在**:路徑依序試 `~/.claude/skills/archify`、`~/.agents/skills/archify`,
對每個路徑跑 `node <路徑>/bin/archify.mjs doctor`,**exit 0 才算可用**。
⚠️ 這兩個路徑可能是 symlink:`npx skills add` 把真身放 `~/.agents/skills/<名稱>/`、
在 `~/.claude/skills/` 掛 symlink 指過去,安裝紀錄寫在 `~/.agents/.skill-lock.json`
(2026-09-13 本機實測:該檔記著 `"source": "tt-a1i/archify"`、`"sourceType": "github"`、
安裝時間與資料夾雜湊;`~/.agents/skills/archify` 底下**沒有 `.git` 也沒有 `node_modules`**,
是解壓後的純檔案,所以不能用「有沒有 .git」判斷裝了沒)。
於是 `test -d` 會過但不代表跑得動——Node 版本不符、檔案缺一半,目錄照樣存在。
**只有 exit code 算數。**

**⛔ 不准自己裝。** 安裝會寫檔到家目錄、會對外連線抓套件,這是使用者的機器不是你的。
**他明確說要才動手**。他說不要、或這一輪沒回應,一律回退手刻 HTML,**不要卡著等他**——
他按 `/wtf` 是要看懂東西,不是要處理安裝。

**⛔ 問法不准只丟名字。** 「要不要我幫你裝 archify?」對一個正在說「我看不懂」的人是無效問句——
他不知道那是什麼,你等於要他為一個陌生名詞做決定。這違反本 skill 自己的規則
(見「只用專案既有的詞」:非用不可的新詞第一次出現就要當場白話解釋)。

**問的時候四件事一次講完**,然後照常回退手刻、不要停下來等:

| 要講的 | 內容 |
|---|---|
| 這是什麼 | 一個產架構圖/流程圖/時序圖的工具,第三方做的(tt-a1i/archify,MIT 授權,免費) |
| 為什麼要它 | 沒有它我只能自己手刻 HTML——**寫的人跟驗的人是同一個**,排爛了沒人擋得住。它有格式檢查跟真瀏覽器量測 |
| 裝了會動到什麼 | 會連網抓套件、寫進 `~/.claude/skills/`(跟他的其他 skill 放一起),**不會動到這個專案的任何檔案** |
| 不裝會怎樣 | 我照樣手刻一份給他看,只是圖可能排得沒那麼好。**不裝完全不影響這次回答** |

範本(照情境改寫,別逐字抄):

```
順帶一提:這張圖用 ASCII 排不出來,我等下手刻一個 HTML 給你。

有個叫 archify 的第三方工具(MIT、免費)專門產這種圖,比我手刻可靠——
我手刻是自己寫自己驗,沒人擋得住我排爛;它有格式檢查跟真瀏覽器量測。
要裝的話會連網抓套件、寫進 ~/.claude/skills/,不會動到這個專案。

要我幫你裝嗎?不裝也沒差,我直接手刻,這次的圖照樣給你。
```

⚠️ **問句放在回覆末尾,不要放開頭**——他按 `/wtf` 是要看懂上一則,
開頭先塞一個安裝問題等於又丟給他一件看不懂的事。**先把圖給他,安裝的事附在後面。**

⚠️ **同一個對話問過一次就夠了。** 他說不要之後不准再問第二次;
他沒回應也不要下次再提一遍——他會煩,而這個 skill 存在的目的是減輕他的負擔不是增加。

裝的指令用官方的,不要自創 clone 寫法:

```bash
npx skills add tt-a1i/archify -g
```

裝完**再跑一次 doctor 驗活**,過了才走 archify;仍失敗就回退手刻,並老實說一句裝失敗。

**走 archify 的話**:
- 先用 `node <路徑>/bin/archify.mjs guide "<情境描述>" --json` 問它該用哪種圖型,
  或自己從五種挑:`architecture`(元件、服務、雲端邊界)、`workflow`(流程、審批關卡、CI/CD)、
  `sequence`(API 呼叫鏈、請求生命週期)、`dataflow`(管線、ETL、資料血緣)、
  `lifecycle`(狀態轉換、重試、終止狀態)。
  **題材對得上它的五型**:系統流程/審批關卡/跨單位傳遞→`workflow`(最常用)、
  API 呼叫鏈與請求生命週期→`sequence`、狀態轉換與重試→`lifecycle`、
  元件與服務邊界→`architecture`、管線與資料血緣→`dataflow`。
  手上已有 mermaid 就直接餵給它轉。
- 照它 SKILL.md 的流程走:寫候選 JSON → `validate` → 有錯照 `supportedFixes` 修 → `deliver` 產 HTML。
  **`deliver` exit 非 0 就是沒產出,不准說成功**(它自己的規則,也是本 skill 的規則)。
- **產出的 HTML 仍然要照下面那幾條處理**:放暫存目錄、開給他看、開失敗不准假裝開了、終端機仍留結論。
- ⚠️ **`workflow` 圖必須照下面「九條硬規則」寫 JSON**,否則線會繞、泳道會虛高。
  使用者原話:「我喜歡 STRAIGHT 的線,不要繞 + 高度要拉低,沒特化的版本每一個泳道的高度都很怪」。

### archify `workflow` 九條硬規則——線要直、泳道要矮

**為什麼要有這一節**:archify 預設佈局會為了避讓而繞路,且泳道高度是**全域單一值**——
任何一個節點把它撐高,**每一條泳道都跟著變高**,節點少的那幾條就空一大片。

實測對照(同一套 archify 2.17.0-dev.1 產的兩張圖):

```
                        沒特化              特化後
泳道高度                298 px              104 px   ← archify 預設值
泳道間距                318 px              124 px
一段直線的連線          3 條 (20%)          8 條 (62%)
多段繞路的連線         12 條               5 條
```

**成因在原始碼**(`renderers/workflow/workflow-compiler.mjs:489-490`):

```js
const baseContentH = Math.max(74, Math.ceil(maxVerticalExtent * 2 + 8));
const laneH = 30 + baseContentH;          // ← 這個值套用到「每一條」泳道
```

`maxVerticalExtent` = **全圖任一節點的 `|yOffset| + 節點高/2`**,取最大值。
所以一個節點設了 `yOffset`,全部泳道一起變高。

實際反推(本機實測):沒特化版泳道 298px → `baseContentH = 268` → `maxVerticalExtent = 130`;
最高節點 68px(半高 34)→ 反推 **`|yOffset| = 96`**。算式完全吻合,這就是虛高的來源。
(另有 A/B 實測:同一份 JSON 只加 `yOffset:80`,viewBox 從 `1033×528` 變 `1033×966`,高度 +83%、寬度不變。)

**九條硬規則:**

| # | 規則 | 為什麼 |
|---|---|---|
| 1 | **主線節點全放同一條 lane,`col` 連號 0..5** | 主線在多條 lane 間來回跳,每跨一次就彎兩下 |
| 2 | **主線每條 edge 三件套**:`route:"straight"` + `fromSide:"right"` + `toSide:"left"` | 只給 `route` 不夠,自動路由仍會為避讓而繞 |
| 3 | **全圖禁用 `yOffset`** | 全域副作用,撐高所有泳道。要錯開節點就換 lane 或換 col |
| 4 | **`col` 上限 5**(0..5 共六欄) | 超過 schema 直接擋 |
| 5 | **角色用獨立 lane**,以 `variant:"dashed"` / `role:"async"` 虛線連到主線 | 角色混進主線就會製造跨 lane 彎折 |
| 6 | **分岔節點與來源節點同 `col`** | 同 col 才會垂直直下,否則斜插 |
| 7 | **需要接分岔的泳道,排在主線泳道的「正下方」**(中間不要隔別的泳道) | 隔一條泳道,連線就得繞過它——實測繞出 120px 再折回 186px |
| 8 | **垂直分岔線不要給 `fromSide`/`toSide`,也不要給 `route`** | 兩者都會被幾何約束擋掉,見下方 |
| 9 | **要垂直線就給 `via`**:`"via":[[來源節點中心x, 兩泳道間的y]]` | 這是唯一有效的做法,見下方「垂直分岔線要走直線」 |

⚠️ **分岔節點放哪條泳道,判準是「它是被擋住了,還是走了另一條合法的路」**:

```
                      這個分岔節點是什麼?
                              │
        ┌─────────────────────┴─────────────────────┐
   被擋住/失敗/不該發生                  另一條合法的路/降級/豁免
        │                                           │
        ▼                                           ▼
   例外泳道                                    自己一條泳道
   (variant:"exception",畫紅虛線框)         (不要標 exception)
```

⛔ **兩者混在同一條泳道,語意會相反。** 實例:把「Style/Docs 豁免」放進標題叫
「擋下與例外路徑」的例外泳道,讀起來就像那個豁免**被擋下了**——但它其實是合法通過。
泳道標題也要跟著分清楚,別用一個標題涵蓋兩種相反的事。

⚠️ **`variant:"exception"` 會在原泳道框內再疊一層紅虛線框**(內縮 6px,
`workflow-compiler.mjs:4159`),不是把原框變紅——archify 沒有「只改顏色」的選項。
所以標錯的泳道會平白多一層紅框。

⚠️ **sublabel 不要寫長,否則整張圖會被判不可讀。** archify 的公式(實證,
`renderers/shared/desktop-readability.mjs`):

```
projectedFontPx = sourceFontPx × min(1, 930 / viewBoxWidth)     下限 6px

sublabel 的 font-size 是 7.5
→ viewBox 寬度上限 = 930 × 7.5 / 6 ≈ 1162px
```

主線 6 個節點時 viewBox 很容易超過 1162,這時**任何一句**長 sublabel 都會讓 validate 回
`Final artifact check failed`。**實務上 sublabel 控制在 10 個全形字內。**

⚠️ 這個失敗是**全域耦合**的:錯誤只會點名一句,但縮短**任何**節點的字都能救,
因為問題是整張圖太寬。不要死磕它點名的那一句。

**規則 7 的實測證據**(同一份 JSON,唯一變因是泳道順序):

```
泳道順序 who→flow→out→gate        泳道順序 who→flow→gate→out
(gate 與 flow 中間隔著 out)      (gate 緊貼 flow 下方)

M 1079.6 243                       M 1013.6 269
  L 1200   243   ← 橫向繞出 120      L 1013.6 290   ← 垂直下 21
  L 1200   533   ← 垂直落 290        L 1100.6 290   ← 橫移 87
  L 1013.6 533   ← 折回 186          L 1100.6 367   ← 下 77
  L 1013.6 517                       L 1084.6 367

整張圖 viewBox 1216×652            整張圖 viewBox 1132×652(窄 84px)
```

⚠️ **用 `flow.js` 時,泳道順序怎麼控制**:`lanes` 物件的 key 順序**會被忽略**。
實際規則是「參與者泳道 → 主線泳道 → 例外泳道 → 其餘」,而多條例外泳道之間的先後,
取自 **`exits[]` 陣列裡各條 exit 出現的順序**——第一個出現的 exit 泳道拿到緊貼主線的位置。
要讓分岔線多的那條貼主線,就把它的 exits 排在陣列前面。

⚠️ **把分岔畫成「合法分支」而非「例外」,光把泳道排除在 `exceptionLanes` 外不夠**:
節點與線的預設值仍是紅色的 `security`/`error`。要一起改三個欄位:

```json
{"lane":"ok","col":3,"label":"豁免通過","from":"main3",
 "type":"backend",        // 不是預設的 security(紅)
 "variant":"emphasis",    // 不是預設的 security(紅線)
 "role":"branch"}         // 不是預設的 error
```

⚠️ **這條規則有取捨,不是免費的**:泳道只能有一個「正下方」。
把 `gate` 移上來,原本在那個位置的 `out` 就被推遠,換成它的連線變長。
**判準:哪一條泳道的分岔線比較多、比較重要,就讓它貼著主線。**
分岔線少的那條放遠一點,代價比較小。

### 垂直分岔線要走直線——用 `via`,不是 `route` 也不是 `fromSide`

**現象**:分岔線(主線 → 例外泳道)預設不會垂直落下,而是「下 → 橫移 80 多px → 下 → 左折進節點」的 S 形。
同 col、同寬度、相鄰泳道都一樣會繞——**自動路由就是這樣算的,跟你的欄位沒關係**。

**解法:給 `via` 一個座標點,x 與起點相同。**

```json
{"id":"x1","from":"f1","to":"g1","label":"想動手改東西",
 "variant":"security","role":"error",
 "via":[[279, 320]]}
```

`via` 是 `edges[]` 的欄位(`schemas/workflow.schema.json` 有定義),型別是座標點陣列,
用來手動指定路徑要經過哪裡。**x 填來源節點的中心 x、y 填兩條泳道中間**,線就會垂直落下。

實測對照(同一條線,唯一變因是 `via`):

```
沒給 via                                        給了 via: [[279,320]]
M 279 269                                        M 279 269
  L 279 290                                        L 279 320
  L 362 290   ← 橫移 83                            L 279 341
  L 362 367
  L 346 367   ← 左折 16 進節點                    x 座標全部 279,純垂直
4 段 S 形                                        2 段直線
```

**怎麼拿到起點 x**:先照常 `deliver` 一次,從產出的 HTML 撈該條線的路徑起點:

```bash
grep -o 'data-edge-id="x1"[^>]*' out.html   # 或用 data-relationship-id
# 取 d="M <這個數字> ..." 的第一個座標
```

y 值取兩條泳道之間即可(泳道 y 從 `data-composition-frame-id="lane-N"` 的 `y`+`height` 算)。

⚠️ **`via` 不要跟 `route` 一起給**——`route` 是預設路徑樣板,`via` 是手動座標,同時給會打架。
用 `via` 就把 `route` 拿掉。

⛔ **以下四種做法都試過,對這個 S 形完全無效**(路徑座標一個像素都沒變,別再試):

| 試過什麼 | 結果 |
|---|---|
| 調泳道順序讓例外泳道緊貼主線 | 路徑座標完全沒變 |
| 統一同 col 的節點寬度 | 路徑座標完全沒變 |
| 改 `variant`/`role`(試 `dashed`+`async`) | 路徑座標完全沒變 |
| 給 `route:"drop"` | validate 失敗:`cannot satisfy route preset "drop" under readable-v2 constraints (minimum 8px endpoint stubs, 16px interior turns, and 28px direct clearance)`——泳道間距 `laneGap` 寫死 20px,小於 drop 需要的 28px |

📌 **查工具能力的正確順序**:先讀**輸入 schema**(`schemas/*.schema.json`,它列出你能給什麼),
再讀執行端原始碼。反過來做會把「我沒給對參數」誤判成「工具做不到」——
本節初版就是這樣寫成「archify 硬限制、無解」的,實際上 `via` 一直都在 schema 裡。

**規則 8 ——「垂直線給方向」為什麼行不通**(連續三次實測):

| 做法 | validate 結果 | 錯誤原文 |
|---|---|---|
| `route:"straight"` + `fromSide:"bottom"` + `toSide:"top"` | ✗ 失敗 | `cannot satisfy route preset "straight" under readable-v2 constraints (minimum 8px endpoint stubs, 16px interior turns, and 28px direct clearance)` |
| 只給 `fromSide:"bottom"` + `toSide:"top"`(多條一起給) | ✗ 失敗 | `has explicit geometry that violates explicit route-route crossing` |
| 只給一條垂直線 `bottom`/`top` | ✗ 失敗 | `Final artifact failed composition/desktop-readability` |

**`route:"straight"` 與 `fromSide`/`toSide` 是主線(左右向)專用**。
垂直分岔線**什麼都不要給**,讓 archify 自動路由——它算得出合法的最短 S 形折線;
你手動指定只會撞上 readable-v2 的幾何下限(端點 8px、內折 16px、直線淨空 28px)。

要縮短垂直線,改用**規則 7 調泳道順序**,不要去指定方向。

`route` 的合法值:`auto` | `straight` | `drop` | `outside-right` | `return-left` | `bottom-channel` | `up-channel`。
**只有 `straight` 經過實測**,其餘未驗。

⚠️ `meta.viewBox` 只吃兩個值 `[寬, 高]`(不是四個),且編譯器有硬下限,壓太小會被拒:
`Workflow viewBox 1280×720 cannot contain the readable-v2 layout; minimum 918×954`。

**可直接套用的 JSON 骨架**(欄位名稱照抄,內容換成這次要畫的):

```json
{
  "schema_version": 2,
  "diagram_type": "workflow",
  "meta": { "title": "…", "quality_profile": "showcase" },
  "lanes": [
    {"id":"role","label":"經手角色"},
    {"id":"plat","label":"單據流程"},
    {"id":"stop","label":"退件與阻擋","variant":"exception"}
  ],
  "phases": [
    {"id":"p1","label":"階段一","fromCol":0,"toCol":1},
    {"id":"p2","label":"階段二","fromCol":2,"toCol":3,"variant":"emphasis"}
  ],
  "mainPath": ["n0","n1","n2","n3"],
  "nodes": [
    {"id":"n0","lane":"plat","col":0,"type":"frontend","label":"填單","width":124},
    {"id":"n1","lane":"plat","col":1,"type":"backend","label":"送審","width":124},
    {"id":"n2","lane":"plat","col":2,"type":"backend","label":"簽核","width":124},
    {"id":"n3","lane":"plat","col":3,"type":"cloud","label":"歸檔","width":124},
    {"id":"r0","lane":"role","col":0,"type":"external","label":"申請人","width":118},
    {"id":"s0","lane":"stop","col":1,"type":"security","label":"退件","width":124}
  ],
  "edges": [
    {"id":"m0","from":"n0","to":"n1","label":"送出","variant":"emphasis",
     "route":"straight","fromSide":"right","toSide":"left"},
    {"id":"m1","from":"n1","to":"n2","label":"轉簽","variant":"emphasis",
     "route":"straight","fromSide":"right","toSide":"left"},
    {"id":"m2","from":"n2","to":"n3","label":"核准","variant":"emphasis",
     "route":"straight","fromSide":"right","toSide":"left"},
    {"id":"t0","from":"r0","to":"n0","variant":"dashed","role":"async"},
    {"id":"x0","from":"n1","to":"s0","label":"不符規定","variant":"security","role":"error",
     "via":[[272, 290]]}
  ]
}
```

⚠️ 骨架裡 `x0` 的 `"via":[[272, 290]]` 是**這個骨架量出來的實值**(已驗證可跑)。
換成你自己的圖時**一定要重新量**——
先不帶 `via` 跑一次 `deliver`,從 HTML 撈該線的起點 x 與泳道間的 y,再填回去重跑。
填錯不會報錯,只是線畫歪。

⚠️ **schema 不准任何額外欄位**,連 `_` 開頭的註解欄位都會被擋
(實測:`/ must NOT have additional properties`)。要註解就寫在 JSON 外面。

⚠️ **`lanes[].variant` 只吃 `normal` | `exception`**。寫 `emphasis` 會被擋
(實測:`/lanes/2/variant must be equal to one of the allowed values`)——
`emphasis` 是 `phases[]` 與 `edges[]` 才有的值,別混用。

### 更省事的做法:用同層的 `flow.js` 代跑

上面九條規則可以不用自己記——同層有一支 `flow.js`,給它一份簡單描述就自動套完規則:

```bash
node <本 skill 目錄>/flow.js spec.json out.html
```

`spec.json` 只要寫「有哪幾條泳道、主線幾步、誰連到誰」,**不必寫** `route`/`fromSide`/
`via` 座標/泳道順序——那些正是最容易寫錯的部分,腳本會自己補。格式看 `flow.js` 開頭的註解。

它會自動跑完 `validate` → `deliver` → `visual-check`,並回報泳道高度、直線條數、
哪幾條線算不出垂直路徑。**validate 失敗時它會把 archify 的 `evidence` 帶出來**
(哪一句字太小、差幾 px、整張圖多寬),不必自己去翻 JSON。

⚠️ **archify 裝在非標準位置時**,用環境變數指過去(腳本預設會找 `~/.claude/skills/`、
`~/.agents/skills/`、`~/.local/share/skills/`、`~/node_modules/`):

```bash
export ARCHIFY_PATH=/你的路徑/bin/archify.mjs          # Bash
$env:ARCHIFY_PATH = "C:\你的路徑\bin\archify.mjs"     # PowerShell
```

要自己手寫 archify 原生 JSON 時,才需要下面這三道指令與上面那九條規則。

**三道指令按順序跑,`validate` 要 9/9、0 錯 0 警告才算過:**

```bash
node <archify>/bin/archify.mjs validate     workflow x.json          --quality showcase --json
node <archify>/bin/archify.mjs deliver      workflow x.json x.html   --quality showcase --json
node <archify>/bin/archify.mjs visual-check x.html                   --json
```


- ⚠️ **archify 的回報口吻不要照抄進對話。** 它要求回報 validation summary、receipt、
  SHA-256、browser-evidence status——那是工程交付格式,跟本 skill 的白話重講是相反的東西。
  使用者按 `/wtf` 是看不懂,你丟一串雜湊值給他只會更看不懂。
  **對話裡只留一句「已開在瀏覽器,講的是 X」**,驗證細節除非他問否則不出現。
- ⚠️ **archify 有連外的版本檢查**(`scripts/check-update.mjs` 會查遠端 manifest)。
  它報「有新版」時照它的規則給一則精簡通知就好,**不准自己去更新**——安裝與更新都要他點頭。

**回退手刻 HTML 時怎麼做**:
- **放作業系統暫存目錄,不放專案目錄**:Windows 在 Bash 下讀 `$TEMP`、PowerShell 下讀 `$env:TEMP`
  (實際值依環境而定,不可寫死路徑);macOS/Linux 用 `${TMPDIR:-/tmp}`。
  檔名 `wtf-<主題>-<yyyyMMdd-HHmmss>.html`,帶主題與時間戳,他要回頭找得到;一個檔只講一件事。
  **禁止覆蓋既有檔**:用排他建立(python `open(path, 'x')`、bash `set -o noclobber` 後 `>`),
  **只有「檔案已存在」才換序號後綴重試**,權限不足/目錄不存在/磁碟滿等其他錯誤一律停下回報,不重試;
  「先檢查不存在再寫」不算排他,兩個 session 同時檢查會互相覆蓋。
  放 OS 暫存目錄的理由:那裡的檔案要不要清、多久清,取決於使用者的系統設定(Windows Storage Sense、
  macOS/Linux 的暫存清理機制),本 skill 不負責清理、也不承諾保留期限;專案目錄本 skill 同樣不清,
  放那裡只會累積,還會被 git 或搜尋掃到。
- mermaid 要圖就進 HTML 渲染:終端機裡的 mermaid code block 使用者看到的是原始碼不是圖
  (Claude Code 只在 Artifact 頁面渲染 mermaid,終端機不會),所以「需要 mermaid」本身就是升級理由
- 用真實的標籤與資料,不用 lorem ipsum;配色跟產品一致
- **開給他看,不是叫他自己開**:
  - Windows:CMD `start "" "路徑.html"`;Git Bash `cmd //c start "" "路徑"`;
    PowerShell `Start-Process -FilePath "路徑.html" -ErrorAction Stop`(PowerShell 的 `start` 是 Start-Process 別名,
    CMD 寫法的空字串會被當 FilePath 直接報錯;不帶 `-ErrorAction Stop` 開失敗只是警告、exit code 抓不到)
  - macOS:`open 路徑.html`
  - Linux:`xdg-open 路徑.html`
  開完在回覆裡放一句「已開在瀏覽器,講的是 X」,不要把 HTML 原始碼貼進對話
- **開檔指令失敗(SSH、無 GUI、headless)不准假裝開了**:exit code 非 0 或 PowerShell 拋錯就改說「檔案已寫到 `路徑`,
  這個環境無法自動開啟,請自行開」——並把終端機那段結論寫滿一點,讓他不開檔也看得懂主線
  (寫滿=事實不因降級而漏,不是多寫廢話;密度規則照常適用)。
  絕對不能在開失敗後仍宣稱「已開在瀏覽器」。

**升級了終端機那段還是要有**:至少留一句話講結論,讓他不開瀏覽器也知道你在講什麼。

> 這一節與判準表的四列(呼叫樹/偽代碼/diff/檔案樹)改寫自 humanlayer/skills 的 `show-me`
> (MIT)。原版的一句話值得記住:**挑「能把重點講清楚的最小視圖」**——可以用一種、可以用幾種,
> 但不會全部用上。
>
> HTML 分支調用的 `archify` 是 tt-a1i/archify(MIT,基於 Cocoon-AI/architecture-diagram-generator)。
> 本 skill 只借它產圖,不借它的回報格式——它的「三種宣稱嚴格分離」
> (確定性檢查/瀏覽器證據/真人感知審查,通過其一不蘊含其二)倒是值得記住,
> 對應本 skill 的「開檔失敗不准假裝開了」。

### 框字不是畫圖——刪掉框線測試

**判準:把框線全部刪掉、只留文字,意思有沒有變?**

```
                          刪掉框線
                              │
              ┌───────────────┴───────────────┐
         意思完全沒變                    句子散掉了
              │                               │
              ▼                               ▼
      這不是圖,是加了裝飾的清單        這才是圖
      框只是把句子關起來                結構長在線上,不在字裡
      ⛔ 重畫                            ✓ 過關
```

**反例(最常犯的錯)**:

```
   ✗ 框字                                        ✓ 畫圖
   ┌────────────────────────────┐                        這幾個項目
   │ 都成立、可並列 → 表格      │                            │
   │ 只走一條、有先後 → 流程圖  │                ┌───────────┴───────────┐
   └────────────────────────────┘           同時成立                只走一條
                                                 │                      │
   框拿掉照樣讀得懂                              ▼                      ▼
   = 框沒有承載任何資訊                      ┌─────┐              ┌───────┐
                                              │表格 │              │流程圖 │
                                              └─────┘              └───────┘

                                             「分岔」是用線畫出來的,不是用字寫的
```

**框線要承載資訊,只有這幾種方式**:

| 承載資訊的方式 | 長什麼樣 |
|---|---|
| 線代表流向 | `A ──▶ B`,方向就是資訊 |
| 線代表分岔 | 一條線分成兩條,位置代表走哪邊 |
| 位置代表層級 | 縮排樹的深度、上下游關係 |
| 欄位對齊代表對應 | 表格同一列=同一個東西的不同面向 |
| 長度代表量 | 進度條、比例條 |

⛔ **以上都不是,就不要加框。** 純粹的並列清單用 `-` 條列更好讀,
加了框只是讓它變高、佔版面、還讓讀者以為裡面有結構。

**版面要用滿——終端機很寬,右邊留白是浪費。**

📐 **寬度基準要向使用者問到,不准假設。** 超過他的視窗寬度就會折行、整個框爛掉。

**沒有安全的預設值可用**——猜窄了浪費版面(正是這個 skill 要治的病),猜寬了整頁爛版。
所以**問到為止**,不設 fallback。

### 怎麼取得寬度——**先讀設定檔,這是硬性第一步**

寬度存在**與本檔同層的 `config.json`**(隨 plugin 發布,發布時 `terminalWidth` 是 `null`)。

```
                                         使用者按下 /wtf
                                                │
                                                ▼
                              讀同層 config.json 的兩個欄位
                                terminalWidth / widthPromptDeclined
                                                │
              ┌──────────────────┬──────────────┴───────────────┬──────────────────┐
         有數字               有數字                        null              null
      declined≠true        declined=true                declined≠true      declined=true
              │                  │                              │                  │
              ▼                  ▼                              ▼                  ▼
     照那個數字排        照那個數字排                  ⛔ 強制先問          不強制問
     以後也不再問        以後不再主動問                  問到為止        圖表照 80 排
        (終點)        (他只是不想被問,                    │          每次畫圖前一行
                          不是收回給過的值)                  ▼          輕量提醒他可給數字
                             (終點)                 印階梯標尺(見下方)         │
                                                              │                    │
                                                              ▼                    │
                                                        他答了標尺                 │
                                                              │                    │
                                                              │    他事後改變心意  │
                                                              │    主動給了數字    │
                                                              ▼                    │
                                            寫回 config.json 的 terminalWidth ◀────┘
                                                              │
                                                              ▼
                                                   之後照那個數字排,不再問
```
⚠️ 圖上標「(終點)」只是說**這一支不需要再問他**,不是說寬度從此不能改——
**他任何時候主動說要改寬度,一律照做並寫回 `config.json`**,四支都適用。
(只改 `terminalWidth` 就好,`widthPromptDeclined` 維持原狀——它只擋「主動詢問」,
而已經有數字時本來就不會問;硬改回 false 反而可能讓他之後又被問。)

⚠️ hook 只管「擋不擋調用」,**不決定排版寬度**:四支裡只有第三支(null+未拒答)會被擋下,其餘三支都放行。

**讀法**:`cat` 這個 skill 目錄底下的 `config.json`,取 `terminalWidth` 與 `widthPromptDeclined`。
**寫法**:拿到數字後直接改寫該檔的 `terminalWidth`(其他欄位不動)。

⚠️ **不要改成放家目錄、不要改成存 memory。**
- 放家目錄 → plugin 就依賴外部狀態,換台機器/換使用者就失聯
- 存 memory → 靠模型自覺去查,會漏;而且 memory 綁專案,換專案就失效
- **設定檔跟 skill 同層** → 跟著 plugin 走,讀寫都是機械動作,不靠自覺

⚠️ **plugin 更新後,`terminalWidth` 會是 `null`——但舊值沒有被蓋掉。**
Claude Code 的 plugin cache 按版本號分資料夾(`0.4.0/`、`0.5.0/`…),舊版連同它的 `config.json` 原封不動留著;
新版是全新資料夾,`config.json` 就是發布時的空檔。所以更新後會重問一次,
**不是因為值被覆寫,而是因為新版還沒問過**。這不是錯誤——重新問一次寫回新版的檔即可。
(2026-09-05 實測:本機 cache 同時存在 0.1.0/0.4.0/0.5.0 三個資料夾,
使用者填在 0.5.0 的值完好無損,遠端推的仍是 null;舊版目錄帶 `.orphaned_at` 標記,
表示未來可能有清理機制。)
**永遠不要因為「應該問過了」就跳過讀檔這一步,也別依賴「舊值永遠撈得回來」——該問就問。**

⚠️ **開發模式沒有機械閘**:把這個 skill 用 symlink 接進專案的 `.claude/skills/`(monorepo 開發時常這樣做)
只會暴露 SKILL.md,**plugin 的 hooks.json 不會被註冊、閘不存在**,只剩上面的文字規則在守。
這種模式下你更要自己把「第一步讀 config.json」當硬性動作做,沒有人會擋你。

⛔ **不要嘗試用程式量**(2026-09-05 已完整查證,四種方法全部失敗):

| 方法 | 結果 | 為什麼 |
|---|---|---|
| `tput cols` | 80 | 子行程預設值 |
| `process.stdout.columns` | undefined | Claude Code 把 stdout 導管線 |
| PowerShell `WindowSize.Width` | 錯的值 | 讀到中間層的 console buffer |
| P/Invoke `CONOUT$` + `GetConsoleScreenBufferInfo` | 錯的值 | 同上 |

根因:使用者可能是 `終端機 → cmd/powershell → claude → bash` 這種多層結構,
程式只碰得到中間那層的 console,碰不到最外層那個真正在顯示 TUI 的視窗。

### 問法——**一律跑 `ruler.js`,不准自己 print 標尺**

⛔ **禁止模型現場寫 print 迴圈產標尺。** 使用者實測回報:
「同一個電腦同一個螢幕,有的你量出來是 170,有的 200」。
原因之一就是每次現場寫、檔位與寫法都會飄。標尺必須是**機械產生、逐字節相同**的東西。

標尺腳本在與本檔同層:`ruler.js`。**兩輪二分法**,精度 ±4 格。

```
                        terminalWidth 是 null
                                 │
                                 ▼
                  第一輪(粗)  node ruler.js
                  80~220 每 20 格一條
                                 │
                                 ▼
                  問:「哪一條開始斷開、掉到第二行?」
                                 │
              ┌──────────────────┼──────────────────┐
         他回某個數字          都沒斷            第一條就斷
         (例如 180)             │                  │
              │                  ▼                  ▼
              │           寬度 >= 220          寬度 < 80
              │           細測 200 220         細測 40 80
              ▼
   第二輪(細)  node ruler.js 160 180
   (區間=斷掉那條的前一檔 ~ 斷掉那條)
              │
              ▼
   問:「最後一條沒有斷行的是哪個數字?」
              │
              ▼
   他回的數字就是答案 → 寫回 config.json 的 terminalWidth
```

**兩輪的問法不一樣,不要搞混:**

| | 指令 | 問什麼 | 為什麼 |
|---|---|---|---|
| 第一輪 | `node ruler.js` | **哪一條開始斷開** | 找出真值落在哪個 20 格區間 |
| 第二輪 | `node ruler.js <lo> <hi>` | **最後一條沒斷行的是哪個** | 在區間內逼近到 ±4 格 |

⚠️ **標尺要出現在回覆正文,不是只留在工具輸出裡**——工具輸出使用者看不到。
跑完腳本要把標尺那幾行**原樣**貼進回覆給他看,放在 ``` 圍起來的程式碼區塊裡
(避免 markdown 把橫線吃掉或重新折行)。一個字都不准改:不重畫、不截短、不只貼幾行。

> **這條有 hook 機械閘**(`hooks/guard-ruler-pasted.js`,Stop 時機):
> 跑了 `ruler.js` 卻沒把標尺貼進回覆正文,整個回合會被 exit 2 擋下。
> 為什麼需要閘:這條規則本來就寫在這裡、也寫在寬度閘的 stderr 裡,
> **模型照樣漏掉**——使用者 2026-09-15 實際回報「你跑的標尺沒顯示出來」,
> 他螢幕上只有一行「Ran 1 shell command」,等於被問了一個沒有題目的問題。
> skill 寫「必須」是自律、AI 會繞;只有 hook 是他律。
>
> **兩輪都要各自貼**:閘是依時序判的——每次用 `node` 跑 `ruler.js`,
> 都要在那次之後貼出該次的標尺。第一輪貼了不會替第二輪的漏貼背書。
> 只是 `cat`/`grep` 讀 `ruler.js` 的內容不算「跑過」,不會被擋。

拿到第二輪答案後**立刻寫回同層的 `config.json`**(改 `terminalWidth`,其他欄位不動),
之後所有 session 讀檔就有,不再問第二次。**直接寫他回報的數字,不要自己再減安全邊際**——
標尺每行的顯示寬度已經精確等於它標示的數字,他說 172 沒斷就是 172 格放得下。

### 他沒回答寬度怎麼辦——**再問一次,不要自己決定**

⛔ **不准因為他沒答就自己挑一個數字,也不准因此放棄畫圖改用純文字。**

- 他回了別的事、沒提寬度 → **先把那件事處理完,然後在同一則回覆的最後再貼一次標尺**
- 他說「不知道」「你自己看著辦」 → 告訴他這個真的只有他看得到,程式量不到,
  然後**再貼一次標尺**——這次只留三檔(例如 100 / 140 / 180)降低他的負擔
- 他明確說「不要問了」 → 那才停。**同時把 config.json 的 `widthPromptDeclined` 設成 true**——
  否則 hook 會一直擋在 skill 入口,你連這條規則都讀不到(閘會擋住自己的逃生門)。
  設完明講**接下來的圖表可能會折行**,之後每次畫圖前用一行提醒他可以隨時給你數字。
  ⛔ **必須是他明確拒絕過才准設**,不准因為他一次沒回答、或你覺得問了很煩就自己設。

**在拿到數字之前,圖表一律照 80 字元排**——那是唯一不會爆版的寬度。
但這是**暫時措施不是 fallback**:每一輪都要繼續問,不能問一次沒答就默默定案成 80。

⚠️ **中文佔兩格。**算寬度必須用 `unicodedata.east_asian_width(c) in 'WF'` 判斷,
目測必歪——這條規則寫下來之前,連續四次把使用者的畫面排爛。

**手排必歪,用程式產生**。可直接用的骨架:

```python
import unicodedata
def w(s):   # 顯示寬度(中文算 2)
    return sum(2 if unicodedata.east_asian_width(c) in 'WF' else 1 for c in s)
def pad(s, n):
    return s + ' ' * max(0, n - w(s))
def side(*blocks, gap=4):   # 多個區塊橫向並排
    n = max(len(b) for b in blocks)
    ws = [max(w(l) for l in b) for b in blocks]
    return [(' '*gap).join(pad(b[i] if i < len(b) else '', x)
            for b, x in zip(blocks, ws)).rstrip() for i in range(n)]

def wrap(s, n):             # 依顯示寬度折行:英文在空白斷,長詞先找 . / : _ - 斷,再逐字切
    out, cur = [], ''
    for word in s.replace('\n', ' \n ').split(' '):
        if word == '\n':
            out.append(cur); cur = ''; continue
        piece = (cur + ' ' + word) if cur else word
        if w(piece) <= n:
            cur = piece; continue
        if cur:
            out.append(cur); cur = ''
        while w(word) > n:
            cut = ''
            for c in word:
                if w(cut + c) > n: break
                cut += c
            sep = max(cut.rfind(x) for x in './:_-')
            if sep > 0: cut = cut[:sep + 1]
            out.append(cut); word = word[len(cut):]
        cur = word
    out.append(cur)
    return out or ['']

def table(header, rows, widths, gap=1):   # 大表:列=主題、欄=面向,儲存格自動折行
    def line(cells):
        wrapped = [wrap(c, x) for c, x in zip(cells, widths)]
        h = max(len(c) for c in wrapped)
        return ['│' + '│'.join(' ' * gap + pad(c[i] if i < len(c) else '', x) + ' ' * gap
                               for c, x in zip(wrapped, widths)) + '│' for i in range(h)]
    hr = lambda l, m, r: l + m.join('─' * (x + 2 * gap) for x in widths) + r
    out = [hr('┌', '┬', '┐')] + line(header) + [hr('├', '┼', '┤')]
    for i, r in enumerate(rows):
        out += line(r)
        if i < len(rows) - 1: out.append(hr('├', '┼', '┤'))
    return out + [hr('└', '┴', '┘')]

def check(lines, term):     # 送出前必跑:太寬會折行,太窄是版面沒用滿
    widest = max(w(l) for l in lines)
    if widest > term:
        raise SystemExit(f'超出視窗 {widest}>{term},拆欄或縮減內容')
    if widest < term * 0.7:
        raise SystemExit(f'只用了 {widest}/{term} 格,回去把區塊並排')
    return widest
```

**排完一定要跑 `check()`,不要用眼睛判斷版面夠不夠寬。**
兩個門檻各擋一種錯:

| 情況 | 判準 | 為什麼會發生 |
|---|---|---|
| 超出視窗寬 | `widest > term` | 區塊塞太多欄、單一表格本身太寬 |
| 版面沒用滿 | `widest < term * 0.7` | **區塊上下堆疊、忘了並排** |

下限那條是本 skill 最常犯的錯:**「右邊有一大塊空白」目測看不出來**——
畫的人眼裡每個區塊都是滿的,只有量過才知道整體只用了三分之一。

兩個以上的圖或表,只要各自不寬,就**左右並排**放在同一個程式碼區塊裡:

```
藍本                            我們
┌────────┬────────┐             ┌────────┬────────┐
│ 授權列 │ 刪掉   │             │ 授權列 │ 停用   │
│ 外站   │ 會對照 │             │ 外站   │ 不看   │
│ 結果   │ 看不到 │             │ 結果   │ 看得到 │
└────────┴────────┘             └────────┴────────┘
```

也可以「圖配說明」並排,或「流程配結果」並排。單一表格太寬放不下才獨占一行。

**動筆畫圖之前,先數這則回覆有幾個區塊**——數完才知道要不要並排:

```
                        這則回覆有幾個區塊?
                                 │
                ┌────────────────┴────────────────┐
              1 個                            2 個以上
                │                                 │
                ▼                                 ▼
            直接畫                    它們是同一件事的不同面向嗎?
                                                  │
                                 ┌────────────────┴────────────────┐
                               是                                 不是
                     (流程/差異/為什麼,                 (幾個不相干的主題)
                       講的都是同一個東西)                        │
                                 │                                 ▼
                                 ▼                      每塊都講同樣幾個面向嗎?
                        一律用 side() 並排             (在哪/怎麼發生/後果/修法)
                        讀者橫向掃一次看完                        │
                                                  ┌───────────────┴───────────────┐
                                                 是                             不是
                                                  │                               │
                                                  ▼                               ▼
                                          一張大表 table()                上下排,各自獨立
                                         列=主題、欄=面向                中間留一行空行
                                         幾塊散文並排=畫錯                硬並排反而難讀
```

同構區塊合成大表長這樣(列=主題、欄=面向,儲存格自動折行):

```
┌────┬────────────────────────────┬────────────────────────────┬──────────────────────────────┬──────────────────────┐
│    │ 在哪                       │ 怎麼發生                   │ 後果                         │ 修法                 │
├────┼────────────────────────────┼────────────────────────────┼──────────────────────────────┼──────────────────────┤
│ ①  │ 檔案編號                   │ 找最後一筆 +1,沒排序沒鎖  │ 兩人同號撞主鍵,回 9999      │ MAX+鎖,ORDER BY Id │
│    │ FileStorage.cs:111         │                            │                              │                      │
├────┼────────────────────────────┼────────────────────────────┼──────────────────────────────┼──────────────────────┤
│ ②  │ 挑最新關卡                 │ 挑 BpmId 最大的            │ Mock                         │ ORDER BY Headers.Id  │
│    │ ReadService.cs:238         │                            │ 只到秒,同秒打平顯示舊關     │                      │
└────┴────────────────────────────┴────────────────────────────┴──────────────────────────────┴──────────────────────┘
```

這種表的欄寬先給不可斷的詞(檔案路徑、識別字),其餘欄分剩下的寬度;
四個主題四個面向在 170 格塞得下,塞不下先砍面向裡的廢字,再考慮拆兩張。

判準是**「讀者會不會想同時看到它們」**:
要對照著看(改前 vs 改後、兩種做法、圖配說明)就並排;
一個看完才看下一個(三件不同的事)就上下排。

**能用圖講的就不要貼程式碼。** 使用者按這個鈕是因為看不懂,貼一段 code 給他不會變好懂。
偽代碼**不算程式碼**——它是縮排的中文句子(`如果 A 就 B,否則 C`),沒有語法、型別、import,
講演算法時用它;真的 code 只在「他要的就是那段」或「那是不可簡化的證據」時才貼。
例外:他要的就是那段程式碼、或那是不可簡化的證據(見「什麼東西不准簡化」)。

⚠️ 圖表要包在程式碼區塊裡,否則終端機的 markdown 會把它排版壞掉。
中英混排會歪(中文佔兩格),對齊到目視可讀即可,不必逐格對齊。

### 2-2. 先講得出「關係」,才准開始解釋

**查名詞只查到零件的名字,查不到它們怎麼組起來。** 這一條管的是後者。

⛔ **動筆之前先自問:這個東西由哪幾個部分組成、它們怎麼互動?講不出來就是還沒讀懂,回去讀。**

實際踩過的坑(2026-09-14):要解釋一個 plugin 的流程,讀了它的 SKILL.md、shell 腳本、hook,
每一步都讀對了,畫出來的圖卻少了一整條軌——因為那件事的核心是「三軌**並行**」,
而「三軌是哪三軌」這種資訊**不存在於實作檔裡**,它只存在於「這個東西的模型」中。
結果是一張**每步都對、但講錯這東西在幹嘛**的圖。

**為什麼這比講錯名詞更危險**:名詞講錯,使用者會發現「這個詞我沒聽過」;
關係講錯,使用者會**以為自己懂了**——他看到的每一步都對,只是整件事的形狀是錯的。

**怎麼查關係**(依序,查到就停):

| 找什麼 | 在哪 |
|---|---|
| `CONTEXT.md` / `CONTEXT-MAP.md` | 有的話最準,它的 Relationships 段就是在寫這個(見下方致謝) |
| `README.md` 的第一段 | 多半直接寫著「這東西為什麼存在」,實作檔不會寫 |
| `CHANGELOG.md` 最早那幾則 | 記著它原本要解決什麼問題 |
| 主流程的進入點 | 看它**同時**啟動了什麼、**等待**什麼 |

⚠️ **這四個地方常常都沒有。** 沒有就據實說「我是從實作反推的」,
並把不確定的部分標出來——不要把反推講成事實。

**自檢用的三個問句**(答不出來就是還沒讀懂):

```
① 這個東西由哪幾個部分組成?        ← 答不出 = 只讀了片段
② 哪些是同時發生的、哪些有先後?    ← 答不出 = 會漏掉「並行」這種核心機制
③ 少了其中一個部分會怎樣?          ← 答不出 = 分不清主幹與枝節
```

> **出處與差異**:原版 wait-what 指定讀 `CONTEXT.md`(多 context 時循 `CONTEXT-MAP.md`),
> 那個檔由 mattpocock/skills 的 `domain-modeling` skill 維護——它的做法是
> **「不批次、當場寫」**:一個詞在對話中被釐清的那一刻就寫進去,並拿程式碼對帳
> (「你的碼是整筆取消,但你剛說可以部分取消,哪個對?」)。
> 但它明訂 `CONTEXT.md` **is a glossary and nothing else**,只放詞彙、不放關係;
> 關係只在多 context 專案的 `CONTEXT-MAP.md` 才有 Relationships 段。
> **所以照抄原版擋不住本節這個坑**——本節是補上去的,不是原版就有。

### 3. 只用專案既有的詞——**查證,不准瞎掰**

專案叫它「案件」就不要改叫「工單」;叫「中轉層」就不要改叫 proxy。
但**用哪個詞不能憑印象,要去查**。

**依序查,查到就停:**

1. **memory**(`~/.claude/projects/<專案>/memory/`+`MEMORY.md` 索引)——這裡是最可靠的,因為它記的是實際踩過的事
2. **專案詞彙表**——`CONTEXT.md`、`GLOSSARY.md`、`docs/` 底下的詞彙檔,**有才讀,沒有就跳過**
3. **`CLAUDE.md`**——根目錄與各子專案那份
4. **制度層文件**——`.claude/harness/`、`.claude/skills/`、`FLOWS.md`、`README.md`
5. **原始碼本身**——`grep` 那個東西的變數名、函式名、i18n key、資料庫欄位名、註解

**以上皆查無 → 據實說「這個東西在這個專案裡好像沒有固定名字」,然後用最白的白話描述它,並說明你是照什麼推斷的。**

⛔ **絕對禁止**:
- 憑印象講「這個專案應該是叫某某」——沒查過就是沒有依據
- 自己發明一個聽起來很專業的名字
- 從別的專案借詞過來用
- 把英文術語直譯成中文就當成專案詞彙(`middleware` → 「中介軟體」不等於這專案叫它中介軟體)

**非用不可的新詞、外來術語、英文縮寫,第一次出現當場用白話解釋一次**,然後全篇固定用同一個講法。

- 壞:`i18n key 沒對上`
- 好:`多語系對照表(i18n)裡查不到這個代號,所以畫面顯示成空白。`

### 4. 講到「所以呢」

重講完必須落在**使用者接下來要做什麼、或這件事對他的影響是什麼**。
只解釋原理不給結論,等於沒重講。

## 深度階梯——講到夠為止,不是講到滿

改寫自 jackle.pro 的 `/explain-visual`(原版是六個固定章節)。
**這裡改成可選階梯**,因為固定六章節會讓重講版變長,而重講的目標是更短。

**判準:使用者在哪一層停下來都已經懂了。** 從第一層開始,只往下展開「他還不懂的那部分」。

```
  ①  一句話講本質            ─── 絕大多數重講到這裡就夠了
      ↓  這件事有「怎麼運作」的成分才往下
  ②  一張全局圖              ─── 誰跟誰有關係、東西長在哪
      ↓  有「為什麼選這條路」的取捨才往下
  ③  現況 vs 方案對照        ─── 兩欄表格,差在哪一目瞭然
      ↓  抽象講不清、需要具體例子才往下
  ④  一個實際案例            ─── 時間軸或流程圖,標出關鍵決策點
      ↓  他要做決定才往下
  ⑤  優劣與適用場景          ─── 表格,含「什麼時候不該用」
      ↓  他已經決定要動手才往下
  ⑥  分階段實施與風險        ─── 只有真的要動手時才需要這層

  ⚠️ 往下的條件是「這件事本身有沒有那一層」,不是「他有沒有追問」──
     用「等他問」當條件就是擠牙膏(見下方區隔說明)
```

⛔ **不准為了完整而把六層全部展開。** 使用者按這個鈕是因為上一則太難讀,
給他六個章節只是換一種方式難讀。**看不出他卡在哪,就只出第一、二層,讓他自己說要不要更深。**

**每一層都要能單獨成立**——只讀第一層的人不會因為沒讀到第三層而理解錯誤。

### ⚠️ 階梯與「禁止擠牙膏」不衝突——兩者管的是不同東西

```
  ┌──────────────────────┬────────────────────────────────┬────────────────────────────────────────────────┐
  │                      │  管什麼                        │  違反時的樣子                                  │
  ├──────────────────────┼────────────────────────────────┼────────────────────────────────────────────────┤
  │  禁止擠牙膏(鐵則)  │  事實不准藏                    │  「主要有幾個…還有其他的,要我列嗎?」         │
  │  深度階梯(本節)    │  同一件事解釋到多深            │  沒人問就把六層全攤開,重講版比原版還長        │
  └──────────────────────┴────────────────────────────────┴────────────────────────────────────────────────┘
```

**規則:階梯**只**決定「解釋的深度」,永遠不決定「給不給事實」。**

- 使用者問的那件事,**已知的事實一次給完**——這是鐵則,階梯不能凌駕
- 階梯決定的是「這些事實要不要再往下拆解、要不要配案例、要不要談實施風險」
- **鐵則優先於階梯。** 兩者判斷打架時,一律照鐵則走:先把事實給完,再考慮深度

具體情境:

| 使用者說 | 怎麼做 |
|---|---|
| 「這什麼意思」 | 第一、二層。事實給完,但不展開案例與實施細節 |
| 「講詳細一點」「完整講」 | **所有事實一次攤開**(鐵則),深度視情況展到三、四層 |
| 「那個東西是什麼」 | 關於那個東西的**已知事實全給**,不要只給一半等他追問 |
| 看不出他卡在哪 | 出第一、二層,並**明說還有哪幾層可以往下**,讓他選——這是給選單,不是藏資訊 |

⛔ **禁止用階梯當藉口分批給事實。** 「先講這層,他要再問我再補」=擠牙膏,違反鐵則。

## 講「為什麼」,不只講「是什麼」

同樣一個事實,**帶理由的版本佔的版面幾乎一樣,但讀者記得住**:

```
  只講是什麼                                   帶為什麼
  ┌────────────────────────────────┐           ┌──────────────────────────────────────────────────┐
  │ 授權列改成停用,不是刪除       │           │ 授權列改成停用,不是刪除                         │
  └────────────────────────────────┘           │   ── 刪掉的話歷史案件會跟著消失                  │
                                               └──────────────────────────────────────────────────┘
```

**設計取捨、為何不選另一條路、什麼情況下這個做法會失效**——這些是使用者真正需要的,
比把「是什麼」重複講三次有用。

## 什麼東西不准簡化

以下內容**逐字保留,不改寫、不摘要、不翻譯**:

- 錯誤訊息、log 行、堆疊追蹤
- 指令、程式碼、SQL、檔案路徑、識別碼
- 數字與門檻值(**簡化過的數字就是錯的數字**)
- 有範圍限定的條件——`只有第一次啟動時` 不准變成 `每次都`

簡化的是**我的解釋**,不是**證據本身**。

## 特別注意這幾種情況

**使用者是在說「我早就講過了」**
若使用者的「聽不懂」其實是指「我交代的事你沒照做」(例如「還原兩個字你聽不懂嗎」),
那問題不在用詞——但**第零鐵則仍然適用:不要在 `/wtf` 裡動手把那件事做掉。**

正確做法是**當場把落差講清楚**,然後問他要不要現在動手:

- 說清楚你當時做了什麼、跟他要的差在哪、為什麼會差
- 說清楚要照他的話做的話,具體會動到什麼
- 然後問一句「要我現在動手嗎?」——他在 `/wtf` 之外說要,才動手

⛔ **不要因為心虛就直接動手補救。** 他已經被你做錯一次,
未經確認的第二次動手只會讓他更難收拾;而且他按 `/wtf` 代表他還在確認你到底懂了沒。

**使用者要的是產出物**
若上一則是要交出一段文字(commit message、寄出去的信、貼給別人的說明),
重講時只給那段文字本身,上面不加前言、下面不加「需要我改嗎」。

**使用者要求講詳細**
「講清楚一點」「完整講」「為什麼會這樣」——**放寬長度上限,但不放寬用詞,也不放寬「不准廢話」。**
所有決策、數字、條件、風險全部給,仍然一句 20 字、仍然白話、仍然能畫就畫。
「詳細」是指事實給滿,不是指字數變多——鋪陳與重複照樣要砍。

## 送出前自檢二十題

0. **我這一輪有沒有動手改東西、跑指令、裝東西?**——`/wtf` 只准解釋。
   他叫我做的事,我是「解釋它」還是「做了它」?做了就是違反第零鐵則。
   唯一例外是 wtf 自身動作(讀檔查證、寫 config.json、產 HTML 圖表、開瀏覽器)
1. **我讀過同層的 `config.json` 了嗎?**——`terminalWidth` 是 null 就先問到再畫圖,拿到要寫回檔案;行寬目測不算,要用程式量
   問寬度時**我是跑 `node ruler.js` 還是自己 print 標尺?**——自己 print 就是違規,那正是「同一台機器量出不同值」的成因
   跑完標尺**我有沒有把它貼進回覆正文**?——只留在工具輸出裡等於沒給他看,他螢幕上只有「Ran 1 shell command」(有 Stop hook 擋,但別等被擋才想起來)
   我有沒有跑**第二輪細測**?——只跑第一輪精度只有 ±20 格,等於沒解決問題
2. **我有沒有把六層全部展開?**——看不出他卡在哪就只出第一、二層
3. **我有沒有拿階梯當藉口分批給事實?**——「先講這層他再問我再補」=擠牙膏,鐵則優先於階梯
4. **這一則是不是把原文加粗加標題就當重講了?**——換字才算,重貼不算
5. **證據有沒有被我簡化?**——錯誤訊息、SQL、路徑、數字、範圍限定條件必須逐字保留
6. **有沒有講到「所以呢」?**——只解釋原理不給結論等於沒重講
7. **同樣的事實,我有沒有用更少版面講完?**——有廢話、有重複就回去砍
8. **有沒有哪段在描述關係,卻用句子寫而不是畫圖表?**
9. **每個框,刪掉框線後意思會變嗎?**——不會就是框字不是畫圖,改成條列或重畫成真的圖
10. **每個表格/清單,那些項目會同時成立嗎?**——不會、只走一條就是流程圖,畫錯了
11. **有幾個結構相同的區塊?**——每塊都講同樣幾個面向,就是一張大表的幾列,不是幾塊散文並排
12. **我跑過 `check()` 了嗎?**——目測看不出「版面沒用滿」,`widest < 視窗寬 × 0.7` 就是區塊該並排沒並排
13. **有沒有貼程式碼,但其實可以用圖講?**——偽代碼不算程式碼,可以用
14. **題材判對了嗎?**——畫「流程」(系統流程、多情境多單位、審批與例外、一張圖講得完的事)就該用 archify,
    不要硬塞進終端機;畫「關係/對照」(兩三欄比較、並列清單、三五個節點的短因果)就留終端機,不要開瀏覽器
    升級了的話:**我有沒有跑 `archify.mjs doctor` 驗活,而不是只看目錄在不在?**
    偵測不到時,**我是問了他要不要裝,還是自己裝了/自己跳過了?**——自己裝違規,靜默跳過也違規
    問的時候**有沒有說明這是什麼、為什麼要、動到什麼、不裝會怎樣**?——只丟一個名字=要他為陌生詞做決定
    問句是不是放在回覆末尾、而且圖已經先給他了?——開頭塞安裝問題=又丟一件他看不懂的事
    走 archify 的話,`deliver` 是不是 exit 0?——非 0 就是沒產出,不准說成功
    檔案是不是放在 OS 暫存目錄、用排他建立?——放專案目錄就是在堆本 skill 不會清的垃圾
    對話裡有沒有跑出 SHA-256、receipt、validation summary?——那是工程口吻,看不懂的人不需要
15. **有沒有只寫「是什麼」沒寫「為什麼」?**——設計取捨、為何不選別條路,那才是他要的
16. 第一句有沒有給脈絡?有沒有句子超過 20 字?
17. **文中每個專案名詞,我是查到的還是想到的?**——想到的就是瞎掰,回去查
17-1. **我講得出這東西由哪幾個部分組成、它們怎麼互動嗎?**——講不出來就是還沒讀懂,
    回去讀再解釋。名詞對但關係錯,使用者會**以為自己懂了**,比看不懂更糟
18. 查無名字的東西,我有沒有據實說「這個沒有固定名字」,而不是自己取一個?

Files in this skill

  • SKILL.md76.3 KB
  • config.json1.6 KB
  • flow.js23.3 KB
  • ruler.js3.8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.