# fx ships with a model in the approval loop

Vercel Labs' fx launched today with a genuinely good deterministic rules grammar — and a default mode that sends every unresolved sensitive call to a hardcoded gpt-5.4 review. What that buys, what it costs, and how to make the stochastic path rare.

[fx](https://fx.sh) launched today — Vercel Labs' minimal coding agent, a 6.3MiB Zig binary built for embedding in sandboxes, benchmarks, and gyms. We read its permission docs the same afternoon, because the first question we ask of any new harness is the same: *when the agent wants to run something sensitive, who decides?*

fx's answer is unusual enough to write about: **by default, another model decides.**

## The part fx gets right

fx's [rules grammar](https://fx.sh/docs/configure-fx/permissions.md) is the deterministic kind we argue for. Wildcard patterns per tool, three verbs, last match wins:

```json
{
  "permission": {
    "*": "ask",
    "bash": { "git *": "allow", "git push *": "deny" }
  }
}
```

Two design decisions deserve credit. Project-level `.fx.json` files **cannot** define permission rules or change the model — so a cloned repo can't quietly rewrite your safety posture, a failure mode we've [watched other harnesses ship](/blog/what-survives-yolo-mode). And `/permissions remember` stores an *exact confirmed rule* without running the action — the "make this a rule" motion, built in. A call that matches a written rule gets the same decision every time, instantly, for free.

## The part worth examining

Rules only cover calls you've written rules for. fx's default mode, `auto`, handles the rest by sending each unresolved sensitive call — terminal commands, file writes and deletes, `install_skill` — to an **automatic review: a separate, hardcoded model request**. Which model? [The docs](https://fx.sh/docs/configure-fx/permissions.md) say `openai/gpt-5.4`. The open-source tree at launch hardcodes `zai/glm-5.2` (`src/core/permissions/auto_classifier.zig`). Either way the docs and source agree on the part that matters: no setting, environment variable, or model choice changes the reviewer. It reviews independently of whatever model your agent runs on — and today you can't even reliably know which model it is.

Taken seriously, that has three consequences:

1. **The decision is stochastic.** The reviewer can allow a call or escalate it to you — and the same call, same context, can be waved through on Tuesday and escalated on Thursday. Nothing anchors the outcome except the reviewer's sampling, and the dangerous direction — an erroneous allow — is the one it takes autonomously. For a harness explicitly built for benchmarking and evals, where run-to-run variance is the enemy, the safety layer is a variance source.
2. **The decision is metered.** Every review is a billed model request on top of the agent's own. fx's docs say it plainly: `auto` can cost more than `ask` for the same call. Your permission system has a per-decision price, and the price scales with how much your agent does — which is to say, with exactly the thing you bought an autonomous agent for.
3. **The dependency is fixed.** Whatever provider runs your agent, the review runs through one hardcoded model you didn't choose. You can't route it, pin it, or swap it — a trust decision made for you, at the layer where trust decisions matter most.

None of this is carelessness. A reviewer model is a reasonable engineering answer to a real problem: nobody can enumerate their rules on day one, and prompting a human for every unresolved call doesn't survive contact with autonomous runs. The `auto` default is fx choosing "keep moving" over "keep asking," with a model as the tiebreaker.

## The alternative isn't "write every rule up front"

It's making the stochastic path *rare, and shrinking*. The mechanism: observe what the agent actually does, then convert observed behavior into written rules — so that over time nearly every call hits the deterministic layer and the fallback, whatever it is, decays toward an exception path. That's the whole design behind [shadow mode and agent-proposed rules](/#learn): replay real calls against a draft policy, see what would have been denied, let the agent draft the rule from the denial it hit, and enforce nothing until a human confirms it. Deterministic where you've decided; a visible, auditable queue for what you haven't decided yet.

fx already has half of this motion in `/permissions remember`. What it doesn't have is the feedback loop that fills the rule set from real behavior — or a documented record of what its reviewer decided and why, which is what you'd need to audit the stochastic path you're paying for.

## Where ACP stands with fx

On launch day, fx had no hook or plugin surface, and its gateway override accepted only loopback URLs. We found the seam instead: fx runs as an [Agent Client Protocol](https://fx.sh/docs/using-fx/acp.md) server (`fx acp`) and sends every approval-gated call to the connected client as `session/request_permission`, carrying the full tool arguments — and fx **fails closed**: any error, cancellation, or malformed answer resolves to deny. ACP protocol sessions default to `ask` mode, so the client sees every sensitive call. ACP governs fx through [@agenticcontrolplane/fx](https://www.npmjs.com/package/@agenticcontrolplane/fx), a governed ACP client for that protocol, answering those requests from workspace policy. We've documented fx's native controls at [/controls/fx](/controls/fx) alongside the governed integration.

fx is a day old and moving fast. The rules grammar suggests its authors already believe the important thing: the same call should get the same decision. The open question is what fills the space between the rules — a fixed model's judgment, or a loop that turns judgment into rules you signed.
