Forked Debug Pattern

When an agent run fails, you don't need to start over. Fork from the last good step, adjust the approach, and resume. The successful early steps are reused — no wasted tokens, no wasted time.

The Problem

An agent completes 7 successful steps — exploring files, reading code, understanding context — then writes buggy code at step 8. Without forking, you'd throw away all 7 good steps and start from scratch.

Step 1: Create a Run That Fails

forked_debug.py
1from opentine import Agent
2from opentine.models.anthropic import Anthropic
3from opentine.tools.fs import read, write, ls
4from opentine.tools.python import execute
5
6agent = Agent(
7    model=Anthropic("claude-sonnet-4-20250514"),
8    tools=[read, write, ls, execute],
9    system="You are a coding assistant. Implement the requested feature.",
10    max_steps=30,
11)
12
13# This run fails at step 8 — the agent wrote buggy code
14run = agent.run_sync("Add CSV export to the reports module")
15run.save("csv_export.tine")
16
17# Check the run status
18print(f"Status: {run.status}")       # Status: error
19print(f"Steps: {len(run.steps())}")  # Steps: 8
20print(f"Cost: {run.total_cost:.4f}")     # Cost: 0.0287

Step 2: Inspect the Failure

Use the CLI to see exactly where things went wrong:

Terminal
tine show csv_export.tine

# Output:
# Run: csv_export
# Status: error | Steps: 8 | Cost: $0.0287 | Duration: 12.4s
#
# [think]  Planning CSV export feature...
# [tool]   ls("src/")
# [tool]   read("src/reports/generator.py")
# [model]  Understanding the report structure...
# [tool]   write("src/reports/csv_export.py")      ← step 5
# [tool]   read("src/reports/csv_export.py")
# [tool]   execute("python -m pytest tests/")
# [error]  Test failed: csv_export missing header row

The agent explored the codebase (steps 1-4), wrote the implementation (step 5), then the tests failed (step 7). The exploration steps are perfectly good — only the implementation needs to change.

Step 3: Fork and Fix

Fork from step 3 (after understanding the codebase) and resume with a hint about what went wrong:

forked_debug.py
1from opentine import Run, Agent
2from opentine.models.anthropic import Anthropic
3from opentine.tools.fs import read, write, ls
4from opentine.tools.python import execute
5
6# Load the failed run
7run = Run.load("csv_export.tine")
8
9# Fork from step 3 — right after the agent understood the codebase,
10# but before it wrote the buggy implementation
11forked = run.fork(from_step_id="step_3_id")
12
13# Create a new agent with a hint about the bug
14agent = Agent(
15    model=Anthropic("claude-sonnet-4-20250514"),
16    tools=[read, write, ls, execute],
17    system="""You are a coding assistant. Implement the requested feature.
18Important: CSV files must include a header row as the first line.""",
19)
20
21# Resume from the fork point — steps 1-3 are reused
22fixed = agent.resume(forked)
23fixed.save("csv_export_v2.tine")
24
25print(f"Status: {fixed.status}")  # Status: completed
26print(f"Cost: {fixed.total_cost:.4f}")  # Only charged for new steps

The forked run reuses steps 1 through 3 from the original. Only the new implementation and testing steps incur cost.

Step 4: Compare Runs

Use tine diff to see exactly how the two runs diverge:

Terminal
# Compare the original and forked runs
tine diff csv_export.tine csv_export_v2.tine

# Output shows which steps diverged and what changed

Automated Debug Loop

You can automate this pattern: fork from the last good step, feed the error message into the system prompt, and retry. This is especially useful for coding tasks where test failures provide clear feedback.

debug_loop.py
1from opentine import Run, Agent
2from opentine.models.anthropic import Anthropic
3from opentine.tools.fs import read, write, ls
4from opentine.tools.python import execute
5
6def debug_loop(tine_file: str, max_attempts: int = 3):
7    """Fork and retry until the run succeeds or we hit the attempt limit."""
8    run = Run.load(tine_file)
9
10    for attempt in range(max_attempts):
11        if run.status == "completed":
12            print(f"Succeeded on attempt {attempt + 1}")
13            return run
14
15        # Find the last successful step
16        steps = run.steps()
17        last_good = None
18        for step in steps:
19            if step.kind == "error":
20                break
21            last_good = step
22
23        if last_good is None:
24            print("No successful steps to fork from")
25            return run
26
27        # Fork from the last good step
28        forked = run.fork(from_step_id=last_good.id)
29
30        agent = Agent(
31            model=Anthropic("claude-sonnet-4-20250514"),
32            tools=[read, write, ls, execute],
33            system=f"Previous attempt failed. Error: {steps[-1].output}. Try a different approach.",
34        )
35
36        run = agent.resume(forked)
37        run.save(f"debug_attempt_{attempt + 1}.tine")
38
39    return run
40
41result = debug_loop("csv_export.tine")

Each iteration forks from the last successful step and includes the previous error in the system prompt. The agent gets a fresh chance to fix the issue without losing the work from earlier steps.

When to Use This Pattern

  • Test failures:The agent wrote code that doesn't pass tests. Fork from before the write step and try again with the error context.
  • Wrong approach: The agent went down the wrong path at step 5 of 15. Fork from step 4 instead of re-running all 15 steps.
  • Model comparison: Fork the same run at the decision point and resume with different models to compare their approaches.
  • Prompt iteration: Fork from the same point with different system prompts to find the best instruction for a tricky step.

Next Steps