Skip to content

Swarm migration

A migration is "the same transform applied to N files" - framework upgrades, lint-rule rollouts, API renames. The swarm_migration module enumerates targets, chunks them, fans out one short-lived agent per chunk, and reduces the results back into a single report. Map-reduce, deterministic, one CLI command.

Why it exists

task_splitter decomposes a parent task into 2-5 subtasks via the manager LLM. That's the right shape for normal feature work. It is the wrong shape for a migration touching hundreds of files where each file gets the same prompt - the LLM-driven split is wasted tokens and the LLM-chosen chunks are worse than a deterministic glob.

Swarm migration is a separate first-class entry point that skips the manager planning loop and dispatches directly.

How to use it

# Dispatch 20 parallel agents, each rewriting up to 5 files
bernstein migrate \
    --glob 'src/**/*.py' \
    --transform 'convert all sync handler functions to async' \
    --chunk-size 5 \
    --max-parallel 20

# Pin a stable id for idempotent re-runs, and preview without spawning
bernstein migrate \
    --glob 'src/**/*.py' \
    --transform 'convert all sync handler functions to async' \
    --id sync-to-async \
    --dry-run

The CLI takes the glob and transform inline. Reference migration plan YAMLs (templates/migrations/sync_to_async.yaml, templates/migrations/pytest_asyncio.yaml) document the same shape the MigrationPlan dataclass accepts:

# templates/migrations/sync_to_async.yaml
glob: 'src/**/*.py'
transform_prompt: |
  Convert every sync function in this file to async, replacing every
  blocking `requests.X` call with `httpx.AsyncClient`. Preserve public
  signatures.
chunk_size: 5
max_parallel: 20

The fanout:

  1. enumerate_targets(plan, repo_root) resolves the glob.
  2. chunk_targets(targets, chunk_size) partitions into chunks.
  3. spawn_swarm(plan) emits one task per chunk with role=backend, scope=small.
  4. reduce_swarm(parent_id, child_results) aggregates pass/fail per chunk and posts a SwarmReport to the bulletin board.

Re-running the same plan ID skips already-completed chunks (idempotent).

Configuration

Knob Default Controls
--max-parallel 20 Parallel agent count; clamped to the chunk count and the hard ceiling.
--chunk-size 5 Files per chunk.
--id derived from the transform Idempotency key for re-runs.

Limitations

  • Chunks must be independent. Cross-file refactors (rename a symbol used by 200 files) need a single agent or a custom split.
  • The transform logic lives in the agent's prompt - Bernstein does not ship code transforms.
  • Rollback on partial failure uses the existing drain + worktree merge logic. There is no migration-specific rollback.
  • Cross-chunk dependency analysis is the operator's responsibility.
  • Source: src/bernstein/core/tasks/swarm_migration.py
  • CLI: src/bernstein/cli/commands/migrate_cmd.py
  • Plan templates: templates/migrations/
  • Adaptive parallelism
  • PR #1010