AGENTS.md@.codex · git:20260831.55d5121 · 2026-08-31 · sha256 85211c9d809fb6e9

AGENTS.md@.codex git:20260831.55d5121B

Immutable. This exact content is served forever at /api/v1/blob/85211c9d809fb6e9.

<!-- 由 .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 自己的目錄裡帶著它需要的東西——腳本、參考文件、範例。

**框架維護 skill,這是它的立場。** 只有非常輕量的個人習慣留在框架裡,其餘都不是它的事。
一次工作要擴到 skill 以外之前,那是使用者的決定,不是推導得出來的結論。

**一個名字不是根據。** 某支腳本、某個欄位、某個目錄的名字看起來像在做某件事,不使它就是
那件事。要拿它當根據,先讀它真正被誰呼叫、回答什麼問題。2026-08-24 的標本:使用者要的是
「skill 分類」,而 `skill_scope.py` 這個名字被當成了那件事的權威——它五個呼叫者問的全部是
「這份東西會不會同步到 polaris」,沒有一個在問 skill 是哪一類。**接下來三步全錯**:拿那支
腳本碰得到的東西(rules、hooks、gate)當成範圍、宣布使用者的分類少一格、然後回頭改寫
使用者的指示。起點只是一個名字。

**skill 分成三類,問的是同一個問題:這支工具是誰的。** 目的地是類別的結果,不是類別的
定義:

| 類別 | 是誰的 | 進哪裡 |
|---|---|---|
| `universal` | 大家的 | 公司 repo + template repo |
| `company` | 某一家公司的 | 只進公司 repo |
| `personal` | 我的 | 哪裡都不進,留在 `~/.claude/skills/` |

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

**那份表是正向表列:只有列名的才出得去。** 沒宣告的、宣告了表上沒有的值的、拼錯的,
三種都不出去。同步的目的地是一個公開 repo,讀寬了收不回來。

**「不依賴環境」不是其中一格,它是所有 skill 的開發準則。** 每一支都要問同一個問題:
照這支 skill 自己的描述,把它做成通用、不依賴環境,是不是更好用?想像的使用者不是你,
是一個不會寫程式、連工作環境都初始化不了的人,由工程師把 skill 匯進他的 Claude Desktop。
**在那裡不成立的東西,就是不該留在 skill 裡的東西**——「主 checkout 在哪」「這個 workspace
的設定在哪」在那裡沒有答案,所以一支 skill 要嘛不問這些問題,要嘛問不到的時候說出來並
照樣工作。

**個人的東西不進公司 repo。** 這個 repo 的 origin 是一個公司的 GitHub,所以「留在 local」
的意思是留在使用者自己的地方。宣告了 `personal` 卻躺在這個 repo 裡,
`gate-template-leaks.sh` 會擋下來並指名。

## 東西住哪,看它負責誰

**框架只負責它自己,一張單的工作只在 `issues/` 那張單裡,skill 只負責 skill 自己。**
沒有「慣例」這種理由:一個位置要嘛推導得出來,要嘛是錯的。

| 這個東西負責誰 | 住哪 |
|---|---|
| 整個 repo(每次 commit 掃全樹的那些檢查、git hooks、壓版) | `<repo>/scripts/` |
| 某一支 skill | 那支 skill 自己的目錄 |
| 某一張單(一次性的驗證、探測、注入腳本) | `issues/{那張單}/` |

以前這裡寫的是「沒有共用的腳本目錄,因為只有 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 的人看不出來**。三個條件同時成立才留,缺
一就不要寫——不是寫小一點,是不要寫。

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

- 「這樣才知道有沒有壞掉」——壞掉會在下一次用的時候壞給人看,那是最便宜的回饋。
- 「這條 assertion 要有機械的量測才算數」——框架自己就有〈靜態檢視〉那一格,判斷報告不擋人。
  選機械 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`——一份文件該住在哪、由誰搬。
- `cross-session-command.md`——這台機器上同時有好幾個 session 在跑的時候,誰說了算。

放在這裡的判準是「需不需要被帶走」: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`。