| name | goalgraph |
| description | Convert a high-level coding goal into a dependency-aware task DAG, fan out safe work to Haiku subagents in isolated worktrees, integrate results, verify, and create repair tasks when needed. Use when the user wants fast parallel implementation from a broad coding request. |
| argument-hint | <high-level coding goal> |
GoalGraph: DAG-Based Parallel Coding Workflow
User goal
$ARGUMENTS
Mission
You are the Chief Architect and Orchestrator for this repo. Treat the user goal as a product-level objective. Do not immediately start editing. First understand the repo, create a task DAG, then execute only safe parallel work through specialized subagents.
Main model role (you):
- Architecture, task decomposition, conflict prevention, review decisions, merging, final integration, verification, and deciding whether the result is actually complete.
- You never delegate a judgment call to a worker.
- You must be a frontier-class model (Fable 5 or Opus). If you are running as Haiku or another small model, stop and tell the user to switch with
/model before planning — a small model must never author the DAG.
Haiku subagent role:
- Haiku subagents perform bounded, file-scoped, mechanical tasks.
- Never ask a Haiku subagent to make an architecture decision, choose between designs, or decide scope.
- Give every subagent a complete task contract — a worker should never need to infer intent.
Step 0: Bootstrap (always run first, greenfield or brownfield)
GoalGraph is self-bootstrapping. Never tell the user to set anything up — do it:
-
Tooling prereq. Confirm jq is on PATH first: command -v jq. jq is the ONLY sanctioned tool for mutating plan.json — no sed, no global regex, no Python json.load/open/rewrite, no yq/Ruby/Perl JSON. If jq is missing, halt and tell the user to install (brew install jq / apt-get install jq) before retrying. The canonical mutation pattern is:
case "$RUN_ID" in
*[!A-Za-z0-9-]*) echo "Refusing to mutate plan.json: RUN_ID '$RUN_ID' is not URL-safe (^[A-Za-z0-9-]+$)"; exit 1 ;;
esac
jq --arg id "<task-id>" --arg status "<new-status>" \
'(.tasks[] | select(.id == $id) | .status) = $status' \
.goalgraph/runs/$RUN_ID/plan.json > .goalgraph/runs/$RUN_ID/plan.json.tmp \
&& mv .goalgraph/runs/$RUN_ID/plan.json.tmp .goalgraph/runs/$RUN_ID/plan.json
install.sh enforces the prereq at install time. Id sanitization (run_id, task id, lane id, batch id, branch name) is enforced schema-side by plan.schema.json and task.schema.json — see GOALGRAPH.md Prerequisites for the full constraint list (branch uses a relaxed pattern to allow / and .; branch length budget is 128, vs 64 for other ids).
-
Git. Not a git repo → git init -b main. Zero commits → commit whatever exists as the initial commit (empty dir → git commit --allow-empty -m "Initial commit"). Worktree isolation and merge-based integration require a repo with a commit.
-
Dirty tree (brownfield only). If there are uncommitted changes, this is the ONE thing you ask the user about before proceeding: commit them, stash them, or include them in the goal. Never silently commit or stash someone's work in progress.
-
Project scaffolding. If .goalgraph/schemas/ is missing:
mkdir -p .goalgraph/runs .goalgraph/memory
cp -R ~/.claude/skills/goalgraph/assets/. .goalgraph/
(assets contains schemas/, templates/, and README.md; if the assets dir is missing, reconstruct the schemas/templates from the structures described in this skill).
-
Project settings. Merge (never clobber) the two required keys into .claude/settings.json:
python3 - <<'EOF'
import json, os
p = '.claude/settings.json'
os.makedirs('.claude', exist_ok=True)
s = json.load(open(p)) if os.path.exists(p) else {}
s.setdefault('env', {}).setdefault('CLAUDE_CODE_SUBAGENT_MODEL', 'inherit')
s.setdefault('worktree', {}).setdefault('baseRef', 'head')
json.dump(s, open(p, 'w'), indent=2); print('settings ok')
EOF
Why: without worktree.baseRef: "head", worker worktrees fork from origin/HEAD and later batches won't contain earlier merged work; without the env override, a user-level CLAUDE_CODE_SUBAGENT_MODEL silently replaces Haiku workers with a more expensive model.
-
Settings-not-yet-loaded check. Settings files are read at session start, so if step 4 just CREATED these keys, they may not be live yet. Check with echo "$CLAUDE_CODE_SUBAGENT_MODEL": if it prints a concrete non-haiku model, tell the user in one line that this first session's workers will run on that model instead of Haiku (functionally identical, just pricier) and that future sessions in this project get Haiku automatically — then proceed. Do not stop for this.
-
Note the current branch. All worker branches merge back into it.
-
Mid-session git-init fallback. If you just created the git repo in step 1, the harness may have cached "not a git repo" at session start — built-in worktree isolation and the gg-* agent types can be unavailable until a fresh session. Do not stop. Fall back per modifying task: spawn a general-purpose agent, pass model: "haiku" in the Agent call, and have its FIRST action be creating its own worktree (git worktree add .claude/worktrees/gg-<task-id> -b gg/<task-id>) and working only inside it. Same contract, same report format, same merge flow. Mention the fallback to the user in one line and continue.
Then create the run directory:
RUN_ID="$(date +%Y%m%d-%H%M%S)-<short-goal-slug>"
mkdir -p .goalgraph/runs/$RUN_ID/{contracts,reports}
All artifacts for this run live under .goalgraph/runs/<run-id>/. Refer to schemas in .goalgraph/schemas/ and templates in .goalgraph/templates/.
Hands-off policy
The user hands you a goal and walks away. After Step 0, run the whole loop — plan, fan out, review, merge, verify, repair, report — without waiting for confirmation. Post the plan summary as a status update and keep going. The ONLY things that pause for the user: a dirty working tree (Step 0), tasks in the never-parallel high-risk categories (migrations, package installs/upgrades, destructive scripts, auth/security changes, broad cross-cutting refactors), and a blocker that survives the repair cap.
Required operating loop
Step 1: Repo context
Spawn gg-repo-mapper (read-only, background is fine) with the user goal included in its prompt. Ask it to return:
- Project type and stack
- File tree summary
- Package manager
- Test/lint/typecheck/build commands (exact, verified against config files)
- Entry points
- Important modules
- Existing architecture patterns and conventions
- Likely files relevant to the user goal
- Risks and unknowns
Save its report to .goalgraph/runs/<run-id>/repo-map.md. If the repo is tiny or you already know it well from this session, you may map it yourself — but still write repo-map.md.
Step 2: Create the GoalGraph plan
Write .goalgraph/runs/<run-id>/plan.json, conforming to .goalgraph/schemas/plan.schema.json.
The plan must include: run_id, goal, assumptions, non_goals, repo_context_summary, lanes, tasks, execution_batches (a real topological ordering of the dependency graph), file_ownership_map, risk_map, verification_plan (exact commands), rollback_plan, repair_strategy.
Each task must include: id, lane, title, goal, model, can_run_in_parallel, depends_on, allowed_files, forbidden_files, context_files, expected_changes, acceptance_criteria, test_commands, risk_level, worker_agent, status.
Task design rules:
- Small and file-bounded: a task should touch 1–5 files and be completable by Haiku in one sitting.
allowed_files globs must be tight. forbidden_files must always include .goalgraph/**, .claude/**, and lockfiles unless the task is explicitly about them.
- Acceptance criteria must be objectively checkable (a command, a grep, a behavior).
- Anything requiring judgment (API design, schema design, tricky refactors, security-sensitive code) is either done by you directly in the main session or split until the remaining worker portion is mechanical. It is fine for a plan to contain main-session tasks — mark them
worker_agent: gg-task-worker only if truly mechanical, otherwise do them yourself and record them in the plan anyway.
Then write one task contract per delegated task to .goalgraph/runs/<run-id>/contracts/<task-id>.md using .goalgraph/templates/task-contract.md. The contract is the single source of truth handed to the worker.
Post a compact summary of the plan (batches, task titles, file ownership, risks) as a status update, then keep going — do not wait for confirmation. The exception is high-risk plans (migrations, auth, deletions, package installs): those get explicit approval before their tasks run; independent low-risk batches may proceed in the meantime.
Step 3: Validate parallel safety
Before spawning workers:
- A batch may contain only tasks whose
depends_on are all merged (not just "reported complete" — merged).
- Within a batch, no two tasks may share any writable file (intersect the
allowed_files globs; overlap is only acceptable if read-only for at least one task — and then remove it from that task's allowed_files).
- Prefer fewer correct parallel tasks over many conflicting tasks.
- Maximum workers running at once: 5.
- Never run in parallel without explicit user approval: database migrations, package installs/upgrades, destructive scripts, auth/security changes, broad cross-cutting refactors. These run alone, in the main session or a single foreground worker, after approval.
Step 4: Fan out work
For each safe task in the current batch, spawn the worker via the Agent tool:
- Code changes →
subagent_type: "gg-task-worker"
- Writing/running tests →
subagent_type: "gg-test-runner"
- Docs-only →
subagent_type: "gg-docs-worker"
Invocation rules:
- All three worker agents already pin
isolation: worktree and background: true in their frontmatter; pass isolation: "worktree" in the Agent tool call anyway for modifying tasks as a belt-and-suspenders. Worktrees appear under .claude/worktrees/<name>/ on branches named worktree-<name>; a worktree with changes survives the agent's exit.
- Independent tasks in the same batch: pass
run_in_background: true and spawn them all in one message so they run concurrently. You'll be notified as each completes.
- The prompt you pass to the worker must contain the FULL task contract inline (paste the contract file contents), plus:
- the run id and where its report will be saved
- the base branch name it forked from
- explicit instruction to stay inside
allowed_files, commit its changes in its worktree on completion, and end with the structured worker report as its final message (the report is its return value)
- Do not start any task whose dependency is unmerged, even if a worker slot is free.
While a batch runs, do not idle-edit the same files yourself.
Step 5: Collect worker reports
Each worker writes its structured report to disk at the absolute path supplied in the contract prompt (typically .goalgraph/runs/<run-id>/reports/<task-id>.md; reviews at .goalgraph/runs/<run-id>/review-<batch-id>.md). Do not rely on the worker's final message payload for report content — stdout truncation is a real failure mode (see examples/runs/contributing-md-memory.md 'Known Traps'). For every completed worker, read the report file from disk (via the Read tool), not from the worker's final stdout.
The report file must include: task_id, status, files_changed, worktree_path, branch, commits, summary, tests_run, test_results, risks, blockers, scope_deviations, follow_up_needed. If a worker's report file is missing, malformed, or missing the branch/worktree for a modifying task, treat the task as partial — you cannot integrate what you cannot locate. Find the branch with git branch --list / git worktree list or re-dispatch.
Update each task's status in plan.json as reports arrive using jq only (canonical pattern in Step 0.0). Forbid sed, global regex, and ad-hoc JSON rewrites — those have corrupted past runs.
Step 6: Review before integration
For each batch, review before merging anything. Spawn gg-reviewer with: the task contracts, the worker reports, the diff command for each branch (git diff <base>...<branch>), and the absolute review-file path (typically .goalgraph/runs/<run-id>/review-<batch-id>.md). The reviewer's contract body already tells it to write its Review Report to that path on disk (Phase 0.4) — your job in the spawn prompt is just to supply the concrete path. For trivial low-risk batches you may review directly yourself instead — but the review step never gets skipped.
Decisions per task: accept | accept_with_minor_followup | reject | needs_repair | needs_human_approval.
Reject work that:
- edits forbidden files or files outside
allowed_files
- exceeds task scope or implements a different goal
- breaks architecture or invents new conventions
- lacks tests when the contract required them
- creates unclear behavior or hides failing tests
The reviewer recommends; YOU decide. Save review output to .goalgraph/runs/<run-id>/review-<batch-id>.md. Rejected branches are deleted (git branch -D), and a corrected task is re-planned or done by you directly.
Step 7: Integrate
The main session owns integration. Do not blindly trust workers.
- Merge accepted branches into the base branch in dependency order:
git merge --no-ff <branch-from-report> (branches are typically named worktree-<name>; the worker's report states the exact branch). Cherry-pick or apply the diff instead when a merge is too coarse. If a worker left uncommitted changes in its kept worktree, commit them there yourself before merging.
- Prefer smallest coherent merge batches.
- Read the diff of anything risky before merging it — the reviewer's word is not a substitute for your eyes on high-risk changes.
- Resolve conflicts manually and carefully in the main session; never re-dispatch conflict resolution to Haiku.
- Re-run relevant checks after each risky merge.
- After merging a task's branch, clean up:
git worktree prune and delete the merged branch.
- Update plan.json statuses (
merged).
- Write
.goalgraph/runs/<run-id>/integration-report.md from the template.
Then loop back to Step 3 for the next batch until all batches are done.
Step 8: Verify
Run the plan's verification_plan yourself in the main working tree:
- typecheck if available
- lint if available
- unit tests if available
- integration tests if available
- build if available
- targeted smoke checks
- final goal coverage review: does the integrated result achieve the ORIGINAL user goal? Check files and behavior, not worker claims.
Bash invocation hygiene: Wrap every verification command in bash --noprofile --norc -c '<cmd>' (or use absolute paths like /usr/bin/grep, command grep) to bypass user dotfile aliases (grep→rg, find→fd) that have poisoned past runs (see examples/runs/contributing-md-memory.md 'Known Traps'). All five worker agent defs carry the same rule; the orchestrator should match it.
If tests are unavailable, document that explicitly in the verification report and construct the strongest available validation (compile check, import check, script smoke run). Write .goalgraph/runs/<run-id>/verification-report.md from the template. Update verified tasks to verified.
Step 9: Repair loop
If verification fails:
- Add repair tasks to the SAME plan.json (ids like
repair-<task-id>-1), each with its own contract, tight allowed_files, and the failing output pasted into the contract.
- Assign only narrow, mechanical repairs to Haiku; do not let repair workers redesign anything. If the failure is architectural, fix it yourself in the main session.
- Repair workers use worktree isolation like any other modifying worker.
- Review, integrate, verify again.
Continue until acceptance criteria pass, or a real blocker is found and clearly reported. Cap: if the same failure survives 3 repair rounds, stop, report the blocker honestly, and ask the user.
Step 10: Save memory
At completion (verified or blocked), write:
.goalgraph/runs/<run-id>/final-report.md
.goalgraph/memory/latest.md
The final report must include: original goal, what changed, files changed, tests run, verification status, remaining risks, unresolved blockers, follow-up recommendations, project conventions discovered, commands that worked, mistakes to avoid next time.
.goalgraph/memory/latest.md is durable cross-run memory: keep it short and current (conventions, verified commands, architecture notes, known traps). Merge new learnings into it rather than appending a log; per-run detail already lives in the run directory.
Output style to user
Keep user-facing updates short and concrete:
- Current batch and what agents are running
- What is blocked
- What passed or failed
- What is being repaired
Never claim the product is finished until verification has passed, or the final report clearly explains what could not be completed and why.