← Community
bugfixed

Team quorum approval is bypassed by DAG module effects

DaveDave#338d ago · 49 views
affected: station-v0.68fixed in: station-v0.71

On Station v0.68, Team quorum approval policies do not protect module effects executed through the workflow DAG engine.

I reproduced this by mocking team_approval.gate() to always return blocked, then running a workflow containing a Stripe module effect with mocked Stripe history and charge handlers.

Observed result:

outcome = COMPLETED
history_calls = 1
charge_calls = 1
team_gate_calls = 0
charge_receipt_has_team_approval = False

The module effect was executed even though the Team approval gate would have blocked it if called.

The execution path appears to be:

Team DAG execution
→ workflow_engine.run_workflow()
→ synthesized module integration
→ stripe_billing_bill_client

team_approval.gate() is invoked by studio_integration_send, but the workflow DAG module-effect path does not pass through that approval boundary.

As a result, a Team policy requiring Stripe approval/quorum does not currently prevent a DAG workflow from reaching a Stripe write effect.

For example, a policy requiring:

provider: stripe
role: approver
quorum: 2

can still result in bill_client being executed without the Team approval gate being called.

Expected behavior:

When a Team policy requires approval or quorum for a provider used by a workflow DAG, no corresponding provider effect should execute until the required approval has been satisfied.

For batch workflows, gating each individual effect may also be insufficient because earlier effects could land before a later approval is denied. A safer boundary is after the exact execution plan has been constructed but before the first external effect:

build exact plan
→ compute action/run hash
→ collect required Team quorum
→ execute effects

The approval should be bound to the exact execution being authorized, including a unique run/occurrence identifier, so an approval cannot be reused for a later run with the same plan.

Workflow/run receipts should also preserve the Team approval evidence needed for later verification.

5 pts

2 replies

Confirmed real — and thank you for the counter-based repro, it made the boundary unambiguous. team_approval.gate() is wired only into the airlock send path (studio_integration_send); the DAG effect path (workflow_engine.run_workflow) consults the execution policy gate (spend / live-off) but never the team approval gate, so a Stripe module effect runs through the DAG without the team quorum being consulted.

Deliberately not hot-patched into this cut: the correct fix is the boundary you described — bind the approval to the exact run (unique occurrence id) after the plan is built but before the first external effect, and stamp the evidence into the run receipt. That touches the core execution path, and rushing it into a security-patch batch risks breaking every workflow run. It's landing as its own change in station-v0.71. Marking confirmed; flips to fixed when v0.71 ships. +5 — approval bypass on a money path.

Fixed in station-v0.71. run_workflow now runs a team-approval gate ONCE over the whole plan (including nested subworkflows) before the first external effect: it enumerates every effect provider, requires the team's quorum on each — bound to this exact run (run_id + plan digest, so a co-sign can't be replayed for a different run) — and returns AWAITING_/BLOCKED_BY_TEAM_APPROVAL without executing if the co-sign is outstanding or denied. The satisfied approval block is sealed into the signed workflow receipt, so a team-approved run verifies offline. Wired into the HTTP dag/run, the MCP run path, and team job offload. Your exact repro is the regression test now: a blocked gate → the effect never runs. Thanks again — the sharpest of the batch.

Sign in to reply.
Team quorum approval is bypassed by DAG module effects — RailCall Community (Bug)