Skip to content
Documentation GitHub
Workflow Issues

Agent Team Orchestration at Scale: Wave-Based Execution with File Ownership

Agent Team Orchestration at Scale: Wave-Based Execution with File Ownership

Problem

Large feature implementations and code review remediations involve many agents working in parallel on overlapping codebases. Without careful coordination, agents:

  • Overwrite each other’s changes to shared files
  • Make incompatible changes that break compilation
  • Run out of context budget before committing their work
  • Create merge conflicts that are difficult for the leader to resolve

The INK-72 Multi-Device Sync epic required:

  • Implementation: 6 agents across 6 dependency-ordered waves (13 issues, 33 points)
  • Review: 8 specialized review agents in parallel
  • Remediation: 4 agents across 2 dependency-ordered waves (57 findings)

Root Cause

Uncoordinated parallel execution fails because:

  1. File contention: Multiple agents editing the same file creates race conditions
  2. Dependency chains: Later agents need output from earlier agents (trait changes, migrations, etc.)
  3. Context limits: Complex agents hit context window limits before finishing, losing uncommitted work
  4. Compilation coupling: Rust’s type system means a trait change in one crate breaks all dependents

Solution

1. Wave-Based Execution with Dependency Gates

Organize agents into waves where each wave runs in parallel, and waves execute sequentially:

Wave 1: [schema-agent, loro-agent] ← Foundation (parallel)
↓ Gate: cargo check passes, leader commits
Wave 2: [auth-agent, infra-agent] ← Auth + Push (parallel)
↓ Gate: cargo check passes, leader commits
Wave 3: [sync-core-agent] ← Pull (serial, needs Wave 2 traits)
↓ Gate: cargo check + test passes, leader commits
...

Gate criteria: Between waves, the leader runs compilation and tests, commits all work, then launches the next wave. This ensures each wave starts from a known-good state.

2. Strict File Ownership Boundaries

Each agent gets an exclusive set of files. No two agents touch the same file in the same wave:

## Wave 2 File Ownership
**sync-engine-agent** owns:
- crates/application/src/sync/sync_engine.rs
- crates/application/src/sync/push_block_updates.rs
- crates/application/src/sync/pull_block_updates.rs
- crates/application/src/sync/services.rs (batch push method ONLY)
- apps/desktop/src-tauri/src/sync.rs
**frontend-test-agent** owns:
- apps/desktop/src-react/lib/realtime-sync.ts
- crates/application/src/sync/disable_workspace_sync.rs
- crates/application/src/sync/enqueue_block_update.rs
- crates/application/src/auth/get_current_user.rs (logging ONLY)
DO NOT MODIFY files owned by other agents.

When a file needs changes from multiple concerns (e.g., services.rs needs both a batch push method and a metadata trait change), either:

  • Assign it to one agent with both responsibilities, or
  • Split changes across waves (Wave 1 agent does the trait change, Wave 2 agent adds the method)

3. Commit Strategy: Leader vs Self-Wiring

3a. Leader Commits All Work (Default)

Agents never commit directly. The leader:

  1. Waits for all agents in a wave to complete
  2. Reviews the changes (quick scan for obvious issues)
  3. Runs tools/precheck.sh --fix (auto-fixes formatting, then validates gitleaks, clippy, tests, oxlint, typecheck)
  4. Stages files in logical groups
  5. Creates discrete commits with clear scope

This prevents:

  • Agents creating half-baked commits that break the build
  • Context-exhausted agents losing uncommitted work
  • Merge conflicts from concurrent git operations
Terminal window
# Leader commits Wave 1 in two logical groups:
git add supabase/ crates/infrastructure/sqlite/src/sync/metadata_storage.rs ...
git commit -m "fix(sync): schema and data integrity review remediation (INK-72)"
git add apps/desktop/src-tauri/src/state.rs crates/infrastructure/supabase/...
git commit -m "fix(auth): auth singleton, config system, and layer violation remediation (INK-72)"

