AI coding

One Agent, One Database: Fixtures and Seed Data for Parallel Work

Antonio Evans8 min read

A clean, technical diagram showing a central database server with multiple schemas labeled 'branch_1', 'branch_2', 'branch_3'.
Key points

Key points

  1. 1Git worktrees isolate files and branches but share nothing else, so parallel agents still collide on ports, dependencies and the database.
  2. 2Give each agent its own schema or database branch, created at the same moment as the worktree and torn down with it.
  3. 3Wrap each test in a transaction that rolls back so a run leaves no rows behind, and reserve truncation or re-seeding for the few cases a transaction cannot cover.
  4. 4Name fixtures by story ("user with expired card"), keep them in version control, and make every reset script rebuild the database from the same migration and seed path.

Why worktrees are only half the isolation

A git worktree gives each agent its own working directory tied to the same repository, so one session can build a feature while a second fixes a bug without editing the same files (Claude Code docs). Worktree creation is cheap because the object database is shared, and the checked-out branch is private to that directory (MindStudio). None of that allocates a new database, port, container name or provider rate limit: "A Git worktree separates files and the index" and nothing else (Superset).

The collision that follows is predictable. Two agents point at the same PostgreSQL instance, agent A runs a migration, and agent B's schema is now out of sync; if both seed data, they trample each other's state (Trigger.dev). Teams running five agents in parallel describe database collisions as the silent killer, because every agent's end-to-end validation writes to the same rows (Cole Medin). The fix mirrors git exactly: a database branch per worktree, created with it and deleted with it, so an agent can run migrations and wipe tables freely because the blast radius stops at its own branch (database branching notes).

A decision rule that saves weeks: if the task never reads or writes persistent state, a worktree is enough. The moment a task runs a migration, a seed script or an integration test, it needs its own data context before it starts, not after it fails.

Technique 1: schema-per-branch

Schema-per-branch means one physical database server, one namespace per agent. Agent A gets agent_a, agent B gets agent_b, and each connection string includes a search_path or schema option so every unqualified table name resolves inside that namespace. Migrations then run against separate sets of tables and cannot alter each other.

A minimal setup on PostgreSQL looks like this:

# one schema per agent, created before the agent starts
AGENT=agent_a
createdb --host=db.internal --owner=app parallel_dev
psql --host=db.internal -d parallel_dev -c "CREATE SCHEMA IF NOT EXISTS $AGENT AUTHORIZATION app;"

# point the agent's environment at its namespace
export DATABASE_URL="postgres://[email protected]:5432/parallel_dev?options=-csearch_path%3D$AGENT"

From the agent's point of view it is the only tenant. Migration tools write schema_migrations inside agent_a, seed scripts fill agent_a.orders, and teardown is a single DROP SCHEMA agent_a CASCADE. The cost is one CREATE SCHEMA per task and one drop at the end, which is negligible next to the setup time of a dependency install.

Two caveats decide whether this technique fits. First, schema-per-branch shares the server, so a heavy query from one agent still competes for CPU and connections with the others; keep a connection limit per schema and watch memory as the trigger.dev team warns when they describe per-worktree services multiplying (Trigger.dev). Second, extensions and roles are cluster-wide, so an agent that needs CREATE EXTENSION must run it once as an administrator rather than inside its own schema. If your application hardcodes schema names in SQL, or you need isolated storage and connection limits, move to a database-level branch instead, either through PostgreSQL's template-database copy or a managed branching service (SpecStory).

Isolation levelWhat you createCostBest forMain limit
Shared database, shared schemaNothingZeroRead-only explorationAny write collides
Schema-per-branchCREATE SCHEMASecondsMost agent test workShared server resources
Database branch (template copy)CREATE DATABASE ... TEMPLATESeconds to minutes, grows with templateIntegration tests needing full isolationCopy cost per branch
Copy-on-write branch (managed)Provider commandNear instantHigh iteration frequencyProvider lock-in
Separate container or instanceDocker runTens of seconds plus image pullSecrets, network or version differencesDisk and memory multiply

Technique 2: transaction rollback per test

The fastest reset is the one that never touches disk. Wrap each test in a transaction, run it, then roll back. One team replaced per-test truncation of more than 20 tables with rollback and reported their 447-test suite falling from 245.87 seconds to 2.84 seconds (dev.to). The old approach paid roughly 40 to 60 milliseconds per test in truncate operations, sequence resets and connection overhead; rollback costs a few milliseconds of memory work.

