nextv1.4.0
Concepts

Why a permission gate

Why Kraft answers a worker's permission prompts itself, and how each harness lets it.

A Kraft worker runs with nobody watching, so a permission prompt has nobody to answer it. This page explains why Kraft answers those prompts itself, and why it does that differently for each harness. For what the gate does and how to configure it, see Permission gate.

The problem with an unattended prompt

Left to a harness's own judgment, an unattended agent does one of two things. It stalls on a tool it is unsure about, or it runs the tool anyway because nobody is watching. Neither is acceptable when you want to know what a worker was allowed to do.

The third option

Kraft's permission gate is a third option. The worker asks Kraft, Kraft answers from the task's own policy, and the answer, allow or deny and why, lands on the work item's timeline. You do not have to watch a session to know what it was let do. The policy comes from allowed_tools, deny_tools and grants, which you set at any layer down from the repository to the task. See Caps and budgets for how those layers combine.

Why it works differently per harness

The gate has to meet each CLI where that CLI offers a hook. The harnesses differ, so Kraft uses the strongest mechanism each one has.

Claude Code settles most tool calls itself with its own classifier, such as a read-only shell command or a routine edit. What it will not settle, it hands to a permission prompt tool. Kraft points that at its own tool, so the CLI calls into Kraft instead of prompting a human who is not there. Whether the gate is reachable, and how much reaches it, follows from the permission mode. With no allowed_tools, Claude runs in auto, and the gate sees only what the classifier declines to settle. With an allowed_tools list, Claude switches to manual, and every call the built-in allowlist does not cover reaches the gate. That is what makes the gate worth configuring.

Cursor and Codex have no prompt tool, but each runs a hook before every tool call. Kraft installs a hook that asks the same gate. Kraft scopes the hook to the launch: Cursor's is written into the worktree and kept out of your commits, and Codex's is passed per launch and trusted by hash, never by bypassing Codex's hook trust, since that would trust every hook a repository ships.

Gemini runs with --approval-mode yolo, so its calls never reach the gate.

OpenCode and Amp offer no reliable per-call hook. Kraft writes the task's deny_tools and allowed_tools into the CLI's own permission configuration for that one launch, and the CLI enforces them itself. The trade-off is that these calls never reach the gate, so they leave no permission_decision on the timeline.

Why a deny is final but an allow may not be

Kraft can only decide; the CLI still runs the call. A deny is a deny on every harness. An allow is the gate's decision, and on Cursor it does not override the CLI's own --auto-review classifier, so a call the gate allows can still be refused. This is why grants are described as the gate's decision and not a guarantee that a command will run.

Why grants are names, not command patterns

A grant such as git-push is a named operation, not a pattern like git push *. A pattern also matches git push x; rm -rf y. Kraft's grant matcher accepts only a call that is exactly one plain git invocation of the granted subcommand, and refuses anything that could run something else. That is also why grants are not written into OpenCode's or Amp's rules, where only a pattern is available.

Why an escalation gets git grants by default

An escalation turn holds git-commit, git-rebase and git-push unless you narrow it, because an escalation that rebased the branch has to push it. A gate's reviewer gets no such default.

Copyright © 2026