<!-- 由 .claude/instructions/compile.sh 產生，不要直接改這個檔。 -->
<!-- 來源：.claude/instructions/manifest.yaml 的目標 codex -->
<!-- 重新產生：bash .claude/instructions/compile.sh --target codex -->
<!-- 檢查有沒有過期：bash .claude/instructions/compile.sh --check -->

# Polaris

## 這個 workspace 是什麼

一組 skill。每支 skill 自己的目錄裡帶著它需要的東西——腳本、參考文件、範例。

**東西有兩種帶走的方式，而它們帶走的範圍不一樣。** 這個分別決定每一樣東西住在哪：

| 通道 | 帶走什麼 | 走這條的 |
|---|---|---|
| template repo | `.claude/rules/`、`.claude/hooks/`、`.claude/skills/`、`_template/`、根目錄檔案 | 框架自己整包搬家 |
| claude.ai／Cowork 單支上傳 | 只有那一個 skill 目錄 | 要能像第三方 lib 一樣單獨用的 skill |

**每一份東西自己說出它走哪一條**，核心不從名字或位置推導：skill 寫在 frontmatter 的
`scope:`，`.claude/rules/` 與 `.claude/hooks/` 沒有 frontmatter 可放，寫成一行
`POLARIS-SCOPE:` 標記。兩種寫法問的是同一份表——`skill_scope.py` 的 `TEMPLATE_FACING`。

**那份表是正向表列：只有列名的才出得去。** 沒宣告的、宣告了表上沒有的值的、拼錯的，
三種都不出去。以前這裡是黑名單加一個「沒宣告就當 standalone」的預設，而同步的目的地是
一個公開 repo——第三類（個人的、還沒想清楚的）出現的那一天，它會靜靜地出去。

- `scope: framework`——**這一格幾乎是空的，而且應該保持空的。** 只有那些照描述做成通用
  就不成立的東西才屬於這裡：釋出尾段讀這個 repo 的 `.changeset/`、推這個 repo 的 tag、
  同步到這個人的 template repo，換一個環境它就沒有意義。
- `scope: standalone`——**其餘全部。** 判準是一個問題：**照這支
  skill 自己的描述，把它做成通用、不依賴環境，是不是更好用？** 幾乎每一次答案都是「是」。

  想像的使用者不是你：一個不會寫程式、沒有維護能力、連工作環境都初始化不了的人，
  由工程師把 skill 匯進他的 Claude Desktop。**在那裡不成立的東西，就是不該留在 skill
  裡的東西**——「主 checkout 在哪」「這個 workspace 的設定在哪」在那裡沒有答案，所以
  一支 standalone skill 要嘛不問這些問題，要嘛問不到的時候說出來並照樣工作。
- `scope: company-only`／`scope: maintainer-only`——留在這個工作區，不進 template repo。
  它們仍然在表上，只是在另一格：這個 repo 的追蹤範圍收得下它們，`gate-template-leaks.sh`
  對兩格以外的宣告判紅並指名。

以前這裡寫的是「沒有共用的腳本目錄，因為只有 skill 目錄會被帶走」。**那句話是假的**：
template repo 追蹤 `.claude/rules/` 與 `.claude/hooks/`，skill 目錄從來不是唯一會被帶走
的東西。它把通道二的限制套到了通道一的內容上，代價是同一個名字散在好幾支 skill 底下的
那些副本——其中回答「這個 workspace 的某個東西在哪」的那幾類，在通道二那些環境裡根本
沒有答案，複製過去只是雜訊。現在有幾份，問它：

```bash
find .claude/skills \( -name '*.sh' -o -name '*.py' -o -name '*.mjs' \) -exec basename {} \; \
  | sort | uniq -c | awk '$1 > 1 {n += $1; m++} END {print n " 個檔案擠在 " m " 個重複的名字上"}'
```

**不要把上一次量到的值抄成一句話留在這裡。** 它會過期，而過期的數字讀起來跟剛量的一樣。

## 事情怎麼進來

**任何會改變程式碼或行為的請求，第一站是 `driving-work-to-done`。** 使用者不需要知道這個
名字，也不會說出來——「幫我做 X」「這個壞了要修」「想重構 Y」都算。

**只讀的問題不走這條。**「這支腳本在幹嘛」「查一下 X」沒有「怎麼算成功」需要人簽，
直接回答。

那支 skill 是唯一回答「下一步是什麼」的地方：要不要立案、現在在哪一站、什麼時候換站、
什麼時候停、這一類工作怎麼算 done。立案之後的三站——`refinement` 簽下成功的定義 →
`engineering` 施工 → `verify-ac` 判定——各自只做自己那一站的事，不決定往哪走。頭尾兩個閘
在，中間才可以很隨便。

**這段散文不重述那支 skill 的判準。** 判準抄成兩份就會漂，而漂掉的那一刻沒有人在看。

## Skill 優先

使用者的話對上某支 skill 的 trigger 時，**先 invoke 那支 skill**，不要先讀檔、先查 API、
先派 sub-agent。skill 自己會處理它的資料抓取與歧義。

「我已經知道怎麼做」不是跳過的理由——skill 裡帶著你不會記得的步驟。一句話真的對上兩支
skill 時先問人，但要在任何 tool call 之前問，不是讀完再問。

## 判斷順序

