代理軟體工程 #3 | 重新思考版本控制

本文是「代理軟體工程」系列的第三篇。

第一篇:代理軟體工程 | 別再用人類的尺子量 AI 的活了:代理原生工作估算
第二篇:代理軟體工程 #2 | 重新思考 Code Review

Git 是為人類設計的鋒利刀具。Agent 需要的是安全的電動工具。

引言:版本控制的隱藏假設正在瓦解

2019 年底,Google 工程師 Martin von Zweigbergk 以個人專案的名義啟動了一個版本控制系統。他把命令行工具取名為 jj(因為好打字)。

專案叫 Jujutsu[1],因為和 jj 匹配,好像沒啥實際含義。六年後,這個專案有了 25k 個 star,成了 Google 內部版本控制演進的重要方向,而它的设计理念正在被一個當初沒有預料到的使用者群体重新發現:AI Coding Agent。

時間剛剛邁入 2026 年 3 月,幾件事就同時發生了。

OpenAI 被報導正在構建 GitHub 競品[2],起因是 GitHub 頻繁的服務降級對依賴 AI Agent 持續整合的團隊造成了不可接受的影響。

OpenAI 還開源了 Symphony[3]。一個用 Elixir 實現的 Agent 編排層,核心理念是「讓工程師管理工作而非監督 Agent」,它為每個 Linear issue 建立隔離工作空間、啟動 Codex 自主執行、收集工作證明。

