LayerFS for Codex parallel verify

previewv0.1.0

One shared LayerFS Store, one Branch per Codex hypothesis, verify commands in parallel.

Earson · for Codex

Install

Paste this into a coding agent on the machine that will run it. It installs v0.1.0 at e440813d6c with LayerFS v1.1.0 at ad78914e36, the commit it was tested with. It runs the author’s build, so read it first.

Install prompt · v0.1.0
Install LayerFS for Codex parallel verify 0.1.0 for me.

Do these steps in order. Stop at the first one you cannot finish and report the step plus the exact error. Do not retry a LayerFS call that failed, and do not run init again for the same project name.

1. Check Node 20 or newer with `node -v`. The first line must be `v20` or a newer major. If it is older, stop.
2. Clone https://github.com/Ephemeral-AI-Lab/layerfs and check out commit ad78914e36b258b360daf13aec164b5655814689 (v1.1.0). Use exactly this commit: it is the one this version was tested with. `git rev-parse HEAD` must print ad78914e36b258b360daf13aec164b5655814689. If it does not, stop. Export `LAYERFS_REPO` as that checkout.
3. Clone https://github.com/earsonlau/layerfs-codex-parallel-verify beside it and check out commit e440813d6ca2cc973ddae517319addeb52b694b6. `git rev-parse HEAD` must print e440813d6ca2cc973ddae517319addeb52b694b6.
4. In the plugin checkout, run `npm test`. Success is exit 0 and every test passing. This does not need Docker, a LayerFS daemon, or a Rust toolchain. You can also run `node bin/codex-parallel-verify.js --help`.
5. In the LayerFS checkout, `api/` is an npm workspace. Run `npm --prefix "$LAYERFS_REPO/api" install`. That links the packages from this commit; it does not download them from the registry. From the plugin checkout, confirm both public entry points load:

```bash
node --input-type=module -e 'import { loadSdk } from "./src/load.js"; const sdk = await loadSdk(); console.log(typeof sdk.LayerFS.open, typeof sdk.LayerFSRuntime.open)'
```

Success prints `function function`. If it does not, stop. Do not import `@layerfs/agent/internal`.

6. A live init needs a Linux `layerfs-daemon` and a runtime image built from commit ad78914e36b258b360daf13aec164b5655814689. Build them the way `api/README.md` and `api/how-to-use/runtime.md` in the LayerFS checkout describe (`npm --prefix "$LAYERFS_REPO/api" run build:native` builds the addon on a macOS or Linux host). Then set `LAYERFS_HOME` (for example `$HOME/.layerfs-codex`), `LAYERFS_IMAGE` to that image's sha256 id, and `LAYERFS_DAEMON` to the absolute path of `layerfs-daemon`. On macOS and Windows also set `LAYERFS_BUNDLE` to the sealed Linux bundle from that same commit (it contains `layerfs-daemon`, `layerfs.node`, and `manifest.json`). The CLI calls `runtime.worker.start()` once and sets `LAYERFS_WORKER_ENDPOINT` to `$LAYERFS_HOME/worker/endpoint.json`. Do not start a second worker for the same home. On Linux, leave `LAYERFS_WORKER_ENDPOINT` unset so Store calls stay in-process. On Windows, pass `--wsl-distro`, `--wsl-user`, `--wsl-node`, and `--wsl-home` when the runtime defaults (Ubuntu, root, `/usr/local/bin/node`) are wrong.
7. Initialize once from the plugin checkout:

```bash
node bin/codex-parallel-verify.js init --name shop --source "$PWD" --json
```

`--name` is a LayerFS project name: 1–63 characters, lowercase letters, digits, underscore, dot, or dash, starting and ending with a letter or digit. Exit 0 means the project exists. Exit 125 means LayerFS refused the call. Read the JSON on stderr and stop. Do not run init again for the same name. The ledger is `$LAYERFS_HOME/.codex-parallel-verify/shop.json`.

Report what you ran and what each step returned. If anything fails, stop and give me the exact error, and don't retry it on your own.

One shared LayerFS Store, one Branch per Codex hypothesis, and verify commands in parallel. Disk stays near the size of one tree instead of N git worktrees.

Built for Codex on LayerFS v1.1.0. The public packages are @layerfs/agent and @layerfs/runtime. This plugin does not import @layerfs/agent/internal or @layerfs/backend. Every LayerFS call is one attempt. A refusal is not retried.

What it adds

Codex CLI sets the session root with -C / --cd. codex exec --worktree and codex --worktree create a managed git worktree and use that checkout as the session root. The desktop app gives each thread its own worktree. A worktree is a full checkout. Dependencies are not in git, so each one runs its own install. N candidates cost about N copies of the tree plus N copies of node_modules.

This plugin is the replacement. You do not pass -C once per candidate, and you do not pass a LayerFS Workspace path to -C.