Use this mode when:

  • Agents may hit context limits before finishing
  • Changes are mutative (refactoring existing signatures, restructuring init logic)
  • Multiple agents touch closely related files across waves
  • You want maximum leader control over commit structure

3b. Self-Wiring Mode (bypassPermissions)

With bypassPermissions mode, agents modify framework files (state.rs, main.rs, commands/mod.rs, lib.rs) directly and commit their own work. The leader’s wave-gate role simplifies from “apply diffs and commit” to “verify compilation, run tests, regenerate artifacts.”

Agent workflow in self-wiring mode:

  1. Implement domain/application/infrastructure changes
  2. Wire framework files directly (add fields to AppState, register commands in main.rs, add mod declarations)
  3. Create WIRING.md as a verification checklist (not a diff to apply)
  4. Run cargo check, cargo clippy, cargo test
  5. Commit their own work with discrete commit messages

Leader gate workflow:

  1. Run tools/precheck.sh --fix (auto-fixes formatting, validates clippy, tests, oxlint, typecheck)
  2. Regenerate artifacts (pnpm generate:types)
  3. Clean up WIRING.md files
  4. Commit any leader-only work (bindings, cleanup)

Use this mode when:

  • Changes are additive (new module, new field, new command registration)
  • Each agent’s framework changes don’t overlap within a wave
  • bypassPermissions mode is enabled for all agents
  • Agent prompts are focused enough to avoid context exhaustion

Key insight: Self-wiring works because framework wiring is typically additive — adding a new field to AppState, appending a .invoke_handler() call, adding a mod declaration. Mutative changes (refactoring state initialization, changing handler signatures, restructuring DI) remain leader-owned.

WIRING.md as hedge: Even in self-wiring mode, agents still create WIRING.md. It serves as a verification checklist at the gate and a fallback if an agent’s self-wiring fails to compile.

4. Agent Prompt Design

Each agent’s prompt includes:

  • Explicit file ownership list with “ONLY modify these” and “DO NOT MODIFY” sections
  • Pre-existing state context (“Wave 1 committed, codebase compiles, all tests pass”)
  • Verification instructions (“Run cargo check, cargo test, cargo clippy when done”)
  • Communication protocol (“Send message to team-lead with summary and test results”)
  • Bypass permissions mode to avoid interactive approval delays

5. Review Agent Specialization

For code reviews, use 5-8 specialized agents rather than one general reviewer:

AgentFocusValue
code-quality-reviewerClarity, patterns, DRYCatches style issues
security-sentinelOWASP, auth, data protectionCatches vulnerabilities
performance-oracleN+1, algorithms, resourcesCatches bottlenecks
architecture-strategistLayers, coupling, patternsCatches structural issues
test-reviewerCoverage, quality, edge casesCatches test gaps
domain-expertLanguage idioms, frameworkCatches language-specific issues
data-integrity-guardianMigrations, transactionsCatches data safety issues
observability-analystLogging, monitoringCatches operational gaps

Each agent reviews the same diff but through its specialized lens. Findings are collected by the leader and deduplicated.

Implementation Notes

Team Sizing

  • Implementation: 1 agent per work stream (e.g., auth, sync engine, frontend). Avoid more than 6 concurrent agents — context management overhead grows.
  • Review: 5 agents for standard reviews, 8 for deep reviews. All run in parallel since they’re read-only.
  • Remediation: Group findings by subsystem. 2-4 agents with strict file boundaries.

Handling File Overlap in Remediation

When review findings span shared files, the leader must decide:

  1. Split by subsystem (preferred): Group findings that touch the same files into one agent
  2. Split by wave: If findings have dependencies, put dependent changes in later waves
  3. Accept pragmatic grouping: Sometimes clean separation isn’t possible. Group by “compiles together” rather than by issue ID

