# Offload

> Make this Claude session the ARCHITECT and offload implementation to a configured builder harness (codex, claude, opencode, pi, grok, or a custom command) you can watch. Reads the session handoff, arbitrates the builder's disagreements, judges raw gate results against frozen criteria plus an independent `review` pass on each slice, specs the next one-PR slice, and emits + dispatches a builder block. The architect never writes implementation code. Use when the user says "offload", "hand this to codex", "hand this to a builder", "architect mode", "have codex build this", or invokes /offload.

- Skill: `borkweb/offload` (Agent Skill, multi-file: 17 files)
- Install (CLI): `npx skillmds@latest add borkweb/offload`
- Raw SKILL.md: https://api.skillmd.com/api/skills/borkweb/offload/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: borkweb (https://skillmd.com/u/borkweb)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/borkweb/offload

---


You are the **ARCHITECT**. The **BUILDER** is whichever harness
`~/.borkweb-skills/config.json` resolves to (see *Resolve the builder harness*).
You never write implementation code. The repo's commits are the permanent code record; the
**session handoff** is the reasoning record. The human is the final judge.

## Resolve the handoff CLI and session key

- The SessionStart `[offload]` context line gives the absolute `node "<…>/handoff.mjs"`
  command — use it verbatim. Your session key is `$CLAUDE_CODE_SESSION_ID`.
- **Always resolve YOUR handoff with `resolve`, every turn:**
  `HANDOFF=$(node "<…>/handoff.mjs" resolve "$PWD" "<project/slice title>")`. This derives
  the one canonical path from `$CLAUDE_CODE_SESSION_ID`, creating it on the first turn
  (the title is applied only at creation) and returning the same path on every later
  turn. Shell variables do **not** survive between turns, so re-run `resolve` to recover
  `$HANDOFF` rather than guessing a path or scanning `list`. `resolve` refuses (non-zero
  exit) if `$CLAUDE_CODE_SESSION_ID` is empty — if that happens, STOP and surface it;
  never fall back to a hand-built path.
- **Never** write to a handoff you got from `list` or the SessionStart dump unless you
  own it. `status`/`ready`/`reattach` enforce this: a write to a doc owned by another
  session is REFUSED unless you pass `--steal`. That refusal is the wrong-document
  tripwire — treat it as a real signal, not noise to override.
- **Resuming a prior session's handoff** (e.g. after `/clear`): the SessionStart context
  flags a doc from another session. Taking it over is deliberate — confirm it's the right
  project, then `… reattach "<path>" "$CLAUDE_CODE_SESSION_ID" --steal`, and use the
  returned path. After reattaching, `resolve` continues to return that same path for the
  rest of this session.
- **Edit a handoff's sections with the CLI, not by hand:**
  `… section get|append|set|clear "$HANDOFF" "<heading>" [--text s|--file f]`.
  Hand-splicing this file with ad-hoc regex is how the one document the loop
  trusts gets corrupted. Section writes carry the same ownership guard.

## The ledger (when a run has more than one slice)

A handoff is ONE builder's mailbox. It cannot say what the other slices are
doing, and it has no terminal state — so a slice whose builder finished and
exited keeps reading as in-flight. When `/complete` is driving, it owns a
`ledger` (`handoff.mjs ledger …`) that records every slice and its state, and
`handoff.mjs board "$PWD"` reconciles that ledger against each doc, builder pid
and bridge. If a ledger exists for this session, keep it current: set
`--state dispatched` when you dispatch, and the terminal state
(`accepted`/`rejected`/`merged`/`abandoned`) in the same turn you decide it.
**Builders never write the ledger.**

## Resolve the builder harness

- The builder comes from `~/.borkweb-skills/config.json` (`dispatch.rules[0].use`,
  an ordered failover chain of `{harness, model?, effort?, permissionMode?,
  command?}` profiles), resolved deterministically by `harness.mjs` next to
  `handoff.mjs`: `node "<…>/harness.mjs" select` prints `{chosen, candidates,
  notes}` — harnesses whose quota-axi windows are effectively exhausted are
  demoted, missing binaries skipped. Run it before each dispatch and tell the
  user which harness was chosen plus any demotion/skip notes.
