Bài 17 (worktree isolation) nói về isolation: "worktree" khi spawn subagent. Bài 26 (daemon architecture) hứa sẽ nói về worktree.bgIsolation thêm vào CC 2.1.143. Bài này thực hiện lời hứa đó.

Trước khi vào chi tiết, một điểm cần làm rõ ngay từ đầu: đây là hai feature khác nhau hoàn toàn, áp dụng cho hai đối tượng khác nhau. Nhầm lẫn giữa hai cái rất dễ vì cùng liên quan đến “worktree” và “isolation”.

Hai feature, hai đối tượng

isolation: "worktree" (bài 17)worktree.bgIsolation (bài này)
Áp dụng choSubagent được spawn bởi session khácBackground session (lead session chạy nền)
Ai quyết địnhNgười spawn subagent (param khi gọi Agent tool)Session chính nó, qua settings.json
Mặc địnhKhông có worktree (phải bật rõ ràng)Có worktree (phải tắt rõ ràng)
Cấu hình ởCode/skill gọi spawn, không có file cố địnhsettings.json ở global, project, hoặc local
Khi nào áp dụngLúc spawn subagentLúc background session bắt đầu chạy tool

Nói ngắn gọn hơn: isolation: "worktree" là param bạn truyền vào khi bảo CC tạo một agent con. worktree.bgIsolation là setting kiểm soát bản thân background session, không liên quan đến việc nó có spawn agent con hay không.

Background session là gì trong ngữ cảnh này

CC từ 2.x hỗ trợ chạy session theo chế độ background: session chạy nền trong daemon, bạn không cần giữ terminal mở, có thể attach vào sau khi nó xong. Tạo background session bằng:

claude --bg "review PR #42 và tóm tắt"

Hoặc nhấn trong session interactive đang chạy để detach nó sang background. Hoặc dispatch từ claude agents.

Background session là một lead session bình thường, có context window riêng, có tool loop riêng, nhưng chạy không cần người dùng giám sát liên tục.

Vấn đề: background session và working copy xung đột

Kịch bản thực tế:

Bạn đang code trên nhánh feature/payment-v2. Working tree có uncommitted edits. Bạn dispatch một background session để “cập nhật CHANGELOG.md với các thay đổi từ tuần này”.

Không có protection gì, background session sẽ edit thẳng vào working tree của bạn, cùng thư mục, cùng nhánh. Nếu bạn tiếp tục edit ở foreground cùng lúc, hoặc nếu background session edit một file bạn đang chỉnh dở, kết quả là conflict.

CC giải quyết bằng cách mặc định cho background session chạy qua EnterWorktree: session được đưa vào một git worktree riêng tạm thời trước khi bắt đầu dùng tool. Mọi edit đều nằm trong worktree đó, không chạm vào working tree gốc của bạn.

Background session khởi động
       |
       v
  EnterWorktree được gọi tự động
       |
       v
  Session làm việc trong .claude/worktrees/<slug>/
       |
       v
  ExitWorktree khi xong (hoặc merge thủ công)

Đây là hành vi mặc định, bảo toàn chính xác.

Vấn đề của approach mặc định

Worktree riêng không phải lúc nào cũng phù hợp.

Repo có submodule lớn. Git worktree chia sẻ object store nhưng từng submodule cần được init riêng trong worktree mới. Nếu repo có submodule nặng vài GB, worktree mới tương đương với phải init lại toàn bộ cây submodule. Tốn thời gian, tốn disk, đôi khi fail hoàn toàn nếu network không có.

Native build artifact và cache theo thư mục. Một số tool build (CMake, Bazel, Rust target cache) lưu cache theo path tuyệt đối. Worktree ở path khác đồng nghĩa cache miss hoàn toàn, build lại từ đầu. Với project lớn, điều này có thể khiến một background task đơn giản mất hàng chục phút chỉ để rebuild dependency.

Background job nhỏ và reversible. Nếu background session chỉ làm việc như cập nhật changelog, format code, hoặc update comment trong một file không ai khác đang chỉnh, overhead của worktree là không cần thiết.

Foreground và background không chạm cùng file. Nếu bạn biết foreground chỉ edit src/api/, còn background chỉ edit docs/, hai bên không có điểm chạm. Isolation chỉ tốn tài nguyên thêm.

Trong những trường hợp này, worktree.bgIsolation: "none" cho phép background session bỏ qua bước EnterWorktree và edit working copy trực tiếp.

Setting và cách dùng

Thêm vào settings.json:

{
  "worktree": {
    "bgIsolation": "none"
  }
}

Không set hoặc set bất kỳ giá trị nào khác "none": background session vẫn bị force qua worktree (hành vi mặc định bảo toàn).

Setting này có thể đặt ở ba mức:

MứcFileScope
Global~/.claude/settings.jsonMọi repo, mọi session trên máy
Project<repo>/.claude/settings.jsonChỉ repo này, tất cả contributor nếu commit
Local (per-machine)<repo>/.claude/settings.local.jsonChỉ repo này, chỉ máy này, không commit

Thông thường nên để ở mức project local nếu lý do opt-out là đặc thù của repo (submodule lớn, build cache), hoặc mức global nếu bạn có quy ước riêng cho tất cả repo của mình.

Hành vi cụ thể khi bgIsolation: “none”

