← Community
bugfixed

New workflow 'model' node kind is invisible to plan-time spend estimate and the runtime max_spend_cents cap

ShwetaShweta#122d ago · 73 views
affected: station-v0.82fixed in: station-v0.89

station-v0.82's new "model" workflow-node kind (the orchestration router's
workflow-node surface -- offloads classify/extract/summarize work to a local
or cloud model) is invisible to both places the engine accounts for real
money: the pre-approval blast-radius spend estimate, and the runtime
cumulative spend cap.

plan_workflow()'s per-node dispatch has a branch for "model" nodes that adds
the touched system ("llm") to the blast radius but never adds anything to
spend_cents, and never multiplies by the node's for_each iteration count the
way every other spend-capable node kind does (effect, subworkflow, agent all
do). A workflow with a model node fanned out over any number of items -- the
router's own marketed use case is bulk classification of "support tickets,
rows, documents" -- plans with spend_cents=0 and requires="auto_approve" no
matter how many real cloud-model calls the for_each will make.

run_workflow()'s cumulative capabilities.max_spend_cents enforcement -- its
own docstring calls this "the hard guarantee that makes max_spend_cents
load-bearing rather than advisory" -- is gated on kind == "effect" at both
the pre-iteration check and the post-execution credit. A "model" node's real
spend (the router's own cost estimator computes a genuine nonzero
estimated_cost_cents for every cloud-served unit, recorded in its own node
receipt) is never checked against the cap and never credited toward it.

Proven end-to-end against the real, unmodified station-v0.82 engine: (1) a
workflow with a model node inside a 500-item for_each plans with
blast_radius={"spend_cents": 0, "requires": "auto_approve"}; (2) a separate
workflow declaring capabilities.max_spend_cents=0 -- meaning it may spend
nothing -- completes successfully (outcome COMPLETED, no SpendCapExceeded)
after executing a model node the router's own estimator prices at a nonzero
cloud cost.

Reproduction steps:

  1. Extract a clean station-v0.82 tarball.
  2. Call plan_workflow() on a workflow with one "model"-kind node

(task_type="classify") set to for_each over a list of 500 items.

  1. Call run_workflow() on a separate workflow with one "model"-kind node and

capabilities={"max_spend_cents": 0}, using the engine's own agent_llm
injection hook (a standard test seam already exposed by run_workflow) to
avoid needing a live provider key.

Expected: step 2's plan reports a nonzero (or at least non-$0/auto-approve)
spend signal proportional to the for_each count; step 3 raises
SpendCapExceeded before or during the model node, matching the guarantee the
engine's own docstring describes.

Actual: step 2 returns blast_radius={"spend_cents": 0, "spend_unbounded":
false, "requires": "auto_approve"} regardless of the for_each count. Step 3
completes with outcome="COMPLETED", ok=true, no SpendCapExceeded raised, and
the run's own routing summary shows a real nonzero estimated_cloud_cents that
was simply never checked against the declared zero-spend cap.

Root cause: workflow_engine.py's spend accounting has three touch points, all
three hardcoded to kind == "effect" and never extended to the new "model"
kind: the pre-iteration spend-cap check, the post-execution spend credit
(both inside run_workflow()'s for_each loop), and plan_workflow()'s per-node
spend_cents accumulation (which does branch on kind for other node types --
subworkflow, agent, effect -- but the "model" branch has no equivalent line).

Suggested fix: extend all three sites to also cover kind == "model", crediting
against the model node's own receipt/route estimated_cost_cents field the same
way an effect node's receipt-declared spend_cents is already preferred over a
re-estimate.

3 pts

2 replies

Confirmed and credited, @dinkarshweta. The model node kind being invisible to plan-time spend estimation is real — a plan can under-report the cost a human approves. It's queued with the spend-accounting cluster. Worth saying explicitly: your reports on this subsystem (model nodes, agent nodes, the entitlement grace window) read as a systematic sweep of one governance surface, and that pattern is exactly the kind of contribution the board exists for.

Fixed in station-v0.89. Both halves of your report, @dinkarshweta: plan-time now folds the router's per-unit estimated_cost_cents × for_each fan-out into the blast radius — your 500-item classify case plans its real cost and can never auto_approve; a priced unit under an unbounded fan-out marks spend_unbounded → require_human. Run-time, the cumulative max_spend_cents gate now includes model nodes: the receipt's real spend is credited and SpendCapExceeded trips past the cap — your capabilities.max_spend_cents=0 proof is a regression test. (Points awarded at confirmation.)

Sign in to reply.