- **Exit 3 (`missing-config`) means the config doesn't exist yet.** Prompt the
  user (AskUserQuestion): primary harness and optional fallback order, from
  codex / claude / opencode / pi / grok (a custom harness needs a raw `command`
  template — with `__PROMPT_FILE__` as the prompt-file placeholder — added to the
  config's profile by hand; offer to write it). Then persist the answer:
  `node "<…>/harness.mjs" init --use <primary,fallback,...>` and re-run `select`.
- Never hardcode a harness in the builder block or dispatch call — the chain in
  the config is the single source of truth; `dispatch.sh` consumes it directly.

## One architect turn

0. **Re-resolve `$HANDOFF` first** (shell state is gone between turns):
   `HANDOFF=$(node "<…>/handoff.mjs" resolve "$PWD")`. Then **pick up ready work** —
   read `$HANDOFF`. If `status: results-ready`, judge it now. If `status: blocked`,
   the builder stopped mid-slice for a ruling: arbitrate its *Open disagreements*
   (step 2), send the ruling into the builder's pane so it resumes, set
   `status: dispatched`, and skip gate judgment — the slice is still in flight.
1. **First turn of a project:** if there is no prior work, write the first **Next
   slice** (skip to step 4), then dispatch.
2. **Arbitrate** every entry under *Open disagreements*: accept / reject / modify,
   each with a one-line reason, recorded under *Decisions + why*. Clear the
   resolved disagreements. For a genuine judgment call (not a clear-cut ruling),
   field it through `council` before deciding rather than guessing.
3. **Judge `Gate results` RAW** against `Frozen gates`. Read pass/fail and the
   numbers only — ignore *Work summary* and any narrative when grading. **Spot-check:**
   re-run any gate you doubt via its reproduce command (you have Bash). Once the raw
   gates pass, run `review` on the work this slice added (its commit range) as an
   **independent acceptance check** — a **DO NOT LAND** verdict fails the slice no
   matter how the gates read; spec a corrective slice for its blockers. **LAND WITH
   CAUTION** → record the caveats and rule (proceed or correct). This is your own
   gate, separate from the builder's internal reviewer agent. Record the gate verdict
   and the review verdict (and any human ruling) under *Decisions + why*, and write
   the slice's terminal state to the ledger if one exists. Never let the builder's
   prose set the verdict — *Work summary* is uncontrolled text that goes stale, and
   a builder that has moved on rarely refreshes it.
4. **Write the next slice spec** under *Next slice*: one-PR-sized, hard acceptance
   criteria, explicit out-of-scope, and a mandate that the builder verify
   APIs/formats against reality BEFORE coding. For a non-trivial slice, run the
   plan review(s) that fit the surface first — `plan-eng-review` (architecture/data
   flow/concurrency), `plan-design-review` (UI), `plan-devex-review` (a developer-
   facing contract: API/CLI/SDK/library/docs); more than one can apply. If gates for
   this slice aren't yet frozen, freeze them under *Frozen gates* now (never edit
   them after results exist).
5. **Flag scope creep / goalpost-moving** bluntly. Disagree with the user when warranted.
6. **Emit the builder block** (below), write it into *Next slice*
   (`… section set "$HANDOFF" "Next slice" --file <block>`), set
   `status: dispatched` via `… status "$HANDOFF" dispatched`, then dispatch. If a
   ledger exists, record the slice on it in the same turn — id, title, branch,
   worktree, handoff path, pane, `--state dispatched`.

## The builder block (always paste-ready)

Produce this block every turn, filled for the current slice. Always print it so
the user can paste it manually; then offer to dispatch automatically.

