Accepted adr-002 · 2026-10-13

The Agent Has No Clone: It Writes Only in a jj Workspace iter Opens, and isolation Holds the Boundary Around It

Context

isolation first gave the agent its own clones. `agent-clone` cloned an owner repository whose origin host was in agent_git.remote_hosts into ~/work/<repo> as the agent, set push.default=current and switched to an agent/<name> branch; the agent committed and pushed from there. `harden-workspace` would then make the shared roots read-only for the agent group, since the agent no longer wrote in them.

The work moved to the owner's own stores. iter reads unpublished work across jj workspaces of one store; the agent's contract (agent-claude/CLAUDE.md) says it works in a jj workspace of the owner's store under <repos_root>/.iter-ws/<repo>/<workspace>, writes only through jj, never fetches or pushes, and that accepting is the owner's act and publishing the integration identity's. adr-001 shaped the git-dir deny around exactly that: objects/, refs/ and worktrees/<name>/ stay writable for the agent's workspaces.

Measured on 2026-10-13, rigzin: no host held an agent clone, yet `agent-clone` still ran git clone, fetch, config and switch as the agent — a second write path with a push, contradicting the contract. `agent-session-import` placed sessions under ~/work, where the agent no longer works. `harden-workspace` would have removed the group's write on Development, where .iter-ws and the stores the agent's jj writes live. Six plans in ontoref/.coder (2026-10-10 to 2026-10-12) still prescribed `agent-clone <repo>` as their first step. Of the stores those plans named, only iter had an agent workspace; ontoref/code was a jj store without one, and signatio, specdossier, lian-build, stratumiops and DD-eca were plain git repositories. No command opened a workspace: iter's declaration contract named `workspace_root` and nothing read it.

Decision

THE AGENT HAS NO CLONE. Its only write surface is a jj workspace of the owner's store. `agent-clone` and `harden-workspace` are removed, and with them agent_git.workdir and agent_git.branch_prefix.

ITER OPENS THE WORKSPACE. `iter workspace <name> <store> --root <repos_root>` colocates a plain git repository first (`jj git init --colocate`), adds the colocated workspace <name> at <repos_root>/<workspace_root>/<store path>/<name> — workspace_root from the declaration at the local trunk, `.iter-ws` by default — and links .claude and .coder. It runs on its own where isolation is absent.

ISOLATION HOLDS THE BOUNDARY AROUND IT. `isolation.nu agent-workspace <store>` calls iter for the host's agent, applies deny-control and loose refs to that store, reinstalls the guard when its event pass does not watch the store's .jj/repo (a store iter just colocated), and attempts `jj status` in the workspace as the agent. It refuses when iter is not on PATH. isolation declares no workspace location: it reads what jj records (`jj workspace list`), for agent-repos and for the default cwd of agent-session-import.

BOTH ARE IDEMPOTENT. Each step is a row applied only when not ok, so a workspace iter opened, or opened by hand, reports ok when isolation runs, and the reverse.

WHERE AN ISOLATED AGENT WRITES, THE PROJECT IS A JJ STORE. jj stays opt-in for a project (ontoref adr-013), and isolation stays optional for gov, which declares neither; what makes jj required is an isolated agent writing in the project, not membership in gov.

Constraints

Alternatives considered