# Blocked Form

> Use when a guard, hook or sandbox refuses a destructive command and you are about to record the action as blocked, escalate it, or leave a ticket waiting. Read the refusal text first.

- Skill: `mrbinnacle/blocked-form` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add mrbinnacle/blocked-form`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mrbinnacle/blocked-form/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: MrBinnacle (https://skillmd.com/u/mrbinnacle)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mrbinnacle/blocked-form

---


# A Blocked Command Form Is Not a Blocked Action

## Problem

A safety guard refuses a command. The refusal is correct: the form really is dangerous.
The refusal text also names the safe form.

What gets written down is *"the safety net blocks this — it needs the operator."* The item
moves onto a human's list, and it stays there, because nobody re-reads a refusal they have
already read once.

The generalisation is the defect. **A guard scopes to a command form. The note scoped it to
an action.** Those differ whenever a second spelling exists, and for destructive git
operations a second spelling almost always exists.

## Context / Trigger Conditions

- A guard, PreToolUse hook, permission prompt or sandbox refuses a command, and the next
  thing you write is that the task cannot be done.
- A handoff or checkpoint carries an item whose entire justification is a tool refusal.
- An item has waited on a human across more than one session with no new evidence.
- The refused command is a destructive git form: `branch -D`, `reset --hard`,
  `checkout -- <path>`, `stash drop`, `push --force`, `clean -fdx`.
- A refusal message contains the words *use*, *try*, *instead*, *consider*, *prefer*, or
  *alternative*.

## Solution

1. **Read the refusal text to the end.** Guards written to teach rather than merely block
   name the safe route in the same message. That sentence is the answer.
2. **Run the named alternative.** It is one call. A guard that suggests a form has already
   decided that form is acceptable.
3. **If no alternative is named, enumerate the other routes to the same effect** before
   recording a limit. For anything git-shaped, the API and the plumbing command are
   separate surfaces from the porcelain one, and a guard on `git push --delete` says
   nothing about `gh api ... --method DELETE`.
4. **Write down the form, never the action.** Record *"`git branch -D` is blocked; `-d`
   works"* — not *"branch deletion is blocked."* The first is durable and true; the second
   sends work to a human who does not need it.
5. **Before escalating, state which routes were tried.** An escalation whose evidence is a
   single refused spelling is not yet an escalation.

## Verification

The alternative either runs or it does not, and one call settles it:

```sh
$ git branch -D docs/readme-greenfield-S425
BLOCKED: git branch -D force-deletes without merge check. Use -d for safe delete.

$ git branch -d docs/readme-greenfield-S425
warning: deleting branch 'docs/readme-greenfield-S425' that has been merged to
         'refs/remotes/origin/docs/readme-greenfield-S425', but not yet merged to HEAD
Deleted branch docs/readme-greenfield-S425 (was 36d7a32).
```

For the remote half, a different surface entirely:

```sh
$ gh api repos/OWNER/REPO/git/refs/heads/BRANCH --method DELETE
$ git ls-remote origin refs/heads/BRANCH   # empty output: the ref is gone
```

**Verify the outcome, not the exit code.** `git ls-remote` returning nothing is the
evidence; a zero exit from the API call alone is not.

## Example

A session close recorded, as an item owed to the repository owner:

> Two stray `skill-harness` branches named `docs/readme-greenfield-S425` (one local, one
> remote at the squash-merged `36d7a32`) need OPERATOR deletion — the safety net blocks
> `git push --delete` and `git branch -D`.

Measured the next session, in three calls:

- `git branch -D` was refused, and the refusal read *"Use -d for safe delete."*
- `git branch -d` deleted the local branch, exit 0. The branch had been merged to its own
  remote tracking ref, which is the condition `-d` checks.
- `gh api repos/MrBinnacle/skill-harness/git/refs/heads/docs/readme-greenfield-S425
  --method DELETE` removed the remote ref, and `git ls-remote` then returned nothing.

Both branches gone. Neither needed the owner. The item had been on their list for a session
because one refused spelling was recorded as a property of the action.

Note the second half: the guard was on the git porcelain, and the remote deletion went
through the GitHub API, which the guard never covered. Enumerating surfaces would have
found that without needing the refusal text to be helpful.

## Notes

- **This is not advice to fight guards.** The guard was right both times: `-D` skips the
  merge check, and skipping it is how work disappears. The card is about the note written
  *after* the refusal, not about the refusal.
- **`-d` is not a synonym for `-D`.** It refuses a branch that is not merged anywhere, which
  is exactly the protection `-D` skips. In the worked case the branch was merged to its
  remote tracking ref, so `-d` was satisfiable. On a genuinely unmerged branch `-d` will
  refuse too, and that refusal is information rather than an obstacle.
- **Squash merges complicate the merged-ness test.** After a squash merge the source branch
  is not an ancestor of the target, so `-d` can refuse a branch whose content did land. See
  `squash-merge-leaves-a-poisoned-branch`.
- **The reverse error exists.** Recording *"the action is fine, the tool is just fussy"* and
  reaching for `--force` is the opposite failure and a worse one. The move is to find the
  route the guard permits, never to disable the guard.
- Related: `pretooluse-prose`, which covers a guard firing on
  prose that merely mentions what it forbids — a false positive on content, where this card
  covers a correct block whose scope was over-read.

