# Hook bypass phrases

Reverting tracked changes or bypassing the fleet's hook chain requires the user to type the canonical phrase verbatim in a recent user turn. Inferring intent from "go ahead", "skip the hook", "fix it", etc. does NOT count.

The phrase format is `Allow <X> bypass`. Case-insensitive; hyphens, spaces, and dashes in `<X>` are interchangeable (`Allow fleet-fork bypass` ≡ `allow fleet fork bypass`). Every word must still be present, in order.

| Operation                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Phrase                                                                                    |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| Revert (any of: `git checkout -- <files>`, `git checkout <ref> -- <files>`, `git restore <files>` without `--staged`, `git reset --hard`, `git stash drop` / `pop` / `clear`, `git clean -f`, `git rm -rf`)                                                                                                                                                                                                                                                                                             | `Allow revert bypass`                                                                     |
| `git --no-verify` (skips the `.git-hooks/` chain)                                                                                                                                                                                                                                                                                                                                                                                                                                                       | `Allow no-verify bypass`                                                                  |
| `git --no-gpg-sign` / `-c commit.gpgsign=false`                                                                                                                                                                                                                                                                                                                                                                                                                                                         | `Allow gpg bypass`                                                                        |
| `SKIP_ASSET_DOWNLOAD=1` (skips release-asset fetch in build — degraded-mode flag; becomes a bypass when used to push past rate-limited pre-commit)                                                                                                                                                                                                                                                                                                                                                      | `Allow asset-download bypass`                                                             |
| `git stash` (any form: bare, `push`, `save`, `--keep-index`) in primary checkout — shared stash store, another Claude session can pop yours. Use a worktree instead.                                                                                                                                                                                                                                                                                                                                    | `Allow stash bypass`                                                                      |
| `brew` / `choco` / `winget` / `scoop` / `npm` / `pnpm` invocation while that manager's auto-update is still enabled (would let it change a tool version mid-task or pull an unsoaked package) — run `setup-security-tools` to disable it first                                                                                                                                                                                                                                                            | `Allow package-manager-auto-update bypass`                                                |
| `brew` invocation while Homebrew is below 6.0.0 or unhardened (`HOMEBREW_REQUIRE_TAP_TRUST` / `HOMEBREW_CASK_OPTS_REQUIRE_SHA` unset) — `brew upgrade` to clear the floor, run `setup-security-tools` to set the knobs                                                                                                                                                                                                                                                                                    | `Allow brew-supply-chain bypass`                                                          |
| Bash file-write (`python -c '...write...'`, `sed -i`, heredoc `cat << EOF > file`, `tee <source-file>`, `dd of=…`) — typically used to dodge an Edit/Write hook block. Move file / refactor / get original-hook bypass instead.                                                                                                                                                                                                                                                                         | `Allow bash-write bypass`                                                                 |
| `git push --force-with-lease` (refuses if remote moved since fetch — preferred safe form, always reach for this before `--force`)                                                                                                                                                                                                                                                                                                                                                                       | `Allow force-with-lease bypass`                                                           |
| `git push --force` / `-f` (CAN silently clobber remote commits — high-friction phrase; use `--force-with-lease` unless you specifically need the lease check off)                                                                                                                                                                                                                                                                                                                                       | `Allow force-push bypass`                                                                 |
| External GitHub issue/PR reference in a commit message or PR/issue body (`<owner>/<repo>#<num>` or full URL to a non-SocketDev repo) — would auto-link a backref into the upstream maintainer's issue                                                                                                                                                                                                                                                                                                   | `Allow external-issue-ref bypass`                                                         |
| Sub-package `scripts/paths.mts` that doesn't `export *` from the nearest ancestor paths.mts (re-derives REPO_ROOT etc. — drift risk)                                                                                                                                                                                                                                                                                                                                                                    | `Allow paths-mts-inherit bypass`                                                          |
| `gh workflow run` / `gh workflow dispatch` / `gh api …/dispatches` for a workflow without a `dry-run:` input — one-off recovery dispatches or workflows that can't dry-run by design (e.g. node-smol build). **Scoped + single-use**: name the workflow so the phrase authorizes exactly one dispatch of it and can't leak to an unrelated workflow.                                                                                                                                                    | `Allow workflow-dispatch bypass: <workflow>` (filename, basename, or id)                  |
| `git push` to a non-fleet repo. **Scoped (preferred)**: name the repo so authorization can't leak to a different non-fleet push. Bare form still works as a session-wide fallback.                                                                                                                                                                                                                                                                                                                      | `Allow non-fleet-push bypass: <owner/repo>` (or bare `Allow non-fleet-push bypass`)       |
| `gh pr create` / `gh issue create` / `gh release create` against a non-fleet repo. **Scoped (preferred)**: names the repo. Bare form still works as a session-wide fallback.                                                                                                                                                                                                                                                                                                                            | `Allow non-fleet-publish bypass: <owner/repo>` (or bare `Allow non-fleet-publish bypass`) |
| 7-day `minimumReleaseAge` soak for an `external-tools.json` asset bump (pnpm, zizmor, sfw, …) — wires the `--bypass-minimum-release-age` flag in `socket-registry/scripts/update-external-tools.mts`. Justified when the integrity model is binary-download + sha256 recompute done by the script itself (not npm-registry trust). Soak-exclude annotation `# published: YYYY-MM-DD \| removable: YYYY-MM-DD` is still required for any related `pnpm-workspace.yaml` `minimumReleaseAgeExclude` entry. | `Allow soak-time bypass` (alias: `Allow minimumReleaseAge bypass`)                        |
| Importing from `node:child_process` (or bare `child_process`) — use `spawn` from `@socketsecurity/lib-stable/process/spawn/child` instead. Sync `spawnSync` only when genuinely needed, still from the lib.                                                                                                                                                                                                                                                                                             | `Allow async-spawn bypass`                                                                |
| Keeping a **premium model** (Opus) for a command `token-spend-guard` flagged as mechanical (cascade, lint-autofix sweep, format sweep). Type it when the "mechanical" task genuinely needs the headroom.                                                                                                                                                                                                                                                                                                | `Allow model bypass` (alias: `Allow model-spend bypass`)                                  |
| Keeping **high reasoning effort** (`high`/`xhigh`/`max`) for a command `token-spend-guard` flagged as mechanical. Independent of the model bypass — flag each dimension separately.                                                                                                                                                                                                                                                                                                                     | `Allow effort bypass`                                                                     |
| Writing an inline `type` specifier in a value import (`import { type X, Y }`) instead of a separate `import type { X }` statement — `prefer-type-import-guard`. Rare; the separate form is fleet-canonical ~200:1.                                                                                                                                                                                                                                                                             | `Allow separate-type-import bypass`                                                       |
| Reading/writing the system clipboard from a script or hook via a clipboard CLI (`pbcopy` / `pbpaste` / `xclip` / `wl-copy`) or an OSC-52 escape (`no-clipboard-access-guard`). The clipboard is an exfil + overwrite surface; bypass only for an operator-driven need.                                                                                                                                                                                                                        | `Allow clipboard-access bypass`                                                           |
| Capturing the screen from a script or hook (`screencapture` / `scrot` / `grim` / `import` / `snippingtool`), via `no-screenshot-guard`. A screenshot can capture any window on the display; bypass only when the user asked for a screenshot.                                                                                                                                                                                                                                                 | `Allow screenshot bypass`                                                                 |
| Landing a CLAUDE.md edit that leaves the file over the 40 KB whole-file cap (`claude-md-size-guard`). One phrase authorizes one over-cap edit; prefer trimming detail into `docs/agents.md/fleet/<topic>.md` first.                                                                                                                                                                                                                                                                            | `Allow claude-md-size bypass`                                                              |
| Ending a turn with a dirty PRIMARY checkout — uncommitted/untracked/staged-but-uncommitted (`dirty-worktree-stop-guard`). For the rare can't-commit-yet case; prefer committing, or stack WIP in a linked worktree and defer via `git commit --no-verify`.                                                                                                                                                                                                                                     | `Allow dirty-worktree bypass`                                                              |
| Adding a repo-specific path-glob into a fleet-canonical config (`overrides[].files` / `ignorePatterns` in `template/.config/fleet/oxlintrc.json` etc.) — a fleet glob must be universal (`**/`-anchored or a bare extension), so a one-repo tree like `packages/npm/**` is blocked (`no-repo-scope-in-fleet-config-guard`). For the rare path that genuinely applies fleet-wide but can't be `**/`-anchored.                                                                                          | `Allow repo-scope-in-fleet bypass`                                                         |

