# Complete

> Drive a goal to a merge-ready endpoint with Codex orchestrating gpt-5.6-luna subagents, or the offload architect loop for external builder sessions. Run contextual plan reviews, delegate slices, judge raw gates plus an independent `review` pass per slice, and run a final integration `review`. STOPS at the edge of merge by default and hands to the human — only merges when the goal text explicitly authorizes it. You stay the ARCHITECT and never write implementation code. Use when the user says "complete", "drive this to done", "take this to merge-ready", "run the whole loop", or invokes /complete.

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

---


## Choose the orchestration route

**Default: Codex orchestrates; `gpt-5.6-luna` does delegated work.** In Codex,
read [codex-orchestration.md](codex-orchestration.md) and execute that workflow.
It replaces the external-session workflow below, including its dispatcher,
Claude session key, pane ledger, and shell wait bridge. Keep the parent Codex
model unchanged; select Luna explicitly when spawning each worker or reviewer.

Use the external-session workflow below only when the user requests external
builder sessions or runs this skill outside Codex. If native delegation or Luna
is unavailable, surface the limitation; do not silently change model or route.

## External-session workflow

You are the **ARCHITECT/ORCHESTRATOR**. `offload` is your single-turn engine;
the **BUILDER** is whichever harness `~/.borkweb-skills/config.json` resolves to
(offload's `harness.mjs` picks it, with quota-aware failover). You never write
implementation code — you spec
slices, judge raw gate numbers, and drive the loop to **merge-ready**. The human is
the final judge and the one who merges, unless the goal explicitly told you to.

`/complete <goal>` wraps `offload` (one architect turn) in a loop. Everything
`offload` does — handoff, arbitration, gate judging, builder block, dispatch —
still happens; this skill adds the loop, contextual plan reviews, council policy,
the wait bridge, the **slice ledger**, and the final readiness gate.

**Progress is read from `board`, never from memory.** A handoff doc is one
builder's mailbox; it cannot tell you the state of the other five, and it has no
way to say "this slice is finished." The ledger can. Every claim you make to the
human about what is running, done, or stuck comes from the `board` output of
that same turn.

## 1. Resolve goal + endpoint

**The default endpoint is merge-ready, NOT merged.** Build every slice, pass each
per-slice `review`, and land a clean final integration `review` — then STOP at the
edge of merge and hand to the human with the branch and the exact merge command.

Only set the endpoint to **merged** when the user's instructions explicitly
authorize it — phrases like "merge into main", "and merge", "land it", "all the
way to main", "merge it". A bare "complete", "to completion", "done", "ship it",
or "run the loop" does **not** authorize a merge → stop at merge-ready. When in
doubt, do not merge. Hold the goal text verbatim — you check remaining scope (and
whether merge was authorized) against it every turn.

## 2. Open the ledger — first, and before any slice exists

```
node "<…>/handoff.mjs" ledger init "$PWD" "<the goal, verbatim>"
```

This is the architect's own file, one per session, and **builders never write
it**. It is the only place a slice's terminal state lives. Each slice also gets
its own handoff doc (`handoff.mjs init "<worktree>" "$CLAUDE_CODE_SESSION_ID"`),
which is the builder's mailbox — spec in, gates out. Two different records; do
not conflate them.

Register a slice the moment you spec it, and update it the moment its state
changes:

```
node "<…>/handoff.mjs" ledger add "$PWD" --id 3 --title "inline axis widths" \
  --branch fix/inline-axis-widths --slice-cwd "<worktree>" --handoff "<doc>" --state specced
node "<…>/handoff.mjs" ledger set "$PWD" 3 --state dispatched --pane wY:p22
node "<…>/handoff.mjs" ledger set "$PWD" 3 --state accepted \
  --verdict "SAFE TO LAND · unit 2312/0/0 · integration 10/0/0" --pr <url>
```

States: `specced → dispatched → blocked | results-ready → accepted | rejected`,
plus `merged` and `abandoned`. **The four terminal states are the point.** A
slice you have accepted, rejected, merged or dropped MUST be written terminal in
the same turn you decide it. Skip that and it keeps reading as in-flight — which
is exactly how finished slices get reported as "still running" and how a slice
whose builder exited sits unowned for an hour with nobody on it.

## 3. Contextual plan review (before the first gates are frozen)

Pick the review(s) by what the *first* slice touches — more than one can apply:

- architecture / data flow / concurrency / migrations → `plan-eng-review`
- UI / visual / interaction → `plan-design-review`
- developer-facing surface (a contract someone else builds against: API, CLI,
  SDK, library, public interface, docs) → `plan-devex-review`

Trigger devex on *exposed/consumed* interfaces, not on code that is merely a CLI
internally. Skip reviews entirely for a trivial single-slice goal — say so. Fold
review outcomes into the slice spec you hand to `offload`.

## 4. Confirm autonomy posture — ONCE, up front

Ask the user a single time (AskUserQuestion):

- **Dispatch:** auto-dispatch each slice to the configured builder without re-asking, or pause for
  an OK each turn?
- **Merge** — ask this *only if* the goal authorized a merge (step 1): once the
  final review is clean, auto-merge, or pause for an OK first? If the goal did not
  authorize a merge, skip this question entirely — the endpoint is merge-ready and
  you will stop there regardless.

State the safety fact plainly: dispatch launches the builder with its permission
gates relaxed — codex `--dangerously-bypass-approvals-and-sandbox` (sandbox fully
off), opencode all-permissions config, grok `--always-approve`, claude
`--permission-mode auto` interactive / full bypass headless — with full local
access, merely launched from the repo dir. Name the flag for the harness actually
chosen. Record the chosen posture; honor it for the rest of the loop.

## 5. The loop — slices run concurrently; the board is the state

### Builder launch invariant — `dispatch.sh` only

Every builder launch MUST go through `offload` and its `dispatch.sh`. Concurrency
does not change the launcher; it only means several independently dispatched
slices may be alive at once. Invoke `offload` once for each slice and let its
documented dispatcher resolve the harness and frontend.

Never create a builder with raw `herdr agent start`, `herdr agent spawn`,
`herdr pane split`, `tmux new-window`, Terminal, or a headless harness command.
Those paths bypass offload's handoff marker, activity/turn-end wiring, duplicate
builder guard, harness failover, and herdr-tab guarantee. Raw herdr commands are
only for supervising a builder that `dispatch.sh` already launched: reading its
pane, sending a ruling, focusing its tab, or resuming its existing process.

Treat the dispatcher's success line as a postcondition before advancing slice
state. Inside herdr it must name `new herdr tab <tab_id>`, the workspace, and the
root pane. A pane id without a tab id is a failed dispatch. Do not set the slice
to `dispatched`, record its pane in the ledger, or arm its wait bridge; surface
the launch failure and correct it through `offload`.

**This invariant is enforced, not just stated.** `ledger add/set --pane` checks
the pane against dispatch.sh's signature — a herdr tab labeled `builder` holding
exactly one pane — and refuses a pane that lacks it, because a hand-started
builder has no builder marker, no activity sidecar and no turn-end hook, so its
wait bridge can never fire. `board` re-checks every live slice and flags
`UNMANAGED PANE`. If you hit the refusal, the answer is to re-dispatch through
`offload`, never `OFFLOAD_ALLOW_UNVERIFIED_PANE=1` — that override exists for a
frontend the check cannot see, not for routing around a failed dispatch.

### Per-slice loop

Slices that touch disjoint files run at the same time. There is no per-slice
turn-taking: each dispatched slice has its own worktree, its own handoff doc, and
its own wait bridge, and any of them can wake you. What makes that tractable is
that you never hold the fleet in your head.

```
EVERY WAKE, FIRST THING — no exceptions, including the wake that follows a
compaction:
   node "<…>/handoff.mjs" board "$PWD"
Act on what it prints, in this order:
   ! NO OWNER          → a dispatched slice whose builder exited with no bridge
                         armed. NOBODY IS WORKING IT. Decide now: judge what it
                         produced and mark it terminal, or dispatch a corrective
                         onto the same branch. Never leave a wake without
                         resolving every NO OWNER row.
   ! NEEDS ARCHITECT   → arbitrate (blocked) or judge the gates (results-ready).
   ! MISMATCH          → the builder's doc moved past your ledger. The doc is the
                         builder's word — act on it, then bring the ledger level.
   ! CORRUPT DOC       → rewrite the status; until then that slice's bridge is deaf.
   ! DOC MISSING       → STOP. Do not re-dispatch blindly; surface it.
   ! UNMANAGED PANE    → the recorded pane did not come from dispatch.sh. That
                         builder is unreachable by the bridge whatever its pane
                         shows. Judge whatever it produced, mark the slice
                         terminal, and re-dispatch through `offload`.
   (no flags)          → nothing to do on that slice.
```

Then, per slice:

```
a. Invoke the `offload` skill for ONE architect turn on the slice that needs it.
   It arbitrates, judges any ready results, specs the next slice (freezing gates),
   emits the builder block, sets status=dispatched, and dispatches the builder.
   Honor the dispatch posture from step 4 — in pause mode, get the user's OK
   before offload dispatches. Dispatch every slice that is ready to run and
   shares no files with one already in flight; only serialize on real conflict.
   "Dispatch" here always means offload's `dispatch.sh` path — never substitute a
   raw frontend or harness command.
b. After each valid dispatch — including the required tab-id postcondition inside
   herdr — `ledger set … --state dispatched --pane <pane>` and launch that slice's
   WAIT BRIDGE backgrounded (below). Then stop your turn — do NOT poll, do NOT
   ScheduleWakeup. The harness re-invokes you when a bridge exits.
c. On wake, run `board` (above), then read the background task's final `WAITER:`
   line and branch:
     WAITER: ready                       → go to (d)
     WAITER: builder-blocked …            → the builder flipped the handoff to `blocked`: it stopped on a
                                            mid-slice blocker and is waiting for a ruling. ARBITRATE NOW —
                                            follow "Arbitrating a stopped builder" below. This is not a
                                            judgment call and not a reason to wait.
     WAITER: builder-awaiting-input …     → the harness's own turn-end hook reported the builder stopped
                                            without flipping the handoff (blocked but didn't say so, hit a
                                            permission prompt, or finished without running `ready`).
                                            Same mandate: "Arbitrating a stopped builder" below.
     WAITER: builder-idle …               → no handoff flip, no turn-end, no session-log activity, no CPU.
                                            The idle detector now counts harness session-log writes as life,
                                            so a quiet verdict means the builder is not thinking, not
                                            streaming, and not writing — do NOT rationalize it as "just
                                            slow" and do NOT relaunch the bridge on a hunch. Treat it
                                            exactly like builder-awaiting-input.
     WAITER: builder-exited-without-ready → surface as a BLOCKER, pause, ask human
     WAITER: timeout …                    → surface, pause, ask human
     WAITER: never-started …              → no builder ever appeared: read the dispatch output for a launch failure, fix the cause, re-dispatch. Surface as a BLOCKER if it repeats
     WAITER: handoff-deleted …            → the handoff vanished mid-wait: STOP. Do not re-dispatch blindly — resolve the handoff path first, then ask the human
d. Run the `offload` engine again (step a) — its step 0 picks up status
   results-ready and judges the raw gates against the frozen gates. Read the
   verdict it produces.
e. Branch on the verdict. offload's per-slice verdict now bundles an independent
   `review` acceptance check, so a DO NOT LAND counts as a failure here:
     gates or review fail            → `ledger set … --state rejected --verdict "<why>"`;
                                       relay defects; offload specs a corrective slice → (a)
     gates pass + review clean       → `ledger set … --state accepted --verdict "<raw numbers>"`
     any scope left                  → offload specs the next slice → (a)
     goal met across all slices      → go to step 7 (finish)
   Write the terminal state in THIS turn, before you report anything to the human.
```

You are still inside this loop across every harness wake — a `WAITER:` line in a
background result means resume here, not start over.

### Reporting progress to the human

Every status you give — a table, a "still running", a "two are done" — is
assembled from the `board` output of the current turn. Do not narrate slice
state from what you remember dispatching; that memory is what goes stale, and it
does not survive a compaction. If a claim is not in this turn's board, either
run `board` again or say you do not know.

A reversal counts as a state change: if you accepted a slice and later found it
was wrong (CI red, a regression your gates missed), set it back to `rejected`
with the reason in the same turn you learn it. An `accepted` row that is no
longer true is worse than no row.

### Arbitrating a stopped builder (builder-blocked / builder-awaiting-input / builder-idle)

The lesson this procedure encodes: a builder once sat self-reported-blocked in
its pane for ~2 hours because the only wake signal was "results ready" and the
idle signal that DID fire was reasoned away as "probably just slow". The wake is
deterministic now; so is the response. In order, no steps skipped:

1. Read the builder pane (`herdr pane read <pane>` / `tmux capture-pane`) and
   the handoff's `## Open disagreements`
   (`handoff.mjs section get "$HANDOFF" "Open disagreements"`).
2. Rule on every open item — accept / reject / modify, with one-line reasons —
   and record the rulings with
   `handoff.mjs section append "$HANDOFF" "Decisions + why" --text '<rulings>'`
   (genuine judgment calls go through `council` first, per step 6). Use the
   `section` subcommands rather than hand-splicing the markdown: ad-hoc
   regex edits to the one file the loop trusts are how it gets corrupted.
3. Resume the builder: send the ruling into its pane. If it finished the slice
   but never ran `handoff.mjs ready`, tell it to report and stop.
4. Relaunch the bridge (same command as after a dispatch) and end your turn.

If after step 1 there is genuinely nothing to rule on — no disagreement, no
question, no prompt, pane mid-stream — relaunch the bridge once and say so in
your status line. If the same signal fires again, surface it as a BLOCKER and
ask the human. Never relaunch more than once without new evidence.

### The wait bridge (push, not poll)

The builder already ends its run with `handoff.mjs ready`, flipping the handoff
to `results-ready`. The bridge turns that file-write into a harness wake-up so the
Claude side never polls. After each dispatch, run:

```
Bash(run_in_background: true):
  H=$(node "<…>/offload/handoff.mjs" path "<the slice's worktree>" "$CLAUDE_CODE_SESSION_ID")
  bash "<core>/complete/wait-for-ready.sh" "$H" builder
```

One bridge per dispatched slice, each on that slice's own handoff. Re-derive the
path here — shell variables do not survive the harness wake between dispatch and
this bridge. Use `path <worktree> "$CLAUDE_CODE_SESSION_ID"` for a slice in a
worktree, or `resolve "$PWD"` for one in the repo root; both refuse when
`$CLAUDE_CODE_SESSION_ID` is empty — surface that, don't guess a path. Resolve
`<core>` as the parent of the offload dir from the SessionStart `[offload]` line
(i.e. `wait-for-ready.sh` sits next to the offload dir under `skills/core/`). The
script blocks — via `fswatch` when available, else a cheap detached sleep-poll —
until one of its deterministic signals fires, then exits with a final `WAITER:`
line. All of that runs outside your context: zero tokens until it wakes you. The
signals, strongest first:

- the handoff flips to `results-ready` (builder ran `handoff.mjs ready`) or
  `blocked` (builder ran `handoff.mjs blocked` on a mid-slice blocker);
- `"$H.turn-ended"` settles — dispatch wires the harness's own turn-end hook
  (codex `notify`, claude `Stop`/`Notification`) to touch it whenever a builder
  turn ends. That hook fires on every sub-turn, so the bridge treats a fresh
  marker as a *candidate* stop and confirms it only after `WFR_TURN_SETTLE`
  seconds (default 45) with no CPU growth and no session-log writes. A builder
  grinding through its gates no longer wakes you every few seconds; one that has
  genuinely stopped still wakes you in under a minute;
- idle backstop: builder pid alive but no session-log activity (the
  `"$H.activity"` sidecar dispatch writes) AND no CPU for the idle window;
- liveness/timeout backstops: builder died, never started, handoff deleted,
  or the overall deadline passed.

**Never `rm` the `.turn-ended` marker by hand.** The bridge baselines it at
start; deleting it races with `dispatch.sh` and with a running bridge, and eats
real wakes. If a bridge is waking you too often, raise `WFR_TURN_SETTLE` — do
not disable the signal.

## 6. Field questions through council

When you hit a genuine judgment call — an arbitration ruling, a scope dispute, a
design fork — field it through `council` before deciding, rather than guessing.
The builder block already instructs the builder to resolve its own ambiguity via
the bork:council skill before coding (see the offload builder block).

## 7. Finish — merge-ready by default, merge only if authorized

Reached when **every ledger row is terminal**, every accepted slice's gates
passed and its per-slice `review` was clean, AND the goal text is fully delivered
by commits pushed to the branch. Run `board` and confirm that before anything
else: a non-terminal row means you are not finished, however complete it feels.

1. **Final integration review — the readiness gate.** Run `review` on the FULL
   branch diff against the base; per-slice reviews can't see cross-slice integration
   issues. Treat the verdict as the gate:
     - **SAFE TO LAND** → ready.
     - **LAND WITH CAUTION** → ready, but carry the caveats into the report.
     - **DO NOT LAND** → not ready. Feed the blockers back as a corrective slice
       (return to step 5) and re-run this gate after it lands clean.
   Nothing is merge-ready until this gate is green.

   A per-slice gate set proves only what it measured. Before accepting the last
   slice, check what the repo's own CI runs that your frozen gates did not — a
   green local suite next to a red pipeline is a failed slice, not a passed one.

2. **Default — STOP at merge-ready. Do NOT merge.** Report and hand to the human:
     - goal, slices shipped, commit SHAs, final gate + review verdicts (and any
       LAND WITH CAUTION caveats) — taken from `board`, not from recollection;
     - the branch name and the exact merge command they can run.
   Leave the handoff and the ledger in place for the human's follow-up; do not
   end them.

3. **Merge — ONLY if the goal explicitly authorized it (step 1).** Then: confirm
   unless the user pre-authorized auto-merge in step 4; integrate via the repo's
   standard path (the builder already commits + pushes each slice, so this is the
   PR merge / fast-forward, not new code — prefer `gh` for a GitHub repo); set
   each merged slice `--state merged`; end the handoffs
   (`handoff.mjs end "$HANDOFF"`); report as in step 2 plus the merge result.

## Hard rules

- You never write implementation code. If tempted, hand a tighter slice to offload.
- **Every builder launch goes through `offload` and `dispatch.sh`.** Never create
  one with raw `herdr agent start`/`spawn`, `herdr pane split`, tmux, Terminal, or
  a headless harness command. A herdr pane id without a tab id is a failed launch,
  not a dispatched slice. The ledger refuses a pane that did not come from
  dispatch.sh, and `board` flags one that slipped in — treat either as a failed
  dispatch to redo, not an obstacle to override.
- **Run `board` at the start of every wake, and report only what it says.** Slice
  state narrated from memory is the single largest source of wrong progress: it
  cannot see a builder that exited, and it does not survive a compaction.
- **Write terminal states as you decide them**, in the same turn — including
  reversals. A slice with no terminal state reads as in-flight forever.
- **Resolve every NO OWNER row before your turn ends.** Either judge what the
  slice produced and mark it terminal, or dispatch a corrective. Diagnosing
  without dispatching leaves the slice with nobody on it.
- Never poll or ScheduleWakeup for the builder — the wait bridge wakes you. A short-poll
  ScheduleWakeup here is wasted work.
- Never hand-delete a `.turn-ended` marker or hand-splice a handoff's markdown.
  Use `WFR_TURN_SETTLE` and the `section` subcommands.
- Verdicts come from raw gate numbers vs frozen gates — never the builder's prose.
  The builder's `## Work summary` is uncontrolled text that goes stale; it is for
  continuation, never for status.
- Nothing is merge-ready until every slice passed `review` and the final integration
  `review` returns no DO NOT LAND. The review gate is not optional or skippable.
- Never edit frozen gates after results exist (offload enforces this; don't route
  around it).
- **Default is stop-at-merge-ready. Never merge unless the goal text explicitly
  authorized it.** When the instruction is ambiguous about merging, stop and hand
  to the human — do not merge.
- The handoffs and the ledger are session-scoped and never committed. Don't `git add` them.
- Re-derive each handoff path via `resolve`/`path` on every wake — never carry a
  stale path or write to a handoff you don't own. An ownership `refusing:` error
  from the CLI is the wrong-document tripwire: STOP and surface it, never
  `--steal` past it.

