# AgentSession isolation: research, tradeoffs, and recommendation

Status: decision-ready research recommendation with the first bounded Stage 6C
workspace-service slice implemented; not yet an accepted ADR

Date: 2026-08-10

Related design:

- [ADR 0008](source-context/adr-0008-minimal-proposals-environments-and-production-analytics.md)
- [Proposed ADR 0009](decision-record.md)
- [PDB greenfield design and implementation plan](source-context/greenfield-implementation-plan.md)
- [System boundaries](source-context/system-boundaries.md)

## Decision question

PDB needs multiple coding agents to start from the same immutable program state
and work concurrently without observing or overwriting one another's dirty
files. Each agent should receive an ordinary filesystem tree that works with
unmodified editors, language servers, package managers, compilers, test
runners, and development servers. Git, Docker, and a cloud account must remain
optional.

This document evaluates whether PDB should implement that contract with native
copy-on-write clones, Linux OverlayFS, a userspace virtual filesystem, full
copies, containers, or another workspace model. It also separates source-tree
isolation from dependency, process, security, and publication concerns.

The final recommendation is:

1. Keep `Environment` as logical program lineage and `AgentSession` as the
   private pinned writable filesystem view.
2. Define a small snapshotter-style backend contract. Do not expose filesystem
   implementation details in canonical records or public format semantics.
3. Use an ordinary native directory as the reference behavior. Prefer APFS
   copy-on-write clones on macOS, reflink or native clones where available on
   Linux, ReFS block cloning where actually supported on Windows, and a correct
   recursive-copy fallback everywhere.
4. Evaluate AgentFS and Linux overlay implementations as optional backends,
   not universal defaults. In particular, do not make a macOS NFS or FUSE mount
   the default before Node watcher, language-server, and build benchmarks pass.
5. Keep dependency resolution separate: reuse immutable package content, but
   give every Session its own dependency link graph and mutable build output.
6. Checkpoint a Session into immutable PDB objects and an exact changeset.
   Filesystem backends never publish directly to `main`; PDB policy and the
   expected-head commit kernel retain authority.
7. Optimize the initial product for trusted, cooperative coding agents that
   must not step on one another's dirty files. Native directories satisfy this
   workspace-isolation requirement. Hostile-code containment is explicitly
   outside the initial product scope and must not make containers or VMs
   mandatory.

## Research snapshot and decision

This recommendation was rechecked on 2026-08-10 against primary upstream
documentation and released software. The local probe host was Apple silicon,
macOS 26.5.2 (build 25F84), Darwin 25.5.0, on APFS. Local Node tooling was Node
24.7.0, npm 11.5.1, and pnpm 11.13.0. Workload fixtures must pin their own
versions rather than treating those host versions as part of the result.

The macOS-first bake-off should keep the proposed three entries, but name their
roles precisely:

| Backend ID | Role | Decision before bake-off |
| --- | --- | --- |
| `apfs-clone` | leading production default | Keep. It presents a native directory and preserves native watchers. A requested clone that falls back to copying must report `unsupported`, not masquerade as a clone result. |
| `full-copy` | shipping fallback and correctness oracle | Keep. It is the semantic baseline against which every visible tree and checkpoint is compared. It is exempt from copy-avoidance performance thresholds. |
| `agentfs-external` | evaluation-only virtual-filesystem candidate | Keep in the experiment, but do not embed or select automatically. Pin a released binary and checksum, supervise it as an external process, and reject it unless it passes core correctness, lifecycle, watcher, and Node workload gates. |

These are the right three because the first two isolate the performance value
of APFS cloning while holding native filesystem semantics constant, and the
third tests the most relevant genuinely different local design. Adding another
FUSE implementation to the primary matrix would multiply mount and watcher
variables without strengthening the oracle. BranchFS may run as a non-gating
shadow comparator, but its macOS path requires macFUSE, system-extension
approval, and on some Apple silicon systems Recovery-mode configuration. Its
direct commit-to-parent behavior is also outside PDB's authority contract.
ArtifactFS is Git-backed and has the same macFUSE prerequisite. APFS volume
snapshots, sparse disk images, containers, and VMs are not ordinary
same-volume writable directory clones and do not improve the minimal proof.

The AgentFS disposition is **bounded external adapter or rejection**, not
embedding. The evaluated release is v0.6.4, published 2026-03-25, commit
`3a5ed2b88e5d5a5f9b2c7fe02d012b50fd19e3c0`; the Apple silicon archive SHA-256
is `d07772d7c96f16c0d8996c15502f189d449ab0d9460fc2fe68f6edbae91bae73`.
Upstream `main` was
`0a014ebd4918615baff589ed17486e557e7c6a23` as observed on 2026-08-10, but the
experiment must never float on `main`. The project still labels itself beta.

On the probe host, the released v0.6.4 binary could initialize an overlay
database, but two `agentfs exec iso /bin/pwd` attempts failed after about 30
seconds with `connection pool timeout: no connections available`. The base
hashes were unchanged, `agentfs diff iso` reported no changes, and no mount or
live AgentFS process remained. This is a dated single-host observation, not a
general claim; the harness must retain stdout, stderr, mount table, process
state, database/WAL files, and tool versions so the upstream project can
reproduce it.

Primary version references:

