| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Stop spending your context on work you could have sent away.
claude-code-delegate hands bounded work from Claude Code to Codex, OpenCode, or a headless Claude. What comes back is unverified until it is checked: the worker finished rather than stalled or vanished, the result meets a fixed contract, and nothing was written outside the scope the task declared.
The Claude session does not poll for progress. It hands the task over, ends its turn, and is woken only when there is something to collect.
In a long session, context goes to log output, file dumps, directory listings and repeated checks, rather than to the judgement that needed it.
A worker can hang, die, return nothing, or return something that only looks like a result. None of those comes back as success.
claude plugin marketplace add seigo2016/claude-code-delegate
claude plugin install delegate@claude-code-delegate --scope userThen write .claude/delegate.toml in your project, naming one backend you have:
default_worker = "claude"
[workers.claude]
adapter = "claude"
enabled = true
light = { model = "haiku", effort = "medium" }
standard = { model = "sonnet", effort = "high" }
frontier = { model = "opus", effort = "xhigh" }
[roles.artifact-auditor]
level = "standard"
forbids_allowed_writes = true
repo_local_reads = trueexamples/delegate.toml in this repository carries all six roles and all three backends, switched off.
Then, in a Claude Code session:
/delegate check that CHANGELOG.md lists every tag reachable from main
Claude describes the task, hands it to a worker, and ends its turn. A hook wakes the session when the worker finishes, and Claude collects the result.
Installing the plugin replaces no agent, removes no tool, and enables no backend until you declare one.
A role asks for a level, never a model, and each worker says what that level means for itself. The same roles therefore work whichever backend is configured, and --worker picks one per task. A role asking for a level the chosen worker does not define is refused before anything starts.
| adapter | driven by | answers with |
|---|---|---|
| codex | codex exec --json | an output file, plus a JSON schema it is held to |
| opencode | opencode run --format json | its last text part |
| claude | claude -p --output-format stream-json | its closing result event |
Roles that ship in the example: artifact-auditor, repo-cartographer, bounded-implementer, verification-runner, consistency-auditor, adversarial-critic.
Claude writes the task itself. It says what to do, what may be read, what may be written, and what evidence to bring back:
{
"objective": "Check that CHANGELOG.md lists every tag reachable from main.",
"read": ["CHANGELOG.md"],
"allowed_writes": [],
"required_evidence": ["tags missing from the changelog", "tags listed but not in git"],
"host_only": false
}| what happened | what you get |
|---|---|
| a tool call never returned | timeout, failure_class: tool_stall |
| the worker finished but never handed back a result | timeout, finalization_timeout, its last message kept aside |
| the worker exited non-zero | failed, nonzero_exit |
| the worker produced nothing | failed, empty_result |
| the result did not meet the contract | failed, invalid_result, with the reasons |
| the worker wrote outside its declared scope | failed, write_scope_violation, with the paths |
| the backend could not be started | failed, launch_error |
| you cancelled it | cancelled |
| the machine went down under it | orphaned, or degraded if a result survived |
A timeout carries the reading of where it stopped: tool_stall, finalization_timeout, runtime_stall, event_stream_stall, or wall_clock_timeout.
A message that looks like a valid result is never promoted to one. It is kept beside the task.
A result holds at most five strings of 300 characters per list. For a longer one, have a writable worker write a file and return its path. A read-only worker returns the five highest-priority findings and requests another bounded task.
Roles may set `requires_allowed_writes = true` for patchers, `forbids_allowed_writes = true` for auditors, and `repo_local_reads = true` when absolute paths, parent traversal, and URLs must be refused before launch.
For a strict role that must inspect a mounted data root, keep `repo_local_reads = true` and add `allowed_read_roots = ["/mnt/research-data"]`. Only absolute reads that resolve under one of those roots are accepted; a symlink that resolves outside is refused. This option requires `repo_local_reads = true` and does not broaden write scope.
required_evidence is instruction to the worker, and appears in its prompt. The contract checks that the evidence fields are present, well formed and within those limits. It does not check that they answer what was asked, and it cannot tell whether they are true.
Claude marks a task "host_only": true when it needs live session state, or when the judgement is one a person must make rather than approve after the fact. Submit then fails closed instead of sending it. This is an explicit marker written into the packet, not an automatic classifier.
The skill and the hooks run these. You need them yourself only to diagnose something.
| command | for |
|---|---|
| delegate submit --role R --title T --packet P | start a worker and get a handle |
| delegate collect <task-id> --project-root ROOT | take delivery, once |
| delegate status <task-id> | explicit diagnosis, never a waiting loop |
| delegate cancel <task-id> | stop a task that is wrong or runaway |
| delegate reconcile | classify tasks whose worker is gone |
| delegate watch | what the hook runs; waits for something to collect |
submit takes --worker <name> to override the backend for one task. Every command takes --project-root. The submit handle and completion notification carry the resolved root so cross-repository tasks are collected from the same ledger they were submitted to. One Bash call may submit several independent tasks: the hook tracks every returned handle. After collecting one, it re-arms itself for any still-uncollected task from that same call.
Detected:
Not detected:
What a sandbox stops before the fact depends on the backend. A task that declared no writes gets the narrowest posture its backend has:
| backend | a task that may not write | enforced by |
|---|---|---|
| codex | --sandbox read-only, network closed | the operating system |
| claude | auto, minus the editing tools | a classifier, and tools withheld |
| opencode | an injected delegate-readonly agent | OpenCode permissions |
Only codex refuses in the kernel: an attempt to write comes back Read-only file system. Claude Code is not handed the editing tools, and its classifier turned down every way round them that was tried: a shell redirect, tee, and a Python one-liner. That is a judgement made each time rather than a boundary. OpenCode read-only tasks receive a per-run primary agent whose edit, bash, task, webfetch, and websearch permissions are denied. This is a tool-permission boundary, not an operating-system sandbox; the task contract still forbids scratch files in .claude, /tmp, and elsewhere.
A role that runs tests or builds gets a writable filesystem even when it may not change the repository, because build tools write caches under the home directory. Nothing then stops such a run from touching the repository; the scope check above catches it afterwards.
The adapters follow three CLIs whose event shapes were recorded from real runs, and a backend release can move them.
Three things were left out rather than overlooked.
Making delegation compulsory. A PreToolUse hook can refuse a tool call, so a setting could turn the skill from something Claude may reach for into something it has to. Left out for now: Claude was measured delegating a large reading job unprompted and doing a two-file check itself, and a refusal that is walked around by doing the same work another way is not enforcement.
Isolation. Workers run in the work tree you are working in. A worktree each would contain them, but bounded-implementer exists to change that tree, so the containment would have to be undone to be useful.
uv sync --extra dev
uv run pytest
uv run ruff check .
uv run ty checkTests drive a stand-in worker that can be made to fail on demand. They cover stalled and dead workers, a backend that cannot be launched, empty and malformed results, a finished turn that never handed one back, writes outside the declared scope, orphan and degraded reconciliation, and collecting a result exactly once.
MIT
| Back | FazBrowse Home | New Git URL |