← Community
bugopen

The global freeze kill-switch is bypassed by agent nodes and subworkflow effects: they fire live during a freeze that halts static effects

ShwetaShweta#135d ago · 95 views
affected: station-v0.88

The station has a global freeze — an emergency kill-switch whose stated
contract is that "every live send is halted until you unfreeze". It is the
last-resort safety control, and unlike an approval-policy rule it needs no
configuration: any operator can hit it during an incident.

Freeze is enforced through exactly one mechanism: the workflow policy gate. When
the freeze is on, that gate returns a synthetic "block" decision for every
action (shaped identically to a real policy block), and the workflow engine's
runtime backstop refuses the effect. The live-run endpoint and the engine's run
loop never check the freeze state directly — a search for the freeze flag across
the dag/run handler finds nothing; the block rides entirely on the policy gate.

Two effect-firing paths do not consult the policy gate, so the freeze never
reaches them:

  • Agent nodes: the agent's per-action gate checks the coarse live-execution

toggle, the spend cap, and team approval — never the policy gate. So an agent
node's autonomous tool call fires during a global freeze.

  • Subworkflow-nested effects: the engine recurses into the nested workflow

without forwarding the policy gate, so the nested run has no gate at all. An
effect one subworkflow level down fires during a global freeze.

Result: an operator who hits the emergency freeze believing every live send is
halted still has agent nodes and subworkflow effects firing. A static effect at
the top level is correctly refused; the identical effect reached through an
agent node or a subworkflow executes.

Reproduction steps:

  1. Extract the station-v0.88 release tarball and work against workbench/.
  2. Register/pick an effect action that plans+applies cleanly (a mock is fine).

Enable dag live execution in the workspace execution policy.

  1. Model a global freeze exactly as the running station does: a policy_gate that

returns {"decision":"block", "reason":"GLOBAL FREEZE ..."} for every action
(this is the same block-shape the freeze produces).

  1. run_workflow a TOP-LEVEL effect node with that gate -> the effect is refused

(the runtime backstop raises); nothing fires. Freeze holds.

  1. run_workflow an AGENT node whose loop calls that action, same gate -> the

effect FIRES.

  1. run_workflow a SUBWORKFLOW node wrapping the same effect, same gate -> the

nested effect FIRES.
(Equivalently, on a running station: turn the global freeze ON, then dag/run a
static effect vs the same effect wrapped in a subworkflow — the static run is
refused, the subworkflow run reaches and executes its nested effect.)

Expected: a global freeze halts every live effect, on every path — the emergency
stop is absolute.

Actual: the freeze halts only the static effect path. Agent nodes and
subworkflow-nested effects fire live during the freeze.

Root cause: the freeze is enforced solely by the policy gate (a frozen station's
gate returns a "block"-shaped decision), and the dag/run handler + run loop never
check the freeze state independently. The two paths that skip the policy gate —
the agent per-action gate, and the subworkflow recursion that does not forward
the gate into the nested run — therefore skip the freeze.

Suggested fix: enforce the freeze independently of the policy gate. Check the
freeze state once at the run entry point (refuse any live run while frozen), and
additionally give the agent gate and the subworkflow recursion the same gate the
static effect path uses so the block-shaped freeze decision reaches every effect.

0 replies

Sign in to reply.