Monitoring Agent Progress

  • Check TaskList after each agent completes
  • Use SendMessage for status queries (don’t use terminal tools to spy on agents)
  • Set realistic expectations: each agent takes 3-8 minutes for moderate work
  • Be patient with idle agents — idle after message delivery is normal

Known Pitfalls

  • Late-phase agents hitting context limits: Agents with large prompts or many file reads exhaust context. Mitigation: keep agent prompts focused, use bypassPermissions mode, plan for leader to commit on their behalf. If exhaustion occurs, use the continuation agent pattern — see docs/solutions/workflow-issues/agent-context-exhaustion-and-continuation.md.
  • Stale team members after context loss: If the leader session loses context, old team agents remain as zombie processes. Always send shutdown requests before creating new teams.
  • Test count as progress indicator: Track cargo test count across waves. If test count doesn’t increase after a “test coverage” agent runs, investigate.

Prevention

Orchestration Checklist

  • Dependencies mapped between issues/findings before agent assignment
  • Waves ordered by dependency (foundation first)
  • File ownership explicitly assigned per agent (no overlaps within a wave)
  • Agent prompts include “DO NOT MODIFY” lists
  • Commit mode decided per wave (leader-commits vs self-wiring)
  • Leader commits at wave boundaries (not agents) — or verifies agent commits in self-wiring mode
  • Gate criteria defined for each wave (compilation, tests, etc.)
  • Gate criteria includes tools/precheck.sh --fix from main repo
  • Shutdown protocol for team cleanup

Scaling Guidelines

Epic SizeAgentsWavesReview AgentsNotes
1-3 issues1-212 (quick)
4-8 issues2-42-35 (standard)
9-15 issues4-84-68 (deep)Combine polish/verification issues in later waves
15+ issuesSplit into sub-epics first

Results

INK-72 Multi-Device Sync (Implementation + Review + Remediation)

The wave-based approach delivered:

  • 16 commits (12 feature + 4 remediation) — all clean, discrete, single-concern
  • 921 tests passing (14 new tests added during remediation)
  • 57 review findings addressed (12 P1, 25 P2, 20 P3 — all fixed)
  • Zero merge conflicts between agents
  • Zero compilation failures at wave gates

INK-129 Async Migration (Review + Remediation)

Post-merge review of async migration (5 commits, 40 files, +2134/-774 lines):

  • Review: 5 specialized agents in parallel identified 15 findings (3 P1, 7 P2, 5 P3)
  • Remediation: 11 agents across 3 waves, 15 Linear issues (INK-190–204)
  • 13 commits — all discrete, single-concern, passing pre-commit hooks
  • 24 new tests (8 composite unit tests + 16 integration tests)
  • All 15 issues closed as Done
  • Zero merge conflicts between agents
  • Key patterns documented: SQLite queue full-table scan anti-pattern, Rust async lock anti-patterns (mutex across await + TOCTOU token refresh)

Notable: Wave 3 had a compile-time dependency between connection-cache agent and two downstream agents (composite-tests, e2e-tests). The dependency was resolved via inter-agent messaging — the connection-cache agent fixed remaining Self::open_db() references, unblocking the other agents without leader intervention.

INK-232 Types System (Implementation — Self-Wiring Mode)

Full-stack types system epic using self-wiring mode for the first time:

  • 9 issues, 4 waves, 8 agent instances
  • 6 commits (4 feature + 1 chore + 1 leader gate)
  • Agents modified state.rs, main.rs, commands/mod.rs, lib.rs directly
  • Zero merge conflicts between agents
  • Zero compilation failures at wave gates
  • WIRING.md created by agents as verification hedge, deleted after leader confirmed compilation
  • Key difference from prior epics: leader role shifted from “apply diffs” to “verify completeness”

Wave structure:

  1. Wave 1 (2 agents): Domain entity + migration/repository — foundation layer
  2. Wave 2 (2 agents): Use cases + Tauri commands — application + framework wiring
  3. Wave 3 (2 agents): Frontend components + collection views — UI layer
  4. Wave 4 (2 agents): Import type mapping + TypeScript binding regeneration — integration

