# Grok Build runs your Claude Code hooks — and ignores every verdict they return

xAI's coding agent reads hooks straight out of ~/.claude/settings.json and fires them in every mode, including always-approve. It just doesn't parse their answers. Here's the verified contract, the ACP hook we built on it, and the bug the live test found in our own gateway.

[Grok Build](https://docs.x.ai/build) — xAI's terminal coding agent, 1.0 as of August 7 — now has ACP support via [@agenticcontrolplane/grok-build](https://github.com/agentic-control-plane/grok-build-acp-plugin): every tool call policy-checked before it runs, in every permission mode, with an audit record and a session receipt.

Building it surfaced two things worth writing down. One is a trap in Grok Build's Claude Code compatibility that will bite anyone who assumes their existing hooks carry over. The other is a bug the live verification found in *our own gateway* — filed, fixed at the client, and written up below, because a control plane that publishes other harnesses' silent failures should publish its own.

## The verified contract

Grok Build [open-sourced in July](https://github.com/xai-org/grok-build), so none of this is inferred: it's read out of the hook runner's source and the in-repo user guide.

The good news is genuinely good. **PreToolUse hooks fire in every permission mode — including `--always-approve`.** Deny rules too. That's the single most control-friendly property a harness can have: yolo mode silences Grok's own prompts, and the hook still stands on the call path. Hooks register user-globally in `~/.grok/hooks/*.json` with no per-project trust prompt, there's an `updatedInput` field for rewriting tool input before execution, and `type:"http"` hooks can POST the event envelope straight to a URL.

Two properties you have to design around:

**It's fail-open, everywhere.** Default hook timeout is 5 seconds; a timed-out, crashed, or unparseable hook logs a line and the call proceeds. Only an explicit deny blocks. A policy hook that inherits this inherits "proceed" as its failure mode — so ours carries its own posture instead: attended sessions fail open with a loud `UNGOVERNED` warning and a durable lapse-log line, unattended tiers fail closed, and the registered timeout sits far above the hook's internal 4-second budget so Grok's timer never decides.

**There is no ask.** The gate parses a top-level `decision` field: `deny` blocks, `allow` or absent allows, anything else is an unknown-decision error. So an ACP `ask` resolves by mode — attended modes defer to Grok's native prompt, which still stands after the hook; unattended modes turn the ask into a deny that carries the console link.

## The compatibility trap

Grok Build reads hooks out of your real `~/.claude/settings.json` by default, aliases Claude tool names in matchers, and injects `CLAUDE_PROJECT_DIR`. If you have Claude Code hooks installed, Grok Build is already running them.

It just isn't listening to them. The runner doesn't parse Claude's `hookSpecificOutput.permissionDecision` — xAI's own example emits that key "for forward-compatibility" only — and Claude's legacy `approve`/`block` values are unknown-decision errors. A Claude-vocabulary deny parses as decision-absent. Decision-absent is an allow.

Sit with that for a second: the hook fires on every call, appears in the config, visibly executes — and every verdict it returns is discarded. Your session looks governed. It isn't. This is the failure class we keep [writing](/blog/muse-code-acp-integration) [about](/controls): not controls that are missing, but controls that are silently not applying. The fix is a hook that speaks Grok's vocabulary natively (ours emits Claude's alongside, for the day that forward-compatibility note becomes parsing).

## The bug the live test found — in our gateway

Standard launch discipline here is an end-to-end check against production before anything ships. First fixture: `rm -rf /` under Grok's native shell tool name.

```
tool_name: "run_terminal_command"  →  allow
tool_name: "Bash"                  →  deny (hardline floor)
```

Same command, same credential, same tier. Our hardline floor — the tier of policy that is supposed to hold *unconditionally* — keyed on the canonical tool name and never evaluated the unmapped one. Every previously shipped plugin passes its live floor test because its harness's tool names happen to land in the recognized set. Grok's don't.

Filed as [gatewaystack-connect#750](https://github.com/davidcrowe/gatewaystack-connect/issues/750): the durable fix is the gateway owning the vocabulary (or better, applying shell-content floors to any input shaped like a command — a floor that depends on a name isn't a floor). The immediate fix ships in the plugin: Grok's native names map to canonical before the check — the alias table comes from Grok's own docs — and the native name rides along as `client_tool_name` in the audit record. The `rm -rf /` fixture now denies under the native name, exit code 2, and that exact check lives in the test suite.

We publish this for the same reason we publish everyone else's gaps: the whole category's failure mode is silence. A floor that quietly doesn't apply is indistinguishable from a floor that held — right up until it matters. If your control layer never runs adversarial fixtures against its own production path, you don't know which one you have.

## Where Grok Build lands

On the [coverage table](/coverage): pre-call interception over everything (one hook pipeline, MCP tools included as `server__tool`), ask via mode-aware resolution, no inline result rewrite (only `PreToolUse` blocks; post-hoc flags surface loudly and land in the audit log). On the [controls comparison](/controls/grok-build): strongest always-approve story in the lineup, fail-open hook core, and a headless mode — `dontAsk` — that is the *opposite* of an escape hatch: it silently denies anything without an explicit allow rule. We'd earlier listed `dontAsk` as the escape hatch; the source says otherwise, and the [deep page](/controls/grok-build) now has it right.

Install:

```sh
curl -sf https://agenticcontrolplane.com/install.sh | bash
```

Detects Grok Build, registers the hook user-globally, provisions a workspace. [Guide](/integrations/grok-build) · [source](https://github.com/agentic-control-plane/grok-build-acp-plugin) · [what Grok Build ships natively](/controls/grok-build).
