How to Implement Tool Prerequisites in Forge Workflows: A Complete Guide

Forge enables conditional tool dependencies through the prerequisites field in ToolDef, enforced at runtime by StepTracker to ensure tools execute only after specified predecessors complete.

Implementing tool prerequisites in workflows allows you to enforce logical ordering constraints on AI agents, preventing operations like file edits before reads. In the antoinezambelli/forge repository, this capability is built into the core workflow engine through explicit declaration in tool definitions and runtime validation in the execution tracker.

Understanding Tool Prerequisites in Forge

Tool prerequisites are conditional dependencies that restrict when a tool may be invoked. A tool can require either general completion of another tool or specific argument-matched executions. This design keeps the LLM prompt clean—prerequisites are not exposed in the tool schema—while providing robust guardrails enforced by the WorkflowRunner.

How Prerequisites Work Under the Hood

Defining Prerequisites in ToolDef

The ToolDef class in src/forge/core/workflow.py carries an optional prerequisites attribute validated during Workflow construction (lines 49-57). This attribute accepts a list containing:

  • Name-only strings: e.g., "read_file" requires any prior successful call to that tool
  • Argument-matched dictionaries: e.g., {"tool": "read_file", "match_arg": "path"} requires a prior call with the same argument value

During workflow instantiation, Forge validates that every prerequisite name exists among the workflow's available tools.

Runtime Enforcement via StepTracker

The StepTracker class in src/forge/core/steps.py maintains execution state through two structures:

  • completed_steps: A set tracking satisfied requirements
  • executed_tools: A dictionary mapping tool names to lists of argument dictionaries from every successful execution

When a tool succeeds, the record() method (lines 34-44) updates both structures, appending the call arguments to executed_tools and adding the tool name to completed_steps.

The Prerequisite Check Logic

Before execution, StepTracker.check_prerequisites() (lines 70-90) evaluates the ToolDef definitions:

  • For name-only prerequisites, it verifies the tool name exists in executed_tools
  • For argument-matched prerequisites, it searches executed_tools for a prior call where the match_arg parameter equals the current call's value

The method returns a PrerequisiteCheck object indicating success or listing missing prerequisites.

Implementing Prerequisites in Your Workflow

Defining Tools with Dependencies

Declare prerequisites directly in your ToolDef instantiation:

from pydantic import BaseModel, Field
from forge import ToolDef, ToolSpec, Workflow

class ReadFileParams(BaseModel):
    path: str = Field(description="Path to the file to read")

class EditFileParams(BaseModel):
    path: str = Field(description="Path to the file to edit")
    changes: str = Field(description="Patch to apply")

read_file = ToolDef(
    spec=ToolSpec(name="read_file", description="Read a file", parameters=ReadFileParams),
    callable=lambda path: open(path).read(),
)

edit_file = ToolDef(
    spec=ToolSpec(name="edit_file", description="Edit a file", parameters=EditFileParams),
    callable=lambda path, changes: f"Edited {path}",
    # Require a prior `read_file` call on the same `path`

    prerequisites=[{"tool": "read_file", "match_arg": "path"}],
)

workflow = Workflow(
    name="file_edit",
    description="Read a file before editing it.",
    tools={"read_file": read_file, "edit_file": edit_file},
    required_steps=[],
    terminal_tool="edit_file",
    system_prompt_template="You may use the provided tools.",
)

The prerequisites list is automatically validated when the workflow is instantiated, ensuring all referenced tools exist.

Runner-Side Enforcement

The WorkflowRunner implements enforcement by consulting StepTracker before each execution:


# Inside WorkflowRunner.run()

tracker = StepTracker(required_steps=workflow.required_steps)

for tool_call in incoming_tool_calls:
    # 1️⃣ Check prerequisites

    prereq_check = tracker.check_prerequisites(
        tool_name=tool_call.tool,
        args=tool_call.args,
        prerequisites=workflow.tools[tool_call.tool].prerequisites,
    )
    if not prereq_check.satisfied:
        # Emit a nudge and skip execution

        send_prerequisite_nudge(missing=prereq_check.missing)
        continue

    # 2️⃣ Execute the tool

    result = workflow.tools[tool_call.tool].callable(**tool_call.args)

    # 3️⃣ Record successful execution

    tracker.record(tool_call.tool, tool_call.args)
    send_tool_result(tool_call.tool, result)

This mirrors the actual implementation: prerequisite failures produce a blocking response, while successful calls update the tracker state.

Handling Prerequisite Violations

When check_prerequisites() returns unsatisfied dependencies, Forge emits a prerequisite nudge (a special PREREQUISITE_NUDGE message type) and blocks the tool call. According to the architectural decision record in docs/decisions/006-tool-prerequisites.md (lines 21-24), this block creates a paired message (TOOL_CALL + PREREQUISITE_NUDGE) that the compaction layer discards together, ensuring the client never sees incomplete executions.

For batch processing, the check evaluates against the pre-batch state. If any parallel call violates a prerequisite, the entire batch is rejected with a single nudge (Phase 1 behavior as documented in lines 23-24 of the same ADR).

Summary

  • ToolDef in src/forge/core/workflow.py defines prerequisites as name-only strings or argument-matched dictionaries, validated at workflow construction
  • StepTracker in src/forge/core/steps.py tracks execution history via completed_steps and executed_tools, with record() updating state after successful calls
  • check_prerequisites() evaluates dependencies before execution, supporting both general and argument-specific matching
  • WorkflowRunner enforces prerequisites by emitting nudge messages that block violations while keeping the agent in the same turn
  • Prerequisites work with parallel tool calls by validating against pre-batch state, rejecting the entire batch if any violation exists

Frequently Asked Questions

What types of prerequisites does Forge support?

Forge supports two prerequisite types in the prerequisites field: name-only strings that require any prior successful execution of a specific tool, and argument-matched dictionaries that require a prior call with matching parameter values. Both types are declared in ToolDef and validated during Workflow instantiation to ensure referenced tools exist.

How does StepTracker validate argument-matched prerequisites?

When checking argument-matched prerequisites, StepTracker.check_prerequisites() searches the executed_tools dictionary for the prerequisite tool name, then compares the current call's argument values against historical executions. It specifically looks for a prior call where the parameter named in match_arg has the identical value, ensuring contextual dependencies like "edit only files that were previously read" are enforced.

What happens when a prerequisite is missing during execution?

If prerequisites are unsatisfied, Forge blocks the tool call and emits a PREREQUISITE_NUDGE message containing the list of missing tool names. This nudge prevents the model from proceeding while keeping the conversation in the same turn, allowing the agent to correct its approach by calling the required prerequisite tools first.

Can prerequisites work with parallel tool calls?

Yes, prerequisites function with parallel calls by validating against the pre-batch execution state. If any tool in a parallel batch violates a prerequisite dependency, Forge rejects the entire batch and issues a single prerequisite nudge. This Phase 1 behavior ensures strict ordering is maintained even when the model attempts multiple simultaneous operations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →