Contract-First Spawning
Eliminating dependency races with structured agent initialization
What you'll learn
The Dependency Race Problem
When you spawn multiple agents simultaneously and ask them to build parts of a system that depend on each other, you are setting up a race condition — not in the technical concurrency sense, but in terms of assumptions. Without coordination, each agent will make independent decisions about the interfaces between its layer and the layers around it. Those decisions will almost certainly conflict.
Consider this scenario with no contracts:
DatabaseAgent decides: user_id stored as UUID (string)
BackendAgent assumes: user_id is a numeric integer
FrontendAgent expects: userId as a camelCase string field
All three agents:
✓ Write code that compiles
✓ Pass their own unit tests
✓ Report "task complete" to the lead
Integration result: 💥 type errors at every layer boundary
Each agent was working correctly in isolation. The failure is not a bug in any agent's work — it is a coordination failure. The agents simply did not agree on the shape of the data that flows between them before they started building.
This is the problem that contract-first spawning solves. Instead of letting each agent make independent interface decisions and hoping they align, you define the interfaces explicitly before any implementation work begins, and you inject those definitions into each agent's context as a binding constraint.
The Contract-First Principle
"Contract-first spawning" is the term we use in this course for a pattern where upstream agents define their interfaces before downstream agents begin work. A contract, in this context, is a precise specification of the data that flows across a boundary between two agents' work. It might be a database schema, an API request/response shape, a function signature, or a file format. The key properties are:
- It is defined before implementation starts — not discovered during integration
- It is explicit and precise — not "handle payments" but a typed JSON specification
- It is injected into every agent that needs it — not assumed to be known
- It is the lead's responsibility to create and distribute — not left to individual agents
The full pattern runs in five steps:
- The lead analyzes the work and identifies all dependency boundaries
- The lead defines a contract for each boundary (schema, API spec, format)
- Contracts are included in the spawning prompt for each agent that needs them
- Agents implement against their received contracts rather than making assumptions
- Integration succeeds because everyone agreed up front
A Complete Contract Chain: Payment Feature
The clearest way to understand contract-first spawning is to trace it through a real example. Building a payment feature requires three layers — database, API, and frontend — with two contract boundaries between them. This is exactly the scenario where the pattern shines.
The dependency chain runs strictly in one direction:
Database → Backend → Frontend
(Backend cannot finalize its schema until Database defines it)
(Frontend cannot build its UI until Backend defines the API)
Wave 1: DatabaseAgent Defines the Schema Contract
{
"tables": {
"users": {
"id": "UUID PRIMARY KEY DEFAULT gen_random_uuid()",
"token_balance": "INT NOT NULL DEFAULT 0",
"created_at": "TIMESTAMPTZ NOT NULL DEFAULT now()"
},
"purchases": {
"id": "UUID PRIMARY KEY DEFAULT gen_random_uuid()",
"user_id": "UUID NOT NULL REFERENCES users(id)",
"amount": "INT NOT NULL",
"type": "ENUM('purchase', 'refund') NOT NULL",
"created_at": "TIMESTAMPTZ NOT NULL DEFAULT now()"
}
},
"notes": "user_id is UUID everywhere. Use gen_random_uuid() not uuid_generate_v4()"
}
The contract is precise and includes the implementation notes that prevent a backend agent from making the classic uuid_generate_v4() mistake.
Wave 2: BackendAgent Receives Schema, Defines API Contract
# API Contract v1.0 — for FrontendAgent
POST /api/payment/purchase
Request: { package_id: string, quantity: int }
Response: { success: bool, token_balance: int, message: string }
Errors: 400 invalid_package | 402 insufficient_funds | 500 internal
GET /api/balance
Response: { token_balance: int, last_updated: ISO8601 }
# All user_id values are UUID strings.
# All endpoints require Authorization: Bearer {jwt} header.
# Error response shape: { error: string, code: string }
BackendAgent worked directly from the database schema — it never had to guess whether user_id was a UUID or an integer, because the contract told it explicitly.
Wave 3: FrontendAgent Receives API Contract, Implements UI
When all three agents complete, the integration works on the first attempt because every layer was built against the same explicit contracts rather than independent assumptions.
Wave Execution: Sequencing Dependent Agents
Contract-first spawning introduces the concept of wave execution — spawning agents in groups where each wave waits for the previous wave's contracts before beginning implementation. This is different from spawning all agents simultaneously and letting them coordinate in real time.
WAVE 1 (spawned immediately):
DatabaseAgent
└─ implements schema
└─ sends schema contract to lead
└─ transitions to idle
WAVE 2 (triggered when Wave 1 contract received):
BackendAgent
└─ receives: schema contract
└─ implements API
└─ sends API contract to lead
└─ transitions to idle
WAVE 3 (triggered when Wave 2 contract received):
FrontendAgent
└─ receives: API contract
└─ implements billing UI
└─ transitions to idle
Wave execution looks sequential, and for strictly dependent layers it is. But it is not slow — DatabaseAgent's work typically takes 5–10 minutes, after which BackendAgent works for another 5–10 minutes, and FrontendAgent finishes in 5–8 minutes. Total wall time is 15–28 minutes for a feature that would take a single developer 2–3 hours to implement carefully.
For layers that are genuinely parallel — two backend services that don't share a database, for instance — you can spawn both in the same wave and let them work simultaneously.
The Contract-First Prompt Template
The lead's prompt to the team is where contracts are injected. Here is the template structure that ensures every agent receives what it needs:
Create an agent team for [project].
TEAM STRUCTURE:
1. DatabaseAgent: Design schema, write migrations, emit schema contract
2. BackendAgent: Implement API against DB contract, emit API contract
3. FrontendAgent: Build billing UI against API contract
KEY INSTRUCTION:
- Do NOT start implementation until you have received your contract
- When complete, send your contract to me IMMEDIATELY
- Your contract is the source of truth for the next agent
CONTRACTS (injected as each stage completes):
Stage 1 Contract (for BackendAgent — inject after DatabaseAgent completes):
[DATABASE SCHEMA]
User: id (UUID), token_balance (INT), created_at (TIMESTAMPTZ)
Purchases: id (UUID), user_id (FK → users.id), amount (INT),
type (ENUM: purchase|refund), created_at (TIMESTAMPTZ)
Stage 2 Contract (for FrontendAgent — inject after BackendAgent completes):
[API SPECIFICATION]
POST /api/payment/purchase
Request: {package_id: string, quantity: int}
Response: {success: bool, token_balance: int, message: string}
GET /api/balance
Response: {token_balance: int, last_updated: ISO8601}
The "KEY INSTRUCTION" block is doing important work here. Without it, agents may begin implementation based on their prior knowledge of payment systems rather than waiting for the actual contract. The explicit instruction to wait prevents this.
When Contract-First Is Most Critical
Not every multi-agent task needs formal contracts. The pattern adds overhead — the lead spends time defining schemas and API specs that might be implicitly understood in simpler cases. Here is a prioritization guide:
Critical — always use contracts:
- Cross-layer architecture changes (DB + API + Frontend)
- New data models flowing through multiple services
- Any work where type mismatches would cause silent failures
High — use contracts:
- Complex integrations between systems with defined boundaries
- Timing-sensitive data flows (webhooks, events, queues)
- Any work that will be difficult to refactor after the fact
Medium — optional but helpful:
- New features in an established architecture with documented patterns
- Integration points that are already well-specified in existing code
Low — skip contracts:
- Single-agent tasks with no peers
- Purely independent work (code review, documentation, analysis)
- Agents working in completely isolated parts of the codebase
Structured Plan Format
For complex projects, the lead can produce a formal structured plan before spawning any agents. This plan makes the dependency chain and contracts explicit in a machine-readable format that the orchestrator can use to automate wave sequencing:
{
"objective": "Add payment processing",
"team": [
{
"role": "DatabaseAgent",
"responsibility": "Define schema, create migrations",
"dependencies": [],
"contract_deliverable": "Schema contract (tables, types, constraints)"
},
{
"role": "BackendAgent",
"responsibility": "Implement payment API endpoints",
"dependencies": ["Database schema"],
"contract_deliverable": "API specification (endpoints, types, errors)"
},
{
"role": "FrontendAgent",
"responsibility": "Build billing UI",
"dependencies": ["API endpoints"],
"contract_deliverable": "Working UI integrated with API"
}
],
"contracts": [
{
"stage": 1,
"deliverable": "Database Schema",
"recipients": ["BackendAgent"]
},
{
"stage": 2,
"deliverable": "API Specification",
"recipients": ["FrontendAgent"]
}
]
}
A custom /build skill can parse this format to extract the dependency graph, generates wave groups, and automatically injects each contract when the upstream agent marks its deliverable complete.
Contract Validation at Synthesis
After all agents complete, the lead's final responsibility is validating that the implementation matches the contracts. This is the check that catches any drift between what was agreed and what was built:
Lead performs integration validation:
1. Frontend API calls match Backend endpoints?
- POST /api/payment/purchase ✓
- GET /api/balance ✓
- Error handling uses agreed codes ✓
2. Backend database operations match schema?
- user_id referenced as UUID everywhere ✓
- token_balance field name consistent ✓
- Using gen_random_uuid() not uuid_generate_v4() ✓
3. Data formats consistent across all layers?
- user_id is UUID string end-to-end ✓
- Timestamps as ISO8601 strings ✓
4. End-to-end test: purchase flow completes without errors ✓
All contract checks passed. Integration successful.
This validation step is not optional. Even with contracts, agents sometimes make small divergences — a field name that shifts from token_balance to tokenBalance between layers, or an error code that one agent forgot to handle. The validation catches these before they reach testing or production.
Common Contract Mistakes
Most contract-first failures come from three patterns. Knowing them in advance makes them easy to avoid.
Mistake 1: Contracts Too Vague
Bad: "The backend will handle payments"
Good: POST /api/payment/purchase
Request: { package_id: string, quantity: int }
Response: { success: bool, token_balance: int }
A vague contract gives the downstream agent almost as much freedom to make assumptions as having no contract at all. Every field name, type, and endpoint path should be explicit.
Mistake 2: Defining Contracts Before Research
Bad: Define API contracts before reading the existing codebase
Good: Research first → discover patterns and constraints
→ define contracts based on findings
→ spawn agents with informed contracts
A contract written without knowledge of the codebase will likely conflict with existing patterns — naming conventions, error handling styles, authentication mechanisms. Always research first, then define contracts from what you learn.
Mistake 3: Not Injecting the Contract
Bad: "BackendAgent, implement the API. The database schema
was defined by DatabaseAgent."
Good: "BackendAgent, implement the API against this schema:
[full schema text included in the prompt]"
Telling an agent that a contract exists is not the same as giving it the contract. The contract must be included verbatim in the agent's prompt. Do not assume an agent can retrieve it from the task list or infer it from prior messages — include it explicitly every time.
Token Cost of Contract-First
Adding the contract phase to a session costs tokens, but the rework it prevents almost always costs more. A rough accounting for a typical three-layer feature:
- Contract definition (lead): +5,000 tokens
- Contract injection per agent: +1,000 tokens each (~3,000 total)
- Validation pass (lead): +2,000 tokens
- Total contract overhead: ~10,000 tokens
Compare this to the cost of a failed integration: the lead must diagnose the mismatch, potentially re-run one or more agents with corrected context, and re-validate. A single failed integration round typically costs 20,000–50,000 tokens in rework. Contract-first pays for itself the first time it prevents a failure.
The 10,000-token upfront investment in contracts eliminates the 30,000-token rework cost when integration breaks. This is always the right tradeoff for layered architecture work.