SuperPortia 日常運維 — 一個人怎麼管 11 個 Repo
一人團隊管 11 個 repo,聽起來像是在自找麻煩。但如果你把規則寫進工具而不是留在腦子裡,實際上比管一個亂掉的 monorepo 還輕鬆。
這篇文章把 SuperPortia 目前的日常運維架構攤開來說清楚:為什麼要從 monorepo 拆分出去、session 的完整流程是什麼、Work Order 的角色、跨機器 config 怎麼不漂移、以及 hooks 怎麼把規則從「建議」變成「執行」。
日常運維依賴的治理框架詳見 EGS — 工程治理規範;多 agent 協作如何在這個框架下運作,參考 多代理 CLI 協作實錄。
為什麼要拆成 11 個 Repo
SuperPortia 最初是一個 monorepo。所有東西在一起,方便開始,但隨著 agent 數量增加、部署目標分化(Cloudflare Workers、Pages、本地服務),問題開始出現:一個 frontend 的 commit 觸發全域 build、不同服務的依賴互相污染、CI 不知道哪段程式碼改了要重新部署哪個服務。
ADR-0010 確立了拆分原則:1 repo = 1 deployable。每個 repo 有自己的部署單元,可以獨立 commit、獨立部署、獨立管理依賴。用 git filter-repo 拆分,完整保留每個服務的 commit 歷史——不是 git subtree、不是新建空白 repo,是真正的歷史保留。
最終的 11 個 repo 按照用途分類:
| 類別 | Repos |
|---|---|
| 基礎設施 | superportia-ops、superportia-sre、superportia-mcp-server |
| 知識庫 | superportia-vault |
| 產品服務 | superportia-bridge、superportia-command-center、superportia-docs-site |
| AI Pipeline | superportia-ub-pipeline、superportia-ub-worker |
| 內容 | superportia-blog、kol-narratives |
拆完之後,一個 Cloudflare Worker 的改動只影響 superportia-ub-worker。Frontend 改了只跑 superportia-bridge 的 build。每個 deployable 的生命週期彼此獨立,不再互相干擾。
Session 的完整流程
SuperPortia 的工作單位是「session」——agent 開始工作到結束工作的一個完整週期。session 有明確的開始協議和結束協議,不是隨便開個終端機就開始改程式碼。
Session Start
每次 session 啟動,session-startup.sh hook 自動觸發。這個 hook 做兩件事:從 Cloud UB 拉取佈告欄(bulletin board)的最新內容,把它注入到 session context;然後列出開機自檢的待辦清單:
- 查看待辦工單
- 查看 agent 信箱
- agent 報到上線(heartbeat)
- 確認夏哥當前位置
- 回報「開機自檢完成」
這個流程確保 agent 在開始工作之前,先知道公司目前的目標和優先順序。不是靠記憶,是靠工具拉到最新資料。
agent-intelligence-protocol.md 定義了 session start 的具體行為規則:MEMORY.md 是 context,不要在啟動時大量讀 UB entries,只做 2 個呼叫(heartbeat + mailbox check),然後問夏哥要做什麼,按需讀資料。
工作中
工作進行中有幾個關鍵協議:
Pre-Decision 檢查:在做重要決定(選哪個 engine、用什麼架構)之前,先搜尋 UB 有沒有前例。找到就先回報前例再決定;找不到就繼續,備注「no UB precedent」。
Discovery 記錄:學到重要的新事實、模式、洞察時,立刻寫進去——個人知識進 Memory MCP,團隊知識進 UB ingest_fragment。不是「之後再說」,是當下就做。
Correction Capture:夏哥說「這個不對」時,dual ingest——Memory MCP 用 CORRECTION: 前綴,UB 用 ingest_fragment 加 correction tag。個人記憶和團隊記憶都更新。
Session End
session 結束只有一個觸發條件:夏哥明確說「收工」、「結束」、「下班」。Agent 不會自己說「要結束了嗎?」——這是規定,不是禮貌。
結束流程包含三件事:
- 確認所有 ingestion 完成
- 寫當日工作日誌到 vault(
60-Daily/YYYY-MM/YYYY-MM-DD.md) - 寫 session handoff 到 UB(
ingest_fragment,tag:session-handoff)
session_end_daily_log.sh hook 在 session 結束時自動寫一個 stub 到 vault,確保即使 agent 忘記,也有最低保障的記錄。但 stub 只是保障,完整的 handoff 仍然需要 agent 親手寫。
Work Orders 是唯一的任務頻道
公司憲法(Company Constitution)§3 只有一句話:Tasks via WO system. No verbal promises.
不管任務是什麼——寫程式、做研究、部署服務——都必須走 Work Order。口頭說「等等做這個」不算數。沒有 WO,沒有任務。
這個規定解決了一個實際問題:一人帶多個 agent,各個 agent 都有自己的 session,如果任務散落在各種對話裡,很快就會出現「我以為你在做」和「我以為那個不急」的狀況。WO 是唯一的真相來源。
WO 的生命週期:建立(create_work_order)→ 開始工作 → 完成後提交審查(review_work_order)→ 夏哥批准或退回。每個 WO 都有 ID,可以追蹤,可以搜尋,可以跨 session 接手。
Company Constitution §8 補充了一條規則:在同一個 session 內開始或回報 WO,不說「下次再做」。 這防止了 WO 在系統裡堆積成未被處理的債務。
Cross-Ship Sync 與五鐵律
SuperPortia 跑在兩台機器上:SS1(Mac)和 SS2(Windows)。兩台機器都跑 Claude Code,都需要一致的 agent 行為——一樣的 rules、一樣的 skills、一樣的 hooks。
問題是:如果每台機器各自維護自己的 .claude/ config,遲早會漂移。2026-03-10 的事故就是這樣發生的:SS2 比 SS1 少了 11 個 hooks,EGS 落後兩個版本。Agent 在 SS2 上的行為和在 SS1 上不一樣,而沒有人注意到,直到出了問題。
解法是確立 superportia-ops 為唯一 SSoT(Single Source of Truth),然後用五鐵律防止漂移再發生。
五鐵律
鐵律一:SSoT 唯一性。 superportia-ops 是 .claude/ 的唯一來源。想改 skill、rule、hook → 改 ops repo,不改本地。
鐵律二:單向流動。 Config 只從 ops 流向各台機器,永不逆流。SS2 上寫了新 skill?先 PR 到 ops,merge 後再 sync 下來。直接在 SS2 的 .claude/ 改然後「之後再同步」——這就是 drift 的根源。
鐵律三:Sync 覆蓋本地。 sync_from_ssot.sh 執行時,本地 .claude/ 被 ops 版本完全覆蓋。本地未提交的修改會被丟棄。這是設計,不是 bug。
鐵律四:新建必歸源。 在任何機器上新建 skill、rule、hook,必須在 ops repo 上建。流程:ops repo 建檔 → push → 各機器 git pull / sync 取得。
鐵律五:衝突時 ops 贏。 任何 .claude/ 內容衝突,ops repo 版本為準。沒有例外。
實務同步方式
# SS1(Mac)— 一次性設定 symlink
cd ~/Documents/superportia-bridge # 任一 repo
ln -s ../superportia-ops/.claude .claude
# ops 一 push,所有 symlink repo 立刻生效
# SS2(Windows)— 每次 ops 有新 push 後執行
cd C:/Users/XYZ/superportia-ops && git pull
bash scripts/sync_from_ssot.sh # 完全覆蓋本地 .claude/
# 確認 symlink 正確(SS1)
ls -la ~/Documents/superportia-bridge/.claude
# 應顯示 → ../superportia-ops/.claude
SS1 的 symlink 設計意味著:ops repo 一旦有新的 hook 或 rule 被推送,所有使用 symlink 的 repo 立刻反映,不需要手動同步。這消除了「我以為我在用最新版本」的模糊地帶。
sync_from_ssot.sh 執行時,本地 .claude/ 被 ops 版本完全覆蓋,本地未提交的修改會被丟棄。這是設計,不是 bug。如果你在本地的 .claude/ 裡改了什麼但沒推回 ops,那份修改會消失。正確做法:先 PR 到 ops,merge 後再 sync。
SS1(Mac)用 symlink:每個 repo 的 .claude/ 是一個指向 superportia-ops/.claude/ 的符號連結。ops 一 push,所有 repo 的 agent config 立刻生效,不需要手動同步。
SS2(Windows)用複製覆蓋:git pull ops 之後執行 sync_from_ssot.sh,把本地 .claude/ 完全換成 ops 版本。
Hooks 是規則的執行層
規則寫在文件裡,但文件沒有人讀的時候什麼都不是。Hooks 是規則和現實之間的橋樑——它們在工具使用的節點自動觸發,不需要 agent 記住要做什麼。
superportia-ops/.claude/hooks/ 目前有 16 個 hooks。幾個關鍵的:
deploy-gate.sh(PreToolUse, DENY)
攔截所有 wrangler deploy 或 wrangler publish 指令。如果沒有 --dry-run flag,直接 deny,並要求先跑 dry-run 確認,再拿夏哥的 go-ahead。
這個 hook 的起源是一個教訓:agent 自以為在測試環境部署,結果直接推到 production。現在這個情境不可能再發生——工具層面就擋住了。
commit-format-check.sh(PreToolUse, ASK)
檢查 git commit message 是否有 [bracket] 前綴。格式:[Stage X] Verb + Content,例如 [EGS] Add deploy gate hook。格式不對就提問(ASK mode)——不是強制 deny,是給 agent 一個修正的機會。
protect-files.sh(PreToolUse, DENY)
保護敏感 config 不被 agent 直接修改。受保護的檔案清單:.env、.env.local、.env.production、settings.json、settings.local.json、wrangler.toml。任何嘗試 Edit 或 Write 這些檔案的操作都被 deny,並要求夏哥授權。
post-edit-lint.sh(PostToolUse, LOG)
在 Edit 或 Write 操作之後自動執行語法檢查。Python 檔案用 ast.parse;JSON 用 json.load;Shell 腳本用 bash -n。語法錯誤立刻回報,agent 在同一個 context window 內就能修正,不用等到執行時才發現。
pre-commit-build-check.sh(PreToolUse, DENY)
在 commit 之前,如果 staged 的變更包含 frontend 程式碼,先跑 npm run build。Build 失敗就 deny commit。這個 hook 的起源:frontend 壞掉的程式碼被 commit 進去,deploy 之後才發現,要花時間 rollback。現在這個情境在 commit 階段就截住了。
session-startup.sh(Session Start)
前面提到的開機 hook,拉取 bulletin board 並注入 pre-flight checklist。
規則沒有 Hooks 只是建議
回頭看這整套架構,核心原則其實很簡單:把決策留給人,把執行交給工具。
五鐵律防止 config 漂移,但沒有 symlink 和 sync script 的話,鐵律只是文字。WO system 確保任務可追蹤,但沒有 create_work_order 工具的話,還是得靠記憶。Hooks 把規則編碼進 agent 的工具呼叫鏈,讓「deploy 前先 dry-run」不再是一條需要背的規則,而是一個不可跳過的步驟。
一人管 11 個 repo,靠的不是超人的記憶力,而是讓基礎設施記住你該做的事。
SuperPortia 是一個以 AI agent 為中心建構的營運系統,目前由 Mac CLI 小克(Claude Code)在 SS1 執行日常工程工作。架構持續演進中,本文描述截至 2026-03-16 的現況。