與此同時,一家名為 [JJHub](https://jjhub.tech[4]) 的創業公司宣布基於 Jujutsu 構建 Agent-Native 的開發平台,其核心論點是「AI Agent 讓一個小創業公司在 commit 量上看起來和 Google 一樣」。

我也看到曾經的 Rust 核心維護者 Steve(官方 Rust Book 作者,也為我的 Rust 書作過序),在 2025 年年底也基於 JJ 自己開了創業公司 ersc[5],Steve 也寫了一篇 JJ 教程[6]

GitHub 自己則在 2026 年 1 月初推出了 Agentic Workflows[7] 技術預覽,試圖把 AI Agent 嵌入現有的 CI/CD 框架中。

所有這些動作指向同一個事實:Git 這個統治了軟體工程二十年的版本控制系統,其核心設計假設正在被 AI Coding Agent 系統性地挑戰。

但挑戰 Git 不是目的。版本控制只是手段,它服務的根本目標是:安全地管理程式碼變更的歷史和並發。 在代理軟體工程的框架下,這個目標沒有變,但實現方式必須根本性地重新思考。

第一部分:Git 的五個隱藏假設

要理解為什麼 Git 在 Agent 時代遇到困難,我們需要先解剖它的設計假設。這些假設在人類編碼時代完全合理,但在 Agent 時代正在一個接一個地瓦解。

假設一:操作者理解上下文

Git 的每一個互動設計都假設操作者,人類開發者,理解自己在做什麼。

  • git add -p(互動式暫存)假設你能逐行判斷哪些修改應該進入下一個 commit。
  • git rebase -i(互動式變基)假設你能理解 commit 之間的依賴關係並做出正確的編輯決策。
  • git merge 產生衝突時假設你能理解雙方修改的語意意圖並做出合併決策。

Agent 不「理解」上下文。它基於統計推理做出決策。它可以執行 git add -A && git commit -m "..." 這種確定性的命令序列,但面對 git rebase -i 的互動式介面,需要在文字編輯器中重排 pick/squash/edit 行時,就變成了一個脆弱的操作,每一步都增加了出錯的機率。

假設二:操作是順序的

Git 的並發模型假設一個工作目錄在同一時間只有一個操作者。git stash 的存在就是證明。當你需要切換上下文時,必須先「暫存」當前狀態。git rebase 遇到衝突時進入一個 "in progress" 狀態,在這個狀態被解決之前,你不能做其他操作。

在 Agent 時代,並發是常態。像 OpenAI 的 Symphony 這樣的編排系統可能同時管理 5-10 個 Agent,每個處理不同的 issue,它們可能需要在同一個程式碼庫上工作。Git 的鎖文件機制(.git/index.lock)和 "in progress" 狀態在這種場景下變成了嚴重的並發瓶頸。

假設三:變更需要顯式暫存

Git 的 staging area(index)是一個為人類認知設計的中間層。它的價值在於讓你能「精選」哪些修改進入下一個 commit,這對編寫精心組織的 commit 歷史很重要。

但 Agent 不需要「精選」。它在一個隔離的工作空間中執行一個明確定義的任務,所有檔案修改都是為了完成這個任務。git add 對 Agent 來說是一個純粹的儀式動作,100% 的情況下是 git add -A。每一次這種儀式動作都消耗工具呼叫的 token 預算,增加上下文噪聲,但不產生任何決策價值。

假設四:分支需要命名

Git 的分支模型要求每個分支有一個名字。这在人類工作流中有意義。你需要和同事溝通「我在 feature/user-registration 分支上」。但 Agent 的工作分支是臨時的、隔離的、一次性的。為每個 Agent 會話取一個分支名是無意義的開銷。

更深層的問題是:Git 的 branch 概念把「分支指標」和「程式碼變更」綁定在了一起。當 Agent amend 一個 commit 時,commit hash 變了,branch pointer 需要更新,任何引用舊 hash 的系統都需要跟著更新。這種引用不穩定性在自動化編排中造成了大量的協調開銷。

假設五:衝突必須立即解決

Git 在遇到合併衝突時會停下來等待人類輸入。git merge 產生衝突標記(<<<<<<<),git rebase 進入 "in progress" 狀態,兩者都要求你在繼續之前解決衝突。

這對人類是合理的。衝突意味著兩個人對同一段程式碼有不同的理解,需要人的判斷來決定正確的合併。但對 Agent 編排系統來說,「停下來等待」是災難性的。一個管理 10 個並行 Agent 的編排器,如果任何一個 Agent 的 rebase 被衝突阻塞,就需要介入處理。而介入本身需要上下文(理解為什麼會衝突),這個上下文可能不在 Agent 編排器的範圍內。

第二部分:Jujutsu 如何系統性地解決這些問題

Jujutsu(jj)不是「更好的 Git CLI」,而是對版本控制資料模型的根本重新設計。它碰巧使用 Git 作為儲存後端(保持相容性),但其使用者模型和 Git 完全不同。它用 Rust 實現,底層使用 gitoxide 庫,所有核心開發者用 jj 開發 jj 自身。

解決方案一:一切皆 Commit,消除儀式性操作

jj 把 Git 中四種不同的「狀態容器」(working directory、staging area、stash、commit)統一為一種:commit

你的工作副本(@)就是一個 commit。你編輯檔案時,jj 自動把變更快照為這個 commit 的更新。沒有 staging area,沒有 stash,沒有 "dirty working directory" 的概念。

┌─────────────────────────────────────────────────────────────┐
│  Git 的狀態模型:4 種容器,需要手動在她們之間轉移             │
│                                                              │
│  ┌──────────┐  git add  ┌─────────┐  git commit ┌────────┐  │
│  │ Working  │──────────►│ Staging │────────────►│ Commit │  │
│  │ Directory│           │  Area   │              │        │  │
│  └────┬─────┘           └─────────┘              └────────┘  │
│       │                                                       │
│       │ git stash   ┌─────────┐                               │
│       └────────────►│  Stash  │  (另一個臨時容器)               │
│                     └─────────┘                               │
│                                                               │
│  Agent 操作:git add -A && git commit -m "..."                │
│  = 2 次工具呼叫,純儀式,零決策價值                             │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│  jj 的狀態模型:1 種容器,自動快照                            │
│                                                              │
│  ┌──────────────────────────────────────────┐                │
│  │  @ (working copy commit)                  │                │
│  │  ← 編輯檔案自動快照為這個 commit 的更新      │                │
│  └──────────────────────────────────────────┘                │
│                                                              │
│  Agent 操作:直接編輯檔案,完成。                              │
│  = 0 次額外工具呼叫                                            │
└─────────────────────────────────────────────────────────────┘

對 Agent 來說這意味著:

工具呼叫從 2 步變成 0 步。 Git 的 git add -A && git commit -m "..." 在 jj 中不需要。修改檔案就是「提交」。Agent 只需要在想表達語意邊界時執行 jj new(開始一個新 change)和 jj describe(描述 change 的意圖)。

永遠不會出現阻塞性錯誤。 "Your local changes to the following files would be overwritten by merge"這種 Git 錯誤在 jj 中不存在,因為沒有「未提交的修改」這個概念。

示例

# jj:編輯檔案後
vim src/auth.rs            # 修改檔案——完了。
# jj 自動把當前工作副本快照為 @ commit 的更新
# 不需要 add,不需要 commit

jj describe -m "實作登入"  # (可選)給這個 change 加個描述

關鍵機制:jj 執行任何命令時(jj statusjj logjj new 等),都會先自動快照工作副本的所有變更到當前 @ commit 中。所以你的「未儲存修改」實際上每時每刻都是被儲存的。

Git 的心智模型:
  檔案修改 ──是一種──→ "未追蹤的髒狀態"(危險,可能遺失)

jj 的心智模型:
  檔案修改 ──是一種──→ "@ commit 的最新內容"(安全,已快照)

jj 沒有 staging area,但它用 split 來做同樣的事,而且更靈活:

# jj:此時 @ commit 已經包含了所有 3 個檔案的修改(自動快照的)

jj split    # 互動式地把當前 @ commit 拆分成兩個 commit
# 介面讓你選擇哪些檔案/哪些 hunk 進入第一個 commit
# 剩餘的自動成為第二個 commit
Git 的思路:
  修改 → 挑選哪些 "進入" staging → commit → 剩餘留在 working dir
  (先選後存)

jj 的思路:
  修改 → 全部自動進入 @ commit → split 拆分出你想要的部分
  (先存後拆)

區別:jj 的方式永遠不會 "丟東西"——所有修改始終在某個 commit 中。
Git 的方式中 working directory 的修改可能因為誤操作遺失。

所以,jj 的心智模型比 git 更加簡單。

核心思想轉變是:Git 要求你在做修改之前決定「這個修改去哪」(working dir? stage? stash?),jj 讓你先做修改,之後再組織。 所有修改永遠安全地存在於某個 commit 中,你可以事後拆分(split)、移動(squash/move)、描述(describe)。這種「先存後整理」的模型天然消除了資料遺失的風險。

對 Agent 來說,這意味著 Agent 只需要知道兩個操作:jj new(開始一個新的邏輯變更)和 jj describe(給它寫描述)。其他一切,諸如儲存、暫存、臨時儲存,版本控制系統自動處理了。

解決方案二:Change ID,穩定的邏輯標識

jj 區分了兩個層次的標識:

  • Revision:一個不可變的快照(類似 Git 的 commit hash),每次修改內容都產生新的 revision
  • Change:一個穩定的邏輯標識(change ID),無論你怎麼修改一個 change 的內容,其 change ID 不變
┌─────────────────────────────────────────────────────────────┐
│  Git:amend 打斷引用鏈                                       │
│                                                              │
│  commit abc123 ──"實作登入"                                  │
│       │                                                     │
│       ▼  (agent amend 修改了內容)                             │
│  commit def456 ──"實作登入 (修復)"  ← 新 hash!                │
│                                                              │
│  問題:CI、PR、編排器中引用 abc123 的地方全部失效               │
│  編排器:"我追蹤的 abc123 去哪了??"                          │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│  jj:Change ID 跨修改保持穩定                                 │
│                                                              │
│  change kkmpptqz ──"實作登入"                                │
│  (revision: abc123)                                          │
│       │                                                     │
│       ▼  (agent 修改了內容)                                   │
│  change kkmpptqz ──"實作登入 (修復)"  ← 同一個 change ID!      │
│  (revision: def456)  ← revision 變了,但 change ID 沒變         │
│                                                              │
│  編排器始終用 kkmpptqz 追蹤,不受 amend 影響                    │
└─────────────────────────────────────────────────────────────┘

這對編排系統至關重要。 像 Symphony 這樣的 Agent 編排系統需要追蹤「這個 issue 對應的程式碼變更」。用 Git 的 commit hash,Agent 每次 amend 都會打斷這種關聯。用 jj 的 change ID,無論 Agent 修改多少次,編排器都能透過 change ID 穩定地追蹤。

解決方案三:衝突是一等物件,不阻塞,不中斷

這是 jjDarcs/Pijul 借鑑的最深層創新。

Git 中衝突是一種阻塞狀態。你被卡住了,必須先解決衝突才能繼續。jj 中衝突是資料,一個 commit 可以「含有衝突」,這和它含有一個函式或一個檔案沒有本質區別。

技術實現上,jj 不儲存文字衝突標記(<<<<<<<),而是維護衝突樹的邏輯表示:一系列 diff 的組合。這意味著 rebase 一個含衝突的 commit 不會產生「嵌套衝突標記」的噩夢。

┌─────────────────────────────────────────────────────────────┐
│  Git:衝突阻塞流水線                                          │
│                                                              │
│  Agent 執行 git rebase main                                   │
│       │                                                     │
│       ▼                                                      │
│  commit 1 ── rebase 成功 ✓                                   │
│       │                                                     │
│       ▼                                                      │
│  commit 2 ── 衝突!✗ ──── 流水線卡住                           │
│       │              需要互動式解決                            │
│       ×              git rebase --continue                    │
│  commit 3 ── 無法繼續   Agent 可能不理解衝突上下文               │
│  commit 4 ── 無法繼續                                          │
│                                                              │
│  狀態:REBASE IN PROGRESS(中間狀態,危險)                    │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│  jj:衝突是資料,操作永遠完成                                  │
│                                                              │
│  Agent 執行 jj rebase -d main                                 │
│       │                                                     │
│       ▼                                                      │
│  change 1 ── rebase 成功 ✓                                   │
│       │                                                     │
│       ▼                                                      │
│  change 2 ── 有衝突,標記為 conflicted ⚠ ── 但操作繼續!          │
│       │                                                     │
│       ▼                                                      │
│  change 3 ── rebase 成功 ✓(衝突解決後自動傳播)                │
│       │                                                     │
│       ▼                                                      │
│  change 4 ── rebase 成功 ✓                                   │
│                                                              │
│  狀態:操作完成。change 2 標記為 conflicted,可以之後解決。       │
│  沒有中間狀態,沒有阻塞。                                      │
└─────────────────────────────────────────────────────────────┘

更重要的設計含義:

rebase 永遠「成功」。 沒有 git rebase --continue,沒有 "in progress" 狀態。操作是原子的——它要麼完成(可能產生含衝突的 commit),要麼因為其他原因失敗。

衝突解決可以傳播。 如果你在某個 change 中解決了衝突,jj 會自動把解決方案傳播到所有後代 change。這類似 Git 的 rerere(record/replay resolve),但它是預設行為且更可靠。

衝突可以延後處理。 Agent 完成工作後,編排器可以檢查是否有含衝突的 change,然後決定是讓 Agent 解決、啟動一個專門的衝突解決 Agent、還是升級給人類。衝突不再是「流水線卡住了」,而是「產出中有一個需要額外處理的標記」。

解決方案四:Operation Log,完整的操作審計

Git 有 reflog,但它只記錄 branch tip 的移動,格式晦澀,主要用於災難恢復。

jj 的 operation log 記錄你對倉庫執行的每一個操作。每條記錄是一個原子操作,即使一個 rebase 涉及 10 個 commit,它也是 operation log 中的一條記錄。

┌─────────────────────────────────────────────────────────────┐
│  Git reflog:粗粒度,只記錄 branch tip 移動                     │
│                                                              │
│  abc123 HEAD@{0}: commit: fix login                           │
│  def456 HEAD@{1}: rebase: updating HEAD                       │
│  789abc HEAD@{2}: checkout: moving from main to feature       │
│                                                              │
│  問題:一次涉及 10 個 commit 的 rebase 在 reflog 中                │
│  顯示為 10 條分散的記錄,很難原子回退                            │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│  jj operation log:原子級,每個操作一條記錄                     │
│                                                              │
│  @ kmlprxqv  rebase 4 commits onto main                       │
│  ○ zxsewqpw  describe change kkmpptqz                         │
│  ○ tnrqpaxy  new empty change                                 │
│  ○ vqrpmzxn  fetch from origin                                │
│                                                              │
│  撤銷整個 4-commit rebase:jj op restore zxsewqpw              │
│  一條命令,原子回退。                                          │
└─────────────────────────────────────────────────────────────┘
# 查看操作歷史
jj op log

# 撤銷最近一次操作(原子的)
jj undo

# 回退到任意歷史操作的狀態
jj op restore <operation-id>

對 Agent 來說,這是「時間機器」。 Agent 做了一個災難性操作?不需要從 reflog 中人肉找到正確狀態,一個 jj op restore 精確回到之前的時刻。編排系統可以在 Agent 每次操作前記錄 operation ID,失敗時一鍵回滾。

解決方案五:自動 Rebase,多 Agent 並發的基礎

當你修改一個 commit,jj 自動把所有後代 rebase 到新版本上。配合衝突是一等物件的設計,這個自動 rebase 即使產生衝突也不會阻塞。

┌─────────────────────────────────────────────────────────────┐
│  場景:修改歷史中的 commit B(B → B')                          │
│                                                              │
│  修改前: A ← B ← C ← D                                       │
│                                                              │
│  ─── Git ───                                                 │
│  修改 B 後:A ← B' (手動 amend)                                │
│              A ← B ← C ← D  (C, D 還指向舊 B!)                 │
│  你需要: git rebase --onto B' B D                            │
│  如果 C 衝突:rebase 卡住,手動解決                              │
│                                                              │
│  ─── jj ───                                                  │
│  修改 B 後:A ← B' ← C' ← D' (自動 rebase,一步完成)            │
│  如果 C 衝突:C' 標記為 conflicted,但 D' 仍然生成                 │
│  無需手動操作,無中間狀態                                      │
└─────────────────────────────────────────────────────────────┘

這是 jj 最核心也最反直覺的設計。

Git 在產生衝突時會產生嵌套衝突標記——<<<<<<< 裡面套 <<<<<<<,基本上不可讀。我相信這是每个人類開發者最頭疼的地方吧。

jj 對衝突的理解完全不同。它不在檔案裡插入文字標記,而是在資料模型層面記錄「這個檔案有多個版本,她們之間有分歧」。

內部表示是一個衝突樹(conflict tree),不是文字,而是結構化資料。

jj 內部對一個衝突檔案的表示(概念模型,不是實際格式):

file: src/auth.rs
conflict:
  base:    "fn login(user: &str, pass: &str) -> bool { ... }"  # 共同祖先
  side_1:  "fn login(user: &str, pass: &str) -> bool {          # 一方的修改
              validate_credentials(user, pass)
            }"
  side_2:  "fn login(user: &str, pass: &str) -> Result<...> {   # 另一方的修改
              let valid = check_password(user, pass)?;
            }"

