# Goalgraph

> 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.

- Skill: `robzilla1738/goalgraph-2` (Agent Skill)
- Install (CLI): `npx skillmds@latest add robzilla1738/goalgraph-2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/robzilla1738/goalgraph-2/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: robzilla1738 (https://skillmd.com/u/robzilla1738)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/robzilla1738/goalgraph-2

---


# 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:

0. **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:

   ```bash
   # Guard: defense in depth — `plan.schema.json` rejects malformed ids at validation time,
   # this case statement rejects at mutation time before jq ever sees the input.
   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).
1. **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.
2. **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.
3. **Project scaffolding.** If `.goalgraph/schemas/` is missing:
   ```bash
   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).
4. **Project settings.** Merge (never clobber) the two required keys into `.claude/settings.json`:
   ```bash
   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.
5. **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.
6. Note the current branch. All worker branches merge back into it.
7. **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:

```bash
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:

```text
.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.

