Files
ISEP/docs/cloud-session-bootstrap.md
Claude 762c28c522 雲端 setup:git 認證改用讀環境變數的 credential helper,並讓驗證有能力變紅
雲端實測(真的雲端 session,不是本機模擬):
- session 是 root/HOME=/root,setup 階段寫進 $HOME 的三個機制一個都沒到
  (沒有 insteadOf、沒有 ~/.git-credentials、沒有 /etc/gitconfig)
- 薄殼 settings.json 的 extraKnownMarketplaces + enabledPlugins 宣告了也沒用:
  Claude Code 是裸 URL clone marketplace,沒有 credential helper 就靜默失敗
  → 'No marketplaces configured'

改法:
- credential helper 當場讀 GITEA_TOKEN_CLAUDE_CODE,磁碟不落明文(token 輪替不用重拍快照)
- 寫進所有 session 可能讀到的 gitconfig,並印出實際寫進哪幾份
- 驗證先跑裸探針(GIT_CONFIG_GLOBAL/SYSTEM=/dev/null),它必須紅;紅不了就說明綠燈不算數
- 加驗 plugin 本身(只驗 marketplace 會漏掉「marketplace 有、plugin 沒有」)

在雲端容器內實跑過:裸環境正確失敗 → 補 helper 後 marketplace 就位、
isep@inkstone 0.3.1 installed/enabled,腳本 exit 0。
2026-08-20 15:46:47 +00:00

12 KiB
Raw Permalink Blame History

雲端 session 怎麼載到 ISEP —— inkstone/ISEP#5

對照 docs/governance/sdd-gitea-governance.md §11.3「載入契約」三條硬規則 L11.3.1L11.3.2L11.3.3)與其驗收標準(L11.3.4:能貼出雲端實際載到的清單, 逐條對上 ISEP 的註冊條數)。本檔記錄機制、實測結果、還缺什麼。

舊模型死在哪(不要重蹈)

InkStoneCo/.claude/cloud-shell/generate-shell-payload.py + 薄殼)的模型是: 真身 hook/command/skill → 產生器跑出「薄殼該長的樣子」→ 人推一份複製本進 GitHub 私 repo youlinhsieh/inkstoneco。這條鏈上兩個環節都「要有人記得」:

  • 忘了重跑產生器 → 產物落後真身
  • 產物沒推 → 薄殼落後產物

inkstone/InkStoneCo#57 的實測:薄殼比真身少 7 支閘,其中兩支才立一天。 #14 更早:雲端 33 支閘一支都沒生效。兩次同一個病:任何「複製一份」的設計, 新鮮度只能靠人記得,而人會忘。

新機制:讓 Claude Code 自己的 plugin marketplace 去裝真身

不做複製,改用 Claude Code 原生支援、且經官方文件證實可行的路徑:

  1. ISEP 本身已經是一個合法 plugin.claude-plugin/plugin.json + .claude-plugin/marketplace.json,另一張票的產物),hook 一律用 ${CLAUDE_PLUGIN_ROOT},不寫死路徑。
  2. Cloud environment 的 Setup scriptcode-on-web 原生功能, 在 Claude Code 啟動之前跑,跑在同一台會被拍成快照的 VM 上)裡跑:
    claude plugin marketplace add https://git.uncle6.me/inkstone/ISEP.git --scope user
    claude plugin install isep@inkstone --scope user
    
    這兩行不是複製——跟本機 claude plugin install 是同一條路徑,裝的內容 100% 來自 ISEP 這個 repo 的 HEAD,沒有第二份、沒有產生器、沒有「子集」。
  3. Setup script 跑完,Anthropic 把整個檔案系統(含 ~/.claude/plugins/)拍成快照, 之後每個新 session 直接沿用快照,在 Claude Code 啟動當下(不是「clone 完才補」) plugin 就已經在磁碟上——commandskill 啟動時的目錄掃描掃得到,不再是 #14 那個「hook 可以晚到、skill/command 不行」的破口(見 L11.3.2)。