## Inline sentinels (scoped auto-bypass)

Two batch flows run the same blocked operations repeatedly and would otherwise need a fresh typed phrase per command. Each marks intent with an inline `NAME=1` assignment on the command (opt-in per command — no global env-var poisoning), scoped to exactly the operations that flow needs. Anything else carrying the sentinel falls through to the normal phrase-gated checks.

### `FLEET_SYNC=1` — wheelhouse cascade

Allows, on a command prefixed with `FLEET_SYNC=1`:

- `git commit --no-verify` whose message starts `chore(wheelhouse): cascade template@`
- any `git push` (`--no-verify` included)
- broad-stage `git add -A` / `-u` / `.` inside a fresh worktree (via `overeager-staging-guard`)

### `SQUASH_HISTORY=1` — `squashing-history` skill

The skill collapses the whole default branch into one commit, then force-pushes it. The collapse commit trips the `--no-verify` rule and the push trips the force-push rule, yet both are intrinsic to the squash — the resulting tree is byte-verified identical to a backup branch before the push, so the hook chain has nothing new to check. Allows, on a command prefixed with `SQUASH_HISTORY=1`:

- a `git commit --amend` whose `-m` message is exactly `chore: initial commit`
- a `git push` carrying `--force` / `--force-with-lease` / `-f`