衝突時從尾巴開始放棄，第一項絕不放棄：

1. **功能完整**——交付物要真的解決問題，不得裁掉必要功能去換其他屬性。
2. **易讀**——接手的人要能直接看懂。
3. **效能與簡潔**——前兩項相同時，短的、快的、抽象少的贏。

出現候選方案時直接依這個順序決定一個，附理由與取捨；不要把等價選項列出來讓人選。

## 提新東西之前

要開新的單、發明新機制、加新結構之前，先回答「現在有什麼在管這件事」並把它用盡。
證明既有的不夠、而且拿得出證據，才談新增。

自己剛寫出來的判斷是草稿，不是根據。任何驅動決策的句子（「X 需要 Y」「因為 Z 所以卡住」）
說出口時要嘛有證據，要嘛當成待驗的假設，不要拿來當下一步的地基。

### 寫死之前（使用者 2026-08-13 拍板）

**該由人決定的事不要留給推測；推得對的事不要寫死。**

以前這裡的說法是「推測的東西越少，做得越對」。那一句只對一半，而錯的那一半很貴：把本來
推得對的東西也寫成固定流程，付的是 context 與維護，收的是零。外部量到的方向正好相反
——**更強的模型需要更少的規定性工程**，規定性是隨模型能力遞減的東西，不是遞增的。

分界線不需要另外發明，`refinement` 那四格（what／when／why／how）問的就是它：只有人才
答得出來的事（這一版要做什麼、什麼時候要、真正想解決什麼、拿什麼測）必須問，其餘先推
一版。**推錯的成本是重做一次，寫死的成本是每一次都付。**

下面這一條是同一句話用在檢查上的特例。

### 檢查類腳本的門檻（使用者 2026-08-13 拍板）

**預設是不要有。** 每多一道閘、每多一支 selftest，都要先證明它守著一個**不可逆、或會出去
到這個 repo 之外**的後果，而且那個後果**看 diff 的人看不出來**。三個條件同時成立才留，缺
一就不要寫——不是寫小一點，是不要寫。

會被誤認成理由、但都不算的：

- 「這樣才知道有沒有壞掉」——壞掉會在下一次用的時候壞給人看，那是最便宜的回饋。
- 「這條斷言要有機械的量測才算數」——框架自己就有〈靜態檢視〉那一格，判斷報告不擋人。
  選機械 oracle 是因為機械的 PASS 看起來比要人讀的報告強，那是虛榮不是嚴謹。
- 「順手把它守起來」——順手加的東西不會順手被刪掉。

**散文斷言散文**（某個檔案裡有沒有某句話、某個詞出現在哪一層）**不是檢查，是重複。** 它擋
不住任何不可逆的事，但每一次無關的改動都要付錢。

**檢查自己的檢查一律不做。** 一道閘的第二層保險由「它紅的時候有人在看」提供，不由另一支
腳本提供。

2026-08-13 量到的：`.claude/skills` 底下 43,296 行 shell，其中 18,568 行（43%）是檢查類；
而 12,974 行 selftest 裡有 7,382 行（37 支）守的是流程機具自己——閘、oracle、輪次紀錄、
釋出尾段。同一天釋出的九張單，只有兩張改到了使用者會經歷的東西，其餘七張都是機具在修
自己。**這條門檻就是為了讓那個比例不再發生。**（同一天照這條門檻減完之後：檢查類 109 個檔
21,538 行 → 85 個檔 16,518 行，整套 selftest 74 支 121.8 秒 → 58 支 105.7 秒。兩組數字
的「檢查類」定義不同——前面那組只算 selftest 與 gate，後面這組把 check-／validate- 也算
進去，所以不要拿它們相減。）

## 工具不存在時

**停下來並說出修法，不要偷偷安裝。** 禁止 `brew install`、`npm -g`、`pip install`、
`curl | sh`，以及任何往 `PATH` 上丟二進位檔的動作。屬於這個 workspace 的工具，修法是
`mise install`；屬於產品專案的，指向該團隊的 setup 文件。`git` 與 coreutils 假設存在。

這一段在這裡、不在 `.claude/rules/`，是因為它指名了這台機器用什麼裝工具——而那一份必須
到哪裡都成立。它也是這個檔裡唯一一條**安全邊界**：其餘幾段是判斷準則，這一段擋的是一個
收不回來的機器狀態改動。

## 常駐規則

`.claude/rules/` 底下兩份，只有這兩份：

- `style-and-language.md`——用什麼語言寫、寫成什麼樣子。
- `document-flow.md`——一份文件該住在哪、由誰搬。

放在這裡的判準是「需不需要被帶走」：skill 目錄會到 claude.ai 與 Cowork，rules 不會。所以
只有在這個 repo 裡才成立的知識才進 rules，其餘都該長在某支 skill 自己的目錄裡。

## Codex

Codex 讀 `AGENTS.md`。這個 workspace 自己維護一份，`.agents/skills` 是指向
`.claude/skills` 的 symlink，所以 skill 兩邊看到的是同一份。產品 repo 根目錄的
`AGENTS.md` 屬於那個 repo，不由這裡安裝或修改。

**Codex 沒有 hook。** 任何靠 hook 擋下來的東西在 Codex 這邊都不存在，包含語言檢查。
送出回覆前自己確認 `workspace-config.yaml` 的 `language`。