Each wave’s agents self-wired framework files and committed their own work. The leader verified compilation and tests at each gate, then regenerated TypeScript bindings after Wave 4.

INK-333 Cloud Template Catalog (Review → Remediation Pipeline)

First use of the full review→remediation pipeline — /workflow:review produces prioritized findings, which feed directly into a remediation agent team plan:

  • 22 findings (5 P1, 10 P2, 7 P3) from deep code review
  • 3 waves, 6 agents, leader-commits mode
  • 4 commits (wave 1 + review findings + wave 2 + wave 3)
  • 2 findings deferred to follow-up issues (INK-345, INK-346)

Key patterns discovered:

  1. Cross-agent field rename reconciliation: Wave 3A renamed TemplateCatalogEntry.filestorage_path in domain + frontend. Wave 3B wrote Playwright tests using the old file name (instructed to for safety). Leader reconciled by updating test mock data and assertions after both agents completed. Lesson: when multiple agents in the same wave touch data models and test fixtures, plan for leader reconciliation of shared field names at the wave gate.
  2. lint-staged failure drops staged files: Pre-commit clippy failure during Wave 2 commit caused lint-staged to unstage all 16 files. Only 3 files made it into the first commit; a second commit caught the remaining 6. See docs/solutions/workflow-issues/lint-staged-failure-drops-staged-files.md.
  3. Specta preserves snake_case: Plan assumed storage_pathstoragePath in TypeScript. Specta actually generates storage_path (preserving snake_case). Frontend agent discovered and adapted at implementation time.

Wave structure:

  1. Wave 1 (3 agents): App layer fixes + infra hardening + CI/CD hardening — parallel foundation
  2. Wave 2 (1 agent): Framework wiring — commands, state, new use case (depends on Wave 1)
  3. Wave 3 (2 agents): Frontend auth/UX + Playwright integration tests — parallel, depends on Wave 2

INK-233 Block Content Types (Implementation — Continuation Agent Pattern)

Full-stack block content types epic, first use of the continuation agent pattern:

  • 8 issues, 4 waves, 6 agent instances (5 planned + 1 continuation)
  • 4 commits (1 per wave, leader-commits mode)
  • Self-wiring mode with bypassPermissions for all agents
  • Zero merge conflicts, zero compilation failures at wave gates

Key patterns:

  1. Foundation wave pre-inclusion: Wave 1 pre-included domain methods (searchable_text(), to_markdown(), validation methods) specified in Wave 2 issues. Added ~30 lines but eliminated domain file conflicts for 2 parallel Wave 2 agents.
  2. Continuation agent: Wave 4 agent (insert-flow) exhausted context after completing application layer. Leader detected via git status (partial modifications), spawned insert-flow-2 with remaining scope only. Combined output: 12 files, 798 insertions, all gates passed.
  3. Bonus feature discovery: Wave 4 continuation agent proactively added upload_attachment_bytes Tauri command for clipboard paste support — not in the original plan but naturally needed for the ImageBlockDrop paste handler.

Wave structure:

  1. Wave 1 (1 agent): Domain enum + migration V009 + SQLite storage — foundation
  2. Wave 2 (2 agents): Metadata API + FTS5 indexing || markdown export + badge — parallel backend
  3. Wave 3 (1 agent): TipTap ImageBlock node view — frontend rendering
  4. Wave 4 (1+1 agents): Insert menu + drag-drop — full-stack (exhausted → continued)

See docs/solutions/workflow-issues/agent-context-exhaustion-and-continuation.md for the continuation pattern details.

INK-235 Layout Definition and Rendering (Implementation — Largest Wave Count)

