A small, organized project
sourceRoot and world in firedrill.json. Resource files are discovered
recursively by their .tool, .scenario, .target, .drill, and .suite suffixes.
References use stable IDs inside the files, not their display titles.
Use JSON for every resource if that is easier for you or your coding agent.
YAML/YML is an equivalent authoring choice; you do not need to mix formats.
Executable behavior remains JavaScript or TypeScript. Markdown can explain the
setup, but arbitrary prose is not an executable world definition.
The complete quickstart is the smallest runnable example. It
deliberately uses a simple deterministic client to teach the format;
passing that example is not evidence about an LLM’s quality.
Tools: declaration and implementation
A tool has two parts:- Declaration: the operation names, input/output shapes, record schemas, permissions needed, and any declared errors, events, faults, or HTTP routes.
- Implementation: the function that reads or updates fake state and returns the response. This is where a simulated action’s consequences are written.
workspace.tool.yaml declares records.set. Its neighboring
behavior.js
stores the requested value and returns it. The compiler does not invent this
implementation from a name or description.
An operation can be reached through direct functions, HTTP, MCP, or Firedrill’s
CLI adapter. Those interfaces reach the same implementation and state. Custom
HTTP routes can reproduce a particular request shape; they are not automatically
generated replicas of every external API. Use your normal test runner’s mocks
for existing imported functions and SDK methods: mocking guide.
You or a coding agent can write a tool, or select an installed reusable package.
Keep tool-specific behavior in that tool, not in the framework. Tools are trusted
local test code, not sandboxed code from an arbitrary stranger.
Schema and fake data
The tool’s declared state namespaces define record schemas. The world and scenarios supply records matching them. There is no required second schema database to maintain. For example, the quickstart’srecords namespace requires an integer value.
Its empty scenario puts a record named primary there with value 0. Calling
the fake operation changes that record to 7.
At execution time, Firedrill materializes the selected starting conditions into
one isolated SQLite world per attempt. All tools in that world share the same
state, clock, pending events, and ordered activity journal. SQLite is Firedrill’s
storage for the synthetic surroundings—not a replacement for the agent’s own
Postgres, MongoDB, filesystem, or other internal storage.
Runtime changes never rewrite your source files. Editing source changes future
builds; it does not overwrite a retained run. A whole-world reset restores the
initial state of that world. A scoped reset affects selected tools and deliberately
preserves global time and prior evidence. Exact reset behavior.
People and permissions
An actor is an identity that can perform granted tool operations. Define it in the world or a scenario, then select it in a drill or timeline interaction. Its optional shortdescription helps a reader understand the role.
Persona attributes describe that identity—for example, experience or customer
tier. Attributes do not grant access, run a model, or automatically change the
agent’s prompt. Use explicit grants for permissions, and explicitly pass any
needed context through your task or test adapter.
Data records for customers or users are different: they are records the tools
return or mutate. A customer record does not need to be a runtime actor unless
that customer will actually perform actions in the drill.
You can keep one actor and omit persona details for a simple test. When a scenario
replaces an actor with the same ID, it replaces the complete actor; repeat the
description and permissions that should remain.
Scenarios, targets, and drills
A scenario changes starting data, identities, declared faults, pending events, time, or tool overrides. It can be reused by several drills. It does not itself start your agent. A target tells Firedrill how to reach the agent: import a repository module, start a command, call a local HTTP endpoint, or invoke a callback owned by the test harness. It also declares the tool interfaces the agent may receive. An external callback target needsrunDrills({ agent }); the CLI cannot invent
that callback. Connection options.
A drill selects a target, scenario, and actor, supplies the task, and states
what must be true afterward. The quickstart drill
checks both that the action happened once and that the stored value is correct.
The returned message alone cannot satisfy those checks.
Longer drills can schedule multiple interactions and actors over virtual time,
with checkpoints, stop policies, and budgets. They use the same tool behavior
and result format as a short test. Advancing the world’s clock does not speed
up a remote model or change the operating system’s clock.
What to commit
Commit the configuration, world resources, behavior modules, and test adapters. Keep generated builds, SQLite files, captures, and reports under.firedrill/
and out of Git. firedrill init --path ... adds that ignore rule. Keep provider
keys in your normal ignored environment or secret store, never in source or
captured logs. Share report files deliberately: they can contain agent output
and test data.