技術觀點 · 2026-09-25 · 11 分鐘閱讀
六個 repo 不是為了整理檔案,是為了讓「誰說了算」可以被機器驗證
把知識拆成六個 repo,解決的不是「東西太多找不到」,是「找到三份互相矛盾的東西,而你沒有辦法判斷哪一份算數」。這篇拆解為什麼目錄結構在這種架構裡不是分類法,而是權限法,借 Ostrom 的制度設計、Lessig 的「程式碼即法律」與 Scott 的「合法可讀性」,說明把規則寫進結構要付出的兩個代價。
Tags: 系統架構, 工作流
把知識拆成六個 repo,解決的不是「東西太多找不到」,是「找到三份互相矛盾的東西,而你沒有辦法判斷哪一份算數」。
在這種架構裡,目錄結構不是分類法,是權限法。
分層分的是權限,不是主題
六個 repo 各自被指定一個 canonical role,這個 role 決定它有權宣告什麼是真的。
| 層 | repo | 有權宣告 | 明文禁止 |
|---|---|---|---|
| 真相層 | ObsidianBook |
原始素材、決策、長期記憶 | 跨倉 AI 政策 |
| 控制層 | Ai-agent |
AI 規則、架構契約、review gate | 私人知識全文、公開文章正文 |
| 隔離層 | agent-skill-lab |
外部工具的評估結果 | 正式 AI 規則、canonical memory |
| 公開層 | lab / soda-digital-garden |
對外發布的版本 | 作品集真相、AI 治理 |
| 執行層 | soda-cloud-agent |
批次結果、draft PR | canonical memory 直寫、最終批准 |
關鍵在右邊那欄。每個 repo 被禁止的事,比它被允許的事更能定義它。 控制層不放知識全文,所以規則不會因為引用了過期的內容而失效;執行層不能直寫記憶,所以批次跑錯不會污染真相。
這張表其實在回答 Elinor Ostrom 在《Governing the Commons》裡問的同一個問題:一群人共用同一批資源時,要靠什麼機制才不會互相踐踏。Ostrom 觀察真實運作良好的共有資源制度後,整理出幾條反覆出現的設計原則,最基本的兩條是「邊界要清楚定義」與「規則要匹配在地情境」。六個 repo 之所以分層,不是因為知識天生分六種,是因為長期記憶、跨倉規則、外部工具評估、對外發布、批次執行這幾種使用情境各自需要不一樣的寫入規則;勉強塞進同一個 repo,等於用同一套規則管完全不同性質的資源,遲早有一邊被犧牲。分層,是先承認情境不同,再讓規則跟著情境走。
三條把判斷寫進結構的規則
第一,一個問題只能有一份機器真相。
docs/repo-registry.json 退休時,架構文件寫的是「不得恢復第二份機器 registry」。被禁止的不是那個檔案,是「兩份 registry 同時存在」這個狀態。差別在於:禁止檔案,換個檔名就繞過了;禁止狀態,繞不過。
第二,權限差異寫在 repo 邊界,不寫在守則裡。
人類可以直接維護 ObsidianBook/main;雲端自動化只能開 feature branch 與 draft PR,不得 push ObsidianBook/main、lab/main、soda-digital-garden/main。這條規則的執行者是 git,不是自律。
這個選擇背後有一個更一般化的原則,Lawrence Lessig 在討論網路治理時講得最清楚:規範一個系統的力量有四種——法律、社會規範、市場,以及程式碼/架構本身,而程式碼是最不需要對方配合、也最難討價還價的一種。他的名言「code is law」講的就是這件事:架構怎麼寫,就決定了系統裡實際上能做什麼,跟寫在文件裡「應該」怎麼做是兩回事。寫在守則裡的規則,趕時間時會被跳過,因為它終究依賴人記得、願意遵守;寫在分支保護裡的規則不會,因為違反它的動作在技術上根本執行不了。守則管的是「大家同意該怎麼做」,分支保護管的是「就算有人不同意,系統也不讓步」——真正不能被繞過的規則,要往後者靠。
第三,狀態用資料夾表示,不用欄位表示。
寫作生命週期是 草稿/ → 待發佈/ → 已發佈/,資料夾就是狀態的唯一真相。一篇文章在哪一格,ls 一次就知道,不需要打開檔案讀 frontmatter,也不會出現「欄位寫 published 但人在草稿夾」這種只有腳本看得懂的矛盾。
代價:契約會跟現實漂移
這套做法有代價,而且今天就撞到了兩個。
第一個:發布流程的契約規定,必須從目標 repo 的 dev 分支開工作區。但 lab 實際上只有 develop,沒有 dev。契約與現實漂移了,發布流程直接停住。
這說明了守門機制的好壞不在於它會不會擋錯,而在於它失敗的方式。這裡的漂移是以「流程停下來」暴露的,不是以「悄悄發到錯的分支」暴露的。前者要花十分鐘修契約,後者要花一天回收一篇上線的文章。
第二個:把已發布的文章全部退回草稿之後,待發佈/ 與 已發佈/ 因為變空,從版本控制裡消失了。狀態的載體是資料夾,資料夾空了,狀態就沒有地方站。補一個 .gitkeep 就解決,但前提是你知道這個代價存在。
這兩個代價其實是同一件事的兩面。James C. Scott 在《Seeing Like a State》裡討論的是國家為了治理而把複雜現實簡化成可讀的地圖——地籍、戶籍、標準化的度量單位——這種「合法可讀性」(legibility)讓治理變得可能,但地圖終究不是地形本身,一旦現實跑到地圖沒畫到的地方,簡化的代價就會現形。把知識架構寫進資料夾與分支保護,本質上也是在製造一份「可被機器讀懂」的地圖:資料夾名字就是狀態,分支名字就是權限邊界。這份地圖讓 AI 和自動化能可靠地運作,但它同時假設了 dev 這個分支名字、草稿/待發佈/已發佈 這三個資料夾永遠存在。契約不會自己去核對現實有沒有改變,只有現實真的偏離的那一刻,才會發現地圖和地形已經不一樣了。
把規則寫進結構之後,規則自己也變成一份需要維護的真相。 這不是這套做法的漏洞,是它的使用條件。
有人可能會問:這樣豈不是用六個 repo 的維護成本,去換一個原本一個 repo 加權限管理就能解決的問題?這個質疑的前提,是相信「權限管理」只靠設定使用者角色就能完成。但這裡談的權限不是「誰可以讀寫哪個資料夾」,是「誰的宣告算數」——語意層的問題,不是存取控制層的問題。同一個有權限的人,可以在一個 repo 裡兩個地方寫下互相矛盾的答案,資料夾權限擋不住這件事。六個 repo 解決的,正是把「誰說了算」提前寫進邊界,而不是事後靠人比對哪一份新一點。
你的下一步
- 列出你現在有幾個地方在存同一類東西 —— 筆記軟體、雲端硬碟、聊天記錄、某個資料夾。
- 對每一類問一次:哪一份算數。答不出來的那幾類,就是你真正的問題所在。
- 把答案寫成機器能檢查的形式 —— 一條分支保護規則、一個目錄約定、一次 CI 檢查。寫不成機器能檢查的形式,代表那個答案還不夠清楚。
知識管理的難題從來不是儲存,是授權。你不需要六個 repo,你需要的是對每一類資訊都能回答「哪一份算數」,而且那個答案不能只存在你腦子裡。