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

> Learn how to implement tool prerequisites in Forge workflows. Ensure tools run only after predecessors complete with this complete guide for the antoinezambelli/forge repository.

- Repository: [Antoine/forge](https://github.com/antoinezambelli/forge)
- Tags: how-to-guide
- Published: 2026-05-22

---

**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`](https://github.com/antoinezambelli/forge/blob/main/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`](https://github.com/antoinezambelli/forge/blob/main/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:

```python
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:

```python

# 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`](https://github.com/antoinezambelli/forge/blob/main/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`](https://github.com/antoinezambelli/forge/blob/main/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`](https://github.com/antoinezambelli/forge/blob/main/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.