當你 checkout 或查看這個檔案時,jj 渲染成類似 Git 的衝突標記格式給你看。但這只是展示層,底層儲存的是結構化的三方資料,不是文字標記。

儲存層:結構化的衝突樹(base + side_1 + side_2)
          │
          ▼ (當你打開檔案時,渲染為人類可讀格式)
展示層:類似 Git 的 <<<<<<< 標記(但這只是視圖,不是資料)

這個區別至關重要。因為結構化資料可以被程式精確操作,而文字標記只能被人類閱讀

自動 Rebase 時衝突怎麼處理

情況一:rebase 沒有衝突

最簡單的情況。兩個 change 修改了不同的檔案或同一檔案的不同區域:

修改前:
  main: A ← B ← C
              ↑
          你修改了 B 變成 B'(比如改了 src/auth.rs)

自動 rebase:
  C 依賴 B。B 變成了 B'。jj 自動把 C rebase 到 B' 上。

  結果:A ← B' ← C'

  如果 C 修改的是 src/config.rs(和 B' 修改的 src/auth.rs 沒有重疊)
  → C' 乾淨地套用,沒有衝突。自動完成,你什麼都不需要做。
情況二:rebase 產生衝突,jj 怎麼「成功」

關鍵來了。假設 B 和 C 都修改了同一個檔案的同一個區域:

修改前:
  A ← B ← C ← D
       ↑
    你修改了 B 變成 B'(改了 src/auth.rs 第 10-20 行)
    但 C 也修改了 src/auth.rs 第 10-20 行

Git 的行為:
  git rebase --onto B' B D
  → 處理 C 時發現衝突
  → 停!在 src/auth.rs 中插入 <<<<<<< 標記
  → 進入 "rebase in progress" 狀態
  → 等你解決衝突並執行 git rebase --continue
  → D 還沒被處理,掛起

jj 的行為:
  jj 修改 B → B',自動 rebase C 和 D
  → 處理 C 時發現衝突
  → 不停!建立 C',其中 src/auth.rs 被記錄為 "衝突狀態":
     conflict:
       base: B 版本的 src/auth.rs 第 10-20 行
       side_1: B' 版本的第 10-20 行(你的修改)
       side_2: C 版本的第 10-20 行(C 的修改)
  → C' 是一個 "含有衝突的 commit"——但它是一個合法的 commit!
  → 繼續處理 D → D' rebase 到 C' 上
  → 如果 D 沒碰 src/auth.rs 第 10-20 行,D' 是乾淨的
  → 如果 D 也碰了,D' 也被標記為含有衝突
  → 操作完成。整個 rebase 鏈沒有中斷。
┌──────────────────────────────────────────────────────────────┐
│  Git rebase 遇到衝突                                            │
│                                                              │
│  A ← B' ← C  ← D                                              │
│          ↑                                                   │
│        衝突!停在這裡                                           │
│        狀態:REBASE IN PROGRESS                               │
│        C 變成了一個檔案裡插著 <<<<<<< 標記的半成品                  │
│        D 還沒被處理                                            │
│        你必須:解決衝突 → git add → git rebase --continue      │
└──────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────┐
│  jj rebase 遇到衝突                                            │
│                                                              │
│  A ← B' ← C' ← D'                                              │
│         ⚠    ✓                                               │
│     衝突被記錄   D' 乾淨(如果它不碰衝突區域)                    │
│     在 C' 中                                                   │
│     但 C' 是一個                                                │
│     合法的 commit                                              │
│                                                              │
│  狀態:操作完成。沒有 "in progress"。                            │
│  C' 被標記為 conflicted,你可以之後解決。                        │
└──────────────────────────────────────────────────────────────┘
情況三:怎麼解決衝突

當你決定要解決 C' 中的衝突時:

bash

# 查看哪些 changes 有衝突
jj log
# 輸出中會標記:
# ⚠ kkmpptqz (conflict) "feature: 實作註冊"

# 切換到衝突的 change
jj edit kkmpptqz

# 此時你打開 src/auth.rs,看到的是渲染出來的衝突標記:
# <<<<<<< Side #1 (你的修改 B')
# fn login(...) -> bool {
#     validate_credentials(user, pass)
# ||||||| Base (共同祖先 B)
# fn login(...) -> bool {
#     check(user, pass)
# ======= Side #2 (C 的修改)
# fn login(...) -> Result<...> {
#     let valid = check_password(user, pass)?;
# >>>>>>> 

# 你(或 Agent)編輯檔案,刪除標記,寫出正確的合併結果
# 儲存檔案

# jj 自動檢測到衝突標記消失了 → 這個 change 不再是 conflicted
# 而且——關鍵——D' 會被自動 rebase 到解決了衝突的 C' 上
# 如果 D' 之前因為 C' 的衝突而連帶 conflicted,
# 現在衝突解決後 D' 可能也自動變乾淨了
情況四:衝突解決的傳播,這才是殺手級特性

解決衝突前:

A ← B' ← C'(⚠衝突) ← D'(⚠連帶衝突) ← E'(⚠連帶衝突)

解決 C' 的衝突後:A ← B' ← C''(✓乾淨) ← D''(?) ← E''(?)

 jj 自動把衝突解決傳播到 D'' 和 E''。
 如果 D 和 E 的衝突完全是由 C 的衝突引起的,
 D'' 和 E'' 自動變乾淨,不需要你逐個解決。

 Git 中你需要對 C, D, E 每一個都手動解決衝突。
 jj 中你只需要解決 C 的衝突,D 和 E 可能自動修復。

這就像 Git 的 `rerere`(record/replay resolved conflicts),但 jj 的實現更根本——它不是 "記住你之前解決過類似的衝突然後重放",而是 "衝突解決本身就是一個可以被 rebase 傳播的 diff"。

為什麼「衝突是資料」如此重要

把衝突從「狀態」變成「資料」的本質影響:

┌──────────────────────────────────────────────────────────────┐
│  "衝突是狀態"(Git)                                            │
│                                                              │
│  • 衝突阻塞操作——你必須先解決                                   │
│  • 衝突不能被 commit——只能存在於 working directory               │
│  • 衝突不能被傳遞——每次 rebase 都可能重新產生                     │
│  • 衝突讓倉庫進入 "中間狀態"——危險,需要清理                      │
│  • 解決衝突的時間視窗:rebase 進行中(有壓力,不能做別的)          │
│                                                              │
│  對 Agent 的影響:                                              │
│  Agent 遇到衝突 → 流水線卡住 → 需要編排器介入                     │
│  → 但編排器可能沒有解決衝突的上下文 → 升級給人類                   │
│  → 人類需要理解 Agent 在做什麼 → 高成本                           │
└──────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────┐
│  "衝突是資料"(jj)                                            │
│                                                              │
│  • 衝突不阻塞操作——先完成 rebase,衝突標記在 commit 中             │
│  • 衝突可以被 commit——它就是 commit 的一部分                      │
│  • 衝突解決可以傳播——解決一處,後代自動修復                       │
│  • 倉庫永遠處於 "乾淨狀態"——只是某些 commit 含有衝突標記            │
│  • 解決衝突的時間視窗:任何時候(沒有壓力,可以做別的事先)         │
│                                                              │
│  對 Agent 的影響:                                              │
│  Agent 遇到衝突 → 操作完成,衝突被標記 → 流水線繼續                 │
│  → 編排器看到 "change X is conflicted" → 決策:                  │
│     a) 啟動一個衝突解決 Agent(給它衝突的三方資料)                 │
│     b) 暫時跳過,先處理其他不衝突的任務                            │
│     c) 升級給人類(但不緊急,因為沒東西被阻塞)                    │
│  → 什麼時候解決都行,不影響其他 Agent 的工作                       │
└──────────────────────────────────────────────────────────────┘
Agent 解決衝突的可能方式

