delegate
Delegate bounded grunt work to cheap open-weight models via opencode (docs flips, standup drafts, lint fixes, report assembly, pre-review diff critique, well-specified gated implementation). Returns E
SKILL.md.claude/skills/delegate/SKILL.md
GitHubdelegate
Runs a bounded task on a cheap open-weight model through opencode, with
evidence capture. Claude stays the orchestrator and judge; the worker is never
trusted to self-report success.
When to use
| Tier | Use for | Worker agent |
|---|---|---|
grunt |
docs status flips, standup drafts, fixing /lint findings, report assembly, summarization | grunt |
implement |
a well-specified package spec or small fix in an ISOLATED worktree, with build+pytest+lint gates downstream | worker |
verify |
cheap pre-review critique of a diff before spending Claude review budget | critic |
Current tier→model mapping lives in config/tiers.json — update it there as
the model landscape shifts; never hardcode model ids elsewhere.
Never delegate: architect/spec judgment, cycles-parity review, cpp-abi-guard reachability, gate-failure root-cause, the merge decision, visual PNG inspection (workers are text-only), or anything whose mistake ships silently.
How
python .claude/skills/delegate/scripts/delegate.py --tier grunt --dir <workdir> --prompt "<task>"
python .claude/skills/delegate/scripts/delegate.py --tier implement --agent worker --dir <worktree> --prompt-file <specprompt.md>
python .claude/skills/delegate/scripts/delegate.py --tier verify --agent critic --prompt "Review the diff of main...HEAD for defects."
--agent grunt|worker|criticselects the restricted opencode agent from.opencode/agents/(push/gh denied for all; critic is read-only). Always pass the matching agent for the tier.--fallbackswitches to the tier's fallback model (use after a primary failure or bad output).- Long prompts: write to a file and use
--prompt-file. --dir <worktree>is how you delegate into an isolated worktree. The wrapper forwards it to opencode's own--dirflag, which is load-bearing: opencode IGNORES the subprocess cwd and otherwise roots its shell/file tools at the git project worktree — which for any linked worktree resolves (via the shared.gitcommon dir) to the MAIN checkout. Without--dir, every edit is silently redirected into main (contamination, not a rejection) while the wrapper watches the worktree and reportsfiles_changed: []— a false "completed". Sibling (../Astroray-<pkg>) and in-tree (.claude/worktrees/<pkg>) worktrees both work once--dirpoints at them. Verified end-to-end 2026-08-25. See memoryparallel_agent_worktree_contamination.
Prompt composition rules
Worker prompts must be self-contained: absolute file paths, exact expected output, explicit "do not touch anything else". Weak models do not infer intent. Bound the task: one package, one doc, one findings list — never "and anything else you notice".
The evidence contract (non-negotiable)
The wrapper prints a JSON summary: status (completed|timeout|errored| no_clean_finish), files_changed, tokens, cost, transcript
path, plus two additive fields introduced by pkg232:
termination_reason—normal|timeout|cancelled|error, the structured initiating event. A cleanup failure cannot erase it:status: timeoutstaystimeouteven if the orphaned worker would later have completed.cleanup— a cleanup-evidence object{method, confirmed, error}.methodiswindows_job_objecton Windows,direct_childelsewhere, ornot_startedwhen command resolution failed before launch. For a Windows Job,confirmed: truemeans the wrapper verified zero active processes and awaited its helper before taking the final snapshot. Fordirect_child, it proves only the immediate child exited; descendants may remain. Ifconfirmed: false, the wrapper reportserror, skips the final git snapshot and transcript-derived claims, and emitsfiles_changed: null/git_head: null/tool_calls: nullrather than misleading empty evidence. The task file is retained for diagnosis. Cancellation usesstatus: erroredwithtermination_reason: cancelled.status: completedmeans the PROCESS finished — not that the task succeeded. opencode has documented exit-0-on-error bugs; open models over-claim under uncertainty.After every delegation, verify with your own eyes/tools: read the diff, run the build/tests for code changes, grep the claimed edits. For
implementtier, the full gate stack (build, pytest, /lint, CI, HW verify) applies unchanged.The full JSONL transcript is kept for diagnosis — read it when the result looks wrong before re-dispatching.
Process-tree containment (platform boundary)
On Windows, the wrapper launches the worker inside an unnamed Job Object
with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE and no breakaway flags, so every
ordinary CreateProcess descendant of the worker inherits Job membership. On
timeout/cancellation/error — and on ordinary completion — the wrapper
terminates and awaits the full Job tree, confirms ActiveProcesses == 0, and
only then takes the final git snapshot and parses the transcript. No owned
process keeps writing files after a return with cleanup.confirmed: true.
If confirmation fails, the evidence explicitly remains unavailable. Ordinary
completion now ends leftover background subprocesses; delegated tasks are
bounded, so persistent servers/daemons are not supported.
The configured timeout is a worker-runtime budget after launch-gate payload delivery, not a strict whole-launch deadline. OS process creation and the synchronous control-pipe handoff precede that budget. A stalled helper startup or very large single-line prompt can therefore delay the timeout or handled cancellation checkpoint. The bounded cleanup/exit-evidence contract applies when teardown runs; it does not prove a bound on pre-delivery startup latency.
On non-Windows (Linux/macOS), the wrapper keeps its existing
subprocess.run(timeout=) direct-child path: it kills and waits only the
immediate child, with no descendant-tree supervisor. The full-tree containment
guarantee is Windows-only; it does not imply that an escaped/brokered process
is contained on any platform.
Failure handling
timeout: known Windows hang classes exist (stuck "Loading plugins", unnoticed process completion). Re-dispatch once; if it repeats, do the task yourself or escalate tier.- Garbage/wrong output twice from the primary →
--fallback, then give up and do it on Claude. Do not loop a weak model more than twice on the same task.
Privacy
Free-tier models (*-free) may train on submitted data. Only route
.astroray_plan/ docs and public-content tasks through the free variant;
anything touching src/, include/, or unpublished algorithm notes uses paid
endpoints only.