๐ English ยท ็ฎไฝไธญๆ
๐ฟ Give every agent its own copy-on-write branch of your project.
๐ธ Record every tool call as a changeset.
๐ Merge when the task is done, and let a third agent settle the conflicts.
โก No FUSE. Every byte goes through kernel file systems.
๐ง you (vim, cargo, git) ๐ค agent A ๐ค agent B
โ โ โ
โผ โผ โผ
~/proj โโ main โโโโโโโโโโโโโโโฌโโโโ branch A โโโ โโโ branch B
โฒ every write here โ (own mount โ โ (same path,
โ becomes history โ namespace) โ โ own copy)
โ โ โผ โผ
โโโโโโโโโ 3-way merge, follows renames โโโโโโโโ
conflicts โ ๐ค resolver agent โ ๐ง you
๐ก tracefs (AgentComputerFS) is the user-space side:
tracefsd, thetracefsCLI, the SDKs and the plugins. It has nothing to do with Linux's own tracefs at/sys/kernel/tracing. Because of that one, the kernel file system inkernel/is called acfs: two file systems with the same name would collide in the kernel.
Coding agents write to your disk. Several of them at once, sometimes in your working tree while you're also in it. Then you get to answer:
- โ What did it just change? Not "what did it say it changed".
- โ Can I undo that one tool call and keep the ten after it?
- โ Can two agents edit the same file at the same time without stepping on each other?
- โ Can I roll back what an agent did without killing the shell, the editor and the dev server that have files open in that directory?
โ tracefs answers yes to all four, without asking the agent harness to cooperate.
| ๐ฟ A branch per agent | tracefs run -- <agent> starts the agent in its own mount namespace. Every workspace it touches becomes its own btrfs snapshot, mounted at the same absolute path, so tool output and paths need no translation. Creating one is O(1): no data is copied. |
| ๐ธ Changesets, not commits | Each tool call can be an atomic snapshot of all the agent's branches together. The process is frozen while it's taken (41~56 ms with 10k changed files in the prototype, 91 ms in the current daemon) and nothing else pauses. Nothing to git add. |
| ๐ Nothing gets past it | Once a directory is registered, tracefs mounts its main over it. Edits from you, your editor or a build script are recorded as auto changesets once the writes stop. Undo writes back through the mount, so open files and running processes in the directory aren't disturbed. |
| ๐ A merge that follows renames | Three-way merge by file identity (inode numbers within one snapshot lineage), then by content. If one agent renames a directory while another edits a file inside it, the edit ends up at the new path. Text conflicts go to git merge-file. |
| ๐ค A third agent resolves conflicts | --resolver '<cmd>' gets a directory with TASK.md, conflicts.json and base/ours/theirs for each conflict. It writes resolutions.json, and tracefs merges again. Anything it can't resolve is left for you. |
| โช Undo at any granularity | reset to any point, revert one changeset, a range (one agent turn), or a whole session. Every undo is itself a new changeset, so history only grows. |
| ๐ Merge rules | .tracefsignore keeps build output out of history and out of merges. .tracefsmerge sets a strategy per pattern: ours, theirs, newer, union, or unit=<name>, which decides a group of files together, so a lockfile always comes from the same side as its manifest. |
| โ๏ธ Incremental push/pull | Snapshots ship as btrfs send -p streams plus a sha256 manifest, to any directory: local disk, NFS, or an S3 mount. |
| ๐งน History that stays small | Automatic snapshots are kept in full for 24 h, then hourly for 7 days, then daily. Their log and diff summaries are kept forever. |
| ๐ฅ Crash-safe | Branch rewrites are journaled. Agents keep running if tracefsd restarts, and tracefsd finds them again afterwards. |
| ๐ง A kernel file system | kernel/ is a separate in-tree Linux file system with native branches, a diff ioctl that skips shared subtrees, and block-sharing copies. See below. |
# 1๏ธโฃ once: hand a directory to tracefs (it must be under your home)
tracefs ws register proj --from ~/proj
# 2๏ธโฃ start two agents; each sees ~/proj, each gets its own branch
tracefs run -- pi &
tracefs run --resolver 'my-agent "follow TASK.md"' -- claude
# 3๏ธโฃ meanwhile, edit ~/proj yourself; it's recorded too
tracefs log --ws proj -n 20
tracefs diff 01JD3Kโฆ # what one changeset did
tracefs diff 01JD3Kโฆ --patch # โฆwith unified diffs
# 4๏ธโฃ that tool call was a mistake
tracefs revert 01JD3Kโฆ
# 5๏ธโฃ the whole session was a mistake
tracefs revert --session refactor-auth๐ When an agent says a task is done (tracefs proc merge $TRACEFS_PROC), or when it exits, its branches are merged into main. Work that other agents merged in the meantime is merged back into its branch, and it keeps going from there.
๐ค Two agents editing the same file is not an error: whoever finishes first merges cleanly, and only the second merge can conflict.
/var/lib/tracefs/
โโโ ๐๏ธ meta.db SQLite: workspaces, branches, snaps, changesets, journal
โโโ ๐งฑ bases/<id>/ read-only subvolume: l/1 โฆ l/n
โโโ ๐ฟ branches/<id>/ one writable subvolume: l/1โฆl/n + upper/ + work/
โโโ ๐ธ snaps/<id>/ read-only snapshot of a branch at a changeset
- ๐ฅ Data path: overlayfs on btrfs. Each branch is a single subvolume holding its lower layers,
upper/andwork/. Inode numbers stay unique in the merged view, sofindandcp -abehave. Copy-up is a reflink. - ๐ฟ Branches and snapshots: btrfs subvolume snapshots. They take O(1) time, are copy-on-write at block level, and work as incremental send streams for free.
- ๐ Change detection: inotify on
upper/. Every write through the overlay lands there, andupper/only contains directories that changed, so the number of watches stays small. - ๐ Fast diff:
btrfs send --no-data -p A B. The kernel walks only the parts of the two trees that differ, so the cost depends on the size of the change. The full upper-layer walk (v1) is kept as a check: the test suite runs both on every diff and fails if they disagree. - โ๏ธ Merges write through the mount, using
openat2(RESOLVE_BENEATH | NO_SYMLINKS | NO_XDEV). A file taken whole from one side is written withcopy_file_range, which btrfs turns into a clone: no new space and no data written. Resetting a 1 GiB file took 1.3 s, and the result shares the snapshot's extents. - ๐ง Agents:
systemd-run --scope. The agent runs as you, with your environment, in a private mount namespace. The cgroup is used to freeze it for snapshots and to kill whatever it leaves behind. - ๐ฉน Crash recovery. At startup tracefsd re-mounts workspaces, finishes or records half-done operations, finds running agents again by pid and start time, and merges whatever finished while it was down.
๐ Everything is specified, down to the invariants and the reasoning behind each decision, in AGENTS.md.
Measured, not projected. Workload and machine are in AGENTS.md ยง8.
| โธ๏ธ changeset pause, 10k changed files | 41~56 ms (prototype, 3 workspaces), 91 ms (current daemon); other processes stall at most 0.7 ms |
๐ tracefs run to the command starting, 3 workspaces |
170 ms |
| ๐ diff of a 10-file changeset | 17 ms |
| ๐ merge a renamed 2,500-file directory | 154 ms |
| ๐คร8 agents exit at once after editing one workspace | all merged, no conflicts, in 771 ms |
๐ฆ cargo build, fio 4k random write |
same as native btrfs |
๐ rg over the whole repo |
native +4% |
git status on 100k files is 14~20% slower than native, because of overlay metadata. core.untrackedCache / core.fsmonitor help.
The three columns run on the same machine against the same base trees, and each is mounted the way it would be used: x86_64 NVMe, btrfs (compress=zstd:1), a Linux kernel checkout (~100k files) and the ripgrep repo, timed with hyperfine (script: tests/kernel/t07_perf.sh).
fuse-overlayfs is the same layered design served through FUSE, so the gap between the last two columns is the cost of a trip to userspace on every operation.
| workload | ๐ฅ native btrfs | โก tracefs (kernel overlayfs) | ๐ข fuse-overlayfs |
|---|---|---|---|
๐ rg over the whole repo |
baseline | +4% | 6.3ร slower than tracefs |
๐ git status, cold cache |
baseline | +14% | 87ร slower than tracefs |
๐ git status, hot cache |
baseline | +17~20% | not recorded |
| โ๏ธ buffered writes | baseline | โ native | 1.7ร slower than tracefs |
๐ฆ cargo build (mid-size crate) |
baseline | โ native | not recorded |
| ๐ฒ fio 4k random write | baseline | โ native | not recorded |
These are prototype runs on a machine that has since been retired. The raw output isn't in this repo. Other tools (git worktree, containers, other agent file systems) were not benchmarked here, so they get no numbers. ๐
| ๐ณ tracefs | ๐ฒ git worktree |
๐ copy the dir | ๐ข FUSE overlay | ๐ณ container overlay | |
|---|---|---|---|---|---|
| making a branch costs | O(1) snapshot | a checkout of every tracked file | a copy of every byte | O(1) | O(1) |
| untracked & ignored files come along | โ | โ only what's committed | โ | โ | โ |
| edits recorded with no commit step | โ per tool call + automatic | โ | โ | โ | โ |
| your own edits to the directory recorded | โ | โ | โ | โ | โ |
| every agent sees the same path | โ mount namespace | โ another directory | โ another directory | โ another mount point | โ inside the container |
| merging back | โ 3-way, follows renames, resolver agent | after you commit | ๐๏ธ by hand | ๐๏ธ by hand | ๐๏ธ by hand |
| undo one tool call | โ | โ | โ | โ | โ |
| data path | โก kernel | โก kernel | โก kernel | ๐ข userspace on every op | โก kernel |
| agent uses your shell, tools and env | โ | โ | โ | โ | โ needs an image |
sdk/portable gives agent plugins one interface with two backends:
| backend | when | storage |
|---|---|---|
| ๐ง tracefsd | Linux, inside tracefs run |
the daemon, as described above |
| ๐ชถ tracefs-lite | everywhere else; no root, no VM | ๐ macOS: APFS sparse image + clonefile๐ง Linux: btrfs or a btrfs image + reflink ๐ช Windows: an NTFS VHDX |
npm install -g ./sdk/portable # puts tracefs-lite on PATH
tracefs-lite init
tracefs-lite register proj ~/proj # prints the path to work in
tracefs-lite branch proj agent1
tracefs-lite merge proj agent1 --prefer theirs- ๐ฅง pi:
pi -e sdk/pi/index.ts. Every assistant reply that writes files becomes a changeset, recorded in the session./treeand/forkbring the files back to that point./tracefs status|log|diff|undo|merge. - ๐ DeepSeek Harness:
dsh plugin --profile <name> add ./sdk/dsh. One changeset per step and a merge at the end of each turn. It also gives the model atracefstool so it can look at and undo its own work. - ๐งฉ Codex:
codex plugin marketplace add emmettlu/tracefs, then installtracefs. Hooks record a changeset for everyBash/apply_patch/Edit/Writecall and merge onStop. Atracefsskill gives the model status / log / diff / undo / merge. Codex only runs a plugin's hooks after you trust them. - ๐ช Anything else: call
tracefs changeset --author <harness> --tool-call <id>from a hook (for example Claude Code'sPostToolUse).
One JSON request per line over /run/tracefs/tracefs-daemon.sock. The types are generated from schema/tracefs.schema.json, which is generated from the Rust protocol types, so neither side can drift. ๐
// ๐ฆ TypeScript
import { Client } from "@dopamine-org/tracefs";
const log = await new Client().call("log", { ws: "proj", n: 10 }); // fully typed# ๐ Python, zero dependencies
from tracefs_sdk import Client
diff = Client().call("diff", {"a": "01JD3Kโฆ", "patch": True})kernel/ (GPL-2.0) is the same idea as its own Linux file system, acfs: mount -t acfs, with branches as a first-class feature.
mkfs.acfs /dev/sdX
mount -t acfs /dev/sdX /work # main
mount -t acfs -o branch=agent1 /dev/sdX /a1 # another branch, same device- ๐ฒ Copy-on-write B+ trees with lazy reference counts. Creating a branch adds one reference to a root.
- ๐๏ธ
ACFS_IOC_BRANCH_CREATE/DELETE/LIST/OPEN, andACFS_IOC_DIFF, which walks two trees together and skips every subtree they share. - ๐งฌ
copy_file_range,FICLONEandFIDEDUPERANGEshare blocks, even across branches. - ๐ชช A file has the same inode number in every branch it shares. Files created in different branches never collide.
- ๐ก๏ธ Two alternating superblock slots, crc32c on all metadata, commits with preflush + FUA, and crash consistency without a log.
- ๐ค Users can snapshot a file system they own without
CAP_SYS_ADMIN. - ๐งช Comes with
mkfs.acfs,fsck.acfsand selftests (basic, concurrent, crash, stress) that run in QEMU.
๐ Format and ABI: kernel/Documentation/userspace-api/ac/acfs.rst.
Requirements: ๐ง Linux โฅ 6.8 with btrfs, overlayfs and loop; systemd; btrfs-progs, git, diffutils.
๐พ The state directory doesn't have to be on btrfs: if it isn't, tracefsd creates a sparse btrfs image and loop-mounts it with direct I/O.
cargo build --release
sudo bash deploy/install.sh # tracefsd.service + tracefs.slice limits- ๐น Arch packages:
packaging/arch/PKGBUILD. - ๐๐ช macOS/Windows: use tracefs-lite (above).
tracefs-corealso builds and tests there.
๐งช Tests. CI runs:
- the plugins and Rust on Linux, macOS and Windows;
- 15 root integration suites against real btrfs and systemd, each on a throwaway loop image, under memory-pressure guards;
- a seeded random-operation fuzzer that checks diff/revert/reset/merge against a local replay;
- the kernel module's selftests under QEMU/KVM.
See AGENTS.md โบ ๆต่ฏ.
- ๐ Not a sandbox. An agent runs as you and can do anything you can. Only its view of the workspaces is replaced by its own branches. If you need to contain a hostile agent, use a VM.
- ๐ก Not live sharing. Agents don't see each other's unmerged writes. They share at merge time, by design.
- ๐งพ Not per-write auditing. The unit of history is the changeset.
User space: MIT. kernel/: GPL-2.0.