How Worktree Sessions Overlay Changes on Pinned Base Commits in Cloudflare OS
Cloudflare OS implements worktree sessions by pinning a workpiece to a base commit and applying file-tool operations as in-memory operational-transform (OT) rows that overlay the pinned state without materializing a real Git commit until explicitly requested.
Worktrees in Cloudflare OS are ephemeral, chat-scoped workspaces that let agents manipulate files without exposing the full repository state to clients. The system achieves this through a careful separation between pinned base commits (immutable references) and OT overlays (mutable deltas). This article explains the exact mechanism, citing the source implementation in cloudflare/cloudflare-os.
What Is a Worktree in Cloudflare OS?
A worktree is a special WorkpieceRecord with type: "worktree" that exists only within the chat session that created it. Unlike persistent workpieces, worktrees are:
- Session-bound: Destroyed when the chat ends
- Client-invisible: OT rows are stripped from all client deliveries
- Lazy-loaded: Files are fetched on-demand via
readFileAtCommit
The createWorktree tool—mirroring createGadget—initializes this structure according to plans/worktrees.md.
Step 1: Pinning the Worktree at Creation
When an agent calls createWorktree, the backend immediately pins the worktree to a base commit:
const { worktreeId, changeId } = await agentTool.createWorktree({
bindingName: "repo",
commitId: "a1b2c3d4" // resolved against local Git cache
});
The pin { worktreeId, baseCommit } is written as a pin entry in the chat's change stream. This design lets buildChatContent reconstruct the worktree later without additional state, as documented in plans/worktrees.md ("Pin at birth").
Step 2: Building the OT Overlay
Every file-tool operation creates a CodeChange row keyed by WorkpieceId. These rows contain only deltas—not full repository snapshots:
| Operation | OT Row Created |
|---|---|
readFile |
None (lazy fetch) |
writeFile |
Full content insert |
editFile |
Diff delta |
deleteFile |
Deletion marker |
grep |
None (read-only) |
The overlay lives in a session-content map: Map<WorkpieceId, Map<path, string>>. This in-memory structure is never serialized to the client, satisfying the "No UI" constraint in plans/worktrees.md.
Step 3: Lazy Reads and Pull-on-Fault
File reads resolve against the pinned base commit using a hand-rolled walker:
const content = await worktree.readFile("src/main.ts");
If the blob is missing locally, GitCache.ensureGitObjects triggers a pull-on-fault from the gatekeeper. This readFileAtCommit pattern appears in packages/workshop-backend/src/git-cache.ts and minimizes upfront data transfer.
Step 4: Committing the Overlay
The commit(message) operation materializes the overlay into a real Git commit:
const newCommitOid = await worktree.commit("Add new feature");
Per plans/worktrees.md ("commit(message) → oid"), this process:
- Applies OT rows to the base tree to build a new tree object
- Sets the parent to
headCommit(last explicit commit), notpinBase - Writes via
writeChangedFilesAsCommit, reusing unchanged sub-trees frompinBase - Advances only
headCommit, leavingpinBaseunchanged
This separation lets agents build commit chains while preserving the original pinned reference.
Step 5: Epoch Reset and Re-pinning
At mergeChanges boundaries, the system:
- Auto-commits dirty worktrees
- Re-pins each worktree to the newly created auto-commit
As described in plans/worktrees.md ("Epoch reset in mergeChanges"), this guarantees OT overlays always start from a clean base for the next epoch.
Key Implementation Files
| File | Responsibility |
|---|---|
plans/worktrees.md |
Design specification for pinning, OT overlay, and commit handling |
packages/workshop-shared/src/worktree.d.ts |
Agent RPC interface (listFiles, readFile, writeFile, deleteFile, grep, commit) |
packages/workshop-backend/src/overseer.ts |
Orchestrates workpiece creation, OT row writing, and epoch resets |
packages/workshop-backend/src/git-store.ts |
Low-level Git object storage for lazy walker |
packages/workshop-backend/src/git-cache.ts |
GitCache implementation for pull-on-fault and pack building |
packages/workshop-backend/src/approval-queue.ts |
Gatekeeper integration and push authorization |
packages/workshop-backend/src/ot-client.ts |
Client-side OT fetching; contains code stripping logic |
Complete Agent Workflow Example
// Create worktree from PR head
const { worktreeId } = await agentTool.createWorktree({
bindingName: "repo",
commitId: "abc123"
});
// Read and modify files (overlay only)
const src = await worktree.readFile("src/main.ts");
await worktree.editFile("src/main.ts", src.replace("old", "new"));
await worktree.writeFile("src/utils.ts", "export const helper = () => {};");
// Materialize commit
const oid = await worktree.commit("Refactor main, add helper");
// Push via gatekeeper (objects pulled on-demand)
Summary
- Pinning: Worktrees are pinned to a base commit at creation via
pinentries in the change stream - Overlay model: File operations produce OT rows stored in-memory, not full commits
- Lazy resolution: Reads fault-pull from gatekeepers via
GitCache.ensureGitObjects - Explicit materialization:
commit()builds real Git objects only when requested - Epoch safety:
mergeChangesauto-commits and re-pins to maintain clean base states - Client isolation: All worktree content is stripped from client deliveries in
ot-client.ts
Frequently Asked Questions
What happens if two agents modify the same worktree?
Worktrees are scoped to a single chat session. Concurrent access is serialized through the chat's OT stream; the backend processes file-tool operations in order, with each CodeChange row representing the delta from the previous state.
How does Cloudflare OS prevent clients from downloading entire repositories?
Worktree rows are explicitly stripped in packages/workshop-backend/src/ot-client.ts. The client receives only the createWorktree tool call and its worktreeId; all subsequent CodeChange rows are filtered from change replay, pins, and codeBase metadata.
What is the difference between headCommit and pinBase?
pinBase is the immutable commit the worktree was created from; it never changes until an epoch reset. headCommit advances with each explicit commit() call, forming a chain of commit parents while preserving the original pinned reference for overlay calculations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →