handback — end an arc with a work order, not a lab notebook
Spec: broomva/workspace → docs/specs/2026-08-18-agent-handback-contract.html (BRO-2179).
Why this skill exists
Measured over 994 Claude Code transcripts (analysis set: 100 sessions of ≥6h
wall-clock and ≥150 assistant turns):
|
|
| Long arcs whose final message mentions a blocker at all |
31 / 100 |
| Long arcs halting on a human (terminal-stance phrasing) |
18 / 100 |
| …of those 18, containing a conforming ask block |
0 / 18 |
| …containing an imperative anywhere in the message |
3 / 18 |
…containing any ? at all |
13% |
| …with the question next to the blocker it describes |
6% |
| Stating a default if the human stays silent |
5% of all 100 |
| Ever emitting a push notification |
0 / 100 |
| Blockers knowable before the arc started |
81% |
The two blocker rows differ on purpose. The broad count (31) includes messages
that merely mention being blocked — "the pre-commit hook blocked it", "#381
remains blocked by the hang". The narrow count (18) is the defensible one: a
terminal message that stops the arc on a person. The enforcement gate keys on
the narrow definition, because refusing a healthy receipt that happens to contain
the word "blocked" would fight the operator.
The ask usually exists. It is placed last, written in the indicative ("Not
merged — auto-merge correctly blocked"), and carries no default, so the arc
dies on it — median 2.4h of stalled arc-time behind such a message, 25 of them
overnight-length.
Rule zero — asking is the last resort, not the interface
No ask may exist until every rung below has been tried and recorded. A row
with an empty exhausted list is a defect, not a question.
| Rung |
Try this |
|
| 1 · Read |
Is the answer already on disk — ticket history, prior conversations, the knowledge graph, the handoff doc, the code? |
Most "decisions" are recoverable, not new |
| 2 · Standing grant |
Has this exact question been answered before? Check .control/preauth.yaml |
Re-asking a settled question is the cheapest failure to eliminate |
| 3 · Delegate |
Can a fresh agent resolve it — a reader, a researcher, an adversarial reviewer with different context? |
Chain agents instead of chaining to the human |
| 4 · Reroute |
Can another lane, worktree, fan-out, or approach go around it? |
The blocker is often a path, not a wall |
| 5 · Loop |
Can iteration resolve it — retry under a changed assumption, let a persist loop converge? |
Slow beats blocked when nobody is awake |
| 6 · Default |
Is the choice reversible? Take it, log it, tell the human afterwards. Do not ask. — "log it" means an entry in the ledger's decisions:, and "tell the human" means the 🔀 block below |
Reversible decisions are not the human's to make in real time |
| 7 · Ask |
Only now. Record which rungs were tried and why each failed. |
|
Rung 6 is where most asks should die. A choice that can be undone with one
commit does not warrant stopping an arc; it warrants a line in the receipt.
Only actions that are irreversible, externally visible, or spend
money may reach rung 7 without a default — and the ask must say which.
Rule minus-one — never stop the arc while an unblocked lane remains
Blocked is a lane state, not an arc state. When an ask fires: park the
lanes it gates, emit the ask (push it if outside the waking window), and
continue on the next unblocked lane. Run handback only when the unblocked set
is empty. An arc that halts with runnable work left is the failure this skill
exists to prevent.
The shape — six blocks, always in this order
"Always in this order" constrains order, never presence. A block with no
content is omitted, not rendered empty — an empty 🔀 block would assert that
nothing was decided, and an empty ⛔ block would assert that nothing is blocked.
Only the receipt is unconditional.
## ⛔ Blocked on you — N items, ~M min
table: # | ask (imperative, addressed to "you") | unblocks | if you say nothing
a decision row lists options A/B/C, one marked suggested, each with its cost
plain language only; hard cap 7 rows, ranked by what each unblocks
## ▶ Running meanwhile
what is still executing, or what ran with zero input from you
## ✅ Shipped
PR table. One line each. No narrative.
## 🔀 Decided for you — N choices, least-confident first
table: # | what I decided | why it was mine to take | undo
rendered from the ledger's `decisions:` list, never free prose
every row carries its one-line reversal — that is what made it safe not to ask
hard cap 7, same as the ask block; overflow counted in the header
## 📋 Receipt — the nine items, unchanged
1 dep chain · 2 plan vs done · 3 parallel streams · 4 files changed · 5 PRs
6 deploys · 7 validation · 8 merge result · 9 follow-ups
## 📎 Detail → docs/handoffs/<arc>.md
the NARRATIVE lives there: what happened and why, the review sagas, the
corrections. That prose is what this contract displaces.
The ten rules
Each is derived from a measured failure, not a style preference.
- The ask block is first. Not a "Your call" section at 80% depth.
Measured: the ask lands around p80 of the message.
- Every row is an imperative addressed to "you" — a command to run, a link
to click, a value to paste, or a one-line answer to a closed question.
Measured: 0 of the 18 halting messages contained a conforming ask block.
- A decision row offers options, not an open question. Two or three
concrete choices, each with its consequence in one line, one marked as the
recommendation. Never "what should we do about X?" — always "A, B, or C; I
suggest B because …".
- Plain language, no internal vocabulary. No primitive numbers, no gate
names, no acronyms, no ticket ID standing in for the question. If a term
needs the spec to understand, it does not belong in an ask.
- Every row carries a default. "If you say nothing, I do X." A row with no
safe default must say so explicitly and name what it costs.
Measured: 5% stated a default. Silence is otherwise fatal.
- Every row is self-contained. Answerable without opening a ticket. The
ticket ID is a reference for later, never the carrier of the question.
Measured: 58% used a ticket as the carrier; median 1 ticket, up to 13.
- Rows are ranked by what they unblock, stated in the row — "unblocks 9 of
18 rows", "unblocks 1 lane". A tired person answering one thing should be
answering the right one.
- Hard cap: 7 rows. More than seven means the arc should have asked
earlier. Overflow goes to the ledger, with the total stated in the header
("3 of 11 shown").
- The nine-item receipt stays, underneath the asks. It is structured and
proves the work. What this contract displaces is the free-form narrative
— that moves to
docs/handoffs/.
- The ask block ends with the reply already drafted — one line per row,
numbered to match. A copy-pasteable
skeleton, one line per row, recommendation pre-filled — so answering is an
edit, not a composition. Rules 2–6 fix what a row says; none of them
lowers the cost of replying, and a perfectly-formed ask still stalls if
answering it means composing prose at 1am. Reacting beats imagining: hand
the reader something to strike through, not a blank box. The skeleton is
generated from the rows: if it has four lines and the table has five, the
fifth ask is one the reader is never prompted to answer — a silent drop
dressed as a convenience.
The 🔀 block — what rung 6 owes the human
Rung 6 is where most asks are supposed to die: take the reversible default, log
it, tell the human afterwards. For as long as this contract defined five blocks,
there was nowhere for that last clause to land — the ladder told the arc to decide
and say so, and the message shape had no room for it. Every silent-but-correct
decision therefore looked identical to no decision at all, while the user still
inherited it.
The 🔀 block is that room. It is not an ask block: nothing in it is a
question, and the arc did not wait for any of it.
| Property |
Why |
Rendered by ask_ledger.py decisions --render-handback, not written free-hand |
The rules below — ordering, the undo, the cap, the overflow header — are executable there and tested. Stated only in prose they are four things an agent forgets one at a time |
| An irreversible choice may not appear here as a settled default |
Rung 6 licenses reversible choices only. Recording an irreversible one is still right, but the schema forces it to carry verdict: needs-user and name the ask that took it to the human |
A needs-user row is excluded from this block |
It reached the human, so it belongs in ⛔ keyed by its ask. Rendering it in both would print one item twice under contradictory headings — "decided for you" and "blocked on you" — which this workspace's own arc ledger did before the renderer excluded them |
| Least-confident first |
Confidence ranks; verdict groups. The reader's attention is finite and should land on the choice most likely to be wrong, not the one that happened to be made first |
| Every row shows its undo |
The reversal is the entire justification for not having asked. A row that cannot state one is a row that should have been an ask |
| Hard cap 7, overflow counted |
Same discipline as the ask block. More than seven means the plan was foggy — the clustering is the signal, and the fix is upstream in the spec, not a longer table |
Where each verdict surfaces, so nothing is duplicated and nothing is dropped:
| Verdict |
Block |
Why |
sound |
🔀 |
Decided and settled. The user owns it now |
unsound |
🔀, with its corrected decision |
Decided, and known to need redoing |
needs-user |
⛔ only, via its ask |
It was not decided for them — it was asked |
Consequence worth stating plainly: because the schema forces an irreversible
choice to be needs-user, no valid ledger can render one in 🔀 at all. The
renderer keeps a defensive branch for a ledger nobody validated — an empty undo
column would read as "no undo needed", the opposite of the truth — but on a valid
ledger that branch is unreachable.
sound is not skippable. The temptation is to list only the shaky calls, but
a confident decision the user never made is still architecture the user now owns.
Trivial discretion — internal naming, cosmetic calls — compresses to a one-line
count; everything load-bearing gets a row.
Anti-patterns
| Rationalization |
Why it fails |
| "I'll describe the blocker; they'll know what to do" |
A statement about the world transfers no obligation. Zero of 31 measured messages contained a request addressed to a person. |
| "The ticket explains it" |
The reader has no context loaded and may be on a phone. The ticket was written by an agent for an agent. |
| "It's a big decision, I shouldn't presume a default" |
Defaults are for reversible choices, and most are. Reserve no-default for irreversible / externally visible / money-spending. |
| "I'll list everything so they have full context" |
Seven rows maximum. More means the ladder was not climbed or the hour-zero batch was skipped. |
| "I'll ask now and keep working after they answer" |
Wrong order. Park the gated lanes and keep working now; the answer arrives whenever it arrives. |
| "Asking is safer than assuming" |
Asking is expensive — median 2.4h of stalled arc-time, 25 overnight stalls in the measured corpus. Rung 6 exists for this. |
| "I'll write the narrative first so they understand the ask" |
That ordering is the measured defect. Narrative goes to the handoff doc; the ask goes first. |
| "I took a reversible default, so there is nothing to report" |
Rung 6 is a licence to not ask, not a licence to not tell. The whole clause is "take it, log it, tell the human afterwards." A decision with no record is one the user inherits without ever seeing. |
| "The 🔀 rows are really questions — I'll move them into the ask block" |
Then the ladder was not climbed. If it still needs the human it never reached rung 6; if it reached rung 6 it is settled and reversible. Rows do not belong in both. |
| "I'll list only the decisions I'm unsure about" |
sound is not skippable. A confident call the user never made is still architecture they now own — the point is disclosure, not confession. |
| "They can just tell me what they want in their own words" |
That is the blank box rule 10 exists to remove. An answer that requires composing prose costs more than one that requires striking a line through, and the measured stall is hours, not minutes. |
Composition
handoff — same arc, different reader. handoff writes
docs/handoffs/YYYY-MM-DD-<arc>.md for the next agent; handback writes
the chat message for the human. A stopping arc usually produces both, and
the handback's Detail block links to the handoff.
autonomous — supplies the nine-item receipt that block 4 renders, and
owns the pre-flight that builds the ask ledger.
persist / governed-autonomy-loop — a parked lane is a lane the next
iteration's PROMPT.md omits; handback fires only when none remain.
References
references/template.md — copy-paste skeleton with a worked example.
1---2name: handback3description: Terminal-message contract for an autonomous arc that has run out of agent-executable work and genuinely needs a human. Produces a WORK ORDER — asks first, each one an imperative addressed to the reader, carrying a default so silence is never fatal (the exception is narrow and must be named: irreversible, externally visible, or spends money) — followed by the unchanged nine-item receipt. Distinct from `handoff` (same arc, different reader: `handoff` writes `docs/handoffs/` for the NEXT AGENT; `handback` writes the chat message for the HUMAN AS DECISION-MAKER). Asking is the last resort, not the interface: no ask may exist until the seven-rung autonomy ladder has been climbed and recorded. Use when: (1) an autonomous arc is stopping and at least one ask is open, (2) the user asks "what do you need from me" / "what's blocking", (3) a lane has parked on a human dependency and the arc is notifying, (4) writing any message whose purpose is to obtain a decision. Triggers on "handback", "hand back", "what do you need4---56# handback — end an arc with a work order, not a lab notebook78**Spec:** `broomva/workspace` → `docs/specs/2026-08-18-agent-handback-contract.html` (BRO-2179).910## Why this skill exists1112Measured over 994 Claude Code transcripts (analysis set: 100 sessions of ≥6h13wall-clock and ≥150 assistant turns):1415| | |16|---|---|17| Long arcs whose final message mentions a blocker at all | 31 / 100 |18| Long arcs **halting on a human** (terminal-stance phrasing) | **18 / 100** |19| …of those 18, containing a conforming ask block | **0 / 18** |20| …containing an imperative anywhere in the message | 3 / 18 |21| …containing any `?` at all | 13% |22| …with the question next to the blocker it describes | 6% |23| Stating a default if the human stays silent | 5% of all 100 |24| Ever emitting a push notification | **0 / 100** |25| Blockers knowable before the arc started | **81%** |2627The two blocker rows differ on purpose. The broad count (31) includes messages28that merely *mention* being blocked — "the pre-commit hook blocked it", "#38129remains blocked by the hang". The narrow count (18) is the defensible one: a30terminal message that stops the arc **on a person**. The enforcement gate keys on31the narrow definition, because refusing a healthy receipt that happens to contain32the word "blocked" would fight the operator.3334The ask usually exists. It is placed last, written in the indicative ("Not35merged — auto-merge correctly blocked"), and carries no default, so the arc36dies on it — median 2.4h of stalled arc-time behind such a message, 25 of them37overnight-length.3839## Rule zero — asking is the last resort, not the interface4041**No ask may exist until every rung below has been tried and recorded.** A row42with an empty `exhausted` list is a defect, not a question.4344| Rung | Try this | |45|---|---|---|46| 1 · Read | Is the answer already on disk — ticket history, prior conversations, the knowledge graph, the handoff doc, the code? | Most "decisions" are recoverable, not new |47| 2 · Standing grant | Has this exact question been answered before? Check `.control/preauth.yaml` | Re-asking a settled question is the cheapest failure to eliminate |48| 3 · Delegate | Can a fresh agent resolve it — a reader, a researcher, an adversarial reviewer with different context? | Chain agents instead of chaining to the human |49| 4 · Reroute | Can another lane, worktree, fan-out, or approach go around it? | The blocker is often a path, not a wall |50| 5 · Loop | Can iteration resolve it — retry under a changed assumption, let a persist loop converge? | Slow beats blocked when nobody is awake |51| 6 · Default | Is the choice reversible? **Take it, log it, tell the human afterwards. Do not ask.** — "log it" means an entry in the ledger's `decisions:`, and "tell the human" means the 🔀 block below | Reversible decisions are not the human's to make in real time |52| 7 · Ask | Only now. Record which rungs were tried and why each failed. | |5354**Rung 6 is where most asks should die.** A choice that can be undone with one55commit does not warrant stopping an arc; it warrants a line in the receipt.56Only actions that are **irreversible**, **externally visible**, or **spend57money** may reach rung 7 without a default — and the ask must say which.5859## Rule minus-one — never stop the arc while an unblocked lane remains6061Blocked is a **lane** state, not an **arc** state. When an ask fires: park the62lanes it gates, emit the ask (push it if outside the waking window), and63continue on the next unblocked lane. Run `handback` only when the unblocked set64is empty. An arc that halts with runnable work left is the failure this skill65exists to prevent.6667## The shape — six blocks, always in this order6869*"Always in this order" constrains **order**, never presence.* A block with no70content is omitted, not rendered empty — an empty 🔀 block would assert that71nothing was decided, and an empty ⛔ block would assert that nothing is blocked.72Only the receipt is unconditional.7374```75## ⛔ Blocked on you — N items, ~M min76 table: # | ask (imperative, addressed to "you") | unblocks | if you say nothing77 a decision row lists options A/B/C, one marked suggested, each with its cost78 plain language only; hard cap 7 rows, ranked by what each unblocks7980## ▶ Running meanwhile81 what is still executing, or what ran with zero input from you8283## ✅ Shipped84 PR table. One line each. No narrative.8586## 🔀 Decided for you — N choices, least-confident first87 table: # | what I decided | why it was mine to take | undo88 rendered from the ledger's `decisions:` list, never free prose89 every row carries its one-line reversal — that is what made it safe not to ask90 hard cap 7, same as the ask block; overflow counted in the header9192## 📋 Receipt — the nine items, unchanged93 1 dep chain · 2 plan vs done · 3 parallel streams · 4 files changed · 5 PRs94 6 deploys · 7 validation · 8 merge result · 9 follow-ups9596## 📎 Detail → docs/handoffs/<arc>.md97 the NARRATIVE lives there: what happened and why, the review sagas, the98 corrections. That prose is what this contract displaces.99```100101## The ten rules102103Each is derived from a measured failure, not a style preference.1041051. **The ask block is first.** Not a "Your call" section at 80% depth.106 *Measured: the ask lands around p80 of the message.*1072. **Every row is an imperative addressed to "you"** — a command to run, a link108 to click, a value to paste, or a one-line answer to a closed question.109 *Measured: 0 of the 18 halting messages contained a conforming ask block.*1103. **A decision row offers options, not an open question.** Two or three111 concrete choices, each with its consequence in one line, one marked as the112 recommendation. Never "what should we do about X?" — always "A, B, or C; I113 suggest B because …".1144. **Plain language, no internal vocabulary.** No primitive numbers, no gate115 names, no acronyms, no ticket ID standing in for the question. If a term116 needs the spec to understand, it does not belong in an ask.1175. **Every row carries a default.** "If you say nothing, I do X." A row with no118 safe default must say so explicitly and name what it costs.119 *Measured: 5% stated a default. Silence is otherwise fatal.*1206. **Every row is self-contained.** Answerable without opening a ticket. The121 ticket ID is a reference for later, never the carrier of the question.122 *Measured: 58% used a ticket as the carrier; median 1 ticket, up to 13.*1237. **Rows are ranked by what they unblock**, stated in the row — "unblocks 9 of124 18 rows", "unblocks 1 lane". A tired person answering one thing should be125 answering the right one.1268. **Hard cap: 7 rows.** More than seven means the arc should have asked127 earlier. Overflow goes to the ledger, with the total stated in the header128 ("3 of 11 shown").1299. **The nine-item receipt stays, underneath the asks.** It is structured and130 proves the work. What this contract displaces is the **free-form narrative**131 — that moves to `docs/handoffs/`.13210. **The ask block ends with the reply already drafted — one line per row,133 numbered to match.** A copy-pasteable134 skeleton, one line per row, recommendation pre-filled — so answering is an135 *edit*, not a composition. Rules 2–6 fix what a row **says**; none of them136 lowers the cost of **replying**, and a perfectly-formed ask still stalls if137 answering it means composing prose at 1am. Reacting beats imagining: hand138 the reader something to strike through, not a blank box. The skeleton is139 generated *from the rows*: if it has four lines and the table has five, the140 fifth ask is one the reader is never prompted to answer — a silent drop141 dressed as a convenience.142143## The 🔀 block — what rung 6 owes the human144145Rung 6 is where most asks are supposed to die: *take the reversible default, log146it, tell the human afterwards.* For as long as this contract defined five blocks,147there was nowhere for that last clause to land — the ladder told the arc to decide148and say so, and the message shape had no room for it. Every silent-but-correct149decision therefore looked identical to no decision at all, while the user still150inherited it.151152The 🔀 block is that room. It is **not** an ask block: nothing in it is a153question, and the arc did not wait for any of it.154155| Property | Why |156|---|---|157| Rendered by `ask_ledger.py decisions --render-handback`, not written free-hand | The rules below — ordering, the undo, the cap, the overflow header — are executable there and tested. Stated only in prose they are four things an agent forgets one at a time |158| An **irreversible** choice may not appear here as a settled default | Rung 6 licenses reversible choices only. Recording an irreversible one is still right, but the schema forces it to carry `verdict: needs-user` and name the ask that took it to the human |159| A `needs-user` row is **excluded** from this block | It reached the human, so it belongs in ⛔ keyed by its `ask`. Rendering it in both would print one item twice under contradictory headings — "decided for you" and "blocked on you" — which this workspace's own arc ledger did before the renderer excluded them |160| **Least-confident first** | Confidence ranks; verdict groups. The reader's attention is finite and should land on the choice most likely to be wrong, not the one that happened to be made first |161| Every row shows its **undo** | The reversal is the entire justification for not having asked. A row that cannot state one is a row that should have been an ask |162| Hard cap 7, overflow counted | Same discipline as the ask block. More than seven means the plan was foggy — the clustering *is* the signal, and the fix is upstream in the spec, not a longer table |163164**Where each verdict surfaces**, so nothing is duplicated and nothing is dropped:165166| Verdict | Block | Why |167|---|---|---|168| `sound` | 🔀 | Decided and settled. The user owns it now |169| `unsound` | 🔀, with its corrected decision | Decided, and known to need redoing |170| `needs-user` | **⛔ only**, via its `ask` | It was not decided for them — it was asked |171172Consequence worth stating plainly: because the schema forces an irreversible173choice to be `needs-user`, no valid ledger can render one in 🔀 at all. The174renderer keeps a defensive branch for a ledger nobody validated — an empty undo175column would read as "no undo needed", the opposite of the truth — but on a valid176ledger that branch is unreachable.177178**`sound` is not skippable.** The temptation is to list only the shaky calls, but179a confident decision the user never made is still architecture the user now owns.180Trivial discretion — internal naming, cosmetic calls — compresses to a one-line181count; everything load-bearing gets a row.182183## Anti-patterns184185| Rationalization | Why it fails |186|---|---|187| "I'll describe the blocker; they'll know what to do" | A statement about the world transfers no obligation. Zero of 31 measured messages contained a request addressed to a person. |188| "The ticket explains it" | The reader has no context loaded and may be on a phone. The ticket was written by an agent for an agent. |189| "It's a big decision, I shouldn't presume a default" | Defaults are for **reversible** choices, and most are. Reserve no-default for irreversible / externally visible / money-spending. |190| "I'll list everything so they have full context" | Seven rows maximum. More means the ladder was not climbed or the hour-zero batch was skipped. |191| "I'll ask now and keep working after they answer" | Wrong order. Park the gated lanes and keep working *now*; the answer arrives whenever it arrives. |192| "Asking is safer than assuming" | Asking is expensive — median 2.4h of stalled arc-time, 25 overnight stalls in the measured corpus. Rung 6 exists for this. |193| "I'll write the narrative first so they understand the ask" | That ordering is the measured defect. Narrative goes to the handoff doc; the ask goes first. |194| "I took a reversible default, so there is nothing to report" | Rung 6 is a licence to **not ask**, not a licence to **not tell**. The whole clause is "take it, log it, tell the human afterwards." A decision with no record is one the user inherits without ever seeing. |195| "The 🔀 rows are really questions — I'll move them into the ask block" | Then the ladder was not climbed. If it still needs the human it never reached rung 6; if it reached rung 6 it is settled and reversible. Rows do not belong in both. |196| "I'll list only the decisions I'm unsure about" | `sound` is not skippable. A confident call the user never made is still architecture they now own — the point is disclosure, not confession. |197| "They can just tell me what they want in their own words" | That is the blank box rule 10 exists to remove. An answer that requires composing prose costs more than one that requires striking a line through, and the measured stall is hours, not minutes. |198199## Composition200201- **`handoff`** — same arc, different reader. `handoff` writes202 `docs/handoffs/YYYY-MM-DD-<arc>.md` for the next *agent*; `handback` writes203 the chat message for the *human*. A stopping arc usually produces both, and204 the handback's Detail block links to the handoff.205- **`autonomous`** — supplies the nine-item receipt that block 4 renders, and206 owns the pre-flight that builds the ask ledger.207- **`persist` / `governed-autonomy-loop`** — a parked lane is a lane the next208 iteration's `PROMPT.md` omits; `handback` fires only when none remain.209210## References211212- `references/template.md` — copy-paste skeleton with a worked example.