brainstorming
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or planning anything new, in software or out of it (a talk, a business, a renovation).
orchestrate-batch-refactorRuns a large refactor or migration across many files by splitting it into units that cannot collide, mapping the code with read-only subagents, dispatching one worker per unit in dependency order, and verifying everything in the main session before reporting. Includes the decision of when not to parallelize, a plan format with file ownership, a worker brief…
Independent GitHub snapshot · Automated classification · Original documentation governs
A change that touches a hundred files is slow in one context window and dangerous when several agents edit at once. The way through is to act as an orchestrator: keep the requirements, the plan and the verification in the main session, hand the reading and the mechanical editing to subagents, and make collisions impossible by giving every file exactly one owner. Subagents start with an empty context and return only a summary, so the quality of the result depends on two artefacts this skill defines: the plan and the brief each worker receives.
| Situation | Approach |
|---|---|
| Fewer than about ten files, or one tightly coupled module | Do it in the main session |
| A purely syntactic change (rename a symbol, change an import path) | Use a codemod, the compiler or the language server's rename; review the diff. No subagents needed |
| Many files, each edit needs judgment, units can be checked on their own | Orchestrate with parallel workers |
| Many files but every edit depends on the previous one (a type that ripples outward) | Orchestrate in sequence: one worker at a time, or do it yourself |
Parallel work costs more tokens than a single run, so use it for speed and context relief, not by default.
Ask the user: what must change, what must stay identical (public API, behaviour, wire formats, database schema), what is out of scope, whether the work lands as one commit series or one pull request, and whether workers may commit.
Look up yourself:
git status --short # start from a clean tree; stash or commit nothing yourself, ask
git grep -l "fetchJson(" -- 'packages/*/src' | wc -l # how many files
git grep -c "fetchJson(" -- 'packages/*/src' | sort -t: -k2 -nr | head # where the weight is
Find the project's checks in package.json, Makefile, pyproject.toml or the CI workflow, run them once, and record the result and the duration. A baseline that already fails must be reported before any edit, otherwise later failures cannot be attributed.
Split the scope into areas that follow the repository's own seams (packages, services, top-level directories) and send one read-only explorer per area, in parallel. Give each the pattern to look for and this return format:
AREA: packages/billing
FILES AFFECTED: 31 (list with occurrence counts)
VARIANTS: forms the pattern takes here, one example each (file:line)
SHARED FILES: files that other areas also import or would need to edit
HIDDEN USES: re-exports, string-based lookups, generated code, config, mocks, docs
TESTS: which suites cover these files and the command to run only them
RISKS: anything that cannot be changed mechanically, with the reason
| Read-only explorer | Worker that edits | Parallelism | Isolation between workers | |
|---|---|---|---|---|
| Claude Code | built-in Explore | general-purpose, or a custom agent in .claude/agents/ | Several Agent calls in one turn; 20 running at once by default | isolation: worktree in the agent's frontmatter gives each worker its own git worktree |
| Codex | built-in explorer | built-in worker, or a TOML agent in .codex/agents/ | Ask in words ("spawn one agent per unit and wait for all"); cap with agents.max_concurrent_threads_per_session | Same checkout and sandbox as the parent; rely on disjoint file lists |
| Cursor | built-in Explore | custom agent in .cursor/agents/ | Several Task calls in one message | Shared checkout unless you ask for "each in its own environment", which gives each a worktree and branch |
| Gemini CLI | codebase_investigator | generalist | Subagents cannot call other subagents; plan for one delegation at a time | Shared checkout |
Facts that shape the plan:
worktree.baseRef is set to "head" in settings. A worker in a fresh worktree will not see an uncommitted foundation change.Merge the explorer reports into a single plan and show it to the user before any worker edits.
# Refactor plan: fetchJson callbacks → http.get promises
Invariants: public exports of every package unchanged; no behaviour change; retry and timeout semantics identical
Baseline: pnpm -r typecheck (ok, 48 s) · pnpm -r test (ok, 612 tests, 3 min 10 s)
| Unit | Objective | Owns | Depends on | Check | Done when |
|------|-----------|------|------------|-------|-----------|
| F0 | Add http.get next to fetchJson; fetchJson delegates to it | packages/http/** | none | pnpm --filter @tern/http test | both APIs exported, tests pass |
| U1 | Migrate billing callers | packages/billing/src/** | F0 | pnpm --filter @tern/billing test | no fetchJson( left in owned files |
| U2 | Migrate catalog callers | packages/catalog/src/** | F0 | pnpm --filter @tern/catalog test | same |
| C9 | Delete fetchJson and its tests | packages/http/** | U1, U2 | pnpm -r typecheck && pnpm -r test | symbol gone, repo green |
Waves: [F0] → [U1, U2 in parallel] → [C9]
Reserved for the orchestrator: pnpm-lock.yaml, root tsconfig, CHANGELOG.md
Planning rules:
Run one unit as a pilot. If its result exposes a gap in the brief, fix the brief before launching the rest. Each worker gets a self-contained message:
You are making one part of a larger refactor. Other agents are editing other files in this
repository at the same time.
OBJECTIVE: replace every fetchJson(url, callback) call with await http.get(url) in the files you own.
YOU OWN (edit only these): packages/billing/src/**
DO NOT TOUCH: any other path, package.json files, lockfiles, generated code. If the task seems to
require it, stop and report the path and the reason instead of editing.
RULE: before → fetchJson(u, (err, data) => { if (err) return fail(err); use(data) })
after → try { const data = await http.get(u); use(data) } catch (err) { fail(err) }
Enclosing functions become async; update their callers only inside your files.
KEEP: exported names and signatures, log messages, retry and timeout options.
CHECK: pnpm --filter @tern/billing test && pnpm --filter @tern/billing typecheck
Run only these. Do not run repo-wide formatters or the full suite.
GIT: do not commit, push, stash or revert anything. Leave changes in the working tree.
RETURN: files changed (count), occurrences replaced, cases you could not convert (file:line and
why), exact check output tail, anything outside your files that now needs a change.
Launch all units of a wave in one turn so they run concurrently, and cap the number so the machine can run that many test processes at once.
A worker's "all tests pass" is a claim, not evidence. After each wave:
git status --short # every changed path must fall inside some unit's list
git diff --stat
git grep -n "fetchJson(" -- packages/billing/src packages/catalog/src # expect no output
pnpm -r typecheck && pnpm -r test
## Refactor report: fetchJson → http.get
| Unit | Status | Files | Notes |
|------|--------|-------|-------|
| F0 | done | 4 | http.get added, fetchJson delegates |
| U1 | done | 31 | 2 call sites converted by hand (streaming response) |
| U2 | done | 27 | none |
| C9 | done | 3 | fetchJson removed |
Verification: pnpm -r typecheck ok · pnpm -r test ok (612 passed, same count as baseline)
Deviations from plan: packages/catalog/src/legacy/feed.ts kept a thin wrapper, see TODO(http-get)
Not done / risks: docs/api.md still mentions fetchJson; no integration test covers the retry path
Request: "We have about 200 fetchJson callback calls across the packages. Move them all to http.get."
The orchestrator finds 212 occurrences in 58 files across billing and catalog, runs the baseline (green), and sends two Explore subagents, one per package. Their reports show one shared file, packages/http/src/index.ts, and two streaming call sites that cannot be converted mechanically. The plan in step 5 is shown to the user and approved. F0 runs alone; the orchestrator verifies and commits it. U1 runs as the pilot and returns "29 files converted, 2 skipped: streaming". The brief gains a line about streaming responses, U2 is launched, and both streaming sites are converted in the main session. After C9 the repository-wide check prints:
$ git grep -c "fetchJson(" -- packages | wc -l
0
$ pnpm -r test
Test Files 94 passed (94)
Tests 612 passed (612)
Result: the report in step 8, three commits (foundation, migration, removal), and one follow-up noted for the documentation.
Request: "Rename Order.total to Order.grand_total everywhere, use subagents to make it fast."
git grep -l "\.total" -- 'app/*.py' | wc -l reports 38 files, but the explorer's map shows they all hang off one SQLAlchemy model: the column, a migration, serializers that read the attribute by name, and templates. Splitting by directory would give three workers the same model file and leave the tree broken between waves.
Decision told to the user: one sequential pass in the main session. Add grand_total with a migration and keep total as a property alias; update callers directory by directory, running pytest tests/orders -q after each; remove the alias last. A single read-only subagent is still used to list string-based uses (getattr(order, "total"), JSON keys, template variables) that a symbol rename would miss.
Result: 38 files changed in three commits, pytest -q shows 417 passed before and after, and the JSON field name total in the public API was deliberately left unchanged, as the report states.