This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
ForrestRun is an embeddable Python library for running AI agent workflows. Define agent pipelines in YAML mixing three step types — agent (LLM reasoning), function (deterministic Python), and tool (external actions) — with built-in persistence, replay, resume, branching, and multi-tier memory.
The primary interface is the Python SDK (from agent_runtime import run_workflow). A CLI (ai) is also provided for running, inspecting, and debugging workflows from the terminal.
Python 3.10+. Minimal dependencies (PyYAML, typing-extensions). All LLM adapters use stdlib urllib — no third-party HTTP libraries. PyPI package name: forrestrun.
# Install (development)
pip install -e ".[dev]"
# Run all tests (797 passing as of 2026-05-09)
pytest -q
# Run a single test file
pytest tests/test_runtime.py
# Run tests matching a keyword
pytest -k "test_replay"
# Lint (ruff configured in pyproject.toml)
ruff check src/ tests/
# SDK usage (primary interface)
from agent_runtime import run_workflow
result = run_workflow("workflows/example.yaml", inputs={...})
# CLI (secondary interface)
ai run workflows/example.yaml
ai inspect <run_id> --steps
ai resume <run_id>
ai replay <run_id> --verify-stateThe Executor (core.py) runs workflows step-by-step. Each step reads from and writes to a namespaced RuntimeState (state.py) with three top-level namespaces:
inputs.*— request/context payloadsteps.<step_id>.*— per-step outputs (write boundaries enforced)runtime.*— metadata, memory tiers, timing
All extensible components are managed through registries (plugin pattern):
- AgentRegistry — discovers agents from
agents/*.yaml - ToolRegistry — registers tool implementations (protocol:
name,description,input_schema,execute) - WorkflowRegistry — version-aware workflow resolution
- LLMRegistry — multi-provider adapters (OpenAI, Anthropic, Gemini)
Three provider adapters in src/agent_runtime/llm/adapters.py, all using urllib.request with exponential-backoff retry (_urlopen_with_retry):
- OpenAIAdapter — native
tool_callsformat, configurablebase_url - AnthropicAdapter — native
tool_usewithinput_schema - GeminiAdapter — native
functionCallformat, parameter aliasing (max_tokens→maxOutputTokens)
A MockAdapter exists for offline/test use (deterministic responses, no HTTP).
SQLite backend with a single persistent connection and explicit transaction management. Every step's input, output, and state snapshot is persisted atomically. Workflow content hashing (SHA256 of raw YAML) blocks unsafe resumes — if the workflow changed since the failed run, resume is rejected with WorkflowIntegrityError.
Four tiers hydrate under runtime.memory.<tier> with deep-merge semantics:
- Working — in-process scratch store with byte budget and sliding-window eviction. Fully implemented.
- Episodic — SQLite-backed run summaries with truncation. Fully implemented.
- Semantic — SQLite FTS5 full-text search with BM25 ranking, plus tag-based queries. Fully implemented.
- Procedural — key/value store with SQLite persistence. Minimal implementation — no auto-learning from episodic history yet (roadmap).
safe_eval() in utils.py uses AST validation (_SafeExprValidator) to sandbox branch condition expressions. Supports comparisons, boolean logic, len/min/max/abs, and string methods. Used by the executor's _resolve_next_step() in core.py and resume.py. Never use raw eval/exec.
The Executor emits five events via _emit() callbacks during workflow execution:
RUN_START— when workflow begins (payload: run_id, workflow_id)STEP_START— when a step begins (payload: run_id, step_id, step_type, attempt_count)STEP_COMPLETE— when a step succeeds (payload includes duration_ms, tool_duration_ms)STEP_ERROR— when a step fails (payload includes error, attempt_count)RUN_COMPLETE— when workflow finishes (payload: run_id, status, error)
These names are stable contracts. Used by CLI for progress display and by the debugger.
src/agent_runtime/— core runtime engine (cli, core, workflow, state, config, agent/, llm/, memory/, storage/, tools/, visualization/)examples/reference_project/— bundled sample project (agents/, workflows/, functions/, tools/, prompts/)tests/— test suite (55 files, 797 tests)documentation/— product docs, architecture, changelogsplanning/— roadmap, feature designs
The repo is library-first. When users run ai init, the runtime scaffolds project directories in their target folder. The primary public API is run_workflow() / run_workflow_async() in __init__.py.
- Async-first internals with sync wrappers — avoid nested event-loop anti-patterns.
- Persist via storage abstractions only — no direct SQLite calls outside
storage/(memory tiers are the exception — they own their own SQLite tables). - Workflow step types (
agent,function,tool) are distinct from agent pipeline step types (model,tool) — do not conflate them. - TODO governance — all TODOs must use
TODO(<category>): ...with categories:roadmap,pain-point,ux,security,eng. Milestone tags likeTODO(0.2.0)are allowed. - Schema versioning — baseline is
v1for all YAML schemas and SQLite metadata. Minor bumps (v1.1) for component-local changes, major bumps (v2) for cross-system changes.
Read before major edits:
documentation/about/architecture.md— execution model, state contract, persistencedocumentation/about/status_2026-03-17.md— module inventory, implementation statusdocumentation/about/gaps_2026-03-17.md— known limitationsdocumentation/changelog/CHANGELOG_2026-03-31.md— recent changesplanning/vision/todos.md— prioritized backlog
handoffs/*- folder with dated handoff filesbacklog.md— living TODO. Tags:[active],[next],[blocked: <reason>], no tag = someday.- Both gitignored.