**Hardening (malicious-bypass surface).** A poisoned prompt (from a dep, fixture, or fetched doc) must not be able to ride the sentinel to clobber an arbitrary remote, delete refs, or chain extra destructive work. The guard parses the command and honors `SQUASH_HISTORY=1` **only** when the line is exactly one statically-resolved `git` segment: no `&&` / `;` / `|` chaining, no `$(…)` substitution, no `$VAR` / `eval` indirection, no second inline env assignment, and on the push form no refspec (`src:dst`), `--mirror`, `--all`, `--delete`, or `--no-verify`, with at most one plain positional branch ref. Any deviation voids the sentinel and the command falls back to needing the typed phrase.

## Scope

A phrase from a previous session does not carry over. Only the current conversation's user turns count. The hook reads the active session's transcript (passed by Claude Code as `transcript_path` in the PreToolUse payload) and searches the concatenated user-turn text for the exact phrase.

The match is **case-insensitive** and **substring-based**. Hyphens, spaces, and dashes inside `<X>` are folded together, but every word of the phrase must appear in order:

- ✓ `Allow revert bypass; please drop my last edit`
- ✓ a multi-line user message with `Allow revert bypass` on its own line
- ✓ `allow revert bypass` (lowercase)
- ✗ `please revert that file` (paraphrase)
- ✗ `Allow revert` (missing `bypass`)
- ✗ `--no-verify is fine` (no `Allow ... bypass` shape)

## Why a phrase

Without the gate, the assistant has historically reverted whole batches of autofix changes mid-cleanup or used `--no-verify` to push past a failing hook, both of which destroy work and erode trust. The phrase is short enough to type when truly intended and specific enough that no other utterance accidentally triggers it.

## Defense in depth

The bypass policy is enforced at three layers:

- **CLAUDE.md** documents the rule (`### Hook bypasses require the canonical phrase`).
- **Memory** keeps the assistant honest across sessions even before the hook fires.
- **`.claude/hooks/fleet/no-revert-guard/`** is the enforcement: a `PreToolUse(Bash)` hook that scans the proposed command, parses the transcript, and exits 2 with a stderr message naming the phrase the user must type.

The hook fails open on its own bugs (exit 0 + stderr log) so a bad deploy can't brick the session. Trade-off: a buggy hook silently allows the destructive command. Acceptable because the alternative (hook crash wedges the session) is worse for development velocity.

## How to add a new bypass

When introducing a new destructive flag or hook bypass:

1. Add a new entry to the `CHECKS` array in `.claude/hooks/fleet/no-revert-guard/index.mts`. Each check is `{ pattern: RegExp, bypassPhrase: string, label: string }`.
2. Add a row to this reference's table.
3. Add a test case to `.claude/hooks/fleet/no-revert-guard/test/index.test.mts` covering both the blocked-without-phrase and allowed-with-phrase paths.
4. Cascade via `node socket-wheelhouse/scripts/sync-scaffolding.mts --all --fix` so every fleet repo picks up the change.
