Skip to content

mapVerdictToAction

mapVerdictToAction(command, verdict, opts): RaterDecision

Defined in: _worktrees/docs-release/gaunt-sloth/packages/core/src/core/shell/rater.ts:2039

CFG-27 — pure, testable mapping from a ShellSafetyVerdict + the raw command to a RaterAction, keyed on the rung (spec §4.2, §8):

Outcome manual/write assisted auto bypass
— (no rating) escalate approve
safe approve approve
destructive escalate reject — §5’s negotiation ([[EXT-29]]); escalate when a preflight floors the command and §4.6’s carve-out did not lift it ([[EXT-106]])
catastrophic escalate escalate — never negotiate
attack halt halt

[[EXT-81]] — there is no longer a row cutting across that column. A command whose target the gate cannot statically resolve used to return abstain here, before the safe check and after the two severe outcomes. It is now rated like any other command, carrying a neutral note about the shape the parser saw (buildParserPreflightNote), and this table is the whole mapping again. The table gained nothing it did not have: what it LOST is a branch that made catastrophic and attack unreachable for every composed, substituting or redirecting command.

Order of precedence (fail-closed FIRST — this ordering IS the safety property):

  1. bypassapprove. The gate is off. The declared deny list and the exec-time hardline floor still apply, but neither is decided here.
  2. Unrated rungs (manual, write) → escalate. No model is consulted at all; the allow-list is checked by the caller BEFORE this function, so reaching here means the human decides. (The two rungs behave identically here, and that is not a missing branch: what separates them is which tools they auto-grant, decided once in resolveGatedToolNames when the gated set is built. A call that reaches this function is already one the rung did not grant, and the shell — this function’s only subject — is granted by neither.)
  3. The deterministic preflight FINDINGS, which FLOOR the outcome at destructive and never lower one (preflightFloorReason): the script-env-leak preflight (hasScriptEnvLeakRisk) and EXT-61’s open-world preflight (findOpenWorldHostLiterals — a host literal in a fetch/transfer position, §4.6). Both are recomputed from the RAW command, independently of what the rater said — the open-world arm additionally against opts.provenance, since [[EXT-106]] §4.6 lifts it where the user named every host in the command themselves (effectivePreflightFloorFinding). Either rewrites a verdict that sits BELOW the floor — i.e. safe, and only safe (isBelowDestructiveFloor) — to destructive with an honest reason, before the safe check, so a manipulated safe verdict can never slip one of them through. A rater verdict may only ever make an outcome worse, never better, and so may a preflight: destructive, catastrophic and attack all pass through UNCHANGED. (Before the rescale this branch excluded the single halting outcome BY NAME. Renamed in place it would have let a preflight hit downgrade a catastrophic verdict to destructive — the exact inverse of the invariant above, silently trading an unnegotiable escalation for a negotiable one at auto.)
  4. attackhalt, at both rated rungs, never negotiable.
  5. catastrophicescalate, and MUST NOT enter §5’s negotiation.
  6. safeapprove; destructiveescalate at assisted, and at auto either reject (§5’s negotiation, [[EXT-29]]) or — when a floor in (3) APPLIES to this command — escalate, because [[EXT-106]] §3 will not open a negotiation whose outcome (3) has already decided. The test is isNegotiableCall, shared with the rating prompt, and it is given the same provenance (3) is: a command §4.6’s carve-out lifted the floor from can reach approve again, so it is negotiable again.

EXT-58 (§4.4): the verdict’s suggestedTool is not read here, and that is deliberate. A suggestion is never an approval — it must not change the action, must not approve the original command, and must not pre-approve the suggested tool. The gate also never decides for itself that a shell command is “equivalent” to a built-in and substitutes it: any such equivalence test would be a second command parser, and a second command parser is a second place for the gate to be bypassed. The suggestion is carried, untouched, to the human (§6) and to the model (§7) — nothing else. Note that the fail-closed rewrite in (3) builds a FRESH verdict and therefore drops any suggestion along with the reason it belonged to: a verdict the gate has just declared untrustworthy must not keep recommending anything. A verdict the preflight leaves alone was never declared untrustworthy — the gate is agreeing with it, not overriding it — so it keeps both.

string

The raw command string (used to recompute ambiguity + preflight independently of the rater, so the gate is robust even if the rater is wrong or manipulated).

{ outcome: "safe" | "destructive" | "catastrophic" | "attack"; reason: string; suggestedTool?: string; } | undefined

The rater’s verdict (or FAIL_CLOSED_VERDICT); undefined at the unrated rungs. A missing verdict at a RATED rung is treated as FAIL_CLOSED_VERDICT.

RaterDecisionOptions

The rung in force, and [[EXT-106]] §4.6’s user provenance — see RaterDecisionOptions.provenance, whose default floors exactly as before.

RaterDecision