Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions examples/task-graph/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# task-graph

One big engineering task, about a day of work, runs as a graph of parallel coding agents.

Most multi-agent setups work through subtasks one at a time. That's how "seven subtasks" turns into a
week. This flow takes a plan in which each subtask lists the subtasks it depends on (`dependsOn`), then:

- Each subtask starts **the moment** every subtask it depends on has **merged**.
- Each subtask runs in its own git worktree, on its own branch, as its own Claude agent.
- A finished subtask merges back into the run's branch. If the merge conflicts, a dedicated agent
resolves it, and a gate checks that the subtask's branch really is merged.
- A planned subtask can report **follow-up subtasks** that the task can't ship without. They join the graph
mid-run, up to `maxFollowups` (default 3, `0` turns them off). Follow-ups can't spawn follow-ups of their
own. Without these limits, agents keep filing polish and docs follow-ups: a 4-subtask test run grew to 20.
- Once everything has merged, the repository's tests run once, outside any agent, on the combined result.
The command is `testCommand`, or `npm ci && npm test` when there's an npm test script. With neither, the
run stops as `needs_human` rather than passing untested.
- Subtask branches are named `task-graph/<base-commit>/<id>`, so the flow never overwrites a branch your
repository already has.

A subtask counts as done only when it has **a commit on its branch and a result file**. An agent saying
"done" isn't enough.

## Run it

```sh
npm install -g relayflows && agent-relay cloud login
npm install @relayflows/surface # next to the flow file

# from the repository the work should happen in
flows run --cloud --sync-code task-graph.flow.ts --input example-plan.json
flows status --cloud <run-id> # or https://agentrelay.com/cloud/dashboard/workflow/<run-id>
flows sync <run-id> # the integrated result lands uncommitted in your checkout
```

`example-plan.json` is a 7-subtask "team invitations" feature. It has three parallel waves and a join.
Leave out `plan` and a planner agent reads the repository and writes the graph itself.

To have every new Linear ticket planned and run automatically, with one PR per ticket:

```sh
flows deploy task-graph.flow.ts --repo acme/api --on linear:team=ENG --approver you
```

Locally, from a checkout that has a `flows.json` naming the agent CLI:

```sh
flows run task-graph.flow.ts --local-agent --input example-plan.json
```

## Give it to your agent

`skill/run-task-graph/SKILL.md` is a Claude Code skill. Copy it to `~/.claude/skills/run-task-graph/`
(or to `.claude/skills/` in your repository). Your agent can then:

1. read a Linear issue with its sub-issues and "blocked by" relations;
2. build the plan and show you the parallel waves before anything runs;
3. submit the run to Cloud, report progress, and bring the result back with `flows sync`.

## Limits today