Codex worktree This plugin
git worktree add ../hyp-1 -b hyp-1 spawn-n forks a Branch, with no file copy
npm ci in every worktree one init --install, committed on deps and published to main
codex exec -C ../hyp-1 "npm test" edit branch hyp-1, then verify-all --command "npm test"
codex exec --worktree for another try another Branch forked from the same main
desktop thread per worktree one Branch per hypothesis, then one verify-all
rm -rf ../hyp-2 discard --name shop hyp-2
  • init --name shop --source "$PWD" imports a directory once. --install runs once on branch deps and is published to main only when it exits 0.
  • spawn-n --count 4 forks that many Branches from main (prefix hyp by default).
  • verify-all --command "npm test" runs the command on each Branch.
  • report prints how much disk the Store used against a worktree baseline.
  • discard and keep only change the ledger. They do not copy a tree, check out a worktree, or delete commits.

workspace.mount returns a path such as /workspaces/1. That path is inside the Sandbox. It is not a directory on the host, so codex exec -C /workspaces/1 does not see the candidate. Host Codex stays in the real checkout. Edits for a candidate go to that candidate’s Branch (fs.write / fs.apply, or one workspace.exec plus one workspace.commit). The verify command runs with workspace.exec, which is the step that used to run in the directory passed to -C.

One Codex session, one Branch. The next hypothesis is another Branch, not another -C.

How it works

verify-all runs mount, then exec, then commit, then unmount on each Branch. A non-zero verify command is still committed once, with the exit code in the message. Default concurrency is 4. --sequenced is one Branch at a time. --parallel is all of them. Sandboxes hold 16 Workspaces; One is created when a Branch is about to mount and every Sandbox already started for this run is full, then each is closed once after the run.

If two commits change the same path on one Branch, workspace.commit throws Conflict. detail.conflicts lists the paths and detail.head is the head that won the race. Do not send the commit again and do not pass force. verify-all records the refusal, unmounts that Workspace once with discard set, and moves on. To keep the edit, read the conflicting paths once, resolve them on a new fork, and commit there once.

On Linux, Store calls stay in this process and LAYERFS_WORKER_ENDPOINT stays unset. On macOS and Windows the CLI calls runtime.worker.start() once and sets LAYERFS_WORKER_ENDPOINT to $LAYERFS_HOME/worker/endpoint.json. Do not start a second worker for the same home. Windows can pass --wsl-distro, --wsl-user, --wsl-node, and --wsl-home when the runtime defaults (Ubuntu, root, /usr/local/bin/node) are wrong.

report and verify-all print:

Field Meaning
stored Bytes the Store occupies on disk, from runtime.volume.list for this project’s volume, or one stat of global/store.sqlite when that figure is missing
unitBytes One materialized tree. After a successful --install, this is measured inside the Workspace and includes dependencies. Otherwise it is the source directory scan
worktreeBaseline candidates times unitBytes: the working-tree cost of one git worktree per candidate
savedBytes worktreeBaseline minus stored
timesSmaller worktreeBaseline divided by stored

Forked candidates stay in the baseline after discard, because a worktree workflow would have had those trees on disk and the Store still holds their commits.

You need

  • The Sandbox runtime image needs node and npm (init --install, verify-all --command "npm test", and the size probe).
  • Node 20 or newer (node -v starts with v20 or a newer major)
  • This repository at the listed commit, and a LayerFS checkout at the commit recorded in its layerfs.json
  • npm --prefix "$LAYERFS_REPO/api" install in that checkout, which links @layerfs/agent and @layerfs/runtime from the checkout rather than the registry
  • For a live project: a Linux layerfs-daemon and a runtime image built from that same LayerFS commit, then LAYERFS_HOME, LAYERFS_IMAGE, and LAYERFS_DAEMON. On macOS and Windows, also LAYERFS_BUNDLE, a sealed bundle that contains layerfs-daemon, layerfs.node, and manifest.json

npm test in this repository uses stand-in clients. It checks that each LayerFS call is made once, that a Conflict is not committed again, that macOS and Windows set LAYERFS_WORKER_ENDPOINT after one worker.start, and that the report’s stored size is compared with the worktree baseline. It does not need Docker, a daemon, or a Rust toolchain.

Limits

  • The default tests do not open a real Store.
  • Exit 125 from the CLI means LayerFS refused the call. Exists on a second init of the same name is not a prompt to retry.
  • discard and keep do not delete commits. Discarded losers stay as deduplicated history. Unmounting with discard drops an uncommitted overlay and copies nothing.
  • A Cursor plugin, the DeepSeek Harness UI, and changes to LayerFS itself are out of scope.

Versions

Each version pins the plugin’s commit and the LayerFS release and commit it was tested with.

VersionDatePlugin commitLayerFS
v0.1.0newestLazy sandboxes; keep commit when unmount fails; default prefix hyp; sandbox image needs node/npm; LayerFS pin ad78914e (#436 overlay fix).2026-10-25e440813d6cv1.1.0 @ ad78914e36