Largest orchestrated epic to date — 6 waves, 11 agent instances, zero conflicts:

  • 14 issues, 6 waves, 11 agent instances
  • 12 commits (agent self-commits + leader bindings regeneration)
  • Self-wiring mode (bypassPermissions) for all agents
  • Zero merge conflicts across all 11 agents — including waves where 3 agents ran in parallel
  • Zero compilation failures at any wave gate

Key patterns:

  1. Shared file safety at 3-agent parallelism: Wave 5 had 3 agents running simultaneously, with agents B and C both extending LayoutPicker.tsx. Changes were additive (different sections) — no conflict. This validates that self-wiring mode scales beyond 2-agent parallelism when changes are additive and touch different sections.
  2. Cross-wave file evolution: PageView.tsx was modified by Wave 4A (grid rendering), Wave 4B (layout picker integration), and Wave 6 (type assignment badge). Each wave built on the prior’s committed state with zero conflicts.
  3. Context recovery across sessions: Orchestration spanned 2 leader sessions (context exhaustion at Wave 3 boundary). Continuation session recovered state from git log + agent task status with no loss of orchestration context.
  4. Per-cell LoroDoc architecture: Required new per-block Tauri commands (get_block_content_snapshot_by_id, save_block_content_by_id) in Wave 1, enabling Wave 4’s multi-editor grid without cross-cell CRDT contamination.

Wave structure:

  1. Wave 1 (1 agent): Domain + schema — Layout value object, V010 migration, per-block BLOB commands
  2. Wave 2 (3 agents): Layout definitions CRUD || markdown export || sync metadata — parallel backend
  3. Wave 3 (1 agent): Apply/remove/assign layout use cases — depends on Waves 1+2
  4. Wave 4 (2 agents): Grid editor + EmptyCell || Layout picker + toolbar — parallel frontend
  5. Wave 5 (3 agents): Keyboard navigation || compatibility check + conflict dialog || custom layout creator — parallel enhancements
  6. Wave 6 (1 agent): Type-layout association — depends on INK-232 types system

New architectural pattern documented: docs/solutions/architecture/multi-editor-grid-per-cell-crdt.md

INK-253 Property Value Insertion (Implementation — Cross-Layer Feature)

8 issues across 4 waves, spanning frontend (TipTap, React, CSS), markdown serialization, and Rust infrastructure (FTS5 integration tests).

Key characteristics:

  • Pure frontend + infrastructure — no domain or application layer changes needed
  • Cross-context session — continued from a planning session into execution across context compaction boundary
  • 3-agent parallel Wave 4 — markdown write-back (Rust), slash command (TypeScript), click-to-navigate (TypeScript) with zero shared files