- **Needs the next `relayflows` release.** Running agents at the same time and `f.agent({ cwd })` both land
in the SDK and kernel fix that follows 2.0.26. On 2.0.26, a second concurrent agent is set aside ("parked")
and `cwd` is refused.
- **No budget header.** A budgeted authored flow currently admits one step at a time
(`packages/sdk/src/authored-budget.ts`), which would serialize the whole graph. Until that is fixed,
cap spend with `maxParallel` (default 4, maximum 8) and the 20-subtask ceiling.
- **Cloud labels each step by its position in the run, not by subtask, for now.** Until flows#553 and
cloud#3945 ship, the Cloud run page shows `agent-4`, `run-7`, and so on, with no edges. With them, it shows
each subtask's name and the edges between subtasks, but only once the run finishes. While the run is going,
nodes show live status without names or edges.
- **Don't resume; rerun.** Authored step ids come from call order, and with subtasks finishing
concurrently that order differs between runs. So `flows resume` can pair a step with another step's
journal. After a crashed runner, start a new run. Setup clears the previous run's worktrees and branches.
18 changes: 18 additions & 0 deletions examples/task-graph/example-plan.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"task": {
"title": "Team invitations",
"body": "Admins can invite teammates by email. The invitee gets an email with a link, accepts, and joins the team with the role the admin chose. Invitations expire after 7 days and can be revoked."
},
"maxParallel": 4,
"plan": {
"subtasks": [
{ "id": "schema", "title": "Invitations table and model", "detail": "Migration + model: email, team_id, role, token (unique), expires_at, accepted_at, revoked_at." },
{ "id": "email-template", "title": "Invitation email template and sender", "detail": "Template with inviter, team name and accept link; a send function with a test using the mail stub." },
{ "id": "api-create", "title": "POST /teams/:id/invitations", "detail": "Admin-only. Creates an invitation and sends the email. Tests for auth, duplicate invite, bad email.", "dependsOn": ["schema", "email-template"] },
{ "id": "api-accept", "title": "POST /invitations/:token/accept", "detail": "Validates token, expiry and revocation, adds the member with the invited role. Tests for every rejection path.", "dependsOn": ["schema"] },
{ "id": "api-revoke", "title": "DELETE /teams/:id/invitations/:invitationId", "detail": "Admin-only revoke. Tests that a revoked token can no longer be accepted.", "dependsOn": ["api-accept"] },
{ "id": "ui-invite", "title": "Invite modal on the team settings page", "detail": "Email + role picker, calls api-create, shows pending invitations with a revoke button.", "dependsOn": ["api-create", "api-revoke"] },
{ "id": "ui-accept", "title": "Accept-invitation page", "detail": "Landing page for the email link: signed-out, expired and revoked states.", "dependsOn": ["api-accept"] }
]
}
}
163 changes: 163 additions & 0 deletions examples/task-graph/skill/run-task-graph/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
---
name: run-task-graph
description: Run a large engineering task as a graph of parallel coding agents on Agent Relay Cloud. Turns a Linear issue (with its sub-issues and "blocked by" relations) or a written spec into a dependency plan, runs every subtask as soon as the subtasks it depends on have merged, and pulls the integrated result back into this checkout. Use when the user wants to parallelize a big ticket, run a Linear issue's sub-issues with agents, or watch several agents make progress on one task.
---

# Run a task graph on Agent Relay Cloud

The flow is `task-graph.flow.ts` from github.com/AgentWorkforce/flows
(`examples/task-graph/`). What it does:

- Each subtask runs as its own Claude agent, in its own git worktree and on its own branch.
- A subtask starts only once every subtask it depends on has **merged** into the run's branch.
- An agent may report follow-up subtasks. Those join the graph while the run is going.
- When everything has merged, the repo's test suite runs once on the combined result.

Follow these steps in order. Do not skip step 3: the user should approve the graph before any agent starts.

## 1. Preflight (once per machine)

```sh
npm install -g relayflows # provides the `flows` CLI
agent-relay cloud login # or export FLOWS_CLOUD_TOKEN=<Cloud API token>
flows runs --limit 1 # proves the credential works
```

A `REFUSED [cloud_auth_missing]` or `[cloud_auth_rejected]` means the login step
did not work. Ask the user to redo it, and don't carry on until it succeeds.

Put the flow in a scratch directory outside the repo, and install its one
dependency there:

```sh
TG="${TMPDIR:-/tmp}/task-graph" && mkdir -p "$TG" && cd "$TG"
curl -fsSL https://raw.githubusercontent.com/AgentWorkforce/flows/main/examples/task-graph/task-graph.flow.ts -o task-graph.flow.ts
npm init -y >/dev/null && npm pkg set type=module && npm install --no-audit --no-fund @relayflows/surface
```

## 2. Build `plan.json`

The input shape is:

```json
{
"task": { "title": "…", "body": "…", "url": "…" },
"maxParallel": 4,
"maxFollowups": 3,
"testCommand": "npm ci && npm test",
"plan": { "subtasks": [
{ "id": "schema", "title": "…", "detail": "what done means", "dependsOn": [] },
{ "id": "api", "title": "…", "detail": "…", "dependsOn": ["schema"] }
] }
}
```

