Back to skills
SKILL.md
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
Works with
Security analysis
100/100Pro scans all 4 files and shows the line behind each finding
npx -y skills add abs1294/fulin-claude-plugins --skill wtf --agent claude-codeAre you the author of Wtf?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/abs1294-wtf)---
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.md
- config.json
- flow.js
- ruler.js
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.