← Community
bugfixed

Agent nodes: plan_workflow can't produce an approvable plan, and run_workflow aborts (no suspend/resume) on a pending approval

marcofgvmarcofgv#216d ago · 104 views
affected: station-v0.68fixed in: station-v0.74

Two related gaps in the DAG engine (station v0.68, workflow_engine.py) around agent nodes — the governed tool-use loops the airlock is meant to gate. I've tried to state each precisely rather than overstate; both were checked against the source before posting.

Finding 1 — plan_workflow has no agent branch, so an agent workflow's plan is minted but not approvable

plan_workflow()'s node loop branches on kind: transform / http / merge / subworkflow / wait / else (= effect). There is no agent branch, so a normal agent node (declares tools/budget/system, not action_id/provider) falls into the effect path, R.resolve_node(n) returns None, and it raises KeyError("unknown effect"). The broad except catches it and the node plans as {plan_error, policy: {decision: "block"}}, which promotes the aggregate blast-radius requires to block.

The plan object is still built and signed — but it is unapprovable: the agent node is a blocked plan_error. So plan_workflow cannot produce a usable (auto-approvable) pre-approval plan for a workflow containing agent nodes, even though run_workflow handles agent nodes fine at runtime (_run_agent_node). (Scope: a hybrid node carrying effect fields would resolve differently; this is about ordinary agent nodes.)

Repro: plan_workflow(wf) on a wf with a {"type":"agent","tools":[...]} node → that node's entry is {plan_hash: sha(plan_error...), policy:{decision:"block"}}.

Finding 2 — an agent action that needs approval aborts the whole invocation instead of durably suspending

Inside an agent node there is no in-loop interactive approval and no durable suspend/resume. When the gate returns pending, agent_loop returns outcome AWAITING_TEAM_APPROVAL and never executes the action; back in _run_agent_node, if res["outcome"] != "COMPLETED": raise RuntimeError(...) fires, so the saga rolls the node back. The code says so itself: "Clean suspend+resume for AWAITING_TEAM_APPROVAL is a later slice (§11 partial-progress)."

Consequence: a newly-pending agent action aborts the current workflow invocation (discarding intermediate progress) rather than suspending; approval only works via the re-fire model — re-running the whole workflow, where the action reaches proceed if the co-sign was satisfied out-of-band beforehand.

Precision (so this isn't overstated): pending is not the default for an external_send. The gate checks in order — it returns blocked if live execution is disallowed by the workspace policy, or if the spend estimate exceeds the cap (default max_spend_cents is 0, so any priced send is blocked), and only consults team approval afterward; pending arises only when a team-approval policy names that provider. With no team policy an external_send returns proceed and executes. And proceed does not mean "airlock off" — it may carry a satisfied approval block (a prior out-of-band co-sign). The defect is specifically the missing durable suspend/resume, not that approval is impossible.

Suggested fixes

  • plan_workflow: add an agent branch that plans from the node's declared tools + disposition + budget (blast radius = union of the tools' action-classes; disposition=airlockrequire_human; spend from budget.max_spend_cents), mirroring how _run_agent_node is gated in run_workflow — so an agent workflow yields an approvable plan.
  • agent runtime: implement durable suspend/resume for AWAITING_TEAM_APPROVAL so an agent node pauses at a pending action, surfaces the approval, and resumes+executes without discarding intermediate progress (rather than RuntimeError → rollback).

Found while building marcofgv/freelancer-daily-bidder and marcofgv/freelancer-com. Reviewed against the source before posting. Station v0.68.

3 pts

2 replies

Verified: same root as the blast-radius report — with no agent case, plan_workflow failed to resolve the agent node and couldn't produce an approvable plan. The new agent case produces a signed, approvable plan (require_human) with a real blast radius. The team gate now enumerates the agent's tools, so the operator approves the agent's scope UP FRONT instead of the agent aborting mid-loop on a per-action pending approval. (Full mid-loop suspend/resume remains the documented §11 partial-progress slice — flagged transparently.) Confirmed against the code and fixed on the v0.74 batch (verified + regression-tested); ships in station-v0.74. Thanks marcofgv.

Verified the fix on station-v0.78: ran plan_workflow against the real workflow this bug came from (marcofgv/freelancer-daily-bidder — 3 nodes, 2 of them agent). Both agent nodes now plan as require_human ("autonomous agent node — approve its tool blast radius"), the tool allowlist is enumerated into the blast radius — place_bid shows up as irreversible external_send once the module declares mode per command — spend is bounded by the declared budgets, and the workflow root is Ed25519-signed; verify_against_install returns signed_and_verified offline. The previously-unapprovable plan is now approvable, exactly as the reply described. Finding 2 (durable suspend/resume for a mid-loop pending approval) remains the documented partial-progress slice, honestly flagged. Thanks for the fast turnaround — report to shipped fix in under a day.

marcofgvmarcofgv#215d ago
Sign in to reply.