The mechanics matter more than the number. Setup that loads schema and reference data should run outside the transaction, once, so it is not repeated per test. Only the test body runs inside it. Teardown that clears caches runs after the rollback, because the transaction that the test saw is already gone. Spring's test framework does exactly this declaratively: a @Transactional test is rolled back by default, and method-level lifecycle hooks such as @BeforeEach run inside the test-managed transaction while class-level hooks do not (Spring Framework).

Rollback has hard limits, and knowing them prevents silent false positives:

  • Code under test that commits. If the application opens its own transaction and commits, the outer rollback cannot undo it. Spring's documentation warns that propagation settings other than REQUIRED or SUPPORTS behave unexpectedly in test-managed transactions, and that preemptive timeouts run the test in a new thread where the transaction is not visible, so writes get committed instead of rolled back (Spring Framework).
  • Background jobs and queues. A worker that picks up a job on another connection writes outside your transaction.
  • Anything testing the commit itself. Migration tests, constraint checks after commit, and end-to-end flows need a real reset.

For those cases, drop the schema or re-copy the database from a seeded template, which is the same pattern used to give each task a disposable database built from a parent filled by a seed script (SpecStory).

Technique 3: named fixtures

A fixture is a named data set with a purpose. "User with expired card" tells a reviewer and an agent what the test assumes; "user_8391" does not. Named fixtures earn their keep in parallel runs because two agents can each insert customer_with_expired_card into their own schema without guessing at ids or counts.

Keep fixtures as code in version control, one file per story, and load them by name. A practical layout:

fixtures/
  customers.yaml
  catalog.yaml
  orders_expired_card.yaml
  orders_partial_refund.yaml
# load only what a test declares, inside the test's schema
fixtures.load("customers", "orders_expired_card")

Three rules keep named fixtures from becoming a second database:

  1. Fixed, readable identifiers. Assign primary keys explicitly in the fixture file (customer 1001, order 5001) so assertions can name rows instead of querying for them. Explicit ids also make failure output legible when five agents' logs are interleaved.
  2. One story per fixture. A fixture that seeds customers, products, carts and refunds couples every test that touches any of them. Small fixtures compose; large ones rot.
  3. Fixtures are additive, tests are not. A test may add rows on top of a fixture, but it never edits the fixture. If a test needs different starting data, that is a new fixture, not a mutation of the old one.

Named fixtures also solve the review problem that parallel agents create. When three agents produce diffs, a fixture name in a test file tells the reviewer which scenario the agent believed it was covering, which is exactly the kind of intent a diff of code alone does not carry (Augment Code).

Technique 4: resets between runs

A reset script should be boring: drop the namespace if it exists, create it, run migrations to head, load the base fixture set. The same script runs on a developer laptop, in the agent sandbox and in CI, so a migration that fails for one agent fails identically for all of them.

#!/usr/bin/env bash
set -euo pipefail
SCHEMA="${1:?usage: reset.sh <schema>}"

psql "$ADMIN_URL" -v ON_ERROR_STOP=1 <<SQL
DROP SCHEMA IF EXISTS "$SCHEMA" CASCADE;
CREATE SCHEMA "$SCHEMA" AUTHORIZATION app;
SQL

export DATABASE_URL="${BASE_URL}?options=-csearch_path%3D${SCHEMA}"
migrate up
seed --fixture customers --fixture catalog

Three habits make resets trustworthy at parallel scale. First, version the reset separately from application code only if you must; the most common failure is a reset script that drifts from the migrations and seeds a column that no longer exists. Second, reset at the start of a task, never at the end, so a crashed agent cannot leave a half-cleaned schema that the next agent inherits. Third, delete the schema and the worktree in the same cleanup step, the discipline that keeps branches from accumulating as orphans (database branching notes).

Isolation of the database is not the same as isolation of the environment. An agent that can read your .env can reach the production connection string regardless of which schema it is supposed to use. Sandboxing guidance is blunt about the blast radius: a coding agent with filesystem and shell access is an execution environment, and the goal is to bound what it can touch when it is wrong, not to assume it never will be (Penligent). Production credentials, cloud keys and deploy hooks do not belong in any workspace an agent can write to, which is the central argument for keeping agents in an isolated runtime rather than on a laptop with broad permissions (Robert Brennan, OpenHands).

A worked example with numbers shows why the combination matters. Suppose you run three agents, each with 120 tests, and each test touches six tables. With truncation at roughly 40 to 60 milliseconds per test from the earlier report, cleanup alone costs 3 agents × 120 tests × 50 milliseconds, about 18 seconds per full run. With rollback at 2 to 4 milliseconds, cleanup drops to under 1.5 seconds. That difference compounds every time an agent reruns the suite after a fix, which in practice is many times per task.

Putting the four techniques together

