When the Repo Isn't the Right Boundary

Primitives over workflows for coding agents

The useful version of a coding agent is the one you do not have to babysit. The risky version is the one with broad permissions pointed at your normal working tree.

That tension gets sharper once the task stops fitting inside one repo. A lot of the work I care about spans multiple repos, often code and docs, plus local datasets and sometimes a prepared environment with GPU access. A typical task is not “edit repo A.” It is “change code in one repo, update notes in another, and run against local data in the same attempt.” The repo is often the wrong unit of isolation.

I want the agent to see that whole context. I do not want that context sharing a branch, a working tree, or a blast radius with my host machine.

That is a dispatch problem.

By dispatch I mean the mechanics around the agent: where it runs, what it can touch, how many sessions I can run at once, and how their changes come back for review. This piece is about that layer, and about a tooling taste that became clearer to me once I started pushing on it: at the dispatch layer, I prefer primitives over workflows.

That is the rest of this piece, and why I ended up building scad, short for scoped agent dispatch.

Workflows and Primitives

Most agent tools do more than expose capabilities. They imply a workflow.

They decide, explicitly or implicitly, what counts as a session, how code gets isolated, where state lives, how results come back, and where the human steps in. That is often a good thing. A workflow tool can remove a lot of choices, and if those choices are the same ones you would have made yourself, you should take the help.

But there is another shape a tool can have. Instead of giving you one opinionated loop, it gives you a few smaller parts that compose cleanly: define the project, build the environment, start a session, inject work, fetch code back, inspect status, tear everything down. The workflow is still there. You just assemble it yourself.

This is not a new argument. Software keeps rediscovering it because the structure of small composable parts versus end-to-end loops keeps recurring. The cleanest example is probably the Language Server Protocol. Before LSP, deep editor support meant each editor needed bespoke integration with each language. After LSP, both sides could target one protocol instead. The point was not aesthetic purity. The point was that the right primitive collapsed an integration matrix.

You can see the same split in newer agent tooling too. Anthropic makes almost this exact distinction in Building Effective AI Agents: workflows are predefined code paths, while agents direct their own process more dynamically. Anthropic’s own Claude Agent SDK is framed in the same spirit, as a lower-level set of building blocks rather than a full end-to-end product workflow.

For my own work, the drag showed up at the dispatch layer first.

Why Dispatch Needed Its Own Shape

The operational issue was not abstract composability. It was permissioning under concurrency.

The general pattern is simple: a coding agent gets much more useful when it can act with less friction, and much riskier when that freedom is pointed at the wrong surface. In my own setup, the concrete version of that tradeoff is Claude Code with --dangerously-skip-permissions. Running one session that way on the host is already a trust decision. Running several sessions around the same task, with access to overlapping repos and local resources, is where the need for boundaries becomes hard to ignore.

What changed the tradeoff for me was bounded blast radius. If each session lives in its own disposable container, with only the repos and mounts I explicitly gave it, then full permissions become a different proposition. The agent can move fast inside the box. The box is the boundary. If something goes wrong, I throw the session away.

This is containment, not magic security. The goal is practical blast-radius reduction, not a formal sandbox guarantee.

That is also why I think of dispatch as its own layer. It sits between “one safe session” and “many parallel branches”: many sessions, separate branches, isolated workspaces, predictable environments, and a clean path back to reviewable code, regardless of which coding agent is running inside.

What I Found in the Current Tooling

Before building anything, I spent some time mapping the tools already in this space. The clusters were fairly clear.

  • Anthropic’s own material validates the pattern but stays intentionally close to the metal. The devcontainer docs, sandbox-runtime, and Agent Teams all point at real pieces of the problem without trying to package the whole dispatch layer.
  • Docker wrappers like claudebox solve the safe single-session case.
  • Sandbox platforms like Docker Sandboxes, E2B, Daytona, and Cloudflare sandbox tooling offer stronger isolation primitives, but the remote versions are aimed at a different locality model than the one I cared about.
  • Orchestration tools like claude-swarm, claude-flow, Overstory, and Container Use care more about coordination than dispatch.
  • Worktree tools like Uzi and Anthropic’s own parallel worktree workflow make branch-level parallel work much lighter when the repo boundary is the right boundary.

Many of these tools are more sophisticated than what I needed, and several solve adjacent problems very well. My issue was not that the ecosystem lacked features. It was that the combination I cared about did not sit naturally inside the shapes those tools were optimized for.

That is what pushed me toward a smaller set of primitives.

What I Actually Needed