私有 repo 的認證:不把 token 寫進任何檔案,改用官方文件建議的 CI/CD 寫法—— 用 GITEA_TOKEN_CLAUDE_CODE(既有機器帳號 tokenInkStoneCo#14 已建立的同一把, 沒有新造)在 Setup script 裡做一次 git URL 重寫:

git config --global url."https://x-access-token:${GITEA_TOKEN_CLAUDE_CODE}@git.uncle6.me/".insteadOf \
  "https://git.uncle6.me/"

marketplace add 用乾淨網址(不帶 token),認證完全交給上面那條重寫, 所以 known_marketplaces.json 裡存的來源網址也不帶 token (本機實測驗過,見下面「已驗」第 2 輪)。

完整腳本:docs/cloud-setup-script.sh(貼進 code-on-web 的 Setup script 欄位用)。

🔴 2026-08-20 雲端實測訂正:上面那條 url.insteadOf 不是正解

在真的雲端 session(不是本機模擬)量到的:

量到什麼
session 身分 rootHOME=/root
git config --global --list 只有 harness 自己塞的 identityproxy 那幾條,沒有 insteadOf、沒有 credential.helper
~/.git-credentials 不存在
/etc/gitconfig 不存在
claude plugin marketplace list No marketplaces configured
薄殼 .claude/settings.json extraKnownMarketplaces enabledPlugins 都宣告了

⇒ 兩個結論:

  1. setup 階段寫進 $HOME 的東西沒有到 session 手上。 舊版三個機制(insteadOf~/.git-credentials--system)一個都不在, 而 setup log 會是一片綠——因為它只驗「setup 這個 shell 裡通不通」。

  2. 光在薄殼 settings.json 宣告 extraKnownMarketplaces 沒有用。 Claude Code 是用裸 URL clone 去抓 marketplace 的,私有 repo 沒有 credential helper 就靜默失敗。裸環境重現出來的原話:

    Failed to clone marketplace repository: HTTPS authentication failed.
    Please ensure your git credential helper has valid credentials for git.uncle6.me
    

    補上 helper 之後同一條指令:Successfully added marketplace: inkstoneclaude plugin install isep@inkstoneisep@inkstone 0.3.1 · enabled紅過也綠過,不是只看到綠。

⇒ 改法(已落在 docs/cloud-setup-script.sh):

  • credential helper 當場讀環境變數,磁碟上不落明文 token token 輪替只要改 Environment variables,腳本與快照都不用動):

    git config --file <某份 gitconfig> credential."https://git.uncle6.me".helper \
      '!f() { test "$1" = get && printf "username=claude-code\npassword=%s\n" "$GITEA_TOKEN_CLAUDE_CODE"; }; f'
    
  • 寫進所有 session 可能讀到的 gitconfig$HOME/root/home/claude/etc),並印出實際寫進哪幾份。

  • 驗證要先讓它失敗一次:用 GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null 跑裸探針, 它必須紅;紅不了代表環境裡另有憑證捷徑,後面的綠燈就不能當證據。

已驗(本機,隔離環境,不影響本機正在跑的任何 session)

🔴 怎麼保證沒有干擾:全程把 $HOME 指到 scratchpad 底下的隔離目錄 isep-test-homeisep-test-home2),從未寫到真正的 ~/.claude/ 也沒有動到 InkStoneCo/.claude/ 那份舊設定。兩者互不相干, 本機目前跑著的其他 session/agent 全程沒受影響。

第 1 輪token 直接嵌在 marketplace URL 裡,沿用 ISEP 這個 git checkout 本來就有的、已解析好的 origin 憑證——不是我另外造的憑證,是既有機制解析出來的那份):

$ claude plugin marketplace add "https://claude-code:<token>@git.uncle6.me/inkstone/ISEP.git" --scope user
Adding marketplace…Refreshing marketplace cache (timeout: 120s)…
Cloning repository (timeout: 120s): https://***:***@git.uncle6.me/inkstone/ISEP.git
Clone complete, validating marketplace…
✔ Successfully added marketplace: inkstone (declared in user settings)

$ claude plugin install isep@inkstone --scope user
Installing plugin "isep@inkstone"...✔ Successfully installed plugin: isep@inkstone (scope: user)

$ claude plugin list
Installed plugins:
   isep@inkstone
    Version: 0.0.0
    Scope: user
    Status: ✔ enabled

第 2 輪(重跑一次,改用實際要交付的「乾淨 URL + git config url.insteadOf 重寫」 寫法,驗證 §建議腳本 那段真的可行,而不是理論上可行):

$ git config --global url."https://x-access-token:<token>@git.uncle6.me/".insteadOf "https://git.uncle6.me/"
$ claude plugin marketplace add https://git.uncle6.me/inkstone/ISEP.git --scope user
✔ Successfully added marketplace: inkstone (declared in user settings)
$ claude plugin install isep@inkstone --scope user
✔ Successfully installed plugin: isep@inkstone (scope: user)

$ cat ~/.claude/plugins/known_marketplaces.json
{
  "inkstone": {
    "source": { "source": "git", "url": "https://git.uncle6.me/inkstone/ISEP.git" },
    ...
  }
}

⇒ 存在磁碟上的 marketplace 來源紀錄不帶 token——符合 L11.3.3「憑證只准取名字」。

逐條對照claude plugin details isep@inkstone 的輸出 + 直接數快取目錄裡的檔案, 對照 ISEP 這次測試當下的 Gitea main HEAD):