The techniques layer rather than compete. Schema-per-branch separates agents; transaction rollback separates tests inside one agent's run; named fixtures give both a shared vocabulary; the reset script guarantees that every agent starts from the same known state.

LayerSeparatesImplementationWhen it is not enough
WorktreeFiles and branchesgit worktree addAnything touching a database or port
Schema or database branchSchema, migrations, seed dataCREATE SCHEMA or template copyShared server resources, cluster-level objects
Transaction rollbackIndividual testsWrap test body, roll backCode that commits, background workers
Named fixturesIntent and starting dataVersioned fixture filesFixtures that mutate each other
Reset scriptRuns from each otherDrop, create, migrate, seedNever, if it is run every time

The order of operations for one agent task is then fixed: record the base commit, create the worktree, create the schema or branch, run the reset, hand the agent its connection string, let it work, run tests under rollback, and delete the schema and worktree together. Record the base commit with git rev-parse HEAD so every workspace shares a known comparison point, and give each agent a bounded acceptance check that says what to run and what not to touch (Superset). Limiting each agent to a spec-scoped task with explicit file ownership is the other half of the coordination, because isolation prevents collisions during work but not incompatible changes at merge time (Augment Code).

Where multiple agents must agree on a shared contract, such as an API shape or a schema change, consider having them negotiate that contract before any of them writes code. The spec-first pattern routes proposals through a mediator so conflicts surface as disagreements about intent rather than as broken files (Spec-Driven multi-agent workflow). That is cheaper than resolving a migration conflict after three agents have each altered the same table.

When your agents need to generate or check data with a model as part of the test loop, such as classifying fixture records or generating edge-case rows, Quanta Inference puts text, image, video, audio and embedding models behind one OpenAI-compatible API key, so the seed tooling talks to one endpoint instead of several SDKs. Teams that want the setup work itself handled end to end, from worktree scripts to reset pipelines, can put a Quanta expert on the job through Quanta Experts.

FAQ

FAQ

Do I need a separate database per agent, or is a separate schema enough?

A separate schema is enough for most work. It isolates tables, migrations and seed data inside one server at the cost of a single CREATE SCHEMA statement. Move to a separate database or a copy-on-write branch when you need isolated connection limits, different extensions, or storage-level separation, which a schema cannot provide because those objects are cluster-wide (SpecStory).

Why did my rollback-based test still leave rows in the database?

The most common cause is code under test that commits its own transaction, or a test framework running the test body on a new thread for a timeout, which detaches it from the test-managed transaction. Spring's documentation lists both cases explicitly and notes that writes in that situation are committed even though the framework rolls back (Spring Framework). Check propagation settings and timeout behavior first.

How do I stop agents from stepping on each other's ports and dependencies?

Worktrees do not allocate ports, so give each dev server a distinct port through the project's supported configuration and use separate test data for any task that writes (Superset). Each worktree is a fresh checkout and needs its own dependency install, which is why the trigger.dev team found per-worktree setup costs adding up quickly in a monorepo (Trigger.dev).

Should fixtures live in the repository or be generated at runtime?

Keep them in the repository as named, versioned files. Generated data is fine for volume tests, but named fixtures give reviewers and agents a stable vocabulary for scenarios and keep assertions readable when several agents' logs are interleaved. Load only the fixtures a test declares so that changing one story does not ripple through every test that touches the database.

What is the minimum isolation for a single agent running migrations locally?

At minimum, a schema that the agent alone owns plus a reset script that drops and rebuilds it. Without that, one failed migration leaves the database in a state that is neither the old schema nor the new one, and a git revert of the code does nothing to recover the data layer because the migration already ran (PingCAP).

Sources

Sources

  1. Run parallel sessions with worktrees - Claude Code Docs
  2. How to Run Parallel AI Coding Agents With Git Worktrees - MindStudio
  3. Run three AI coding tasks in parallel, then review each change - Superset
  4. We ditched worktrees for Claude Code. Here's what we use instead
  5. Parallel Claude Code + Git Worktrees: This Setup Will Change ...
  6. cole-medin-knowledge-base/concepts/database-branching ...
  7. What is database branching? | AI coding agents
  8. How Database Transaction Rollback Revolutionized Test ...
  9. Transaction Management :: Spring Framework
  10. How to Run a Multi-Agent Coding Workspace (2026) | Augment Code
  11. Sandboxes for Coding Agents - Penligent
  12. Sandboxes Aren't Optional: 4 Ways to Isolate Coding Agents - YouTube
  13. Multi Agent Orchestration Without Conflicts - YouTube
  14. Database Branching for AI Agents: TINE Solves Schema ...
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles