Reproduction steps:
- Use a clean RailCall Station v0.78 workspace.
- Run one
type="agent"node with a tool allowlist and a zero-spend budget:
{
"id": "a",
"type": "agent",
"tools": ["fake_read"],
"budget": {"max_steps": 2, "max_tool_calls": 1, "max_spend_cents": 0}
}
- Inject an LLM response that returns a final answer on the first turn with
tool_calls=[]. - Run the real
workflow_engine.run_workflow(..., allow_live_effects=true). - Pass the resulting node receipt to the real
receipt_summary.summarize()helper.
Expected:
tool_calls=0,action_leaves=[], andspent_cents=0mean no integration was planned or applied.- The receipt must say
external_api_touched=false. - The summary must say that nothing left the station.
Actual:
workflow outcome = "COMPLETED"
tool_calls = 0
spent_cents = 0
action_leaves = []
external_api_touched = true
The summary then reported:
Touched: 1 actions · an internal step
Control result:
- The same no-tool agent with
allow_live_effects=falsereportedexternal_api_touched=false. - No provider call, socket, credential, or external write occurred in either run.
Root cause:
workbench/workflow_engine.py:962-969sets action receipts'external_api_touchedfrombool(allow_live_effects).workbench/workflow_engine.py:997-1003sets the node-level agent receipt from the same flag, without checkingtool_calls,action_leaves, or the actual integration mode.workbench/primitives/receipt_summary.py:94-123treatsexternal_api_touchedas the fact used to render the “Touched” line.
Impact:
A signed, integrity-valid receipt and its standard summary can falsely attest that an external effect occurred when the agent produced only a local answer. This corrupts operational/audit interpretation of whether data or an effect left the station. No external request occurred in the reproduction.
Suggested fix:
Derive external_api_touched from the action receipts that actually executed in live mode. An agent with no tool calls or only mock/pure actions must produce false.
★ 3 pts