Wave structure:

  1. Wave 1 (1 agent): PropertyRef TipTap inline node — foundational extension
  2. Wave 2 (2 agents): Markdown serialization || render-time resolution — both touch PropertyRef.ts in non-overlapping sections
  3. Wave 3 (2 agents): {{ autocomplete (frontend) || FTS5 verification (Rust) — completely different layers
  4. Wave 4 (3 agents): Markdown write-back (Rust) || /property slash command (TypeScript) || click-to-navigate (TypeScript) — zero file overlap

Lessons reinforced:

  • Wave 2 shared PropertyRef.ts safely because agents modified different sections (parseHTML vs NodeView rendering)
  • Wave 3 demonstrated cross-language parallelism (TypeScript + Rust) as the safest form — zero conflict risk
  • Wave 4 validated 3-agent parallel with distinct file ownership across both TypeScript and Rust codebases

New architectural patterns documented:

  • docs/solutions/patterns/tiptap-extension-option-mutation-for-nodeview-rerender.md
  • docs/solutions/architecture/fts5-unicode61-implicit-syntax-tokenization.md

10-issue epic introducing [[Display|slug]] wiki-link syntax with reference index, demonstrating late-wave issue combining:

  • 10 issues, 4 waves, 6 agent instances (vs 10 if 1:1)
  • 7 commits (5 feature + 1 docs, leader-commits for Waves 3-4)
  • Mixed commit mode: self-wiring (Waves 1-2), leader-commits (Waves 3-4)
  • Zero merge conflicts, zero compilation failures at wave gates
  • Zero continuation agents needed (aggressive combining kept per-agent scope within context budget)

Key pattern — Late-Wave Issue Combining:

  1. Wave 3: 3 issues → 2 agents. INK-316 (export verification) and INK-317 (Loro CRDT confirmation) were lightweight verification tasks combined into a single agent. INK-315 (Import rewrite) remained standalone due to substantive implementation scope.
  2. Wave 4: 2 issues → 1 agent. INK-318 (Ghost link UI) and INK-319 (Heading navigation) shared the same component layers (WikiLink.ts, InklingsEditor.tsx, PageView.tsx). Combining prevented file ownership conflicts that would have required serialization into sub-waves.

When to combine issues into fewer agents:

  • Later waves where foundation is stable and scope is verification/polish
  • Shared file layers where splitting would create ownership conflicts
  • Lightweight scope (verification, docstrings, round-trip tests) that doesn’t justify a dedicated agent
  • Combined scope fits in ~40% of context budget (leave margin for iteration)

When NOT to combine:

  • Foundation waves (Waves 1-2) where each issue introduces new architectural concepts
  • Cross-layer implementations spanning 4+ layers (domain → infrastructure → application → framework → frontend)
  • Issues with different dependency chains that could execute in separate waves

Result: 10 issues completed with 40% fewer agent instances than 1:1 assignment. No context exhaustion, no conflicts, no rework.

Wave structure:

  1. Wave 1 (2 agents): Parser + domain || reference index migration — parallel foundation
  2. Wave 2 (3 agents): Index-backed queries || index-backed rename || TipTap extension — parallel core features
  3. Wave 3 (2 agents): Import rewrite || export + Loro verification (combined) — parallel ecosystem
  4. Wave 4 (1 agent): Ghost link UI + heading navigation (combined) — single polish agent

References

  • Branch: matt/ink-72-multi-device-sync (16 commits)
  • Branch: matt/ink-129-convert-supabase-block_on-to-async (merged to main, 13 remediation commits)
  • docs/solutions/workflow-issues/agent-team-uncommitted-work.md — earlier pattern for single-wave teams
  • docs/solutions/workflow-issues/batched-commits-anti-pattern.md — why per-story commits matter
  • docs/solutions/database-issues/sqlite-queue-full-table-scan-anti-pattern.md — O(n²) queue pattern from INK-129 review
  • docs/solutions/logic-errors/rust-async-lock-anti-patterns.md — async lock patterns from INK-129 review
  • docs/solutions/workflow-issues/lint-staged-failure-drops-staged-files.md — lint-staged stash behavior from INK-333
  • Branch: main (INK-232 commits 390af08..2682477)
  • Branch: main (INK-333 remediation commits c02eeee..0309aba)
  • Branch: matt/ink-233-introduce-block-content-types (INK-233 commits e6c2ff6..2e8721e)
  • docs/solutions/workflow-issues/agent-context-exhaustion-and-continuation.md — continuation agent pattern from INK-233
  • Branch: matt/ink-235-layout-definition-and-rendering (INK-235 commits d4a8566..b376190)
  • docs/solutions/architecture/multi-editor-grid-per-cell-crdt.md — per-cell CRDT multi-editor pattern from INK-235
  • Branch: feat/INK-253 in worktree .claude/worktrees/ink-253/ (INK-253 commits ff430e0..aa7ffaf)
  • docs/solutions/patterns/tiptap-extension-option-mutation-for-nodeview-rerender.md — NodeView re-render pattern from INK-253
  • docs/solutions/architecture/fts5-unicode61-implicit-syntax-tokenization.md — FTS5 tokenizer insight from INK-253
  • Branch: feat/INK-254 (INK-254 commits e7436a4..f9455e3, worktree at .claude/worktrees/ink-254/)

Was this page helpful?