因為 jj 的衝突是結構化資料(base + side_1 + side_2),Agent 可以獲得比 Git 更豐富的資訊來解決衝突:

# Agent 看到的資訊

# Git:一個檔案裡混著 <<<<<<< 標記的文字
# Agent 需要從文字中解析出 "哪是 ours,哪是 theirs,哪是 base"
# 容易解析錯,尤其是嵌套衝突

# jj:結構化的三方資料
# base:   原始版本(共同祖先)
# side_1: 第一方修改
# side_2: 第二方修改
# Agent 可以精確獲取三方各自的完整內容

# 一個合理的 Agent 衝突解決策略:
# 1. 提取 base, side_1, side_2 的完整內容
# 2. 理解 side_1 的意圖(從 change description 或關聯的 Contract)
# 3. 理解 side_2 的意圖(同上)
# 4. 生成合併結果
# 5. 驗證合併結果滿足兩方的 Contract

如果我們進一步把 Contract (某種 Spec 的約束) 和 change 綁定,衝突解決就變得更有意義了:

change kkmpptqz (side_1) → contract-042 "實作用戶註冊"
change rrsnntyz (side_2) → contract-057 "修復登入驗證"

衝突發生在 src/auth.rs

衝突解決 Agent 可以獲取兩個 Contract 的意圖:
  - contract-042 要求:註冊時用 bcrypt-12 哈希密碼
  - contract-057 要求:登入時驗證密碼並返回 JWT

