Skip to content
Zit
Esc
↑↓navigate↵open⌘Jpreview
On this page

Limits

What version 0.1 does not do, and where its guarantees stop.

Platform

  • macOS and Linux. CI runs every test on macOS (arm64), Linux (x86_64, plain checkouts; failures there do not fail the build), and Linux on btrfs and on XFS with copy-on-write required. The code is Unix-only (signals, libc); there is no Windows support.
  • Copy-on-write needs a file system that supports it: APFS on macOS; btrfs or XFS with reflink on Linux (FICLONE reflinks), each tested in CI with copy-on-write required. Other file systems with FICLONE, such as bcachefs, are not tested. On ext4 and others, a workspace is a plain checkout: its files are new copies, and [prepare] installs dependencies again in each one. On a CI runner, ten reflink workspaces of a 30,000-file repository added no measurable disk on btrfs, against 24 MB for worktrees; on ext4 both used 1.27 GB (Benchmarks).
  • Requires git 2.38 or newer (merge-tree --write-tree); Zit checks and names the version it needs.

Semantics

  • Prose merges like git. Two edits to one Markdown section are accepted when their text merges; Zit does not judge them further. Code is stricter: two edits to one function are refused even when the text merges.
  • Adjacent insertions conflict. Two contributors adding different lines at the same place in a list is a text conflict, as in git. In the 50-contributor simulation this was the most common remaining rejection (Lessons).
  • Inferred reads match by name. A change that mentions Config depends on every symbol called Config in any file; a method Type::method is depended on by code that names Type. Paths are not resolved. Member calls on values (cache.get()) are not references; member calls on imported modules (lib.price(), fmt.Println) are. It cannot see dynamic dispatch, reflection, or names built from strings.
  • Only an interface change makes a reader stale. An inferred reader goes stale when a symbol’s interface changes (its text without function bodies and comments: signatures, fields, constants), not when only a body changes. A body change that breaks a caller’s behaviour is left to the checks. Declared reads (zit read) still see any change. The scripted benchmark’s 21–23% false refusals were measured before this rule; not re-measured yet.
  • Five languages have symbol granularity: Rust, Python, JavaScript, TypeScript/TSX, Go, plus Markdown by heading. Top-level items and methods (Type::method) are units; nested functions are part of their parent. Every other file (Java, C#, C++, Kotlin, Ruby, PHP, Swift, manifests, lockfiles) is one resource with no inferred reads, so semantic staleness is not detected for it; zit record says so for source files it cannot parse.
  • Imports are their own unit (path#(imports)): two changes that both add imports compose when their text merges.
  • Renames are a delete plus an add.
  • Check inputs are trusted, not verified.
  • Symlinks and submodules are whole-file resources. Git LFS is untested.

Operations

  • Per-operation overhead is higher than raw git because every git operation is a process (ADR 7). Numbers in Benchmarks.

  • zit status computes every speculative change’s status each time; its cost grows with their number.

  • The checks of one verification run sequentially. Verification views keep ignored files between checks (ADR 10).

  • Each agent workspace is at a new path, so on a compiled project every agent rebuilds the project once, and a shared build directory accumulates output for paths that no longer exist. Measured: about 1.9 GB after ten agents on a Rust project (Lessons).

  • Claims are voluntary and local. They are not replicated, and an agent that has neither claimed nor started writing is invisible. What open workspaces are writing is reused for up to 2 seconds, so a claim may be decided on a slightly old picture.

  • TMPDIR is private per workspace; ports and other machine-wide state are shared between agents.

  • Discarded changes stay in the object database until git gc removes them.

  • A workspace orphaned by SIGKILL is listed, not cleaned up automatically.

  • Interactive zit run signals only the agent. Run headless (no terminal on stdin), the agent leads its own process group, and whatever it started is stopped before its work is recorded. With a terminal, the agent must stay in the terminal’s group to read it; children it leaves running are not stopped.

  • zit run passes stdin through. Some agents (Codex) wait for stdin to close when it is not a terminal; use </dev/null in scripts.

  • A workspace shares the repository’s refs. Branches, stashes or tags an agent creates with git inside a workspace appear in the real repository.

  • The terminal Ctrl-C path of zit run (delayed forwarding) is implemented but has no automated test; the non-terminal SIGINT/SIGTERM paths do.

  • No free-space check. When the disk fills, git cannot write and agents’ work is not landed (accepted states are not damaged). Seen once, in Lesson 02.

  • [prepare] needs copy-on-write to save space. Without it, the install still runs, once per workspace.

  • Sandboxed agents need Zit’s home. Agents that sandbox their commands must be allowed to write to Zit’s home (~/.zit), and only that: Zit never needs them to write the repository’s git directory, whose hooks and config would let them run code outside the sandbox. The codex preset grants this repository’s directory under it.

  • The presets let agents act without asking. claude runs with --permission-mode acceptEdits, autohand with --yes, codex in its workspace-write sandbox. They edit only their workspace, but run whatever commands their own settings allow.

  • zit clean sees only what Zit can see. It refuses while a workspace has a live zit run or unrecorded edits, or a clone or verification holds its lock; an MCP agent that has not written anything yet is invisible to it.

  • Liveness is a pid. A workspace whose zit run died may look alive if its pid is reused.

Not built

  • No server, accounts or access control: whoever can write the repository’s refs can accept. Agents using MCP cannot: zit_accept and zit_discard are offered only by zit mcp --integrator.
  • No signed evidence. Check results fetched from a remote are not trusted unless you set zit.trustFetchedEvidence (Another machine); with it set, they are trusted without proof of who produced them.
  • No batching: changes are verified and land one at a time (Compared).
  • No CI integration beyond running zit accept in a job yourself (Teams and review).
  • No replication protocol beyond git fetch/git push of refs/zit/*, and no automatic reconciliation of two machines that both moved current.
  • No remote, container or VM materialisers.
  • No dependency tracking on build artifacts, database schemas or runtime assumptions; resources are files and symbols.
  • No automatic tracking of what an agent read. Reads are inferred from written code or declared.
  • Cost is what the agent reports. Claude Code reports tokens and dollars, Codex tokens only, Autohand and Pi nothing yet. No way for an agent to set the intent of the change zit run records for it.
  • “Landed” means it composed and passed the checks, not that it was good. Quality has been scored once, by a blind model judge (Lessons), not by people.
  • No shared place for a question about the task that every agent should see answered once.
  • The largest run measured is in Benchmarks. Nothing larger is claimed.

Was this page helpful?