**From a Linear issue.** Use the Linear MCP/API to read the parent issue, its
sub-issues, and each sub-issue's relations. Then map them into the plan:

- `task` comes from the parent issue: its title, its description as `body`, and its `url`.
- Each sub-issue becomes one subtask:
- `id` is the lowercased identifier, e.g. `ENG-142` → `eng-142`.
- `title` is the sub-issue's title.
- `detail` is its description plus its acceptance criteria.
- `dependsOn` comes from "blocked by" relations, but only between sibling
sub-issues. Drop blockers that are outside the parent issue.
- Skip sub-issues that are already Done or Canceled.
- If a sub-issue has sub-issues of its own, flatten them into the plan. The
child depends on whatever its parent depends on.

**No sub-issues, or a written spec instead of Linear.** Write the plan yourself. Read the repository
and split the task into subtasks that each fit one focused agent session. Always pass `plan`, so the
user approves the real graph in step 3. If you leave `plan` out, the flow's own planner writes the graph
and it starts running with nobody approving it. That is only for hands-off mode, below.

Rules. The flow refuses a plan that breaks any of these, so check them before submitting:

- `id` matches `^[a-z0-9][a-z0-9-]{0,39}$` and is unique.
- Every `dependsOn` entry names another subtask in the plan.
- There are no cycles.
- There are at most 20 subtasks.
- `maxParallel` is between 1 and 8.
- `maxFollowups` is between 0 and 10. Use 0 when the user wants exactly the plan and nothing more.
- Set `testCommand` to the command that tests this repository. You can leave it out only when
`package.json` has a `test` script. Without either, the flow stops before testing and doesn't
pass by default.

Only list a dependency when a subtask needs the other's **code merged**
first. Every dependency you add removes some parallelism.

## 3. Show the graph and get a yes

Print the plan as waves:

- Wave 1 is the subtasks with no dependencies.
- Wave 2 is the subtasks whose dependencies are all in wave 1.
- Continue the same way until every subtask has a wave.

```
wave 1: schema, email-template (parallel)
wave 2: api-create, api-accept (parallel)
wave 3: api-revoke
wave 4: ui-invite, ui-accept (parallel)
```

Tell the user the working tree will be uploaded. Uploads respect `.gitignore`,
untracked files are included, and `.git` and `node_modules` never are. Then ask
for confirmation. Nothing has run until they say yes.

## 4. Submit

Run this from the root of the user's repository:

```sh
flows check "$TG/task-graph.flow.ts"
flows run --cloud --sync-code "$TG/task-graph.flow.ts" --input plan.json
```

It prints the run id. Give the user the live view:
`https://agentrelay.com/cloud/dashboard/workflow/<run-id>`

## 5. Watch

```sh
flows status --cloud <run-id> # every step: state, timing, gate verdict, cost
flows logs <run-id> --step <step-name> # one agent's transcript
```

Poll `flows status --cloud` every few minutes and report changes in terms of the
plan's subtask ids. Say which subtasks are running, which have merged, and
which follow-ups have appeared.

A failed step means one of two things:

- a subtask's gate failed: the agent produced no commit, or no result file; or
- a merge could not be resolved.

For either one, show that step's `flows logs` and ask the user how to proceed.
Do not rerun the whole flow unasked.

## 6. Bring the result home

When the run completes, run this from the same repository:

```sh
flows sync <run-id> --dry-run # what would change
flows sync <run-id> # apply it, uncommitted
git diff --stat
```

Walk the user through the diff before committing. It is agent output. If the
plan came from Linear, offer to move the merged sub-issues to Done.

## Hands-off mode

To have every new ticket in a Linear team planned and run automatically, with
one PR per ticket:

```sh
flows deploy "$TG/task-graph.flow.ts" --repo <owner/name> --on linear:team=<KEY> --approver <github-user>
```

A ticket-triggered run receives the ticket as `issue` and uses the planner.
It pushes the run's branch and opens one pull request, with each subtask's
summary in the PR body.
Loading
Loading