→ Agent 有足夠的語意資訊來判斷如何合併:
     兩者修改了不同的功能(註冊 vs 登入),
     合併結果應該同時包含註冊邏輯和修復後的登入邏輯。

這就是衝突是資料 + Spec-Change 綁定的協同效應,衝突不再是「兩段不知為何打架的程式碼」,而是「兩個已知意圖的變更在同一區域的重疊」,有了意圖資訊,解決方案就有了判斷依據。

解決方案六:workspace,多 Agent 並發的優勢

Git worktree 是多 Agent 並發工作的一個重要場景。jj 也有自己的方案,而且比 Git worktree 更乾淨。

Git worktree 的核心場景:你想同時在同一個倉庫的不同分支上工作,但不想來回 stash/checkout。

jj 對應的概念叫 workspace。但它的模型比 Git worktree 更簡潔,因為 jj 沒有 index/staging area,每個 workspace 的狀態就是一個 @ commit,沒有別的東西。

# jj:建立多個 workspace
jj workspace add ../feature-login
jj workspace add ../bugfix-auth

# 現在你有三個目錄:
# ./repo            ← 預設 workspace,@ = 某個 change
# ../feature-login  ← workspace "feature-login",@ = 另一個 change
# ../bugfix-auth    ← workspace "bugfix-auth",@ = 又一個 change

# 她們共享同一個 jj 倉庫資料
# 每個 workspace 有自己獨立的 @ (working copy commit)

# 查看所有 workspace
jj workspace list

# 輸出:
# default: kkmpptqz (當前 change)
# feature-login: rrsnntyz (另一個 change)
# bugfix-auth: ppmmlkqz (又一個 change)

# 關鍵區別
┌──────────────────────────────────────────────────────────────┐
│  Git Worktree                                                 │
│                                                              │
│  每個 worktree 有:                                            │
│  ├── 獨立的 working directory   (檔案)                          │
│  ├── 獨立的 index / staging area (暫存狀態)                      │
│  ├── 獨立的 HEAD             (指向哪個 commit/branch)           │
│  └── 共享的 .git/objects      (物件庫)                          │
│                                                              │
│  限制:                                                        │
│  • 每個 worktree 必須指向不同的 branch                            │
│    (不能兩個 worktree checkout 同一個 branch)                  │
│  • 每個 worktree 有自己的 index,狀態複雜度 ×N                     │
│  • 如果一個 worktree 處於 "rebase in progress",                   │
│    它會鎖住相關引用,影響其他 worktree                            │
└──────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────┐
│  jj Workspace                                                  │
│                                                              │
│  每個 workspace 有:                                            │
│  ├── 獨立的 working directory   (檔案)                          │
│  ├── 獨立的 @ (working copy commit)                             │
│  └── 共享的 repo 資料        (所有 changes + operation log)     │
│                                                              │
│  沒有:                                                        │
│  • 沒有 index(不需要)                                          │
│  • 沒有 branch 指向限制(workspace 指向 change,不是 branch)    │
│  • 沒有 "in progress" 狀態(衝突是資料,不阻塞)                  │
└──────────────────────────────────────────────────────────────┘

jj workspace 的每個例項只有 **一層狀態**:

每個 jj Workspace 的狀態:
  @ commit(當前 working copy change)

  沒有 staging area,沒有 "in progress" 狀態。
  編排器只需要知道 "這個 workspace 的 @ 指向哪個 change"。

這就是「一切皆 commit」在 workspace 場景下的體現:每個 workspace 的完整狀態就是一個 change ID。編排器追蹤 N 個 Agent 的狀態,就是追蹤 N 個 change ID,而不是 N × (working dir + index + HEAD + 可能的中間狀態) 的笛卡爾積。