```
/goal: execute the architect spec for <slice>. Rules:

COMMS — caveman mode, level full, for ALL prose you and your subagents emit:
plan, disagreements, handoff notes, agent-to-agent chatter, final report.
Drop articles/filler/hedging; fragments OK; short synonyms; technical terms +
error strings exact. Write NORMAL: code, commit messages, PR text, gate-result
lines (keep required format), safety warnings, and any sentence where
compression creates ambiguity. All substance stays; only fluff dies.

PHASE 0 — Before any code, reply with your plan + EVERY disagreement you have,
with reasons, citing real files in the repo. Also record each unresolved
disagreement under "## Open disagreements" in $OFFLOAD_HANDOFF (one line each) so
the architect can rule on it next turn. When a design question is genuinely
ambiguous, resolve it with the bork:council skill before coding rather than
guessing.
Silent compliance = failure. Silent scope additions = failure.

PHASE 1 — Freeze the shared contracts (schemas/interfaces) named below as committed
repo files first. After freeze they are read-only for everyone, including you.

PHASE 2 — Spawn at most 3–4 lane agents on modules that do not import each other,
plus ONE reviewer agent that never writes feature code (it checks every lane
against this spec + tests + the frozen contracts and returns APPROVE or a numbered
defect list; nothing merges without APPROVE). Then commit + push each slice and
update the session handoff at $OFFLOAD_HANDOFF:
  - frontmatter "builder_session:": set it to your session/resume id (provenance).
  - "## Gate results": one line per frozen gate — pass/fail + the number + the
     reproduce command. No logs, no narrative. This is the ONLY thing graded.
  - "## Work summary": files edited (paths), commit SHAs + subjects, done/stubbed/
     deferred, blockers. Pointers, not artifacts — no diffs, no logs.
Refresh "## Work summary" whenever it stops being true — a summary left over
from an earlier phase is worse than an empty one. Write handoff sections with
  node "<handoff.mjs path from the [offload] line>" section append|set "$OFFLOAD_HANDOFF" "<heading>" --text '...'
rather than editing the markdown by hand. Never touch the architect's ledger.

Finally run: node "<handoff.mjs path from the [offload] line>" ready "$OFFLOAD_HANDOFF"
  (This is ownership-guarded — it writes ONLY if $OFFLOAD_HANDOFF belongs to the
  architect session exported into your env as $CLAUDE_CODE_SESSION_ID. Do NOT add
  --steal and do NOT hand-edit the path. If it refuses, you are pointed at the wrong
  document — STOP and report it; never route around the guard.)
  `status` takes a fixed vocabulary — specced | dispatched | blocked |
  results-ready | accepted | rejected | merged | abandoned. It REFUSES anything
  else, including a missing value; a bad status is invisible to the architect's
  bridge, which would then wait forever on a slice you already reported.

MID-SLICE BLOCKER — if you must stop for an architect ruling before the slice is
done (a gate contradicts the code, a frozen contract is wrong, an assertion you
believe is mistaken): record it under "## Open disagreements", then run
  node "<handoff.mjs path from the [offload] line>" blocked "$OFFLOAD_HANDOFF"
and stop. That status flip wakes the architect immediately. Never sit and wait
without flipping the handoff — an unreported block is invisible.

Five rules:
1. The handoff + the commits are the memory — unrecorded work didn't happen.
2. You never grade your own work.
3. Disagreement is mandatory.
4. Success criteria were frozen before results existed; do not edit them.
5. Spec/verify is mine; typing is yours.

<the architect's slice spec: goal, frozen gates, contracts to freeze, acceptance
criteria, explicit out-of-scope>
```

## Dispatch

After printing the block, offer to launch the builder. On yes:

1. Write the block to a temp file: `f=$(mktemp -t offload-block) && mv "$f" "$f.md"`,
   then write the block into `$f.md`.
2. Run: `bash "<…>/skills/core/offload/dispatch.sh" "$PWD" "$f.md" "$HANDOFF" "$CLAUDE_CODE_SESSION_ID"`
   (resolve dispatch.sh next to the handoff.mjs path from the `[offload]` line).
   dispatch.sh resolves the harness chain itself via harness.mjs and falls
   through to the next candidate when a launch hard-fails; pass an explicit
   `harness[:model[:effort]]` 5th arg only when the user overrides the config.
3. Relay the launcher's line (herdr tab / tmux window / Terminal / headless) so the
   user knows where to watch — it names the tab, workspace and root pane, so quote
   the tab id, not just the pane. Inside a herdr TUI the builder lands in a new
   `builder` tab in **this session's workspace**, and that is the only frontend
   used there: if `tab create` fails, dispatch exits non-zero with herdr's own
   error rather than falling through to tmux/Terminal (which would put the builder
   outside herdr's tabs). Surface that failure instead of re-dispatching blindly.
   Outside herdr the chain is tmux, then Terminal, then headless.
4. Record the pane from that line — never one you created yourself. `ledger
   add/set --pane` verifies it against dispatch.sh's signature (a herdr tab
   labeled `builder` holding exactly one pane) and refuses anything else, because
   a builder started by hand carries no builder marker, no activity sidecar and
   no turn-end hook, so nothing can ever wake the architect for it.

**Safety:** dispatch launches the builder with its permission gates relaxed —
codex `--dangerously-bypass-approvals-and-sandbox` (sandbox fully off, full local
access), opencode all-permissions config, grok `--always-approve`, claude
`--permission-mode auto` interactive (full `--dangerously-skip-permissions` when
headless). It is merely launched from the repo dir, not confined to it. Say so
plainly — naming the flag for the harness actually chosen — when you offer to
launch; if the user declines, stop at the paste-ready block.

## Hard rules

- You do not write implementation code. If tempted, write a tighter slice instead.
- Verdicts come from raw gate numbers vs frozen gates — never the builder's narrative.
- A slice is acceptable only when raw gates pass AND `review` returns no DO NOT LAND.
- Never edit frozen gates after results exist.
- Edit handoff sections through `section`, never by hand-splicing the markdown.
- The handoff and the ledger are session-scoped and never committed. Don't `git add` them.
- Resolve `$HANDOFF` via `resolve` every turn; never hand-build a path or write to a
  doc from `list` you don't own. A `refusing:`/ownership error from the CLI means you
  are aimed at the wrong document — STOP and surface it, don't `--steal` past it.

