Workstreams — Parallel Implementation Dispatch
Decompose work items into dependency-aware workstreams, dispatch agents in worktree isolation, and coordinate merges. This is the implementation counterpart to /critique.
You ARE the orchestrator. Follow these phases in order.
Setup
Resolve paths once before starting:
LIB=~/.claude/skills/lib/claude-lib.sh
Argument Parsing
| Argument | Form | Default |
|---|---|---|
| source | Bare path to a file containing work items (e.g., critique output) | from conversation context |
--items |
Comma-separated work item seeds | from context |
--max-parallel |
Integer | 4 |
--auto-merge |
Flag | false (confirm each merge with user) |
--dry-run |
Flag | Phases 0-2 only, no dispatch |
--test-cmd |
String | auto-detect |
--skip-negotiation |
Flag | false |
--max-items-per-stream |
Integer | 5 |
--streams |
JSON or natural language stream definitions | skip to Phase 3 |
Lightweight Dispatch Mode
When --streams is provided, or the user's message already specifies the decomposition (e.g., "dispatch three agents: one for X, one for Y, one for Z"), skip Phases 0–2 and jump directly to Phase 3.
In this mode:
- Parse the stream definitions from the argument or conversation. Each stream needs at minimum: a name and a description of what to do. File lists are optional — agents will figure it out.
- Run
workspace pruneto clear stale branches from prior sessions:bash "$LIB" workspace prune - If any files appear in multiple streams' descriptions, run Phase 1.5 (shared file pre-editing) before dispatch.
- Proceed to Phase 3 (workspace setup + agent dispatch).
- After collection, proceed to Phase 4 as normal.
This mode is for users who have already done the thinking and just want the parallelism. Don't second-guess their decomposition — dispatch what they asked for.
Phase 0: Intake
Step 0: Clean stale workspaces
bash "$LIB" workspace prune
Remove orphaned branches and worktrees from prior sessions before starting.
Step 1: Survey the project
bash "$LIB" survey <project_root>
Read the JSON output for project profile.
Step 2: Gather work items
Identify work items from one of these sources (in priority order):
- Conversation context — if
/critiquewas run earlier, extract its recommendations and cross-cutting themes. - Source file argument — if a file path was provided, read it and extract actionable items.
--itemsflag — use as seeds. Read the codebase to expand them into concrete changes.
Step 3: Classify each work item
For each item, determine all of the following:
| Field | Description |
|---|---|
id |
Short slug (e.g., add-handler-tests) |
type |
additive (new files only), body (logic changes, not signatures), interface (type/API changes), or refactor (moves, renames) |
reads |
Files the item imports from or depends on |
writes |
Files the item will create or modify |
description |
What changes and why (1-2 sentences) |
size |
small (<50 lines), medium (50-200), large (200+) |
Classification requires reading the actual source files to understand what kind of change each item involves. Read the target files before classifying.
Classification parallelism: If the total files to read for classification is under 20, classify inline or with 1-2 agents. Reserve 4+ classification agents for larger projects where the read set is genuinely large. Over-parallelizing small read sets wastes agent overhead.
Output: A JSON array of 3-20 classified work items.
Time budget: Under 60 seconds.
Phase 1: Dependency Analysis
Step 1: Run deps
Pipe the classified work items to the deps script:
echo '<work_items_json>' | bash "$LIB" deps
This returns:
conflicts— pairs of items with shared writes, hard deps, or soft depssuggested_streams— items clustered by write conflicts (union-find)ordering— topologically sorted parallel groups
Step 2: Refine the groupings
Review the script's output and adjust:
- Conceptual cohesion — items addressing the same theme belong together even without file conflicts.
- Stream size — split streams exceeding
--max-items-per-stream(default 5). - Naming — replace
stream-1with descriptive names (test-coverage,resource-cleanup,type-hardening). - Edge overrides — the LLM may override an edge type if it understands the change is mechanical (e.g., "this interface change is just a rename, treat as soft").
Step 3: Finalize the DAG
Produce the final dispatch plan: named workstreams, parallel groups, ordering, and dependency rationale.
Step 4: Prepare context seeding
Agents waste tokens re-reading files the orchestrator already read during classification. Identify each stream's read set — the files an agent will need to understand before making changes. These will be pre-seeded into each worktree during Phase 3 setup.
For each stream, build a context list: the files from its reads set, plus any shared foundational files (types, interfaces) that appear in 2+ streams. These files will be concatenated into a .context.md file in each worktree by workspace setup.
Phase 1.5: Shared File Pre-Editing
Purpose
Files written by multiple work items ("registry files" — e.g., index files, setup modules, token registrations) cause merge conflicts when agents edit them in parallel. Instead of serializing all writers into one stream, the orchestrator edits these files itself before dispatch, removing them from agents' write sets so the remaining work can parallelize.
This phase also asserts Edit permissions early. If the user's permission mode requires approval for file edits, the orchestrator triggers that approval now — not mid-agent-run where a denial wastes an agent's work.
Step 1: Identify shared files
The deps output includes a shared_files array:
{
"shared_files": [
{"file": "src/setup.ts", "writers": ["extract-audio", "extract-canvas", "extract-events"], "writer_count": 3}
]
}
Any file with writer_count >= 2 is a candidate.
Step 2: Classify the shared edits
For each shared file, read it and determine what each work item needs to add:
| Pattern | Example | Pre-edit strategy |
|---|---|---|
| Import + registration | Adding a line to an index/barrel file, registering a token | Orchestrator adds all imports and registrations now |
| Config addition | Adding entries to a config object or array | Orchestrator adds all entries now |
| Structural change | Multiple items rewriting the same function body | Cannot pre-edit — force these items into one stream |
For the first two patterns, make the edits directly using the Edit tool. For the third, leave the items clustered.
Step 3: Update write sets
After pre-editing, remove the shared file from each item's writes array. Then re-run deps with the updated items:
echo '<updated_items_json>' | bash "$LIB" deps
The new output will have fewer conflicts and more parallelism. Use this output for Phase 2 onward.
Step 4: Record pre-edits
Keep a list of files the orchestrator pre-edited and what was added. Include this in each agent's prompt so they know:
- The file has already been modified — do not overwrite the orchestrator's additions
- Their service/module/component is already imported and registered — they only need to create the implementation file
Phase 2: Plan Negotiation
Skip this phase if --skip-negotiation was passed.
Present the dispatch plan:
## Dispatch Plan
### Parallel Group 1 (launch immediately)
**Stream A: Test Coverage** — additive
Items: add-handler-tests, add-extraction-tests
Writes: tests/api/handler.test.ts, tests/extraction.test.ts
Reads: src/api/handler.ts, src/extraction.ts
**Stream B: Resource Cleanup** — body
Items: add-sigterm-handler, add-tempfile-cleanup
Writes: src/server.ts, src/extraction.ts
Reads: src/config.ts
### Parallel Group 2 (after Group 1 merges)
**Stream C: Branded Types** — interface
Items: brand-dimension-ids, brand-manifest-ids
Writes: src/types.ts, src/api/handler.ts, src/models/dimension.ts
Depends on: A (hard — changes type signatures imported by A's new tests)
### Dependency Rationale
- A and B are independent: no shared write targets, additive and body types.
- C depends on A: branded types change signatures that A's new tests import.
Decision points for the user:
- Approve, merge/split streams, reorder groups, drop items
- Promote soft dep to independent (accept merge cost)
- Demote independent to sequenced (reduce risk)
Wait for user confirmation before proceeding.
If --test-cmd was not provided, also confirm the test command. Auto-detect from root files:
package.json->npm testMakefilewith test target ->make testpyproject.toml->pytestCargo.toml->cargo testgo.mod->go test ./...
Ask the user: "I'll have agents run <detected-cmd> to verify their work. Correct?"
If --dry-run was passed, stop here. Present the plan and exit.
Phase 3: Dispatch
Step 1: Create workspaces
Pass the stream definitions with their context file lists from Phase 1 Step 4:
echo '<streams_json>' | bash "$LIB" workspace setup --json
Input:
[
{"name": "test-coverage", "context": ["src/types.ts", "src/api/handler.ts"]},
{"name": "resource-cleanup", "context": ["src/types.ts", "src/config.ts"]}
]
This creates all worktrees sequentially, write-tests each, and seeds a .context.md file in each worktree containing the pre-read source files. Returns JSON:
{"workspaces": [{"name": "...", "path": "...", "branch": "...", "status": "ok", "context_seeded": true}], "all_ok": true}
If all_ok is false, stop and tell the user which workspace failed and why.
Step 2: Dispatch and collect
Call the Workflow tool with an inline script that fans out one agent per stream in the current parallel group, using parallel(), and collects each stream's structured completion report in one call.
export const meta = {
name: 'workstream-dispatch',
description: 'Dispatch implementation agents for one parallel group of workstreams',
phases: [{ title: 'Implement' }],
}
const REPORT_SCHEMA = {
type: 'object',
properties: {
filesChanged: { type: 'array', items: { type: 'string' } },
filesAdded: { type: 'array', items: { type: 'string' } },
testsPass: { type: 'boolean' },
issues: { type: 'string' },
},
required: ['filesChanged', 'filesAdded', 'testsPass'],
}
const results = await parallel(args.streams.map(s => () => {
const opts = { label: s.name, agentType: 'general-purpose', schema: REPORT_SCHEMA }
if (s.model) opts.model = s.model
if (s.effort) opts.effort = s.effort
return agent(s.prompt, opts).then(result => result && ({ stream: s.name, ...result }))
}))
return results.filter(Boolean)
Pass args: { streams: [{name, prompt, model?, effort?}, ...] } for every stream in the current parallel group, where prompt is built from the template below and model/effort are set per the rule below. Do NOT pass isolation: "worktree" on the agent() calls — workspaces are already pre-created by workspace setup in Step 1; each stream's prompt names its own workspace path instead.
Model selection
Set model and effort per stream using your own judgment of its difficulty — not mechanically off size/type alone. Those classifications measure change footprint, not how hard the implementation judgment is. A size: small stream can still be a subtle concurrency fix; a size: large one can be pure boilerplate.
- Default: omit both fields. This is correct for most streams — the session's default model/effort is the safe fallback.
- Set
model: "haiku"only when you're confident the stream is genuinely mechanical — scaffolding that mirrors an existing pattern, tests that follow an established shape — not just because it's classifiedsmall/additive. - Set
effort: "high"when the stream touches interfaces/refactors with wide blast radius, or where correctness is easy to get subtly wrong — concurrency, auth, data migration, shared state — regardless of size.
Bias toward omitting model over downgrading it: a wrong downgrade risks a stream landing with subtle bugs and no signal anything went wrong, while a wrong effort bump just costs more for no harm.
Agent prompt template:
Context: {2-3 sentences about the project — root path, languages, size.}
Worktree: Your working directory is
{workspace-path}. All file reads and writes must use paths under this directory. Do not modify files in the main repo.Pre-seeded context: Read
{workspace-path}/.context.mdbefore starting — it contains the source files you'll need for orientation. Do not re-read files that are already in the context file.Assignment: Implement the following changes:
- {item 1: description, target files}
- {item 2: description, target files}
Boundary: You own these files (may create or modify): {write set}. If you need to modify a file outside your boundary, note it in your report and stop.
Pre-edited files (do not overwrite): {If Phase 1.5 pre-edited files, list them here with a summary of what was added. E.g.: "The orchestrator already added
import { AudioMixService } from './audio-mix.service'and registered the token insetup.ts. Do not modifysetup.ts— just create the implementation file." Omit this section if no pre-edits were made.}Verification: Run
cd {workspace-path} && {test-cmd}before reporting. PopulatetestsPasstruthfully — never reporttestsPass: truewith failing tests.Commit: Before reporting completion, stage and commit your changes:
cd {workspace-path} && git add -A && git commit -m "workstream: {stream-name}"Completion report: Populate
filesChanged,filesAdded,testsPass, andissues(empty string if none).
Dispatch rules:
- Never include more than
--max-parallelstreams (default 4) in oneargs.streamscall. - Never dispatch two streams writing to the same file.
- Always include the workspace path and test command in each stream's prompt.
Tell the user briefly: "Dispatched N agents for this group." The call runs in the background — wait for its task notification, then read the returned array before moving to per-agent validation in Phase 4.
Phase 4: Collection & Merge
Per-agent on completion
- Read each stream's structured completion report from the Workflow result (
filesChanged,filesAdded,testsPass,issues). - Validate the workspace:
Ifbash "$LIB" workspace check <stream-name>existsis false, mark the stream as failed — do not trust the agent's report. - Report to user with the diff stats from the check output.
Handling failures
- Agent fails tests: Report the failure. User chooses: fix, abandon, or take over.
- Agent times out: Mark as incomplete. Offer narrower re-dispatch.
- Workspace missing: Mark as failed. Offer re-dispatch.
Per-group merge
Present each stream's check results. Propose merge order from deps output (merge_order field). Wait for user confirmation before each merge (unless --auto-merge).
For each confirmed stream:
bash "$LIB" workspace merge <stream-name> --test-cmd "<test-cmd>"
This merges the branch, runs tests, and cleans up the worktree on success — one call per stream. If the merge output shows tests_pass: false or merged: false, stop and surface the error.
Partial group advancement
If some streams succeed and others fail, merge the successful ones and advance to the next group — but only if the next group's dependencies are satisfied. Failed streams must be resolved before the pipeline ends.
Between groups
Once the current group is merged and tests pass, loop back to Phase 3 for the next group.
Phase 5: Reconciliation
After all groups are merged (or failed streams resolved):
- Run the full test suite:
{test-cmd} - Run type checking if applicable (infer from survey).
- Clean up any orphaned workspaces:
bash "$LIB" workspace cleanup - Produce the delta report:
## Workstream Results
### Stream A: {Name} — {COMPLETE|FAILED|ABANDONED}
- {Added/Modified}: {file count} ({line count} lines)
- Changes: {1-line summary}
- Issues: {none, or description}
### Integration
- Full test suite: {PASS|FAIL (details)}
- Type check: {PASS|FAIL|N/A}
- Failed streams: {count and names, or "none"}
Error Recovery
| Situation | Response |
|---|---|
workspace setup reports all_ok: false |
Check which workspace failed. Fix permissions or fall back to sequential execution. |
| Agent denied Write/Edit mid-run | The agent did useful research. Use its output to drive changes in the main conversation. |
workspace check reports exists: false |
Workspace lost — mark stream as failed, offer re-dispatch. |
workspace merge reports merged: false |
Merge conflict. Show error, attempt mechanical resolution, ask user if non-trivial. |
workspace merge reports tests_pass: false |
Tests broke after merge. Bisect or revert and investigate. |
| Survey or deps script fails | Fall back to manual classification. |
| All agents fail | Tell the user. No empty reconciliation report. |
| User interrupts while the Workflow dispatch is running | Workflow calls run to completion in the background; merge whatever streams have reported once it returns, note incomplete ones. |
| Orphaned workspaces after crash | workspace cleanup in Phase 5 catches these. |