- [AgentFS v0.6.4 release](https://github.com/tursodatabase/agentfs/releases/tag/v0.6.4)
- [AgentFS v0.6.4 changelog](https://github.com/tursodatabase/agentfs/blob/v0.6.4/CHANGELOG.md)
- [AgentFS specification v0.4](https://github.com/tursodatabase/agentfs/blob/v0.6.4/SPEC.md)
- [BranchFS macOS prerequisites](https://github.com/multikernel/branchfs#macos-support)
- [ArtifactFS build prerequisites](https://github.com/cloudflare/artifact-fs#build-and-install)

## Design goals

The choice is evaluated against PDB's approved product direction:

- one-command useful local operation;
- no mandatory Git, Docker, cloud provider, or privileged mount setup;
- native macOS, Linux, and Windows support;
- ordinary filesystem behavior for existing coding agents and tools;
- two or more agents starting from one Environment with private dirty state;
- pinned immutable bases rather than a live main directory;
- resumable attempts, including failed attempts;
- independently variable dependency versions when lockfiles or toolchains
  differ;
- efficient use of disk and startup time in large npm monorepos;
- exact checkpointing into PDB's canonical object and ledger model;
- explicit authority and MVCC/OCC validation at publication time; and
- an open-source foundation whose hosted form can later add managed compute,
  synchronization, retention, and stronger isolation.

## The problem is not a generalized Python virtualenv

Python virtual environments isolate an interpreter and installed packages.
Python explicitly describes a venv as disposable, excludes project source from
it, and recommends recreating rather than moving or copying it. A venv does not
give two processes private writable views of the same source directory.

PDB has at least five independent concerns:

1. **Program snapshot**: the immutable exact source and semantic state from
   which work begins.
2. **Writable source view**: one Session's additions, edits, renames, and
   deletions over that snapshot.
3. **Dependency/toolchain resolution**: Node, npm/pnpm, native tools, package
   versions, and their immutable caches.
4. **Runtime state**: generated files, temporary paths, ports, local databases,
   background processes, and development-server state.
5. **Security boundary**: whether a process can intentionally escape the
   assigned root, inspect other Sessions, access credentials, or use the
   network.

No single filesystem primitive solves all five. PDB should compose them and
state each guarantee precisely.

Reference: [Python `venv` documentation](https://docs.python.org/3.12/library/venv.html).

## Required semantics

### Environment

An Environment is a named logical program lineage. Its head is immutable and
content-addressed. `main` is an Environment protected by policy, not a distinct
storage or protocol type.

Changing a terminal's active Environment affects future commands and Sessions.
It does not rewrite files underneath a running Session.

### AgentSession

An AgentSession is a host-local, private, pinned writable view of one exact
Environment head. Multiple agents may work from the same Environment, but each
gets a new Session by default. Multiple agents may intentionally attach to one
Session when shared live editing is desired; that mode is explicit and does not
claim writer isolation between those attached agents.

A Session must support:

- start from an exact immutable Snapshot;
- receive a normal filesystem root;
- read unchanged source without duplicating it when the backend permits;
- privately create, edit, rename, delete, and chmod files;
- privately maintain generated and build state;
- resume after the initiating agent or terminal exits;
- derive exact changes without relying on Git status;
- checkpoint successful and failed attempts;
- discard without changing its parent Environment; and
- publish only through an expected-head PDB transaction.

### Isolation claims

PDB should use two separate terms:

- **Workspace isolation**: PDB assigns separate writable roots, and ordinary
  work within one root cannot accidentally overwrite a sibling Session.
- **Security isolation**: the operating system prevents a hostile or buggy
  process from traversing outside its allowed root or affecting sibling
  processes, credentials, and network resources.

Native cloned directories provide workspace isolation, not a security boundary.
Linux namespaces, macOS sandbox profiles, containers, and microVMs can add
security isolation, with different portability and maintenance costs.

The initial PDB product assumes trusted coding agents and requires only the
first claim. Security isolation remains vocabulary for honest capability
reporting and a possible future runtime profile; it is not a gate, dependency,
or implied threat model for the workspace service.

## Survey of comparable systems

### Agent-native filesystems

#### AgentFS

[AgentFS](https://github.com/tursodatabase/agentfs) is the closest existing
open-source implementation to PDB's AgentSession requirement. It is MIT
licensed and currently marked beta. It stores agent filesystem state in a
SQLite database and provides a copy-on-write overlay over an existing host
directory. Unmodified tools see a mounted filesystem; writes enter the SQLite
delta, and deletions are represented with whiteouts.

AgentFS provides:

- named persistent Sessions;
- separate overlays over the same base;
- explicit diff and discard workflows;
- a portable single-file writable delta after a sound database checkpoint;
- a queryable final filesystem state and a separate explicit tool-call log;
- FUSE mounting on Linux; and
- a localhost NFS server plus `sandbox-exec` profile on macOS.

Its examples explicitly show parallel experiments from the same codebase and
multiple terminals intentionally joining one shared Session.

Advantages for PDB:

- nearly identical user semantics;
- open source and local-first;
- Rust implementation and SDK;
- no Git requirement;
- exact accounting of upper-layer changes; and
- potential portability of a failed Session's mutable state.

Risks and costs:

- beta maturity;
- a daemon, database, and mount lifecycle become part of every Session;
- v0.6.4's copy-up reads a whole base file into memory and writes the whole
  file into the delta before modification;
- storing large dependency installations in SQLite can be expensive;
- Linux FUSE and macOS NFS have different behavior and performance;
- macOS uses deprecated-but-still-available `sandbox-exec`; the v0.6.4 profile
  allows all file reads and broad writes to temporary, device, user Library,
  configuration, cache, and package-manager paths, so it does not meet PDB's
  sibling-confidentiality security profile;
- the macOS mount is NFSv3 with `locallocks` and `soft` timeout behavior;
- specification v0.4 treats xattrs and ACLs as future extensions;
- the database uses WAL, so copying only `agent.db` while it is live is not a
  sound checkpoint protocol; a quiesced/checkpointed SQLite snapshot is
  required;
- the CLI's `diff` classifies delta paths as added or modified by asking
  whether the path currently exists in the base and reports whiteouts as
  deletes; PDB must not treat that presentation as its canonical exact
  changeset without an independent tree scan;
- Node, Watchman, editors, and development servers may not receive reliable
  native watcher events over NFS; and
- published documentation does not yet establish performance for large npm
  installs, TypeScript servers, Vite HMR, or filesystem-heavy test suites.

AgentFS is therefore a useful evaluation backend and source of adversarial
fixtures, not a presumed product dependency. It should remain outside the PDB
process and automatic backend selector unless the released binary passes the
full correctness, lifecycle, watcher, and workload matrix.

The upstream README's stronger phrases about every filesystem operation being
auditable, copying one live database file to snapshot it, and being safe for
untrusted agents should not become PDB guarantees. Specification v0.4 defines
mutable current-state tables plus an explicit tool-call table, not a canonical
append-only filesystem-operation history. A live v0.6.4 database observed in
this research had a 4 KiB database file plus a 234,872-byte WAL; copying the
database file alone would omit current state. PDB should rely only on behavior
it independently tests and on its own checkpoint/object validation.

References:

- [AgentFS introduction](https://docs.turso.tech/agentfs/introduction)
- [Copy-on-write overlay guide](https://docs.turso.tech/agentfs/guides/overlay)
- [Shared Sessions](https://docs.turso.tech/agentfs/guides/sessions)
- [Overlay implementation on Linux and macOS](https://turso.tech/blog/agentfs-overlay)
- [SQLite-backed FUSE design](https://turso.tech/blog/agentfs-fuse)
- [Agent filesystem specification](https://github.com/tursodatabase/agentfs/blob/main/SPEC.md)
- [v0.6.4 whole-file copy-up implementation](https://github.com/tursodatabase/agentfs/blob/v0.6.4/sdk/rust/src/filesystem/overlayfs.rs#L501-L599)
- [v0.6.4 macOS NFS mount options](https://github.com/tursodatabase/agentfs/blob/v0.6.4/cli/src/mount/nfs.rs#L155-L176)
- [v0.6.4 macOS sandbox profile](https://github.com/tursodatabase/agentfs/blob/v0.6.4/cli/src/sandbox/darwin.rs)

#### BranchFS

[BranchFS](https://github.com/multikernel/branchfs) is an MIT-licensed,
FUSE-based filesystem for speculative agent branching. It offers private
file-level copy-on-write deltas, nested branches, commit-to-parent, abort, and
multiple virtual branch paths under one mount. It requires FUSE on Linux or
macFUSE on macOS.

The accompanying paper frames the abstraction as `fork -> explore ->
commit/abort`, including private filesystem views and process groups.

Useful lessons:

- Session creation should be cheap and independent of base size;
- failed work should be discardable without parent mutation;
- nested speculative attempts may eventually be useful; and
- process lifecycle is part of real isolation, not merely an adjacent detail.

PDB should not adopt BranchFS's direct commit-to-parent semantics. PDB needs to
retain multiple Proposals and Implementations, validate expected heads and
semantic conflicts, and allow a separate merge authority. The filesystem
backend should produce an exact changeset; it must not decide which sibling
wins or mutate canonical main.

BranchFS also argues that FUSE overhead is usually dominated by LLM latency.
That may hold during model calls but does not establish acceptable performance
for npm installs, TypeScript graph construction, local builds, tests, status
walks, or HMR. Those workloads require measurement.

References:

- [BranchFS repository](https://github.com/multikernel/branchfs)
- [Fork, Explore, Commit paper](https://arxiv.org/abs/2602.08199)

#### Cloudflare ArtifactFS

[ArtifactFS](https://github.com/cloudflare/artifact-fs) is an Apache-2.0 FUSE
driver that exposes a Git repository immediately and lazily hydrates blobs. It
has a writable overlay and reconciles ordinary Git operations. It demonstrates
a useful future direction for enormous PDBs: expose a complete tree immediately
while loading untouched objects on demand.

It is not a present default because it is Git-oriented, requires FUSE or
macFUSE, and documents meaningful traversal overhead: approximately seven
seconds for `git status` on a repository with more than 5,800 entries in its
published example. PDB should revisit lazy hydration only after native
materialization costs become a measured bottleneck.

### Version-control workspace models

#### Git worktrees

[Git worktrees](https://git-scm.com/docs/git-worktree.html) provide multiple
ordinary working directories attached to one shared repository and object
store. Each working tree has independent HEAD and index state. They remain the
compatibility baseline: native filesystem behavior works naturally with Node,
editors, watchers, compilers, and test runners.

They do not satisfy PDB's canonical design because they require Git and encode
branch/index concepts PDB does not want as core semantics. They also do not
prevent a same-user process from intentionally editing sibling paths.

The lesson is not to recreate Git internals. It is to preserve the ordinary
directory behavior that makes worktrees dependable.

#### Jujutsu workspaces

[Jujutsu workspaces](https://jj-vcs.github.io/jj/latest/working-copy/) attach
multiple working copies to one repository. Each workspace can have a different
commit checked out. Jujutsu explicitly identifies long-running tests in one
workspace while development continues in another as a use case.

Jujutsu detects a stale working copy if another workspace rewrites its commit
and requires an explicit update. Its broader concurrency design uses an
operation log and accepts divergent local operations rather than assuming a
coarse repository lock.

PDB should copy two ideas:

- running Sessions stay pinned instead of being hot-swapped; and
- repository concurrency is represented and reconciled explicitly rather than
  hidden behind filesystem mutation.

Reference: [Jujutsu concurrency design](https://jj-vcs.github.io/jj/latest/technical/concurrency/).

#### GitButler parallel branches

[GitButler parallel branches](https://docs.gitbutler.com/ai-agents/parallel-agents)
let multiple agents assign changes to distinct logical branches while sharing
one physical working directory, dependency installation, generated output,
and runtime state.

This reduces setup cost for deliberately independent work, but it does not meet
PDB's requested default. GitButler itself recommends separate worktrees for
competing attempts, incompatible checkout state, and isolated runtimes. It is a
useful explicit `shared` mode, not an isolation implementation.

#### EdenFS and Sapling

[EdenFS](https://github.com/facebook/sapling/blob/main/eden/fs/docs/Overview.md)
is a virtual filesystem designed for very large source trees. It materializes
files lazily and integrates with source control and Watchman. Its cross-platform
backend work demonstrates that a highly compatible virtual source filesystem
is possible, but it is a major standalone system with deep operating-system,
watcher, and source-control integration.

PDB should not attempt an EdenFS-class implementation before measurements show
that native clones or materialized snapshots cannot meet startup and disk
goals.

### Container, microVM, and managed-workspace systems

#### Docker Sandboxes

[Docker Sandboxes](https://docs.docker.com/ai/sandboxes/) give each coding agent
a microVM, filesystem, network, Docker daemon, and persistent runtime. Their
optional clone mode mounts the source read-only and gives the agent a private
clone; direct mode deliberately edits the host working tree.

This is a strong security and complete-runtime model. It is too heavy and
requires too much platform setup to be PDB's mandatory local source-isolation
layer. It is suitable for PDB's optional extended execution service.

References:

- [Docker Sandboxes security model](https://docs.docker.com/ai/sandboxes/security/)
- [Docker Sandboxes architecture](https://docs.docker.com/ai/sandboxes/architecture/)

#### Coder, Daytona, DevPod, and OpenHands

- [Coder Agents](https://coder.com/docs/ai-coder/agents) provision complete
  isolated workspaces from infrastructure templates.
- [Daytona Sandboxes](https://www.daytona.io/docs/sandboxes) provision
  containers or VMs with dedicated filesystems, networks, compute, snapshots,
  and forks.
- [DevPod](https://devpod.sh/docs/what-is-devpod) uses the open Dev Container
  standard across local, remote, and cloud providers.
- [OpenHands](https://docs.openhands.dev/openhands/usage/architecture/runtime)
  uses a sandboxed runtime, commonly Docker, and warns that read-write host
  mounts remain directly mutable by the agent.

These systems prove that complete environment isolation is valuable and that
humans often need to enter the agent's live workspace. They also demonstrate
the operational weight: provisioning, image lifecycle, networking, credentials,
resource allocation, and remote IDE access.

PDB should integrate with these systems through an optional execution boundary.
It should not make any one of them the local Program Database format or require
them to obtain private source trees.

### Build and package isolation

#### Bazel sandboxes

[Bazel sandboxing](https://bazel.build/docs/sandboxing) constructs a private
execution root containing declared inputs and generated outputs. On supported
systems it adds Linux namespaces or macOS sandbox profiles to prevent writes
outside the root. Its generic fallback creates a symlink forest but is not a
complete security boundary.

Relevant lessons:

- a private directory and a security sandbox are distinct mechanisms;
- generated output should be isolated from declared source inputs;
- sandbox setup and teardown have measurable cost;
- reusable directories and persistent workers improve performance while
  weakening or complicating some isolation guarantees; and
- undeclared input access undermines reproducibility.

#### Nix and devenv

The [Nix store](https://releases.nixos.org/nix/nix-2.24.5/manual/store/index.html)
stores immutable content-addressed build inputs and outputs. Tools such as
[devenv](https://devenv.sh/) compose reproducible toolchains and services from
that store and can optionally produce containers.

This is a useful dependency and toolchain pattern, not a source workspace
solution. PDB can record a resolution digest and reuse immutable content without
forcing users to adopt Nix.

#### Cargo target directories and compiler caches

[Cargo target directories](https://doc.rust-lang.org/stable/cargo/reference/build-cache.html)
are mutable build caches and output directories, not immutable dependency
stores. A workspace shares one `target` directory by default, and users can
redirect other checkouts to it with `CARGO_TARGET_DIR`, `build.target-dir`, or
`--target-dir`. The directory holds final artifacts, hashed dependency outputs,
incremental compiler state, build-script output, and fingerprints.

Cargo's
[fingerprint implementation](https://doc.rust-lang.org/stable/nightly-rustc/cargo/core/compiler/fingerprint/index.html)
keys reuse on inputs including the compiler version, compile mode, target,
features, profile, flags, immediate dependency fingerprints, source paths, and
selected configuration. Hashed intermediate names let several configurations
coexist, but the directory remains a shared mutable namespace. Cargo uses
directory-level locks in the stable model; its evolving fine-grained path adds
separate target-artifact, build-directory, and build-unit locks in the
[Cargo source](https://github.com/rust-lang/cargo/blob/master/src/compiler/build_runner/compilation_files.rs).
Pointing concurrent agent checkouts at one target directory can therefore
serialize builds and still couples final-output names and mutable incremental
state.

Cargo's own build-cache documentation recommends
[sccache](https://github.com/mozilla/sccache) for reuse across workspaces. The
lesson for PDB is to keep `target` and build-script output Session-local, while
optionally sharing a compiler action cache whose keys include the complete
toolchain and compile identity. APFS-cloning a quiescent warm `target` baseline
is a separate local optimization; it must not turn the live target directory
into a cross-Session writer rendezvous.

#### npm, pnpm, and graph-addressed virtual stores

[npm workspaces](https://docs.npmjs.com/cli/v8/using-npm/workspaces/) describe
multiple packages within one top-level project and link those packages into one
`node_modules` tree. They do not provide private writable source views.

[pnpm](https://pnpm.io/motivation) stores package content once in a
content-addressed store and constructs project-specific dependency graphs using
hard links, reflinks, and symlinks. Its normal isolated layout has two layers:

1. a global content-addressed store holds package files by content, so even two
   package versions can reuse every unchanged file; and
2. a project-local `node_modules/.pnpm` virtual store expresses the resolved
   dependency graph, with direct-dependency and dependency-edge symlinks.

The package bytes are physically shared, while the graph view remains local.
This is already a better model than copying or sharing a writable npm-style
`node_modules`.

pnpm added the opt-in
[`enableGlobalVirtualStore`](https://pnpm.io/settings/node-modules#enableglobalvirtualstore)
setting in v10.12.1. It moves the graph layer to `<store-path>/links`: each
package is materialized under a hash of its resolved dependency graph, and each
project's `node_modules` contains symlinks into those reusable graph entries.
The actual package files remain hard-linked from the separate content-addressed
store. The
[open-source graph hasher](https://github.com/pnpm/pnpm/blob/main/pnpm11/deps/graph-hasher/src/index.ts)
includes package identity, dependency closure, patch and build policy, local
directory scope, supported architecture, and the relevant script-running Node
version where build side effects require them. A lockfile change therefore
creates new entries only for affected graph closures; unaffected closures remain
shared.

As of the 2026-08-10 documentation review, the global virtual store is disabled
by default for ordinary projects, automatically disabled in CI, and enabled by
default only for pnpm v11 global installs and `dlx`. Hoisted undeclared
dependencies are a compatibility boundary: the global layout uses `NODE_PATH`,
which native ESM resolution ignores. Broken ESM packages may require explicit
`packageExtensions` or pnpm's ESM loader plugin. PDB must expose this as an
opt-in package-manager capability, not silently change a project's resolution
semantics.

Reference: [pnpm symlinked `node_modules` structure](https://pnpm.io/symlinked-node-modules-structure).

#### Bun isolated installs and global virtual store

Bun has converged on the same separation with different platform mechanics.
Its [global package cache](https://bun.com/docs/pm/global-cache) lives under
`~/.bun/install/cache`. With the isolated linker and global store disabled, Bun
constructs a project-local `node_modules/.bun` graph and imports cached package
files with APFS `clonefile` on macOS, hard links on Linux and Windows, or a byte
copy fallback. The physical bytes can therefore be copy-on-write even though
the graph is materialized per project.

The opt-in
[Bun global virtual store](https://bun.com/docs/pm/global-store) changes the
warm path. It materializes an immutable resolved entry once under
`<cache>/links/<package>-<graph-hash>` and makes the project's
`node_modules/.bun/<package>` a symlink to that entry. The entry hash covers the
package and tarball integrity, resolved dependency closure, peer context, and
dependency cycles. Bun's
[open-source isolated installer](https://github.com/oven-sh/bun/blob/main/src/install/isolated_install.rs)
uses a strongly-connected-component pass so dependency cycles receive stable
shareable identities.

The implementation deliberately falls back to project-local materialization
for patched packages, trusted lifecycle-script packages, workspace, `file:`,
and `link:` dependencies, and any entry whose dependency closure contains one
of those mutable inputs. Concurrent writers build a complete entry under a
private temporary name and atomically rename it into the store; a losing writer
discards its identical staging tree. The publication and project-link behavior
is visible in the
[Bun installer source](https://github.com/oven-sh/bun/blob/main/src/install/isolated_install/Installer.rs).

As of the 2026-08-10 review, Bun's global store is off by default. Bun reports a
1,400-package macOS fixture shrinking from 391 MB per project to about 5 MB of
project-local symlinks plus one shared 391 MB graph, with the warm install
falling from about 841 ms to 125 ms. These are vendor measurements, not PDB
evidence, but the source makes the storage and concurrency contracts testable.
The important PDB lesson is the eligibility fallback: a logically private
dependency view can share immutable graph entries without ever exposing a
shared mutable install tree.

### Filesystem and snapshot primitives

#### Linux OverlayFS

[OverlayFS](https://www.kernel.org/doc/html/latest/filesystems/overlayfs.html)
combines one or more lower directories with a writable upper directory and a
work directory. Reads fall through to lower layers. First mutation copies an
object into the upper layer, and deletions use whiteouts.

Advantages:

- cheap creation;
- storage proportional to changes;
- mature use in container systems; and
- an easily inspectable upper directory.

Constraints:

- Linux-specific mount and backing-filesystem requirements;
- rootless availability depends on kernel and user-namespace configuration or
  `fuse-overlayfs`;
- lower and upper trees must not be modified behind a mounted overlay;
- lower-directory rename may return `EXDEV` unless redirect behavior is
  enabled;
- copy-up and open-file behavior can be observable;
- not all POSIX semantics are identical to a native directory; and
- crash durability depends on application `fsync` behavior and filesystem
  configuration.

OverlayFS is a good Linux backend but must not define PDB's universal behavior.
The immutable lower layer requirement agrees with PDB's Snapshot model.

Rootless references:

- [Podman rootless storage](https://docs.podman.io/en/latest/markdown/podman.1.html)
- [BuildKit rootless snapshotters](https://github.com/moby/buildkit/blob/master/docs/rootless.md)

#### APFS clones

[Apple File System](https://developer.apple.com/documentation/foundation/about-apple-file-system)
supports copy-on-write file and directory cloning. Foundation copy APIs
automatically create clones when source and destination are on an APFS volume.
The resulting tree is a normal native directory: unchanged file blocks are
shared, and later writes allocate private blocks. Apple documents that
`copyItem(at:to:)` can produce files that share storage. The lower-level
`copyfile(3)` contract is more precise for PDB: recursive cloning traverses the
tree, creates directories normally, and attempts `COPYFILE_CLONE` on each
entry. It is not an atomic directory snapshot, it may fall back to copying a
particular file, it does not preserve hard-link identity, and results are
undefined if the source changes during traversal. PDB must therefore clone
only a quiescent immutable materialization and record per-run clone/fallback
evidence.

Advantages:

- ordinary macOS filesystem and watcher behavior;
- no mount, daemon, kernel extension, or Docker requirement;
- fast and space-efficient on the common local filesystem; and
- transparent fallback to real copying can preserve correctness.

Constraints:

- same-volume/APFS dependency for cloning;
- copying still traverses directory metadata;
- no inherent change log or upper-directory diff;
- no security boundary; and
- setuid/setgid and ACL behavior depends on explicit flags and privilege;
- recursive `copyfile(3)` does not preserve hard links; and
- exact sparse-file, extended-attribute, ACL, file-flag, dataless-file, and
  case behavior must be tested and reported rather than inferred.

For the primary expected local usage, these tradeoffs make APFS clone the
leading macOS default candidate. The bake-off must still show that its measured
benefit earns the additional backend and fallback logic.

Prototype implementation should call `copyfile(3)` with
`COPYFILE_RECURSIVE | COPYFILE_CLONE | COPYFILE_ALL` and a status callback,
or an equivalent small native helper. `/bin/cp -cR` and
`/usr/bin/ditto --clone` are useful differential comparators, not the backend
API: both are best-effort commands, and `cp -c` silently falls back when it
cannot clone. Calling `clonefile(2)` on a whole directory is explicitly
discouraged by the installed macOS 26.5.2 manual in favor of `copyfile(3)`.

References:

- [Apple disk-space explanation of `copyItem` clone sharing](https://developer.apple.com/documentation/metrickit/mxdiskspaceusagemetric)
- [`copyfile(3)` clone and recursive-copy contract](https://keith.github.io/xcode-man-pages/copyfile.3.html)
- [`clonefile(2)` contract](https://keith.github.io/xcode-man-pages/clonefile.2.html)
- [`cp(1)` `-c` fallback contract](https://keith.github.io/xcode-man-pages/cp.1.html)

#### Hyperspace post-hoc APFS deduplication

[Hyperspace 1.7.1](https://hypercritical.co/hyperspace/) is relevant prior art
for storage reclamation, not an AgentSession backend. It scans one or more
existing directory trees for files whose complete data and resource forks are
byte-identical. During reclamation it selects one source file, creates APFS
space-saving clones from it, copies each target's metadata to a clone, verifies
the result, and replaces the redundant target. The paths remain independent:
later modification of one clone does not modify its siblings. Its
[technical FAQ](https://hypercritical.co/hyperspace/#faq-how-does-hyperspace-work)
documents the scan, clone, metadata, replacement, and independence contract.

This reaches a similar steady-state storage outcome to cloning before work
begins, but with important differences:

- it is post-hoc and must hash and compare files after duplicate bytes already
  exist;
- it can share only completely identical files, whereas a file cloned before a
  partial edit may continue sharing its unchanged blocks;
- it does not create pinned bases, private environments, leases, checkpoints,
  or cleanup policy;
- replacing target inodes may be observable to watchers and open-file users, so
  PDB should not run a similar reclamation pass inside an active Session without
  a dedicated compatibility experiment; and
- its default minimum size and file-type filters may omit the many small files
  that dominate JavaScript dependency graphs.

Hyperspace is therefore a useful macOS migration tool and bake-off comparator:
it can estimate or reclaim duplication in pre-existing full copies and Git
worktrees. PDB should prefer proactive APFS cloning at Session creation because
that avoids the scan, pins the base before mutation, and preserves block sharing
when a previously identical file later diverges.

#### ReFS block cloning and Btrfs snapshots

[ReFS block cloning](https://learn.microsoft.com/en-us/windows-server/storage/refs/block-cloning)
copies file regions through metadata and uses allocate-on-write when shared
regions change. Support depends on Windows version, volume format, filesystem,
alignment, and API use; it cannot be assumed on ordinary NTFS developer disks.

[Btrfs snapshots](https://docs.oracle.com/en/operating-systems/oracle-linux/8/btrfs/btrfs-ManagingSubvolumesandSnapshots.html)
create fast writable copy-on-write subvolumes, but only when the project is
already arranged as a Btrfs subvolume. Nested subvolumes are not recursively
snapshotted in the same way as ordinary directories.

Both are useful backend accelerators when detected. Neither belongs in the
portable PDB format or baseline installation requirements.

#### Full recursive copy

A normal recursive copy is the universal correctness fallback. It provides
native filesystem behavior and easy cleanup without mounts or daemons.

Its costs are startup time, complete source duplication, and write I/O. These
costs can be controlled by excluding dependency and build directories from
program snapshots and by using it only when no safe clone primitive is
available. Full copy is valuable as the reference test backend even when it is
not the preferred production backend.

#### CephFS

The platform primitive in the current PDB design is **APFS**, not CephFS.
CephFS is a distributed shared filesystem and does not by itself provide the
private local copy-on-write Session contract. It may host PDB data in a managed
deployment, but it is not a local AgentSession backend recommendation.

## Node and npm compatibility risks

The npm ecosystem makes filesystem transparency unusually important:

- dependency trees contain large numbers of small files and symlinks;
- `npm install` and lifecycle scripts can mutate the project tree;
- pnpm relies intentionally on hard links and symlinks;
- TypeScript and bundlers perform wide metadata scans;
- Vite, Next.js, Jest, Vitest, ESLint, and editors rely on filesystem watchers;
- native addons depend on platform, architecture, libc, Node ABI, and compiler
  state; and
- development servers, test databases, and generated caches are frequently
  rooted inside the repository.

Node documents that `fs.watch()` may be unreliable or impossible on NFS, SMB,
and filesystems presented through some virtualization software. Its polling
fallback is slower and itself described as less reliable. Watchman likewise
warns that remote and distributed filesystems may perform poorly because they
do not expose native notification facilities consistently.

References:

- [Node `fs.watch` caveats](https://nodejs.org/api/fs.html)
- [Watchman filesystem guidance](https://facebook.github.io/watchman/docs/install)

Consequences:

1. A backend passing `read`, `write`, and `rename` tests is insufficient.
2. PDB must test watchers, language servers, package installation, builds, and
   development servers on every selected backend.
3. macOS NFS should not be the default merely because it avoids macFUSE.
4. Dependency directories should not be stored in PDB's canonical program
   Snapshot or copied into a SQLite delta by default.

## Recommended architecture

### Canonical versus host-local state

Canonical PDB state owns:

- Environment identity and immutable head;
- exact Snapshot and object identities;
- dependency/toolchain resolution digest;
- Session start/checkpoint/attempt records where provenance requires them;
- exact checkpointed changesets and Implementations; and
- expected-head publication results and conflicts.

Host-local state owns:

- materialized base trees;
- active writable views and mount metadata;
- dependency link graphs and shared package caches;
- mutable build output, temporary files, logs, sockets, and local databases;
- process identifiers and allocated ports; and
- cleanup leases.

An active Session filesystem is not itself canonical merely because it can be
stored in SQLite. Canonicalization occurs at an explicit checkpoint that hashes
and validates exact objects. A crashed or failed Session may be retained as
recoverable host-local evidence without granting it canonical authority.

### Snapshotter-style internal API

The internal interface should resemble containerd's deliberately small
[Snapshotter lifecycle](https://github.com/containerd/containerd/blob/main/docs/historical/design/snapshots.md):

```text
prepare(session_id, parent_snapshot_id, options) -> PreparedSession
attach(session_id) -> PreparedSession
quiesce(session_id) -> QuiesceGuard
change_hints(session_id) -> optional BackendChangeHints
discard(session_id)
inspect(session_id) -> SessionInfo
capabilities() -> BackendCapabilities
```

The PDB Environment service, outside the backend, owns
`checkpoint(session_id, expected_parent) -> CheckpointResult`. It scans the
quiesced visible root and may use `BackendChangeHints` only as a
differentially verified optimization. This prevents a backend's whiteout,
database, or copy strategy from defining canonical PDB semantics.

`PreparedSession` contains:

```text
root_path
base_snapshot_id
backend_name
backend_generation
workspace_isolation
security_isolation
watcher_profile
metadata_profile
mount_profile
case_profile
hard_link_profile
xattr_acl_profile
fallbacks_observed
cleanup_lease
```

`CheckpointResult` contains:

```text
base_snapshot_id
new_snapshot_id
created_paths
modified_paths
deleted_paths
renamed_paths_when_proven
metadata_changes
object_ids
backend_receipt
```

Checkpointing must not advance an Environment ref. It creates immutable PDB
objects and an exact proposed changeset. Ref advancement remains a separate
transaction validated by policy, checks, semantic conflicts, and expected-head
fences.

### Backend selection

Recommended initial selection order:

| Platform | Default | Optional | Fallback |
| --- | --- | --- | --- |
| macOS on APFS | native APFS clone candidate, selected only after gates pass | AgentFS evaluation; later FSKit/macFUSE only if justified | recursive copy |
| Linux | reflink/native clone when safe | kernel OverlayFS, rootless OverlayFS, AgentFS/BranchFS evaluation | recursive copy |
| Windows | native directory copy; ReFS clone when detected and proven | container/VM extended runtime | recursive copy |
| managed Linux | backend selected by measured workload | OverlayFS, Btrfs snapshot, AgentFS, container snapshotter | recursive copy |

The `auto` selector must report what it chose. It must never silently downgrade
a requested security or watcher capability. Users and CI need a way to require
a profile and fail closed:

```text
pdb session start --require workspace-isolated
pdb session start --require native-watchers
pdb session start --require security-isolated
pdb session start --backend apfs-clone
```

### Dependency and toolchain model

An Environment records a deterministic resolution key such as:

```text
lockfile_digest
package_manager_name
package_manager_version
dependency_graph_digest
runtime_name
runtime_version
platform
architecture
native_abi
relevant_config_digest
patch_set_digest
lifecycle_script_policy_digest
local_dependency_scope_digest
policy_digest
```

For npm-family projects:

- every Session gets a logically private dependency view, but immutable package
  bytes and graph-addressed entries need not be physically duplicated;
- the default compatibility lane keeps a Session-local `node_modules` graph and
  may reuse pnpm's content-addressed store or Bun's APFS clone imports through
  the package manager's supported concurrency contract;
- an opt-in global-graph lane may use pnpm's or Bun's global virtual store when
  the pinned package-manager version reports support and the project passes the
  symlink, realpath, ESM, hoisting, watcher, and lifecycle-script fixtures;
- PDB never mutates, snapshots, or garbage-collects a package-manager-owned
  global store behind that package manager;
- npm registry tarball caches may be shared as a performance cache, never as an
  authorization or correctness source;
- patched packages, local/workspace dependencies, lifecycle-script results, and
  native compilation output remain Session-local unless the package manager
  proves an immutable graph entry under the recorded resolution key;
- a changed lockfile or runtime selects a new resolution key;
- build directories such as `dist`, `.next`, coverage, `target`, and similar
  outputs are excluded from canonical program source unless explicitly added;
  and
- secrets in `.npmrc` or provider configuration are injected through scoped
  runtime mechanisms, not copied into canonical Snapshots.

### Runtime isolation

Each Session should receive private runtime paths and identifiers where tools
permit:

```text
TMPDIR
XDG_CACHE_HOME
XDG_STATE_HOME
package-manager project state
compiler/build output
test databases
Unix sockets
preview and development-server ports
```

Global immutable caches may be shared. Mutable caches require either a
tool-defined concurrency contract or Session-local placement.

For Rust, `target` remains Session-local because it combines final outputs,
incremental compiler state, build-script output, and locks in one mutable tree.
PDB may configure a shared `sccache`-style compiler cache separately. It must not
market a shared `CARGO_TARGET_DIR` as dependency isolation or safe concurrent
artifact publication.

PDB should avoid replacing the user's entire `HOME` by default because coding
agents depend on credentials and configuration there. Extended secure runtimes
should instead mount allowlisted configuration read-only and inject narrowly
scoped credentials.

### Absolute paths

Different Sessions naturally have different host paths, for example:

```text
.pdb-host/sessions/session_a/root
.pdb-host/sessions/session_b/root
```

An agent started with its working directory set to its root experiences a
normal repository and does not need to know that siblings exist. This is the
same successful usability property as Git worktrees and Jujutsu workspaces.

Making every Session appear at the same absolute path requires per-process
mount namespaces, containers, or VMs. Linux can provide that with namespaces
and bind mounts. macOS does not provide an equivalent general per-process mount
namespace. Identical absolute paths are therefore not a baseline requirement.

## Compatibility profiles

The current design language that all backends are behaviorally equivalent is
too strong. Filesystems expose different semantics. PDB should define profiles
and report capabilities instead.

### Native workspace profile

Required for the default local coding experience:

- ordinary file reads and writes;
- atomic file replacement where supported by the host filesystem;
- directory create/delete/rename;
- executable-bit and symlink preservation;
- expected case-sensitivity behavior of the host;
- native watcher delivery sufficient for selected Node fixtures;
- package-manager and language-server compatibility; and
- crash-safe checkpoint scanning.

### Portable workspace profile

Allows a virtual or remote filesystem with documented restrictions:

- basic POSIX-like file operations;
- watcher polling may be required;
- mmap, hard links, locks, xattrs, or sparse files may have restricted support;
- performance limits are reported; and
- the workload opts in or has passed its compatibility suite.

### Security-isolated profile

Adds enforced restrictions:

- writes outside allowlisted paths are denied;
- sibling Session roots are not readable;
- process visibility and cleanup are controlled;
- network access follows explicit policy;
- credentials are scoped and not exposed as ordinary files where avoidable;
  and
- escape tests pass for the selected operating-system boundary.

## Verification plan

The first implementation is a comparative experiment harness, not a production
Environment subsystem. It must be possible to add or remove a backend without
changing scenarios, fixtures, canonical scanning, or result analysis.

### Harness boundary

Place the experiment under `experiments/agent-session-bakeoff/`, outside the
Stage 6C production API. It may depend on accepted `pdb-codec` and
`pdb-objects` APIs, but it must not introduce Environment records, advance
`main`, or edit the user's checkout. Its test-only adapter is:

```text
probe(host) -> BackendCapabilities
prepare(session_id, immutable_base_path, destination, options) -> Handle
attach(handle) -> root_path
quiesce(handle) -> QuiesceGuard
change_hints(handle) -> optional non-authoritative hints
resume(handle)
discard(handle)
inspect(handle) -> processes, mounts, storage_paths, usage
inject_failure(handle, failpoint)
```

Canonical checkpointing is a harness/PDB operation over `root_path`, not a
trusted backend operation. Backend diffs may accelerate a scan only after
differential tests prove they cannot omit changes. Every run has a unique
temporary root, immutable copied fixture input, Session-local runtime
directory, and results directory. It never points a backend at the dirty PDB
working tree.

The initial command surface should be exactly scriptable:

```sh
# Build deterministic fixture trees and verify their recorded BLAKE3 manifests.
cargo run --manifest-path experiments/agent-session-bakeoff/Cargo.toml -- \
  fixture build --manifest fixtures/manifest.json --output .bakeoff/fixtures

# Probe without creating a Session. Requested capabilities fail closed.
cargo run --manifest-path experiments/agent-session-bakeoff/Cargo.toml -- \
  probe --backend apfs-clone --output json

# Run one debuggable scenario.
cargo run --manifest-path experiments/agent-session-bakeoff/Cargo.toml -- \
  run --backend full-copy --fixture fs-contract --scenario two-agent \
  --seed 1 --results .bakeoff/results

# Run the randomized, repeated matrix. Backend order is shuffled per repeat.
cargo run --release \
  --manifest-path experiments/agent-session-bakeoff/Cargo.toml -- \
  matrix --backends full-copy,apfs-clone,agentfs-external \
  --agentfs-bin .bakeoff/tools/agentfs-v0.6.4/agentfs \
  --agentfs-bin-sha256 8a364d8b38d5b45453555e36a1fbdad766725667ba35dc436920477177d7534c \
  --repeat 30 --seed 6840227782638526189 \
  --results .bakeoff/results

# Verify every result against the full-copy oracle and emit JSON plus Markdown.
cargo run --release \
  --manifest-path experiments/agent-session-bakeoff/Cargo.toml -- \
  compare --results .bakeoff/results --oracle full-copy \
  --json .bakeoff/report.json --markdown .bakeoff/report.md
```

The v0.6.4 Apple-silicon release archive has SHA-256
`d07772d7c96f16c0d8996c15502f189d449ab0d9460fc2fe68f6edbae91bae73`,
while the extracted executable passed to `--agentfs-bin` has SHA-256
`8a364d8b38d5b45453555e36a1fbdad766725667ba35dc436920477177d7534c`.
The adapter verifies the executable because that is the artifact it launches;
release acquisition must separately verify the archive. Conflating these two
hashes would either reject the official executable or pretend to validate
bytes that the harness never received.

The manifest records exact OS build, filesystem type/capabilities, case mode,
CPU, memory, backend version and digest, fixture digest, Node/package-manager
versions, dependency lockfile digests, command argv, environment allowlist,
umask, seed, and warm/cold classification. A result without those fields is
invalid, not merely less informative.

### Fixtures

All fixtures are generated or unpacked from digest-pinned local inputs before
timing. Network is disabled during measured runs. Package tarballs and any
native toolchain inputs are prefetched in an untimed setup step and listed by
digest.

| Fixture | Required content and purpose |
| --- | --- |
| `fs-contract` | Empty and small files; 4 KiB, 64 MiB, and 1 GiB regular files; a 1 GiB sparse file; 32-level paths; empty directories; relative, absolute, and broken symlinks; executable and non-executable files; two hard links; xattr/ACL/file-flag samples; Unicode normalization names; and case-only rename candidates. Unsupported names or metadata become explicit capabilities. |
| `many-small` | Deterministically generated 100,000-file, 10,000-directory, approximately 2 GiB tree with fixed fanout and BLAKE3 manifest. It measures metadata traversal, clone preparation, scanning, and mass deletion. |
| `pdb-medium` | A clean immutable PDB source archive at a recorded Commit, excluding `.pdb-host`, build output, and dependency caches. The initial reference candidate is `2c21dab54c8223b0f1bdc29ea4ea4a811bc8a44f`; the archive digest, not Git, is the fixture identity. |
| `ts-workspace` | A pinned npm workspace with at least 20 packages and project references, a small native `node-gyp` addon, lifecycle script, SQLite fixture, and lockfile. It drives npm, TypeScript, Jest, and ESLint. |
| `pnpm-monorepo` | The same logical packages with a pinned pnpm lockfile and workspace file. It drives shared-store concurrency, hard links, symlinks, different peer graphs, and private `node_modules`. |
| `vite-react` | Pinned Vite/React/TypeScript app whose rendered marker changes after source replacement, rename, delete/recreate, and CSS edits. |
| `next-app` | Pinned Next.js app with server and client modules, route creation/deletion, `.next`, and a Session-local SQLite database. |
| `watch-tests` | Pinned Jest and Vitest suites with watch drivers that mutate source, wait for a new completed run identifier, and assert the new result. |

Package versions belong in lockfiles and `fixtures/manifest.json`; prose must
not claim that an unpinned `latest` was tested. Each Session receives a private
`node_modules`. The matrix has three cache modes:

1. `empty`: Session-local empty cache/store, with registry traffic served from
   the local prefetched mirror;
2. `shared-supported`: concurrent access through npm's or pnpm's documented
   cache/store contract; and
3. `shared-sealed`: a prehydrated read-only content store with offline install.

Failure of `shared-sealed` is a reported package-manager capability, not a
reason to share a mutable `node_modules`. The primary backend comparison keeps
`node_modules` inside the Session root. A symlinked native sidecar graph is a
separate follow-up experiment because it changes realpaths and mount semantics.

### Operation scenarios

The `fs-contract` worker executes an ordered JSON action plan, fsyncs where the
scenario requires durability, and records observations after every step:

```text
create -> write -> pwrite -> append -> truncate -> fsync
temp-write -> fsync(temp) -> rename-over -> fsync(parent)
mkdir -> file-rename -> directory-rename -> case-only-rename
symlink -> readlink -> broken-symlink -> unlink
chmod -> executable-toggle -> optional xattr/ACL/file-flag mutation
hard-link -> modify-one-link -> unlink-one-link
delete-base-path -> recreate-same-path
mmap-read/write -> advisory-lock contention
FIFO and Unix-socket create/use/remove
```

Runtime objects such as sockets, FIFOs, databases, logs, and temporary files
must behave correctly while live but are excluded from the canonical source
manifest unless policy explicitly includes them. The runner sets a fixed
`umask` and gives every Session private `TMPDIR`, `XDG_CACHE_HOME`,
`XDG_STATE_HOME`, build output, database, log, and socket paths. A port broker
binds port zero, records a lease, hands a distinct port to each child, and
verifies that A cannot answer on B's port. `HOME` remains unchanged in the
workspace profile and is never counted as isolated.

### Two-agent and pinned-base proof

For every backend and seed:

1. Materialize immutable Snapshot `S0` and record its semantic and physical
   manifest.
2. Prepare A and B independently from the exact same `S0` path and digest.
3. Have both replace the same file with different bytes, create the same new
   pathname with different bytes, delete a common base path, rename a common
   directory to different names, and write distinct private paths.
4. Continuously sample both views from independent observer processes. Every
   observation must equal `S0 + own actions`; no sibling dirty byte or pathname
   may appear.
5. Create `S1` and advance only the test Environment selector. A and B must
   continue to expose `S0`; newly prepared C must expose `S1`.
6. Run tests and servers concurrently with private outputs, databases, sockets,
   temp paths, and ports. Kill one worker; the other's view and process remain
   intact.
7. Checkpoint A and B independently. One deliberately failing test attempt is
   still checkpointed and labeled failed.
8. Rehydrate both checkpoint Snapshots through `full-copy` and compare them to
   the quiesced visible roots.
9. Publish A through a mock expected-head fence. B's filesystem remains
   resumable, while a stale expected head returns a typed conflict. No backend
   writes a parent or Environment ref.

The base manifest and the original repository manifest are recomputed after
every scenario, not only at the end. Any change is a hard isolation failure.

### Exact checkpoint and oracle

The full-copy backend is the behavioral oracle, not the expected performance
winner. The same action plan is run against it first. A backend passes only if
its canonical visible manifest equals the oracle manifest and its rehydrated
checkpoint equals its own quiesced root.

The canonical scanner must:

1. acquire the Session checkpoint lease and quiesce all PDB-managed writers;
2. walk with descriptor-relative, no-follow operations so a swapped symlink
   cannot escape the root;
3. classify each path by policy as canonical source, dependency, generated
   output, or runtime state;
4. store regular-file bytes, symlink target bytes, path, file kind, and
   executable bit; record richer modes/xattrs/ACLs/hard-link identity only as
   declared capabilities;
5. sort entries by the canonical bytewise path order and create immutable leaf
   and tree objects through `pdb-objects`;
6. stat before and after hashing and fail with typed `session_busy` if an entry
   changes; a second complete manifest must match before checkpoint commit;
7. compare base and result maps to emit exact creates, content changes,
   metadata changes, and deletes; and
8. install verified objects before atomically recording the Snapshot and
   changeset with the expected base ID.

Rename identity is not required for exactness. When a backend receipt and
stable identity prove a rename unambiguously, PDB may annotate it. Otherwise a
rename is represented exactly as delete plus create. Equal content alone is
never enough to infer which of several paths was renamed.

The checkpoint test injects death after every object write, after tree
construction, before/after object fsync, before the ledger transaction, and
before/after the checkpoint record. Restart must either return the prior
checkpoint or the complete new checkpoint; a ref must never name missing
objects. Orphan objects are acceptable and later reclaimed by verified
reachability. Backend-provided delta/whiteout lists are cross-checked against
the independent scan for every randomized run.

### Node, language-server, and watcher commands

The runner invokes binaries from the pinned private dependency graph, never
`npx` with an unpinned network resolution. Representative argv is:

```sh
npm ci --cache "$SHARED_CACHE/npm" --foreground-scripts --no-audit --no-fund
pnpm install --frozen-lockfile --store-dir "$SHARED_CACHE/pnpm" --offline
./node_modules/.bin/tsc -b --pretty false
node ./node_modules/typescript/lib/tsserver.js
./node_modules/.bin/vite --host 127.0.0.1 --port "$PORT" --strictPort
./node_modules/.bin/next dev -H 127.0.0.1 -p "$PORT"
./node_modules/.bin/jest --watchAll --runInBand
./node_modules/.bin/vitest --watch --pool forks
```

The harness speaks the tsserver protocol and waits for project loading before
editing a dependency leaf, renaming it, changing `tsconfig.json`, and deleting
and recreating a module. It asserts updated diagnostics and definitions, not a
particular internal watch strategy. Vite and Next probes poll an HTTP marker
and capture server logs; Jest and Vitest drivers wait for a monotonically
increasing completed-run marker. Every watcher scenario exercises create,
modify, atomic replace, rename, delete, and delete/recreate. Raw event counts
are not compared because native APIs legitimately coalesce events; eventual
tool-visible state and absence of sibling events are compared.

Node 26.7.0 documentation still says `fs.watch()` can be unreliable or
impossible on NFS/SMB and that stat polling is slower and less reliable.
Watchman documents very poor results on remote/distributed filesystems and can
be configured to reject `nfs`, `cifs`, and `smb` roots. AgentFS on macOS must
therefore report a network-filesystem watcher profile unless the actual tool
suite proves a narrower capability; mounting localhost NFS does not make it a
native FSEvents filesystem.

References:

- [Node `fs.watch` availability caveat](https://nodejs.org/api/fs.html#availability)
- [Watchman installation/filesystem guidance](https://facebook.github.io/watchman/docs/install)
- [Watchman `illegal_fstypes`](https://facebook.github.io/watchman/docs/config#illegal_fstypes)
- [pnpm's hard-link and symlink graph](https://pnpm.io/symlinked-node-modules-structure)

### Failure and lifecycle injection

Every backend runs these failpoints ten times per fixture:

- worker `SIGKILL` during partial write, atomic replace, install, build, and
  checkpoint;
- supervisor death after prepare but before receipt persistence;
- AgentFS daemon death during idle, read, write, install, and checkpoint;
- mount disappearance and stale mount directory;
- attach after supervisor restart using only durable handle metadata;
- timeout, cancellation, and double-discard;
- cleanup request while a leased process is live, followed by cleanup after
  process exit;
- injected `EIO`, `ENOSPC`, short write, object-store failure, and SQLite
  `BUSY` at each checkpoint boundary; and
- host reboot/resume in a separate opt-in lane with an on-disk continuation
  token.

The normal fault layer injects errors at adapter and scanner syscalls without
privileged mounts. A separate manual APFS disk-image lane may test real volume
exhaustion. Results must distinguish `resumable`, `cleanly discarded`, and
`typed unrecoverable`; silent data loss, parent mutation, leaked live mounts,
or a falsely successful checkpoint always fails.

### Security truth test

Workspace-only backends are expected to be escapable by hostile same-user
code. The test demonstrates and records that truth with absolute sibling reads,
original-repository writes, home reads, process enumeration/signaling, inherited
descriptors, and unrestricted network attempts. Those expected escapes do not
fail the workspace profile; advertising `security_isolation=true` does.

A backend claiming the security-isolated profile must additionally deny `..`,
absolute-path, symlink, and hard-link escapes; sibling reads; writes outside
allowlisted paths; process interference; undeclared inherited descriptors;
disallowed network access; and credential access through environment, files,
arguments, logs, caches, and IPC. AgentFS v0.6.4's macOS profile allows all file
reads, so it cannot claim sibling confidentiality even if its write restrictions
pass.

### Measurements

Each measured subprocess reports monotonic wall time, user/system CPU, peak
RSS, voluntary/involuntary context switches, bytes read/written, exit status,
and open descriptor count. The supervisor separately samples backend daemons.
For storage, record logical bytes, `st_blocks`, `du` allocated bytes, database
and WAL sizes, upper/delta sizes, and quiescent APFS volume free-space deltas.
`du` is not treated as proof of APFS physical sharing. The native helper reports
which regular-file clone attempts succeeded or fell back.

`cold` means a new backend state and new Session view with prefetched immutable
inputs but no prior run of that fixture. `warm` means the second run with the
same allowed package/tool cache and a new Session. The harness cannot flush the
macOS kernel cache without privilege, so it randomizes backend order and labels
OS-cache state rather than claiming a physical cold boot. Use 30 repetitions
for normal timing and report p50, p95, median absolute deviation, and every raw
sample. LLM time is never included.

Measure prepare, attach, first and repeated read/write, whole-file copy-up,
random 4 KiB mutation of the 1 GiB file, directory rename, 10,000-path delete,
canonical scan, checkpoint, rehydrate, discard, cleanup, npm/pnpm install,
TypeScript load/update, build, test, watcher startup/update, and server startup.
Run 1, 2, 10, 50, and 100 idle Sessions and 1, 2, and 10 active build Sessions.
The 50/100 cases are scalability reports, not part of the smallest product
proof, but any cross-session correctness failure is still fatal.

### Exact bake-off matrix and gates

| Area | Repeats | Oracle/comparison | Native-default gate | Portable/experimental gate |
| --- | ---: | --- | --- | --- |
| Probe and prepare | 30 | declared versus observed capabilities | Native APFS, clone success explicitly observed, normal directory root | Every mount/daemon prerequisite declared; no silent fallback |
| Core file and metadata | 30 seeds | canonical manifest equals full-copy; richer metadata reported separately | Zero canonical mismatch; symlink and executable-bit preservation mandatory | Same canonical gate; restrictions accurately reported |
| Two-agent/pinned base | 30 seeds | `S0 + own actions`; A/B/C and base manifests | Zero leak, overwrite, base mutation, or hot swap | Same |
| Crash/resume/discard/cleanup | 10 per failpoint | terminal state and resource inventory | Zero false success or leaked mount/process after lease expiry | Same; typed unsupported reboot is allowed before promotion |
| Checkpoint/object closure | 30 seeds plus failpoints | visible root, rehydrated Snapshot, object reachability | Byte-identical canonical manifests; no missing-object ref | Same |
| npm and pnpm | 10 per cache mode, two concurrent Sessions | full-copy command results and private graph manifests | All installs/builds pass; no shared mutable graph path | Same, with up to 2x elapsed-time allowance |
| TypeScript | 10 | diagnostics/definitions and update marker | All initial and incremental cases pass | All pass; declared polling allowed |
| `fs.watch`, Vite, Next, Jest, Vitest | 30 event scenarios; 10 tool scenarios | eventual marker and no sibling marker | Zero missed update; relevant effect within 5 s; no polling-forcing env/config | Zero missed update within 10 s when declared; polling CPU reported |
| Runtime paths/ports | 30 | private file/socket/database/HTTP observations | Zero collision or cross-talk | Same |
| Security truth | 10 | observed denial/escape versus claims | Must say workspace-only | Must say workspace-only unless every containment test denies escape |
| Performance | 30 | ratios to full-copy on same randomized block | See promotion thresholds below | See promotion thresholds below |

Correctness thresholds are absolute: one unexplained canonical mismatch,
isolation leak, base mutation, missed required watcher outcome, corrupt resume,
or false checkpoint success rejects that backend/version for the affected
profile. Flaky retries do not convert a failure to a pass; both attempts remain
in the report.

Provisional performance thresholds for choosing `apfs-clone` as the automatic
macOS default are:

- p95 prepare at most 1 second for `pdb-medium` and 5 seconds for
  `many-small`;
- median prepare at least 2x faster than `full-copy` on either `pdb-medium` or
  `many-small`, or median quiescent initial APFS space growth at least 50%
  lower; otherwise cloning has not earned default complexity;
- steady-state build/test/watch p50 no more than 10% slower and p95 no more
  than 20% slower than `full-copy`;
- checkpoint p95 no more than 25% slower than `full-copy`; and
- no backend-specific persistent daemon and no unexplained mount resource.

Provisional thresholds for retaining `agentfs-external` as an experimental
portable backend are:

- all core correctness, isolation, checkpoint, npm/pnpm, TypeScript, and
  declared polling-watcher gates pass;
- p95 prepare at most 5 seconds for `pdb-medium` and 15 seconds for
  `many-small`;
- build/test/install p50 no more than 2x and p95 no more than 3x `full-copy`;
- backend daemon peak RSS at most 512 MiB, and a 4 KiB mutation of the 1 GiB
  file may not increase peak RSS by more than 128 MiB;
- source-only delta growth no more than 2x changed logical bytes plus 128 MiB;
  dependency-graph growth is compared separately with the allocated
  full-copy graph; and
- ten idle Sessions consume no more than 1 GiB aggregate backend RSS and leave
  zero mounts/processes after discard.

These are experiment promotion gates, not durable product SLOs. Record the raw
data so an ADR can revise the numbers without rerunning an unversioned workload.
`full-copy` ships if it meets correctness even when it misses clone performance
thresholds.

### Claims that need correction or evidence

- The approved greenfield plan's phrase “behavioral equivalence across
  backends” is too strong. Canonical source outcomes must be equivalent;
  watcher, metadata, locking, mmap, case, and performance capabilities may
  differ and must be reported.
- APFS copying is not an atomic directory snapshot. Recursive `copyfile(3)`
  traverses a hierarchy and may clone or copy individual files. The source must
  already be immutable.
- A backend named `apfs-clone` cannot silently become a full copy. Best-effort
  fallback is correct only when capability and selection results say so.
- Full recursive copy is a correctness oracle only for PDB's declared canonical
  metadata. Different copy tools disagree on hard links, ACLs, xattrs, sparse
  extents, flags, and special files.
- AgentFS has advanced from the early draft assumptions to v0.6.4/specification
  v0.4, but its beta label, whole-file in-memory copy-up, NFSv3 macOS mount,
  missing canonical xattr/ACL support, and recent history of overlay/NFS fixes
  are current evidence, not hypothetical concerns.
- An AgentFS database is not safely snapshotted by copying only the `.db` while
  active. WAL state must be included or checkpointed through a supported
  quiescent database snapshot.
- AgentFS's current-state tables and explicit tool-call table do not by
  themselves prove an append-only audit of every filesystem operation.
- AgentFS's macOS sandbox does not meet PDB security isolation: it allows all
  file reads and broad writes. Native clones and full copies are also
  workspace-only.
- Backend diffs cannot be canonical authority. AgentFS `diff` does not prove
  renames, and native directories have no delta log. Independent exact scanning
  is mandatory.
- Node's NFS watcher warning remains current, and Watchman explicitly supports
  rejecting NFS roots. Watcher claims require real tool outcomes, not successful
  `read`/`write` calls.
- Disk usage from `du` double-counts or otherwise obscures shared APFS extents.
  Clone success and quiescent volume deltas must accompany logical/allocated
  file totals.

## Implementation sequence

### Phase 0: contract and harness

- create only the standalone experiment crate and test-only adapter;
- implement the deterministic fixture builder, semantic manifest scanner, and
  structured result schema;
- implement `full-copy` and the two-agent/pinned-base/core-checkpoint oracle;
- prove that a checkpoint rehydrates byte-for-byte before adding an optimized
  backend; and
- do not add canonical Environment/AgentSession schemas or production CLI
  commands in this slice.

### Phase 1: native reference backends

- implement correct recursive copy;
- implement APFS recursive `copyfile(3)` clone with explicit per-run fallback
  reporting;
- create private dependency/build/runtime paths; and
- run core filesystem, two-agent, checkpoint, watcher-smoke, and `pdb-medium`
  performance gates on macOS first.

This proves product semantics using ordinary directories and does not require a
new filesystem.

### Phase 2: AgentFS evaluation

- execute checksum-pinned AgentFS v0.6.4 as a bounded external backend behind
  the test adapter; do not add it as a Rust dependency;
- keep its SQLite delta host-local and outside canonical authority;
- run watcher, TypeScript, npm/pnpm, build, crash, and disk-growth suites;
- compare exact checkpoint extraction against native scanning; and
- retain it only for profiles where it meets correctness and performance
  gates. Otherwise record the rejection with artifacts and remove the adapter
  from automatic selection.

Only after a released version passes should PDB consider a crate-level
integration. Prefer contributing generic fixes upstream over forking a second
agent filesystem. PDB's canonical scanner should make private AgentFS schema
integration unnecessary.

### Phase 3: Linux overlay

- add kernel OverlayFS where unprivileged configuration and backing filesystem
  support are known;
- add or integrate a rootless userspace option only where necessary;
- declare rename, metadata, and watcher capabilities explicitly; and
- retain native clone/copy fallback.

### Phase 4: optional secure runtimes

- add Linux namespace/Bubblewrap execution where available;
- evaluate macOS sandbox limitations independently of filesystem choice;
- integrate container, microVM, Daytona, Coder, DevPod, or similar providers
  through an external execution contract; and
- keep Docker optional.

### Phase 5: lazy materialization only if justified

If measurements show materializing native source trees dominates startup or
disk for real PDB workloads, evaluate an ArtifactFS/EdenFS-like object-backed
view. Do not undertake that implementation based only on theoretical monorepo
scale.

## Explicitly rejected initial choices

- **One shared directory with logical change ownership**: does not isolate dirty
  files, dependencies, generated state, or runtimes.
- **Git worktrees as the PDB primitive**: dependable UX but violates optional-Git
  and canonical-model goals.
- **Mandatory Docker or microVM per Session**: strong security but too heavy for
  the one-command local core.
- **Universal FUSE/NFS default**: compatibility and watcher behavior are not yet
  proven for the primary npm workload.
- **Specialized cross-platform filesystem written from scratch**: unnecessary
  before existing native clones and AgentFS are benchmarked.
- **Direct filesystem commit to `main`**: bypasses PDB's Proposal,
  Implementation, check, policy, and MVCC/OCC authority.
- **Shared mutable `node_modules` or build directory**: allows cross-agent
  observation, corruption, and accidental dependencies.
- **Hot-swapping a running Session when main advances**: breaks pinned inputs
  and makes agent behavior irreproducible.

## Unresolved decisions

### Should PDB embed AgentFS or call it as a backend?

Recommendation: use only a checksum-pinned bounded external adapter in the
bake-off. Do not embed v0.6.4 and do not parse its private schema for canonical
checkpointing. Embed more deeply only after a released version passes the full
matrix and a written follow-up shows that a stable SDK or database contract
materially improves measured checkpointing or portability. Reject the backend
if it misses any canonical correctness gate or if watcher/tool/performance
results do not earn an experimental profile.

### Should Session file-operation history be canonical?

Recommendation: no. Canonicalize explicit checkpoints, Attempts, and exact
objects. Per-operation filesystem audit can be retained as optional host-local
evidence. Canonicalizing every temporary write would add large volume without
improving commit correctness.

### Are native clones sufficiently isolated?

Recommendation: yes for the baseline workspace-isolation claim, no for hostile
code. Name the distinction and provide optional security-isolated execution.

### Should multiple agents ever share one Session?

Recommendation: allow explicit attachment for supervision, handoff, or pair
work. Default every independently scheduled agent to a fresh Session, even when
the Environment is shared.

### Should the agent see the same absolute path as other Sessions?

Recommendation: no baseline guarantee. Start every process at its own ordinary
root. Same-path virtualization belongs to namespace/container backends.

### How much metadata belongs in exact PDB source?

Recommendation: guarantee source bytes, path, file kind, symlink target, and
executable bit first. Add xattrs, ACLs, sparse extents, and hard-link identity
only where real program workloads require them and every export/recovery path
can preserve them.

## Risks and unresolved questions

The decision is ready enough to build the experiment, but these questions are
intentionally not resolved by assertion:

1. **Canonical path model:** PDB preserves raw Git path bytes on import, while
   ordinary macOS APIs and AgentFS's schema have different string/case/Unicode
   behavior. The Stage 6C source-path contract must say whether native Sessions
   reject non-UTF-8 or unrepresentable paths or expose an escape encoding.
2. **Checkpoint quiescence:** PDB-managed workers can be stopped, but an editor
   or user process may retain a writable descriptor. The product must choose
   between fail-`session_busy`, an explicit force checkpoint with weaker
   guarantees, or a security runtime that can freeze every writer. The research
   recommends fail closed first.
3. **Metadata scope:** bytes, kind, symlink target, and executable bit are a
   defensible v1 source contract, but existing Git-import and future native
   source paths need one shared formal schema before ADR acceptance.
4. **APFS physical accounting:** there is no simple per-directory `du` number
   that proves clone sharing. The experiment must validate the native helper's
   clone outcomes and use quiescent volume-level measurements with noise bars.
5. **Dataless and cloud-managed files:** `clonefile` can require materialization
   and return `EDEADLK` under I/O policy. PDB must either materialize immutable
   bases before prepare or report a typed unsupported base.
6. **Dependency boundary:** keeping `node_modules` inside an AgentFS NFS/SQLite
   root may dominate all other results. Moving it to a native sidecar may be a
   good later optimization, but changes realpaths and must not be smuggled into
   the initial backend comparison.
7. **Package cache immutability:** npm and pnpm stores have their own supported
   concurrency contracts. PDB should not call them immutable until a sealed
   offline store works; otherwise report them as tool-managed shared caches.
8. **Security on macOS:** deprecated `sandbox-exec` is present on the probe host
   but is not a durable hostile-code product boundary. A macOS security-isolated
   profile likely needs a separately designed VM/container/App Sandbox approach.
9. **AgentFS durability:** the observed timeout needs an upstream-quality
   reproduction, and live database snapshot/resume behavior needs an explicit
   supported protocol. PDB must not derive guarantees from copying a WAL-mode
   database file.
10. **Performance representativeness:** the fixture sizes and provisional
    thresholds need validation against at least one real user monorepo whose
    source, generated outputs, and dependency graph are inventoried without
    publishing private content.
11. **Global dependency-store lifecycle:** pnpm and Bun own their global
    content and graph stores. PDB needs usage reporting that distinguishes
    Session-local links from globally retained bytes, but must not invent a
    second refcount or garbage collector that races the package manager.
12. **Global graph compatibility:** symlink-heavy stores change realpaths and
    expose undeclared-dependency bugs. pnpm's global virtual store has an ESM
    `NODE_PATH` boundary, while Bun deliberately keeps patched, scripted, and
    workspace closures local. Promotion requires the same TypeScript, watcher,
    Vite, Next, Jest, Vitest, native-addon, patch, and lifecycle-script fixtures
    used for private materialized graphs.
13. **Post-hoc clone replacement:** Hyperspace proves that identical existing
    files can be consolidated into independent APFS clones while preserving
    metadata, but inode replacement may disturb live tools. Treat it as an
    offline migration/comparator unless an explicit live-watcher experiment
    proves otherwise.

## Smallest implementation slice

The first slice is one standalone, non-product experiment with two backends:

1. add the fixture manifest/result schema and a small `fs-contract` fixture;
2. implement `full-copy`, semantic scanning, exact create/modify/delete/mode
   changes, object installation, and rehydration;
3. implement `apfs-clone` through a tiny `copyfile(3)` helper with explicit
   clone/fallback counts;
4. run the two-agent/pinned-base scenario plus create/edit/delete/rename/
   symlink/chmod and checkpoint/discard; and
5. emit one JSON comparison report with base/session/rehydrated manifests,
   timings, allocated/logical bytes, capabilities, and resource inventory.

That slice is complete only when both backends produce the same canonical
manifests, neither changes the immutable base, and both checkpoints rehydrate
exactly. It deliberately excludes AgentFS, npm/pnpm, production Environment
records, session CLI, process sandboxing, and publication. The second slice
added the pinned AgentFS external smoke adapter. The third added Node fixtures.
This order makes every later failure attributable to one new variable.

### Slice 1 implementation result

The standalone
the original `experiments/agent-session-bakeoff` harness
harness implemented this smallest slice on 2026-08-10 without adding production
Session records or commands. Its checked-in `fs-contract` fixture has canonical
digest
`blake3-256:0dc23dbc0b90445746f537c8fda40a2d7106acf4a488bb7f326975b6a7bf7e91`.

A one-seed smoke run on the research host compared `full-copy` and
`apfs-clone`. Both passed every A/B isolation, S0 pinning, S1/C, base-integrity,
checkpoint, rehydration, and discard assertion, and the comparator found equal
canonical manifests and changesets. APFS reported 19 successful regular-file
clone attempts across A, B, and C with zero copy fallbacks. The source actions
covered create, edit, delete, directory rename, symlink creation, and executable
mode change.

This is the first fixture-level product proof, not the backend-selection
result. It has one seed, a tiny tree, no concurrent process workload, no crash
injection, and no npm, pnpm, TypeScript, watcher, database, socket, port, large
file, many-small-file, or performance lane. It supports proceeding to the
remaining matrix; it does not yet promote APFS to the automatic default.

### Slice 2 AgentFS external smoke result

The next slice added a bounded external AgentFS adapter without linking its SDK
or reading its private schema. It verifies the exact executable SHA-256, clears
the child environment to an allowlist, assigns private home and temporary
directories, captures bounded stdout/stderr and durations, creates a separate
process group, kills that group on harness timeout, inventories database/WAL
artifacts, compares the base before and after, and reports newly leaked AgentFS
processes or mounts relative to a pre-run inventory. External failures become a
structured `agentfs-smoke.json` rejection rather than a hung matrix or a
partially successful Session.

On 2026-08-10, the official Apple-silicon v0.6.4 executable with SHA-256
`8a364d8b38d5b45453555e36a1fbdad766725667ba35dc436920477177d7534c`
reproduced the earlier failure. Version and two overlay initializations passed.
The first A mutation exited after 30.037 seconds with
`connection pool timeout: no connections available`; it did not reach B or the
persistence check. Both `diff` commands reported no changes. The S0 manifest
remained exact, `agentfs ps` reported no active Session, and the differential
inventory found no new process or mount. Retained artifacts were 4 KiB database
files plus 222,512-byte and 210,152-byte WAL files.

The decision for this exact release and host is therefore
`reject-v0.6.4-on-this-host`. This strengthens the external-or-reject
recommendation: the adapter is feasible and useful for versioned evidence, but
AgentFS v0.6.4 is not feasible as PDB's macOS Session backend. A later released
version may rerun the same adapter; PDB should not embed or patch around the
failure in its canonical layer.

The combined comparator reports native and external gates separately and fails
overall when an included experimental backend is rejected. Thus AgentFS cannot
quietly disappear from a green native report: this run is native `PASS`,
external-smoke `FAIL`, overall `FAIL`.

### Slice 3 Node, package-manager, and watcher result

The checked-in `node-workspace` fixture pins npm 11.5.1, pnpm 11.13.0,
TypeScript 5.9.3, Vite 8.2.1, Next.js 16.3.0, Jest 30.4.2, Vitest 4.1.10,
and React 19.2.8. `fixture-manifest.json` records the package, lockfile,
workspace-policy, and workload-script SHA-256 values. TypeScript 7.0.2 was
deliberately rejected for this fixture because the installed package no longer
provided the `tsserver` executable required by the language-server contract;
the fixture does not use an unrecorded `latest` resolution.

On the 2026-08-10 research host, all four native combinations passed one
end-to-end run:

| Backend | Package manager | Install | Workload | Result |
| --- | --- | ---: | ---: | --- |
| `full-copy` | npm | 12.157 s | 18.221 s | PASS |
| `apfs-clone` | npm | 11.945 s | 17.635 s | PASS |
| `full-copy` | pnpm | 4.863 s | 12.377 s | PASS |
| `apfs-clone` | pnpm | 4.950 s | 11.801 s | PASS |

Every run installed into a `node_modules` whose resolved path remained under
its private Session, left the immutable fixture unchanged, and removed the
Session on cleanup. The workload obtained a successful TypeScript build and
`tsserver` semantic-diagnostics response; observed changed Vite and Next HTTP
markers after atomic file replacement; and observed a second completed passing
Jest and Vitest watch run. No polling-forcing setting was used.

pnpm 11 initially returned `ERR_PNPM_IGNORED_BUILDS` even though the files were
installed. Treating that exit as success would have hidden a dependency graph
policy failure. The fixture now records `allowBuilds: { unrs-resolver: true }`
in `pnpm-workspace.yaml`, after which both installs returned zero. This is an
explicit fixture policy, not a global bypass.

These timings came from a single concurrent smoke block with shared warm
tool-managed caches. They prove compatibility and private graphs, not a stable
performance ratio. The remaining promotion run must randomize order and run
the empty, shared-supported, and sealed-offline cache modes independently.

### Slice 4 stress and physical-growth result

The new stress lane made `full-copy` a real byte-copy oracle. The earlier
implementation used Rust `fs::copy`; on APFS, the 1 GiB prepare completed below
millisecond report resolution with no volume growth, showing that macOS had
transparently cloned the supposed full copy. The harness now uses an explicit
userspace read/write loop. Semantic correctness was unaffected, but all earlier
`full-copy` performance observations are invalid and must not be cited.

With the corrected oracle, the exact 1 GiB regular-file lane produced:

| Backend | Prepare | Initial volume delta | 4 KiB mutation | Mutation volume delta | Clone receipt |
| --- | ---: | ---: | ---: | ---: | --- |
| `full-copy` | 357 ms | 1,073,815,552 B | 6 ms | 4,096 B | no clone attempts |
| `apfs-clone` | <1 ms report resolution | -4,096 B | 8 ms | 192,512 B | 1/1 forced clone, zero fallback |

The post-mutation canonical manifests were equal and both Sessions cleaned up.
The negative initial clone delta and the 192 KiB response to a 4 KiB write
demonstrate measurement noise and APFS allocation granularity; they are raw
quiescent volume observations, not exact attribution. Thirty randomized blocks
are still required for noise bars.

The full `many-small` fixture contained 100,000 files in 10,000 directories,
20,480 bytes per file, 2,048,000,000 logical/allocated file bytes, and 110,000
manifest entries. One run produced:

| Backend | Prepare | Scan after mutation | Discard | Initial volume delta | Clone receipt |
| --- | ---: | ---: | ---: | ---: | --- |
| `full-copy` | 28.864 s | 10.355 s | 6.984 s | 2,128,482,304 B | no clone attempts |
| `apfs-clone` | 13.215 s | 21.976 s | 6.652 s | 49,729,536 B | 100,000/100,000 forced clones, zero fallback |

The canonical results matched and cleanup passed. APFS was about 2.18 times
faster to prepare and used far less initial physical space, but its 13.215-second
sample is above the provisional 5-second `many-small` automatic-default gate.
One ordered run is not a p95, and the slower APFS scan may be order/cache noise,
so this result keeps APFS feasible but blocks declaring it the default on
performance grounds. The initial per-file `fsync` version of this scenario was
stopped as an invalid durability benchmark and retained recoverably in Trash;
the corrected lane syncs only at measurement boundaries.

### Slice 5 checkpoint quiescence and crash result

Checkpoint extraction now verifies every stored object ID and size against the
pre-scanned entry, then requires a final complete scan to equal the initial
visible manifest before it creates the manifest object. A changing 256 MiB
file caused a fail-closed rejection with two different scan digests. After the
cooperative writer stopped, checkpoint and full-copy rehydration matched
exactly.

The crash matrix injected failure immediately after each of eight leaf-object
writes. Every case left only digest-valid orphan objects and no checkpoint
record. It then injected at record boundaries:

| Injected boundary | Recovery state |
| --- | --- |
| before record write | prior (no record) |
| after record write, before fsync | prior (no final record) |
| after record fsync, before rename | prior (no final record) |
| after atomic rename, before parent fsync | complete, valid record and object closure |
| after parent fsync | complete, valid record and object closure |

This proves the experiment's fail-closed scanner and atomic record shape. It
does not solve arbitrary-writer quiescence: ordinary workspace isolation cannot
freeze an editor or hostile same-user process. The product recommendation is
therefore a cooperative checkpoint lease for PDB-managed workers and typed
`session_busy` failure by default; a future force mode, if any, must state its
weaker guarantee explicitly.

### Slice 6 AgentFS upstream record

The v0.6.4 macOS failure was reproduced again from the official release asset
on macOS 26.6.1 (build 25G76): the first mutation returned after 30.045 seconds
with `connection pool timeout: no connections available`, both diffs remained
empty, the base was unchanged, and no process or mount leaked. The minimal
reproduction, exact checksums, retained stderr, command timings, and database/
WAL sizes are filed upstream as
[AgentFS issue #342](https://github.com/tursodatabase/agentfs/issues/342).
PDB should continue to reject this exact backend version until an upstream fix
passes the same checksum-pinned adapter.

### Independent APFS host result

Because the PDB Forge remote has no GitHub Actions configuration and no second
SSH Mac was available, the portable harness plus its `pdb-objects` dependency
was copied into an isolated private evidence repository. GitHub's documented
`macos-26` arm64 hosted-runner label supplied a genuinely separate host. The
successful run is retained at
[`swyxio/pdb-agent-session-bakeoff-proof-20260810` run 31417718269](https://github.com/swyxio/pdb-agent-session-bakeoff-proof-20260810/actions/runs/31417718269).

That runner reported macOS 26.5.2 (build 25F84), Darwin arm64, and an APFS root.
At seed 6840227782638526189, `full-copy` and `apfs-clone` both passed every
two-agent, pinned-base, checkpoint, rehydration, and cleanup assertion, and the
oracle comparator found no manifest or changeset mismatch. The independent
1 GiB lane also passed:

| Backend | Prepare | Scan | Initial volume delta | 4 KiB mutation delta | Clone receipt |
| --- | ---: | ---: | ---: | ---: | --- |
| `full-copy` | 1.303 s | 1.980 s | 1,083,654,144 B | 4,096 B | no clone attempts |
| `apfs-clone` | <1 ms report resolution | 1.494 s | 0 B | 20,480 B | 1/1 forced clone, zero fallback |

This satisfies the two-independent-APFS-host correctness requirement for arm64
macOS 26. It does not cover macOS 15, Intel macOS, dataless files, or a real
user monorepo. The external proof repository is evidence, not a new PDB source
remote or product dependency.

## Initial workspace-service implementation result

The bounded product slice is now implemented as the `pdb-environment` crate.
It has exactly four mutating lifecycle operations: prepare from an immutable
Snapshot materialization, acquire/release a cooperative checkpoint lease,
extract an exact checkpoint, and discard. It:

1. formalizes the v1 canonical contract for UTF-8 relative paths, entry kind,
   regular-file bytes, symlink target bytes, and executable bit;
2. enables `full-copy` and reports `apfs-clone` as an unavailable experimental
   capability until production can prove forced-clone success without silent
   fallback;
3. calls the hardened scanner/object installer from a leased root and returns
   typed `session_busy`, `unsupported`, and cleanup failures;
4. runs the existing two-agent and Node fixtures through the service boundary;
   and
5. stops before Environment ref publication, hostile-code sandboxing, shared
   `node_modules`, or an AgentFS SDK dependency.

The crate's deterministic unit proof prepared two Sessions from one base,
applied conflicting writes, proved the base and sibling roots unchanged,
blocked a competing checkpoint and discard while the cooperative lease was
held, installed leaf/snapshot/changeset objects, and discarded both roots
idempotently. The standalone bake-off's `full-copy` backend now delegates
prepare, checkpoint, and discard to this production service.

Before the isolated repeat blocks, two npm runs timed out waiting for Jest's
initial watch event. A diagnostic run passed, then 30 fresh sequential npm
Sessions and 30 fresh sequential pnpm Sessions all passed TypeScript,
`tsserver`, Vite, Next.js, Jest, and Vitest. Every repeated case also confirmed
private `node_modules`, an unchanged fixture base, and successful cleanup. The
earlier timeouts did not reproduce without cross-run resource contention and
are classified as an unreproduced resource/concurrency-sensitive harness
flake, not erased evidence or a demonstrated filesystem mismatch. Compact
per-case receipts are checked in under the bake-off's `evidence/` directory;
raw Session trees and caches remain ignored.

## Host-to-agent adapter result

`pdb-host` now owns `.pdb-host/workspaces`, requires an existing active logical
`AgentSession`, opens `pdb-environment` against the canonical object store, and
passes only the prepared ordinary root to `pdb-agent`. A fake ACP integration
test observed that exact working directory, wrote a file, returned normally,
and then passed exact checkpoint and discard through the host adapter. The
adapter does not create a second Session identity, append a checkpoint record,
advance an Environment, or grant object-store authority to the agent.

## Research `pdb env` switcher and real-agent result

The proof now exposes one intentionally small public concept:

```sh
pdb env                         # list and mark the environment containing cwd
pdb env NAME -c                 # create from the current program view
pdb env NAME                    # enter on a TTY; print its path when piped
pdb env NAME -- COMMAND         # run an agent or tool with the environment as cwd
pdb env NAME -p                 # print its ordinary path
pdb env NAME -d                 # discard it
```

`AgentSession`, checkpoint publication, expected-head advancement, dependency
resolution, and merge authority are intentionally absent from this research
surface. The implementation materializes a stable base while excluding only
top-level `.git`, `.pdb`, and `.pdb-host`, caches it by exact manifest digest,
and delegates each private directory to the production full-copy service.

On 2026-08-10, `codex-proof` and `claude-proof` were created from the same
`blake3-256:a8cd2ba7...b3e807ff` base. Codex CLI 0.142.5 with `gpt-5.4` and
Claude Code 2.1.201 each received only its own ordinary cwd. Both edited
`shared.txt`, created a private output and nested file, deleted and renamed the
same base files, created a relative symlink, and set the same script executable.
Claude ran after Codex's environment was dirty and still observed no Codex
output. Independent verification found distinct results, identical untouched
`package.json` content, and an unchanged source base.

The first parallel launch is retained as tool-adapter evidence rather than a
filesystem failure: Codex's configured `gpt-5.6-sol` required a newer CLI, and
Claude's expired local authentication rejected the process before either agent
mutated a file. Pinning Codex to the locally supported `gpt-5.4` and completing
Claude's standard login produced passing real-agent runs. The successful
processes were sequential, while both environments remained simultaneously
materialized and Codex's dirty state existed before Claude inspected its own
view.

## Smallest next implementation slice

The workspace-isolation research proof is complete at the requested boundary.
Do not add checkpoint publication, Environment heads, dependency resolution, or
merge semantics merely to strengthen this experiment. If PDB chooses to
productize the result, the next separate slice is to append a canonical
checkpoint/attempt bundle only after immutable object IDs exist and to add
expected-head publication behind a new decision. Keep APFS default selection
out of that slice.

In parallel, the experiment—not the product service—should add randomized
backend-performance blocks, the three package-cache modes, macOS 15 coverage,
a real inventory-safe monorepo fixture, and the remaining process/I/O
failpoints. That division lets PDB ship a correctness-first private workspace
without implying that APFS performance or publication authority is settled.

## ADR promotion readiness

The recommendation is promoted into
[proposed ADR 0009](decision-record.md). It selects
`full-copy` as the initial correctness-first default, keeps `apfs-clone`
experimental, rejects AgentFS v0.6.4 on the tested macOS profile, and freezes
the trusted-agent workspace capability vocabulary. Accepting that ADR does not
make APFS the automatic macOS default. APFS promotion still requires:

- version diversity beyond the two passing arm64 APFS hosts (both macOS 26),
  if PDB intends to support macOS 15 or Intel at launch;
- failure injection beyond the passing checkpoint boundaries, including worker
  process death, cancellation, `EIO`, `ENOSPC`, and cleanup with live leases;
- approved default and experimental performance thresholds based on raw data;
- formal acceptance of fail-closed cooperative checkpoint quiescence and a
  decision to omit or precisely define force-checkpoint behavior.

The ADR chooses interfaces and guarantees without freezing AgentFS's
schema, Apple command-line tools, fixture package versions, or benchmark
numbers into PDB's portable canonical format.

## Final recommendation

PDB should not build a new filesystem first, and it should not collapse source
isolation, dependency isolation, process isolation, and security into one
ambiguous `Environment` promise.

The most robust initial architecture is:

```text
immutable PDB Snapshot
        |
        v
adaptive snapshotter
        |
        +-- APFS clone (leading macOS default candidate)
        +-- full copy (correct fallback and reference)
        +-- Linux overlay (optional accelerator)
        +-- AgentFS external adapter (evaluation only)
        +-- container or microVM (optional security-isolated runtime)
        |
        v
private ordinary AgentSession root
        |
        +-- logically private dependency view
        |       +-- Session-local links and mutable fallbacks
        |       +-- shared package-manager content/graph stores
        +-- private generated/build/runtime state
        +-- optional shared compiler action cache; never shared mutable target
        |
        v
explicit checkpoint -> immutable PDB objects + exact changeset
        |
        v
policy/check/MVCC validation -> Environment ref update
```

For the current macOS-first, npm-heavy usage, native APFS clones are the leading
default candidate because they preserve ordinary Node and editor behavior with
no daemon or mount requirement and reduced startup space dramatically in both
1 GiB host trials. The prototype is feasible: core correctness passes on two
independent APFS hosts, and npm, pnpm, TypeScript, Vite, Next, Jest, and Vitest
pass locally. It is not yet justified as the automatic default: the single
100,000-file APFS prepare took 13.215 seconds, above the provisional 5-second
gate, and the workload/performance lanes lack randomized repetitions. Ship the
backend interface with `full-copy` as the default and explicit fallback first;
keep `apfs-clone` opt-in until the repeated matrix either meets an approved gate
or supplies evidence to revise it.

AgentFS is the most relevant substantially different external system, but
v0.6.4 is rejected on the tested macOS host and its failure is filed upstream.
Do not embed its SDK or make its SQLite/NFS state canonical. Linux OverlayFS
remains a strong optional backend over immutable PDB lower snapshots. Full copy
remains the portable correctness baseline and initial shipping choice.

The Stage 6C gate should therefore be a backend-independent two-agent and npm
compatibility suite. PDB earns the Environment design only when two agents can
edit and execute independently from one exact Snapshot, checkpoint reproducible
results, and publish solely through the existing deterministic authority path.