Khi setting này có hiệu lực, background session khởi động mà không gọi EnterWorktree tự động. Các tool edit (Edit, Write, NotebookEdit) hoạt động trực tiếp trên working copy ở cwd của session.

EnterWorktree vẫn callable nếu session cần. Session có thể tự gọi nó chủ động nếu logic yêu cầu. Sự khác biệt là CC không force gọi nó trước khi bắt đầu tool loop.

ExitWorktree tương tự: vẫn có mặt, nhưng không được gọi tự động.

Hooks PreToolUsePostToolUse vẫn fire bình thường, không bị ảnh hưởng bởi setting này. Nếu bạn có hook kiểm tra tool trước khi chạy, nó vẫn hoạt động đúng.

Khi nào nên và không nên dùng

+------------------------------------------+
| Nên dùng bgIsolation: "none" khi:        |
|                                          |
|  - Repo có submodule lớn, worktree init  |
|    tốn thời gian / disk không chấp nhận  |
|  - Build cache bị miss do thay đổi path  |
|  - Background job nhỏ, reversible,       |
|    không overlap với foreground          |
|  - Foreground và background có file      |
|    scope tách biệt theo convention       |
+------------------------------------------+

+------------------------------------------+
| Không nên dùng khi:                      |
|                                          |
|  - Nhiều background session cùng repo    |
|    (race condition, last-writer-wins)    |
|  - Background job làm việc nặng và       |
|    khó rollback (migration, large        |
|    refactor, delete operations)          |
|  - Foreground đang mid-debug với scratch |
|    state quan trọng không nên bị touch   |
|  - Không chắc scope overlap              |
+------------------------------------------+

Nguyên tắc chung: mặc định có isolation là chọn lựa bảo toàn. Chỉ tắt khi có lý do cụ thể và bạn kiểm soát được scope.

Tương tác với các feature liên quan

EnterWorktree và ExitWorktree tool. Khi bgIsolation: "none", CC không auto-call EnterWorktree. Nhưng hai tool này vẫn available trong tool list của session. Session có thể tự gọi chúng chủ động, ví dụ nếu một skill được thiết kế để luôn chạy trong worktree riêng bất kể setting.

WorktreeCreate hook. Hook này (nếu bạn có configure) vẫn hoạt động bình thường khi có ai gọi EnterWorktree. bgIsolation: "none" chỉ ngăn auto-call, không disable hook.

Worktree branch từ subagent. Nếu background session với bgIsolation: "none" lại spawn một subagent với isolation: "worktree", subagent đó vẫn có worktree riêng của nó. Hai setting không xung đột: một cái áp dụng cho lead session, một cái áp dụng cho session con.

Background session từ claude --bg và từ claude agents. Setting áp dụng cho cả hai cách khởi tạo.

Edge cases cần biết

Setting thay đổi giữa chừng khi session đang chạy. CC đọc settings.json khi session khởi động. Nếu bạn thay đổi setting sau khi background session đã bắt đầu, session đang chạy không bị ảnh hưởng. Lần khởi động tiếp theo mới pick up thay đổi.

Repo không có git worktree setup. Với bgIsolation: "none", không cần worktree setup. Session edit thẳng working copy, git worktree không được dùng đến. Nếu setting mặc định (force qua worktree) mà repo vì lý do nào đó không hỗ trợ git worktree add (ví dụ shallow clone), background session sẽ fail ở bước EnterWorktree. bgIsolation: "none" là workaround cho case đó.

Background session với bgIsolation: “none” và multiple concurrent sessions. Đây là trường hợp nguy hiểm nhất. Nếu bạn dispatch hai background session cùng repo cùng lúc, cả hai đều edit working copy không qua worktree, và cả hai cùng chỉnh một file, kết quả là race condition giống như chạy hai terminal edit file đó đồng thời. CC không có lock mechanism ở tầng này. Trách nhiệm thuộc về bạn để đảm bảo scope tách biệt.

Verify setting đang hoạt động

Để kiểm tra setting có được pick up không:

# Xem setting ở mức project local
cat .claude/settings.local.json | jq '.worktree'

# Xem merged settings (global + project)
claude config get worktree.bgIsolation

Nếu đang chạy một background session và muốn xác nhận nó không vào worktree, check trong session output: nếu không thấy dòng nào về EnterWorktree ở đầu session, setting đang có hiệu lực.

Tóm tắt và bài tiếp theo

  • worktree.bgIsolation: "none"isolation: "worktree" là hai feature khác nhau hoàn toàn. Cái trước áp dụng cho lead background session qua settings. Cái sau là param khi spawn subagent.
  • Mặc định của CC là force background session qua EnterWorktree để tránh conflict với working tree chính. Opt-out bằng bgIsolation: "none" khi repo có lý do cụ thể (submodule nặng, build cache, scope tách biệt rõ).
  • Khi dùng bgIsolation: "none", trách nhiệm tránh conflict file scope thuộc về người dùng. Multiple concurrent background session cùng chạm file là race condition.
  • Thêm vào phiên bản 2.1.143. Settings schema: { "worktree": { "bgIsolation": "none" } }.

Series “Claude Code từ zero” kết thúc ở bài 27 này. Từ anatomy session ở bài 1 đến setting nhỏ như bgIsolation ở bài 27, mỗi layer build lên nhau. Mục tiêu không phải nhớ hết, mà là khi gặp behavior lạ, biết mình cần đọc gì.


Bài thuộc series Claude Code từ zero. Series plan tại bài giới thiệu.