裝到本機隔離環境的 ISEP main 當下的來源 對上了嗎
Skills 9cp-writedeep-recallissue-handlesdd-checkship-checkwiki-capturewiki-initwiki-recallwiki-update commands/ 7 支 + skills/ 2 支 = 9 逐支比對名稱一致
Commands 目錄 7 個 .md 7 個 .md diff 兩邊檔名清單完全一致
Skills 目錄 deep-recallship-check 2 個
Hook 腳本(hooks/*.sh 實體檔) 42 支 42 支 ls 兩邊都是 42
hooks.json 裡註冊的 hook 腳本路徑(去重) 39 支唯一路徑 39 支 diff 兩邊 grep 結果完全一致

42 支實體檔 vs 39 支被 hooks.json 引用:差的 3 支是 hooks.json 自己 pre-write-guard.shpre-write-guard.template.sh 這類非直接掛註冊的輔助檔, 兩邊都一樣,不是漏裝。)

意外的額外證據:測試途中 ISEP 的 main 因為別的票(#6#9 等)合併而往前推進 (多出 hooks/release-tag-guard.shdocs/governance/…),下一次 claude plugin marketplace update / 重裝立刻拿到新內容——證明這條路徑讀的是 Gitea 當下的 HEAD, 不是任何時間點的快照複製本。

沒驗到的(明講,不含糊)

  • 沒有在真正的 code-on-web 雲端 session 裡跑過。 本機能做到的最接近測試是 「隔離 $HOME + 真的私有 repo + 真的 claude plugin CLI」,但 Cloud environment 的 Setup script 欄位、Environment variables 欄位是 claude.ai 帳號層級的設定, 我沒有去改——那是 leo 的 dashboard,不是這台機器上的檔案,我也判斷這件事 不屬於「可以自己裁」的範圍(不是 GitHub push,但同樣是帳號層級設定, 比照 D20 的精神交給 leo 動手)。
  • 沒有驗到「hook 真的攔下第一個工具呼叫」這一步的完整 runtime 行為—— 只驗到「plugin 在磁碟上正確就位、claude plugin list 回報 enabled」。 完整跑一個已登入的 claude -p session需要這台機器的 Claude Code 登入憑證 (存在 macOS Keychain,不是可複製的檔案),我判斷把它匯出到隔離測試環境 超出這張票該做的事,沒有做。 Q5(plugin 是否在 SessionStart 前同步就位、保證第一個工具呼叫就有效) 這格的證據來自官方文件(plugin-marketplaces.md §Pre-populate plugins for containers:「At startup, Claude Code registers marketplaces found in the seed's known_marketplaces.json... This works in both interactive mode and non-interactive mode with the -p flag.」),不是我自己重現的 runtime 實測
  • Environment caching 的 ~7 天新鮮度窗口沒有解——setup script 只在 「這個 environment 第一次開 session」跑一次,之後沿用快照,直到快照過期 (約 7 天)或 leo 改了 setup scriptallowed network hosts 才重跑。 這代表 ISEP 若在窗口期內更新,雲端會暫時停在舊版本,直到快照重建。 這不是本票要解的「載不載得到」問題,而是另一種新鮮度問題,留給 leo 決定 要不要另開票(例如:leo 定期手動點一下「rebuild environment」,或接受 7 天週期)。

需要 leo 做的(帳號層級設定,非 GitHub push,但同樣是我不該自己動的地方)

去 code-on-web 的 Cloud environments 設定(claude.ai 帳號設定,不是 GitHub、不是 Gitea):

  1. 選 InkStoneCo 這條線在用的 environment(或建一個新的), Environment variables 欄位加一行:GITEA_TOKEN_CLAUDE_CODE=<既有那把值> (名字沿用 InkStoneCo#14 已建立的那把,不要新造;值只有 leo 知道要填什麼, 我這邊沒有也不該有)。
  2. Setup script 欄位貼 docs/cloud-setup-script.sh 的內容。
  3. 開一個新 session(或用 dashboard 的「rebuild environment」逼快照重建), 驗 claude plugin list 顯示 isep@inkstone enabled,且照 §逐條對照 那張表 再核一次數字。

這張票沒動、也不會動的東西

  • 沒有動 youlinhsieh/inkstonecoGitHub 薄殼 repo)——這個新機制完全不需要 改它:安裝目標是 --scope userVM 家目錄),跟 session 從哪個 cwd 啟動無關。 舊模型需要在薄殼裡放 .claude/settings.json 指標,新模型不需要。
  • 沒有 push 到 GitHub、沒有 push 到本 repo的 main。全部改動只在 leaf/5-cloud 這條分支。
  • 沒有把任何 token 值寫進這個 repo 的任何檔案(docs/cloud-setup-script.sh 只引用環境變數名字 GITEA_TOKEN_CLAUDE_CODE)。