$npx -y skills add Archive228/loopkit --skill hitl-escalateEscalate 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.
| 1 | # Human-in-the-Loop Escalate |
| 2 | |
| 3 | 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. |
| 4 | |
| 5 | ## Trigger — escalate when any of these are true |
| 6 | |
| 7 | - **Ambiguous spec.** `PROMPT.md` admits two implementations and the choice changes user-visible behavior. |
| 8 | - **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. |
| 9 | - **Destructive action needing approval.** Dropping a table, force-pushing, deleting user data, spending money, sending mail to real recipients. |
| 10 | - **3+ consecutive `/verify` failures on the same task.** The loop is thrashing. Stop and get a human eye before the fourth attempt. |
| 11 | - **External dependency down.** A third-party service the feature needs is unreachable and no offline path exists. |
| 12 | |
| 13 | 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). |
| 14 | |
| 15 | ## Primary action — call the human via the configured channel |
| 16 | |
| 17 | Read `LOOPKIT_HITL_CHANNEL`. Supported values: |
| 18 | |
| 19 | | Value | Shape | |
| 20 | |---|---| |
| 21 | | `telegram` | POST to `https://api.telegram.org/bot$LOOPKIT_HITL_TELEGRAM_TOKEN/sendMessage` with `chat_id=$LOOPKIT_HITL_TELEGRAM_CHAT`, `text=<question + repo + short context>`. | |
| 22 | | `slack` | POST JSON `{"text": "..."}` to `$LOOPKIT_HITL_SLACK_WEBHOOK` (incoming-webhook URL). | |
| 23 | | `dial` | Run `$LOOPKIT_HITL_DIAL_CMD` with the message on stdin. Whatever the operator wired up — SMS gateway, ntfy, phone call, pager. | |
| 24 | | `none` (or unset) | Skip the primary action. Go straight to fallback. | |
| 25 | |
| 26 | Message body — always these five lines, in this order: |
| 27 | |
| 28 | ``` |
| 29 | [loopkit] blocked in <repo-name> on <ISO timestamp> |
| 30 | Q: <one-line question> |
| 31 | Context: <one-line what you were doing> |
| 32 | Attempted: <one-line what you tried> |
| 33 | Choices: <A | B | C> |
| 34 | ``` |
| 35 | |
| 36 | Non-2xx from the channel is a soft failure. Log it, then fall through to the fallback so the loop still exits cleanly. |
| 37 | |
| 38 | ## Fallback — write BLOCKED.md and exit |
| 39 | |
| 40 | Whether the primary succeeds or fails, always write `./BLOCKED.md` at the repo root with exactly four sections: |
| 41 | |
| 42 | ```markdown |
| 43 | # Blocked |
| 44 | |
| 45 | ## Question: |
| 46 | <one sentence, answerable with a short reply> |
| 47 | |
| 48 | ## Context: |
| 49 | <what feature, which file, which commit, why now — 3-6 lines> |
| 50 | |
| 51 | ## Attempted: |
| 52 | <bulleted list of what you tried and why each fell short> |
| 53 | |
| 54 | ## Choices: |
| 55 | - A) <option> — <consequence> |
| 56 | - B) <option> — <consequence> |
| 57 | - C) <option> — <consequence> |
| 58 | ``` |
| 59 | |
| 60 | Then `exit 2`. The loop runner (`run.sh`) checks for `BLOCKED.md` at the head of each iteration and exits with code 2 if it exists — that stops the loop until a human clears it. |
| 61 | |
| 62 | ## Human unblocks |
| 63 | |
| 64 | The human reads `BLOCKED.md`, edits `PROMPT.md` / drops the credential / approves the destructive step, then `rm BLOCKED.md` and restarts `run.sh`. The deleted `BLOCKED.md` is the unblock signal. |
| 65 | |
| 66 | ## Anti-patterns — do not do these |
| 67 | |
| 68 | - **Do not fabricate an answer.** "I'll assume the user meant X" is how divergence starts. Assume nothing under ambiguity — escalate. |
| 69 | - **Do not loop endlessly.** After the third consecutive `/verify` failure on the same task, escalate. The fourth attempt is not going to succeed by the same reasoning that failed thrice. |
| 70 | - **Do not silently swallow the missing credential.** Do not commit a placeholder, do not disable the feature, do not "TODO" the auth. Ask. |
| 71 | - **Do not escalate for questions the repo already answers.** Read `AGENTS.md`, `PROMPT.md`, and the last three commits first. Escalation is expensive human attention — spend it on real ambiguity. |
| 72 | - **Do not skip writing `BLOCKED.md` because the primary channel succeeded.** The file is the loop's exit contract. Without it, `run.sh` keeps spinning. |
| 73 | |
| 74 | ## Pairs with |
| 75 | |
| 76 | - `adversarial-verify` — the source of the 3-verify-failure trigger. |
| 77 | - `shift-notes` — after escalating, note the block in `IMPLEMENTATION_PLAN.md` so the next session (post-unblock) has full context. |
| 78 | - `spec-first` — an ambiguous `PROMPT.md` is a spec-first failure; the human's unblock should tighten the spec, not just answer the one-off. |