Handoff gate¶
Use this reference whenever a workflow skill asks the user to choose the next step after a gate.
Contract¶
A handoff gate prevents silent dispatch. It does not mean the agent stops after the user selects an option. Once the user chooses a non-stop option, the current assistant turn immediately starts the selected action โ either dispatching a downstream skill (skill transition) or continuing internal work in the current skill (in-skill continuation).
"Never auto-invoke" means no downstream skill starts before an explicit user selection. It is not permission to answer only with "next: /some-skill" after the user has already selected that option.
Vocabulary¶
- Dispatch โ start a new skill with a concrete command. Reserved for skill transitions.
- Continue / proceed โ keep working inside the current skill (e.g. write a manifest, ask one targeted follow-up, re-run an internal phase). Never write
dispatch:for in-skill continuation; usecontinue:instead. - Stop / pause โ return a final status with no further action.
Gate shape¶
Before asking, render each option as a structured record. The host UI may show this as buttons, a structured question, or a numbered list, but the semantics must be stable. The top-level key is handoff_gate: to distinguish it from per-option context payloads (handoff_context: โ see below):
handoff_gate:
source_skill: /<current-skill>
recommended: <label-or-none>
options:
- label: Run /press <slug>
dispatch: /press <slug>
context:
slug: <slug>
source_report: .cheese/cook/<slug>.md
flags: []
- label: Modify decomposition
continue: ask-for-decomposition-change
context:
scope: current-skill
- label: Stop
dispatch: none
context:
reason: leave pipeline paused
Every option must include:
- Label โ the user-facing choice.
- Exactly one of:
- Dispatch โ the exact command for a skill transition (
/press <slug>,/age <slug> --hard, โฆ), including slug/path/scope and propagated flags such as--hard. - Continue โ a short identifier for an in-skill action the current skill knows how to execute (e.g.
ask-for-decomposition-change,re-run-decomposer,write-manifest-then-seed). dispatch: noneโ terminal options (Stop, Pause, Compact) that return a final status and do not start another skill.- Context โ any prose or structured payload the action needs but that is not part of the command line.
- On select โ execute the dispatch or continue action immediately after the user selects it.
dispatch: none is for terminal options only. Options that keep the current skill running must use continue:, not dispatch: none, so the gate reader can tell "stop" apart from "do something else in this skill".
Host routing guide¶
The handoff_gate: block is the semantic source of truth. After building it,
choose the richest question mechanism the current harness actually exposes;
never name a host tool in the transcript unless it is callable in that session.
| Harness | Prefer | Notes |
|---|---|---|
| Claude Code | AskUserQuestion |
Supports questions[] with question, short header, options[], and optional multiSelect; hooks can fill answers via updatedInput. Source: Claude Code hooks reference. |
| Codex / OpenAI app-server | request_user_input / tool/requestUserInput when exposed |
OpenAI's app server documents tool/requestUserInput for 1-3 short questions and free-form isOther options. In Codex CLI, use request_user_input only when the active tool list and current collaboration mode both allow it; otherwise fall back to numbered text. Source: Codex app-server reference. |
| Conductor | Underlying agent primitive | Conductor runs Claude Code or Codex sessions; route to that agent's question primitive. Conductor Plan Mode exists for both, but Conductor is not a separate question API. Source: Conductor agent modes. |
| OpenCode | question tool |
The built-in question tool asks during execution with header, question text, options, and custom answers; ensure permission.question is not denied. Source: OpenCode tools. |
| GitHub Copilot CLI | ask_user tool |
Copilot CLI lists ask_user as "Ask the user a question" and --no-ask-user disables it. Use it when available; otherwise numbered text. Source: Copilot CLI command reference. |
| Gemini CLI | ask_user tool |
Google codelab output lists Ask User (ask_user) in /tools; use it when present. Source: Gemini CLI codelab. |
| Cursor CLI / ACP | cursor/ask_question when exposed |
Cursor ACP documents cursor/ask_question as a blocking extension method; use it only inside hosts that expose that ACP method. Source: Cursor ACP docs. |
| Windsurf Cascade | Plan-mode interactive questions when in Plan Mode | Cascade Plan Mode can ask clarifying questions and present multiple options with an interactive interface. Outside that mode, fall back to numbered text unless a host tool is exposed. Source: Cascade modes. |
| MCP server flows | elicitation/create |
Use only when an MCP server is requesting user input through a client that supports elicitation. It is not a general assistant-to-user question primitive. Source: MCP elicitation. |
| Aider and unknown harnesses | Numbered text | If no structured primitive is visible, ask a plain numbered question and wait for the next user reply. |
Portable fallback format¶
Question: <one short question>
Recommended: <label> โ <why>
1. <label> โ <effect/tradeoff>
2. <label> โ <effect/tradeoff>
3. <label> โ <effect/tradeoff>
Other: reply with `other: <short answer>`
Use the option label from handoff_gate.recommended; do not assume the
recommended option is numbered 1 unless you deliberately rendered that label
as option 1 in this fallback.
Keep gates small: one decision by default, at most three questions when the
host primitive explicitly supports batching. Include a free-form Other path
when the host supports it; otherwise spell out the other: fallback in text.
After the answer arrives¶
- Normalize the answer to one option label or recognized free-text verb.
- If the answer is ambiguous, ask one clarifying question; do not guess.
- If the selected option has
dispatch: none, stop with the relevant artifact path or pause status. - If the selected option has a
continue:identifier, execute that in-skill action immediately. - If the selected option has a
dispatch:command, immediately enter that skill with the exact command and context packet. - Do not re-run
/cheeseclassification unless the selected option explicitly says to do so.
Context payloads¶
Use context payloads when command-line flags would create an unstable mini-language. Payloads ride alongside the gate under the key handoff_context: so the downstream skill can tell them apart from the gate shape itself:
handoff_context:
source_skill: /age
source_report: .cheese/age/<slug>.md
selection: "1,3,5"
resolved_ids: [1, 3, 5]
Examples of when to attach a handoff_context: block:
/age -> /cureselection ids travel as context, not as a--selectflag./culture -> /cookcarries the compact contract that emerged from discussion./melt -> upstream skillcarries the interrupted operation and original skill invocation.
Keep payloads short and factual. If a payload would exceed a compact screenful, write or reference a .cheese/.../<slug>.md handoff artifact and pass the path instead.
Flag propagation¶
Propagate --hard through every runnable downstream option while the flag is in scope. Propagate --auto inside documented auto-mode chains and inside /cheese's autonomous-by-default dispatch path (see skills/cheese/SKILL.md ยง Escalation โ tier-1 and tier-2 dispatches pre-select the auto variant and run it without a gate unless --safe is set).
Propagate --safe and --open-pr through every runnable downstream option while in scope. --safe re-introduces the gates that the autonomous default skips โ the /age / /affinage cure-selection and /cure's PR push. It only has meaning for skills that have such a gate to re-introduce: /age, /affinage, and /cure. It does not turn a --auto chain interactive (the two flags are opposites) โ /cook --auto and /press --auto have no selection or push gate of their own, so they neither declare nor forward --safe; a /cheese --safe route that dispatches a --auto variant gates only /cheese's own dispatch decision, then runs the chain headless. --open-pr rides all the way to the terminal /cure, authorizing a clean cure to open a new PR when none exists (the default only pushes an already-open one); inside the --auto chain it is threaded through each invocation (/cook โ /press โ /age โ /cure).
Outside those autonomous paths, interactive gates must not add --auto unless the option explicitly says --auto and the user selected it. Inside them, the auto variant is the pre-selected recommended target by design โ --safe is the user's opt-out to a gated flow, where the auto variant remains pre-selected but dispatch waits for confirmation.