對 Agent 編排的意義
┌──────────────────────────────────────────────────────────────┐
│  編排器管理 10 個 Agent workspace 的複雜度                         │
│                                                              │
│  Git Worktree:                                                │
│  • 10 個 branch 要命名和管理                                     │
│  • 10 個 staging area 可能處於不一致狀態                          │
│  • 任何一個可能卡在 "rebase in progress"                          │
│  • merge 衝突阻塞流水線                                          │
│  • 崩潰恢復需要判斷每個 worktree 處於什麼狀態                       │
│  狀態空間:O(N × M)  N=worktree 數,M=可能的中間狀態數               │
│                                                              │
│  jj Workspace:                                                 │
│  • 10 個匿名 change,不需要命名                                   │
│  • 沒有 staging area                                            │
│  • 沒有 "in progress" 狀態                                       │
│  • 衝突標記但不阻塞                                              │
│  • 崩潰恢復:jj op restore 或直接繼續(修改都在 @ 中)               │
│  狀態空間:O(N)  每個 workspace 就是一個 change ID                  │
└──────────────────────────────────────────────────────────────┘

所以 jj workspace 不只是「Git worktree 的替代品」,它透過消除 staging area 和中間狀態,把每個工作空間的狀態複雜度從 多維 壓縮到了 一維(一個 change ID)。對於需要管理多個並行 Agent 的編排系統來說,這是數量級的簡化。

第三部分:當 Spec 遇到版本控制,Spec-Change 綁定

回顧:Spec 在代理軟體工程中的角色

在本系列的前一篇文章中,我們建立了代理軟體工程的核心框架:Spec 是控制面,Code 是資料面,Agent 是執行面。 人類的核心工作是編寫 Spec(規格說明),Agent 負責將 Spec 轉化為程式碼實現,驗證系統確認程式碼滿足 Spec。

在這個框架中,我們把給 Agent 執行的任務級 Spec 稱為 Task Contract(任務合約)。一個結構化的文件,包含四個核心要素:

  • 意圖(Intent):做什麼,以及為什麼
  • 已定決策(Decisions):已經確定的技術選擇,消除 Agent 的決策空間
  • 邊界(Boundaries):允許修改哪些檔案、禁止做哪些事
  • 完成條件(Completion Criteria):BDD 格式的驗收標準,確定性的通過/失敗檢查

Contract 的概念來源於我們對 Code Review 危機的分析:當 AI 程式碼生產速度遠超人類審查能力時,解決方案不是「更快地審查程式碼」,而是把人類的審查點上移到意圖層——人類審查 Spec/Contract 是否正確定義了意圖和驗收標準,機器驗證程式碼是否滿足 Contract。

當前的斷裂:Spec 和版本控制是兩個世界

在今天的工具鏈中,Spec(無論是 GitHub Issue、Jira Ticket 還是我們定義的 Contract)和版本控制系統之間的關聯是脆弱的、約定式的