My requirements were ordinary on their own. The combination was the problem:

  1. Multi-repo work, often code and docs together. Most container wrappers assume one project; most worktree tools are repo-shaped.
  2. A baked Python environment. I wanted pinned dependencies and a warm environment ready in seconds, not per-session reinstall drift.
  3. Local hardware and local data. That rules out a lot of otherwise attractive remote sandbox options.
  4. Parallel sessions with bounded permissions and as little babysitting as possible. That is where the container boundary stops being a convenience and starts being the point.

The lighter branch-level model is appealing when the repo boundary is the right boundary. My problem was that it often was not. I wanted the isolated unit to include multiple repos, local mounts, and a prepared environment together.

The Tool That Fell Out

scad is the tool that came out of that. It packages the primitives I wanted in one place:

  • config: define repos, mounts, environment, and agent settings
  • build: bake the environment once
  • session: create, keep, and tear down the isolated workspace
  • inject: send work into a prepared session
  • code: fetch branches back and compare results from the host
  • status: inspect what is running and what finished

The important design choice is that the session is the environment and injection is the work. A container can stay alive while different agent jobs run inside it. Some can be interactive. Some can be headless. A session can be reused. Code and data can be visible to the container without my host working tree becoming the execution surface. The agent itself only shows up at the end, when work gets injected into that prepared session. Today the injected tool is Claude Code. I do not think the design has to stop there.

That decoupling matters because it changes what a session is for. A session is not “one agent run.” It is a prepared working context that can outlive any single job. I can start it once, inject work, inspect what happened, send more work, or abandon it and spin up another copy. That is a different model from tools where the container, the agent process, and the task are all the same lifecycle.

The execution is isolated, but the work stays local.

The session runs in a container, while the clones and resulting branches remain legible from the host, so review and follow-up still happen in normal local git terms.

There are composite commands on top of this because common paths deserve shorthand. dispatch, harvest, and finish exist because I use them. But the composites are convenience. The primitives are the real shape of the tool.

That distinction matters to me. If the workflow changes next month, I want to rearrange the pieces, not wait for the tool’s baked-in story to match my own again.

The Unit of Work

This is the point where “primitives over workflows” stopped being taste and started being necessity.

The motivating case is a project that spans more than one repository and more than one local resource.

Suppose one repo holds the main codebase, another holds docs or notes, and the datasets live elsewhere on disk. Some runs may also need GPU access. A useful unit of work in that setup is not “edit one repo.” It is one self-contained task that cuts across all of them. That is a dispatch problem.

In practice, I start a session from a project config that names all of those repos and mounts together. scad clones the repos into one isolated workspace, keeps track of them together, and starts the agent inside that combined environment. The agent can change code and revise the supporting docs or notes in the same run because, for the purpose of that task, they belong to one conceptual unit. If I want to try a second approach, that becomes a second session with its own copies of that same multi-repo workspace. Later, I fetch the branches back and review them on the host like any other code.

What I like about this flow is not that it automates the whole project. It does not. It gives me a repeatable way to create a bounded workspace around one real unit of work, even when that unit spans multiple repos, local mounts, specific dependencies, and whatever hardware access the project needs.

That is the part I was missing in more workflow-shaped tools. They often had good answers to interaction and review. I needed a better answer to the question: what exactly is the unit of isolation when the task itself spans several repos, and how cheaply can I create another copy of that unit when I want a second attempt?

Costs and Fit

This setup pays for its boundaries.

Containers are heavier than worktrees. Rebuilding images is real overhead. Local clones are heavier than branch-only isolation. A project config is another thing to maintain. If I only want quick trusted iteration in one repo, I would reach for a lighter tool and skip most of this.

There are also whole categories of problems this does not try to solve. scad is not a coordination framework. It does not try to manage agent hierarchies, task graphs, or production operations. It is a dispatch layer for a local development workflow.

So this is not a universal argument against workflows. It is a fit question.

Dispatch Doesn’t Frame the Work

Reliable dispatch solves one class of problem: where the agent runs, what it can see, and how its changes stay contained.

It does not solve the upstream problem of defining the work clearly. If the task is vague, an isolated multi-repo workspace just gives the agent a cleaner place to be vague in. If I launch several sessions against a half-formed task, I do not get leverage. I get several branches headed in the wrong direction, and now I have to review all of them.

Once the dispatch layer is reliable, the main human leverage moves back upstream into the quality of the spec, plan, or prompt. I keep that boundary explicit. scad shapes the workspace. It does not clarify the work.

The shape of the tool should match the texture of the work, and this is the shape that matched mine.