diff --git a/.claude/rules/07-thin-shell.md b/.claude/rules/07-thin-shell.md index 6a6f995..708119a 100644 --- a/.claude/rules/07-thin-shell.md +++ b/.claude/rules/07-thin-shell.md @@ -99,6 +99,38 @@ CLI / MCP / Python lib / JS lib 全是薄殼:只做「介面轉換 + 暴露」 --- +## 3.6 自舉例外:能力該「只實作一次」,但不一定要是 HTTP API(2026-08-12 立) + +> 立這條的原因:`Arcrun#97`(更新把使用者的工作流與登入弄不見)的修法一開始寫在 +> `cli/src/lib/resource-resolver.ts` ——**能力住在介面層,違反 §0**。 +> 後果不是理論:**安裝器(arcrun-rag)拿不到它,於是同一個 bug 只修了一半**, +> 走 `acr` 的人有保護、走 `install.arcrun.dev` 的人沒有——**而所有真實用戶走後者**。 +> leo 2026-08-12:「**根本就不應該在 CLI,我要的是一個大家都可以用到的規則。**」 + +修法(PR #111)把它搬到 **`shared/resource-rule/`:一份零依賴 ESM**, +`acr` 與安裝器共用。**它刻意不是 cypher 的 API 端點**,三個理由: + +| 為什麼不放 API | 說明 | +|---|---| +| **自舉** | 這條規則要在「決定怎麼裝」的當下用得到,而安裝器的工作正是把 cypher 生出來。放進 cypher = 要先有雞才能有蛋。 | +| **輸入是使用者自己的帳號狀態** | 判斷依據是使用者 CF 帳號上的綁定。送去平台託管的 worker 換答案 ⇒ ①「能不能安裝」綁在平台是否活著 ②使用者的帳號拓撲交給第三方。 | +| **它根本不需要是服務** | 這是**純函式**,唯一的 IO 由呼叫端注入。**§0 要求「能力只實作一次」,不是「能力一定要是 HTTP」。** | + +🔴 **所以本檔 §0 的正確讀法是**:能力**只准有一份**,且**不准住在任何單一介面裡**。 +「放 API」是達成它的**常見手段**,不是唯一手段。 +**判準仍然是那句口訣**:「這段邏輯換一個介面要不要重寫?」要 → 它是能力。 + +📌 **給下一個人**:看到 `shared/` 底下的純函式**不要「修正」成 API 端點**—— +先讀 `shared/resource-rule/README.md §2`,那裡記著評估過並否決的其他形態 +(共用 npm 套件=自舉問題換位置;做成零件=要用 TinyGo 重寫一次,那才是第二份實作)。 + +📌 **打包例外**:`acr` 是獨立 npm 套件,`npm pack` 打不進套件目錄外的檔案 ⇒ +`cli/` 下必須有一份**逐位元組副本**。那不是第二份實作—— +`scripts/sync-resource-rule.mjs --check` 一有漂移就 exit 1,且 `build`/`test` 都會先跑它 +(同 `cli/harness/` 的既有慣例)。**手改副本 = build 紅 = publish 擋下。** + +--- + ## 4. 統一帳號來源(薄殼共用同一身份) 所有薄殼讀**同一份**身份設定: