--- name: hitl-escalate description: Escalate blocked runs to a human via configured channel or fallback to BLOCKED.md and exit the loop. Use when the loop hits an ambiguous spec, a missing credential, a destructive action needing approval, or 3+ consecutive verify failures on the same task. when_to_use: the loop hit an ambiguous spec, a missing credential, a destructive action needing approval, or 3+ consecutive verify failures on same task --- # Human-in-the-Loop Escalate Long-running agent loops fail in a specific way when they meet a question they can't answer: they guess. The guess ships, the next session builds on it, and by the time a human looks the divergence is three commits deep. This skill is the release valve — when the agent is stuck, it stops and asks, cleanly, in a shape the human can act on. ## Trigger — escalate when any of these are true - **Ambiguous spec.** `PROMPT.md` admits two implementations and the choice changes user-visible behavior. - **Missing credential.** A required env var, API key, or config file is absent, and there is no sanctioned way for the agent to create one. - **Destructive action needing approval.** Dropping a table, force-pushing, deleting user data, spending money, sending mail to real recipients. - **3+ consecutive `/verify` failures on the same task.** The loop is thrashing. Stop and get a human eye before the fourth attempt. - **External dependency down.** A third-party service the feature needs is unreachable and no offline path exists. If none of the above hold, do not escalate. Guessing is bad; escalating on a solvable problem is also bad (it trains humans to ignore the channel). ## Primary action — call the human via the configured channel Read `LOOPKIT_HITL_CHANNEL`. Supported values: | Value | Shape | |---|---| | `telegram` | POST to `https://api.telegram.org/bot$LOOPKIT_HITL_TELEGRAM_TOKEN/sendMessage` with `chat_id=$LOOPKIT_HITL_TELEGRAM_CHAT`, `text=`. | | `slack` | POST JSON `{"text": "..."}` to `$LOOPKIT_HITL_SLACK_WEBHOOK` (incoming-webhook URL). | | `dial` | Run `$LOOPKIT_HITL_DIAL_CMD` with the message on stdin. Whatever the operator wired up — SMS gateway, ntfy, phone call, pager. | | `none` (or unset) | Skip the primary action. Go straight to fallback. | Message body — always these five lines, in this order: ``` [loopkit] blocked in on Q: Context: Attempted: Choices: ``` Non-2xx from the channel is a soft failure. Log it, then fall through to the fallback so the loop still exits cleanly. ## Fallback — write BLOCKED.md and exit Whether the primary succeeds or fails, always write `./BLOCKED.md` at the repo root with exactly four sections: ```markdown # Blocked ## Question: ## Context: ## Attempted: ## Choices: - A)