┌─────────────────────────────────────────────────────────────┐
│  當前的鬆散關聯                                                │
│                                                              │
│  Spec/Contract           │      Version Control               │
│  (外部系統)              │      (Git/jj)                       │
│                          │                                     │
│  "實作用戶註冊" ·····靠 commit message 中的····→ abc123          │
│  Issue #42              "fixes #42" 字串關聯       commit          │
│                                                              │
│  問題:                                                        │
│  1. 關聯靠約定(忘了寫 #42 就斷了)                                │
│  2. commit hash 不穩定(amend 就斷了)                            │
│  3. 一個 Spec 對應多少 commits?不知道                            │
│  4. 一個 commit 滿足了 Spec 的哪些驗收標準?不知道                  │
│  5. Spec 變更和 Code 變更沒有關聯的版本歷史                        │
└─────────────────────────────────────────────────────────────┘

這種斷裂在人類手動開發時可以接受——人類在心裡維護著「我正在為 Issue #42 寫程式碼」的關聯。但在 Agent 編排系統中,這種關聯必須是形式化的、可查詢的、不會因為 amend 而斷裂的。

Spec-Change 綁定:jj 提供的機會

jj 的兩個特性為解決這個問題提供了基礎:

Stable Change ID 解決了 "amend 打斷關聯" 的問題。無論 Agent 修改多少次,change ID 不變,編排系統可以始終用 change ID 追蹤「這個 Contract 對應的程式碼變更」。

Git 外部的元資料儲存 解決了 "關聯靠約定" 的問題。jj 已經在 Git 倉庫外儲存了 bookmarks 等元資料。Contract 綁定可以作为另一種元資料,成為版本控制的一等公民。

┌─────────────────────────────────────────────────────────────┐
│  理想的 Spec-Change 綁定                                       │
│                                                              │
│  Contract "用戶註冊"                                           │
│  ├── intent: "實作註冊 API endpoint"                            │
│  ├── decisions: [bcrypt-12, POST /api/v1/auth/register]       │
│  ├── boundaries: [ONLY src/auth/**, DO NOT add deps]           │
│  ├── criteria: [註冊成功→201, 重複信箱→409, ...]                  │
│  │                                                            │
│  ├── bound_changes:  ← 形式化的綁定,不是 commit message          │
│  │   ├── kkmpptqz "實作註冊 endpoint"                            │
│  │   ├── rrsnntyz "新增註冊測試"                                │
│  │   └── ppmmlkqz "修復信箱校驗"                                │
│  │                                                            │
│  └── verification:  ← 驗證狀態關聯到 Contract 級別                 │
│       ├── kkmpptqz: L1=PASS, L2=PASS, L3=PASS                  │
│       ├── rrsnntyz: L1=PASS                                    │
│       └── aggregate: all_criteria_met = true                    │
│                                                              │
│  可查詢:                                                       │
│  "Contract 042 的所有 changes" → [kkmpptqz, rrsnntyz, ...]     │
│  "change kkmpptqz 屬於哪個 Contract" → Contract 042             │
│  "Contract 042 的驗收標準是否全部通過" → true                     │
└─────────────────────────────────────────────────────────────┘

這個模型讓 Spec 和版本控制從「兩個鬆散關聯的世界」變成了「一個統一的追溯鏈」:

人類定義 Spec/Contract
        │
        ▼
Agent 生成 Changes(關聯到 Contract)
        │
        ▼
驗證系統檢查 Changes 是否滿足 Contract 的 Criteria
        │
        ▼
Contract Acceptance(人類審查 Contract 定義是否正確 + 驗證是否全部通過)

Spec 層級與版本控制的對應

在我們的 Spec 設想中,Spec 分為三層:

  • L0 組織級 Spec(安全策略、架構原則),變化極慢
  • L1 專案級 Spec(技術棧、模組邊界、API 契約),變化緩慢
  • L2 任務級 Spec / Contract(具體任務的意圖、約束、驗收標準),每任務一個

這三層和版本控制的對應關係是:

┌─────────────────────────────────────────────────────────────┐
│  Spec 層級與版本控制的對映                                      │
│                                                              │
│  L0 組織級 Spec ──→ 規則檔案(rules/security-baseline.md)       │
│  (安全策略等)      版本化在倉庫中,極少變更                        │
│              Agent 透過規則路由按需載入                           │
│                                                              │
│  L1 專案級 Spec ──→ 專案配置檔案(specs/project.yaml)           │
│  (技術棧、契約)    版本化在倉庫中,Sprint 級別變更                   │
│              Agent 啟動時載入                                    │
│                                                              │
│  L2 Task Contract → 與 jj changes 形式化綁定                      │
│  (任務意圖/驗收)   Contract 變更觸發 changes 重新生成               │
│              驗證結果關聯到 Contract 級別                          │
└─────────────────────────────────────────────────────────────┘

關鍵洞察:L0 和 L1 的 Spec 本身就應該版本化在程式碼倉庫中。她們是程式碼的「憲法」,變更 L0/L1 Spec 應該像修憲一樣慎重,並且有完整的變更歷史。L2 Contract 則是「工單」級別的,它的生命週期和對應的 changes 一起開始、一起結束。

第四部分:Agent-Native VCS 還缺什麼

jj 解決了 Git 在 Agent 場景下的大部分摩擦點。但從代理軟體工程的完整框架來看,還有幾個維度是當前 jj 尚未覆蓋的。

原語一:驗證狀態作為 Change 屬性

當前的版本控制系統不知道一個 change 是否通過了測試。CI/CD 是外部系統,驗證結果存在 GitHub Actions 或 Jenkins 裡,不在版本控制系統中。

在 Agent-Native VCS 中,驗證狀態應該是 change 的內在屬性:

Change kkmpptqz:
  revision: abc123
  description: "實作註冊 endpoint"
  contract: contract-042
  verification:
    L1_type_check: PASS (2s)
    L2_contract: PASS (5s)
    L3_bdd: PASS (30s)
    L4_adversarial: PENDING
  status: verified_to_L3

這讓「這個變更加以合併嗎?」成為版本控制系統可以直接回答的問題,不需要去查外部 CI 系統。合併的門控從「人類點 approve」變成「驗證引擎確認所有層通過」。

原語二:Agent 身份與許可權

Git 的 author/committer 欄位用 name + email 標識。在多 Agent 並發工作的場景下,我們需要更豐富的身份資訊:

Change kkmpptqz:
  agent: claude-code-session-7a3b
  role: implementer
  contract: contract-042
  permissions: workspace-write, files=[src/auth/**]
  orchestrator: symphony-run-019

這讓審計系統可以回答:「這段程式碼是哪個 Agent 在什麼許可權下生成的?」在合規場景中,這比 "Author: bot@ci.example.com" 有價值得多。

原語三:快照時間線而非 DAG

Git 的 commit DAG(有向無環圖)是為人類理解 branch/merge 歷史設計的。但當 10 個 Agent 並行工作時,DAG 變成一團不可讀的義大利麵條。

Agent-Native 的歷史可視化應該是按時間線排列的快照序列,可以按 Contract、按 Agent、按模組過濾。想象一個時間軸,每個 Agent 是一條泳道,Contract 是顏色編碼,你可以 scrub 到任何時刻看到程式碼庫在那個瞬間的狀態。

jj 的 operation log 已經提供了部分基礎。它按時間記錄所有操作。缺的是和 Contract/Agent 身份的關聯,以及可視化層。

第五部分:從 Code Review 到 Contract Acceptance

版本控制的變革不僅僅是工具層面的,它連帶著改變了整個協作模型。

傳統模型:Branch → PR → Code Review → Merge

GitHub 圍繞 Pull Request 構建了整個協作流程。開發者建立 branch,推送程式碼,開 PR,同事 review 程式碼 diff,approve 後合併。

這個流程在 Agent 時代面臨三個根本問題:

1. Review 的物件錯了。 在我們之前的《重新思考 Code Review》文章中詳細分析過,當 AI 以機器速度生成程式碼時,人類逐行審查程式碼 diff 是不可擴充套件的。Faros AI 的資料顯示 PR 合併率暴漲 97.8% 而 Review 時間暴漲 91.1%。

2. Branch 的粒度錯了。 一個 Agent 可能在幾分鐘內完成一個 Contract,建立 branch → push → 開 PR → 等 review → merge 的流程比實際工作時間還長。

3. Merge 的門控錯了。 PR 的 approve 按鈕是一個人類判斷,「這段程式碼看起來對」。但驗證應該是確定性的,「這段程式碼通過了所有驗證層」。

Agent-Native 模型:Contract → Changes → Verification → Acceptance

┌──────────────────────────────────────────────────────────────┐
│  傳統 GitHub 流程                                               │
│                                                              │
│  Issue ──→ Branch ──→ Code ──→ PR ──→ Code Review ──→ Merge    │
│  (模糊)   (命名)    (手寫)   (開 PR)   (人讀 diff)      (approve)     │
│                                                              │
│  人類參與:全流程                                                │
│  門控:人類主觀判斷                                               │
└──────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────┐
│  Agent-Native 流程                                             │
│                                                              │
│  Contract → Changes → Verification → Acceptance                 │
│ (結構化任務契約)  (匿名 jj)   (確定性驗證)   (Contract 級)              │
│                                                              │
│  人類參與:Contract 定義 + 最終驗收                                │
│  門控:驗證引擎確認所有 Criteria 通過                             │
└──────────────────────────────────────────────────────────────┘

在這個模型中:

Contract 取代 Issue + PR description。 Contract 是結構化的、包含驗收標準的、可自動化驗證的。它不是「給人看的任務備忘錄」,而是「給 Agent 消費的可執行規格 + 給驗證系統執行的檢查清單」。

Changes 取代 Branch。 jj 的匿名 changes + stable change ID 意味著不需要 branch 名稱,編排系統透過 change ID 追蹤。

Verification Pipeline 取代 Code Review。 從型別檢查到合約驗證到 BDD 測試到對抗性 Agent 審查,多層確定性驗證提供了比人類 Code Review 更全面、更快速、更可擴充套件的品質保證。

Contract Acceptance 取代 PR Approve。 人類審查的不是程式碼 diff,而是:「這個 Contract 的驗收標準是否明確定義?驗證結果是否表明 Contract 被正確滿足?」

第六部分:實踐指南 - 如何在現有工具鏈中漸進採用

理想很美好,但大多數團隊不會一夜之間拋棄 Git 和 GitHub。以下是一個漸進式的採用路徑。

Level 0:在 Git 倉庫中 colocate jj(零風險)

jj 支持和 Git 倉庫共存(colocate 模式),這意味著你可以在任何現有 Git 倉庫中開始使用 jj,不影響團隊中其他人繼續使用 Git。

# 在現有 Git 倉庫中初始化 jj(兩者共存)
cd your-repo
jj git init --colocate

# 現在可以同時使用 jj 和 git
# jj 操作會同步到 Git,git 操作也對 jj 可見

對 Agent 的即時價值:

  • jj op log 提供了 Agent 操作的完整審計
  • 自動快照意味著 Agent 不會「遺失」未提交的修改
  • jj op restore 在 Agent 犯錯時精確回退

Level 1:Agent 工作空間使用 jj

在 Claude Code / Codex CLI 的工作空間中,用 jj 替代 git 作為 Agent 的版本控制工具:

# Agent 的工具集簡化
# 不需要:git add, git stash, git rebase --continue
# 只需要:jj new, jj describe, jj bookmark set, jj git push

關鍵收益: Agent 的版本控制工具呼叫量減少 30-50%(消除了 git addgit stashgit rebase --continue 等儀式性操作),每次節省的工具呼叫都是更多可用於實際工作的上下文預算。

Level 2:用 Change ID 追蹤任務

在 Agent 編排系統中,用 jj 的 change ID 替代 Git 的 commit hash 來追蹤變更。change ID 跨 amend 穩定,編排器無需在 Agent 每次修改後重新綁定引用。

Level 3:利用衝突即資料的特性

在多 Agent 並發場景中,不再把衝突視為「錯誤」,而是視為「需要額外處理的狀態」:Agent 完成後檢查是否有衝突標記的 changes。簡單衝突自動解決,複雜衝突升級給人類。流水線永遠不會被「卡住」。

第七部分:對比總覽

Git vs jj vs 理想 Agent-Native VCS

維度
Git
Jujutsu (jj)
理想 Agent-Native VCS
資料模型
4 種容器
1 種容器(commit)
1 種容器 + Spec 元資料
變更標識
commit hash(修改即變)
change ID(穩定)
change ID + Contract 綁定
衝突模型
阻塞狀態
一等資料物件
一等物件 + 自動解決策略
操作審計
reflog(粗粒度)
operation log(原子級)
operation log + Agent 身份
分支模型
命名分支
匿名 changes + bookmarks
匿名 changes + Contract 關聯
自動 rebase
手動
預設自動
自動 + 衝突傳播
Spec 整合
無(靠 commit message 約定)
Contract-Change 形式化綁定
驗證整合
無(依賴外部 CI)
驗證狀態是 change 屬性
Agent 友善度
低(大量儀式操作)
高(自動快照,無阻塞)
極高(原生身份/許可權)
儲存後端
Git only
Git(可擴充套件)
可擴充套件(Git / Cloud)
生態相容
原生
Git 相容
Git 相容 + 原生協議

關鍵結論:jj 已經解決了 80% 的 Agent-VCS 摩擦。透過「一切皆 commit」、change ID、衝突即資料、自動 rebase。剩下的 20% 是需要和代理軟體工程上層(Spec/Contract、驗證管道、Agent 編排)聯動的部分。

結語:版本控制反映了它服務的工作方式

回顧版本控制的歷史,每一代系統都反映了它所處時代的工作方式:

CVS/SVN 時代:中央伺服器,順序提交。反映了「一個團隊、一個程式碼庫、一個整合經理」的瀑布式開發。

Git 時代:分散式,分支廉價。反映了「分散式團隊、並行開發、Pull Request 協作」的敏捷開發。

Agent 時代:需要什麼?自動快照、穩定標識、衝突即資料、原子操作、完整審計。反映了「多 Agent 並發、Spec/Contract 驅動、確定性驗證、機器速度」的代理軟體工程。

Git 不是「錯」了。它為人類開發者完美服務了二十年。但它的核心假設——操作者理解上下文、操作是順序的、變更需要顯式暫存、衝突必須立即解決,在 Agent 時代成了摩擦力而不是功能。

Jujutsu 提供了一条務實的演進路徑:保持 Git 相容性(不破壞現有生態),同時引入 Agent-Native 的資料模型和操作語意。 它不需要你拋棄 GitHub,不需要團隊全員切換,不需要重建 CI/CD,但它給了 Agent 一個安全的、確定性的、不會被「卡住」的版本控制介面。

而當我們把版本控制和 Spec/Contract 系統聯動起來。透過 Change ID 實現 Contract-Change 綁定、透過驗證狀態整合實現自動化門控、透過 Agent 身份實現完整審計,版本控制就從一個孤立的「程式碼儲存層」升級為代理軟體工程的核心基礎設施。

Spec 是控制面,Code 是資料面,Agent 是執行面,而版本控制是承載資料面的物理層。 當物理層從「為人類設計的鋒利刀具」變成「為 Agent 設計的安全電動工具」時,整個上層系統的設計空間都會被打開。

參考資料
[1]

Jujutsu: https://github.com/jj-vcs/jj

[2]

OpenAI 被報導正在構建 GitHub 競品:https://www.reddit.com/r/technology/comments/1rk8bfa/openai_is_developing_alternative_to_microsofts/

[3]

Symphony: https://github.com/openai/symphony

[4]

JJHub: https://jjhub.tech/

[5]

ersc: https://ersc.io/

[6]

JJ 教程:https://steveklabnik.github.io/jujutsu-tutorial/introduction/introduction.html

[7]

Agentic Workflows: https://github.com/github/gh-aw


分享網址
AINews·AI 新聞聚合平台
© 2026 AINews. All rights reserved.