Skip to content

feat(examples): task-graph — a plan of subtasks run as parallel agents, plus a skill to run it on Cloud - #555

Merged
khaliqgant merged 5 commits into
mainfrom
feat/task-graph-example
Sep 22, 2026
Merged

khaliqgant merged 5 commits into
mainfrom
feat/task-graph-example

Conversation

@khaliqgant

@khaliqgant khaliqgant commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

What

examples/task-graph: one large engineering task, about a day of work, runs as a graph of parallel coding agents. It also ships a Claude Code skill a customer's agent can use to run the flow on Agent Relay Cloud.

This was built for a prospect whose Claude work gets stuck doing "seven subtasks one at a time". They asked for a graph of subtasks where each node shows its state.

  • task-graph.flow.ts takes a plan (subtasks with dependsOn), or has a planner agent write one.
    • Each subtask starts as soon as everything it depends on has merged. It runs in its own git worktree and branch (f.agent({ cwd })), then merges back. A conflict goes to a resolver agent, gated on the branch actually being merged.
    • Done means a commit on the branch plus a result file, never the agent saying so.
    • Planned subtasks may add at most maxFollowups follow-ups (default 3, and follow-ups can't spawn their own). Without the cap, a 4-subtask test run grew to 20.
    • The full test suite runs once on the integrated result.
    • A Linear-triggered run (flows deploy --on linear:team=…) opens one PR per ticket.
  • example-plan.json: a 7-subtask "team invitations" feature, four waves.
  • skill/run-task-graph/SKILL.md walks the customer's agent through:
    1. setting up the flows CLI and Cloud login;
    2. turning a Linear issue's sub-issues and "blocked by" relations into a plan;
    3. showing the waves and getting a yes;
    4. flows run --cloud --sync-code;
    5. watching progress with flows status --cloud;
    6. bringing the result back with flows sync.
  • examples/tsconfig.json now typechecks the new example.

Depends on

Verified

  • On fix(sdk,kernel): run authored agents in parallel under --local-agent #554's branch: examples/task-graph ran with real Claude agents, a 4-subtask diamond, --local-agent.
    • slugify and truncate overlapped for about 32s; index waited for both to merge.
    • Exit 0, completionReason: success, 137s, and the integrated tests passed.
  • npm run typecheck:examples: no errors in task-graph. Three errors elsewhere are already on main (the ramp helper export and URL in stuck-run-triage).
  • flows check examples/task-graph/task-graph.flow.ts: passed.
  • The follow-up cap is not exercised by a real run: in the verified run every agent reported zero follow-ups.

Limits (also in the README)

  • No budget header: a budgeted authored flow runs one step at a time today (authored-budget.ts).
  • Resuming this flow after a runner crash is untested. With concurrent steps, the order of step calls depends on timing.

🤖 Generated with Claude Code


Summary by cubic

Adds examples/task-graph, a flow that runs one large task as a graph of parallel coding agents, each in its own git worktree and branch, starting once its dependencies have merged. A subtask only counts as done when it has a commit and a result file, and planned subtasks can add up to maxFollowups (default 3) follow-up subtasks mid-run. Also ships a Claude Code skill (skill/run-task-graph) that turns a Linear issue into a plan, gets user approval, and runs it on Cloud, plus a 7-subtask example plan and README.

Plans need not be sorted: dependencies are read after every subtask is registered, and an unscheduled dependency fails closed. Subtask branches are namespaced under task-graph/<base-commit>/<id> so existing repository branches are never reset. Gates check for a parseable result file plus a commit; the planner gate additionally requires an untouched working tree and unchanged HEAD, so a planner that edits files or commits fails instead of leaking changes into the run. Worktree removal is serialized with the other run-branch git operations, and info/exclude is resolved via --git-path so setup works from a linked worktree. A testCommand can be supplied; with neither it nor an npm test script, the run stops as needs_human instead of passing untested.

Depends on #554 for parallel agents and f.agent({ cwd }); without it the example is broken on 2.0.26. Cloud will show generic step names until #553 and AgentWorkforce/cloud#3945 land. No budget header, since budgeted authored flows run one step at a time today. Resuming after a runner crash is untested, and the follow-up cap is not exercised in a real run.

Written for commit a8d1714. Summary will update on new commits.

Review in cubic


Note

Low Risk
New example and documentation only; no changes to SDK, kernel, or production runtime paths.

Overview
Adds examples/task-graph, a new authored flow that runs one large engineering task as a dependency graph of parallel Claude agents, each in its own git worktree and branch, starting only after its dependsOn subtasks have merged back.

The flow validates plans (DAG, ids, caps), optionally uses a planner agent when no plan is supplied, gates completion on commits plus result JSON (not agent claims), handles merge conflicts with a resolver agent, allows capped mid-run follow-ups (maxFollowups), runs the integrated test suite once at the end (or stops as needs_human without testCommand/npm test), and opens a single PR when triggered from a Linear ticket. Git work uses namespaced branches under task-graph/<base-commit>/ and .relayflow/ bookkeeping excluded from merges.

Also ships example-plan.json (7-subtask team invitations), README (Cloud/local/deploy limits), a run-task-graph Claude Code skill for Linear → plan → approval → Cloud run → flows sync, and includes task-graph/*.ts in examples/tsconfig.json for typechecking.

Depends on a post-2.0.26 SDK for concurrent agents and f.agent({ cwd }); intentionally omits a flow budget header because budgeted authored flows serialize steps today.

Reviewed by Cursor Bugbot for commit bdd6624. Bugbot is set up for automated code reviews on this repo. Configure here.

Relayflow Lead and others added 2 commits September 22, 2026 11:00
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ktree via cwd

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-22T19:30:09.552262Z baa8476 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 15 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 04e72595-bc53-4345-a9a1-e19bc9ef6b5e

📥 Commits

Reviewing files that changed from the base of the PR and between 8b33eba and a8d1714.

📒 Files selected for processing (5)
  • examples/task-graph/README.md
  • examples/task-graph/example-plan.json
  • examples/task-graph/skill/run-task-graph/SKILL.md
  • examples/task-graph/task-graph.flow.ts
  • examples/tsconfig.json

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 4 potential issues.

4 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)

Devin Review

Comment thread examples/task-graph/task-graph.flow.ts Outdated
};

const runSubtask = async (s: Subtask, parent?: string): Promise<void> => {
await Promise.all((s.dependsOn ?? []).map((d) => merged.get(d)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Later dependencies start too early

A subtask starts early when its dependency appears later in the input array. merged.get() returns undefined, which Promise.all treats as resolved.

Learn more

The scheduler starts each async runSubtask while it is still populating merged. An async function executes through its first await immediately, so dependency lookup happens before later array entries are registered. The same ordering affects a follow-up that depends on a later sibling in its report.

Example: With [frontend dependsOn backend, backend], frontend reads no promise for backend and acquires a slot immediately. It can edit against the pre-backend tree although the plan requires backend first.

Recommended fix: Register every subtask in a batch before any runSubtask body can execute. For example, defer execution with Promise.resolve().then(() => runSubtask(...)), and route the initial plan through the same schedule helper.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 99cab6f. runSubtask now yields before reading dependencies, so the scheduling loop registers every subtask first. An unscheduled dependency now throws instead of resolving as undefined. Verified with a real run: the 4-subtask plan listed in reverse order (readme, index, truncate, slugify) merged in dependency order and completed success.

Comment thread examples/task-graph/task-graph.flow.ts Outdated
const branch = `task-graph/${s.id}`;
// Branched from the run's branch as it is NOW, so every dependency's code is already in it.
const base = (await onRunBranch(() => f.run(
`git worktree add -q -B ${shellWord(branch)} ${shellWord(tree)} HEAD && git rev-parse HEAD`,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Existing task branches are overwritten

A preexisting task-graph/<id> branch is reset to the run’s HEAD. The user loses that branch’s reference to its commits.

Learn more

git worktree add -B <branch> <path> HEAD force-resets an existing branch before creating the worktree. Subtask IDs are short user-controlled names, so names such as task-graph/schema can already exist in a customer's repository.

Example: A user has unpublished work on task-graph/schema at commit A. Running a plan with ID schema moves that branch to the run's HEAD B, leaving A reachable only through recovery tools.

Recommended fix: Namespace branches with a run-unique identifier and refuse any collision before creating a worktree. Avoid -B; create a new branch with -b, then retain that exact unique name for merge and cleanup.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 99cab6f. Branches are now task-graph/<base-commit>/<id> and created with -b, so an existing branch makes the step fail instead of being reset. A rerun from the same commit clears only that namespace, which only this flow writes.

Comment thread examples/task-graph/task-graph.flow.ts Outdated
Comment on lines +210 to +214
await f.run(
'if [ -f package.json ] && node -e \'p=require("./package.json");process.exit(p.scripts&&p.scripts.test?0:1)\'; ' +
'then npm ci --no-audit --no-fund && npm test; else echo "no test script; skipping"; fi',
{ timeout: "15m" },
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Non-Node test suites never run

Repositories without an npm test script always print skipping and succeed. Their integrated changes can reach the final PR without any test run.

Learn more

The final gate recognizes only package.json with scripts.test. The flow and skill accept an arbitrary repository, and the final result can be pushed into a pull request, so every other build system bypasses the advertised integrated test run.

Example: In a Rust repository containing Cargo.toml, all subtask branches merge. The final command prints no test script; skipping, returns zero, and the ticket-triggered path opens a PR without running cargo test.

Recommended fix: Make the integrated test command explicit plan input or planner output and validate it before agents start. Alternatively, detect supported project manifests with deterministic commands and stop as needs_human when no test command can be established; do not treat an unknown test suite as success.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 99cab6f. New testCommand input. It defaults to npm ci && npm test only when there is an npm test script. With neither, the run stops as needs_human before testing or opening a PR. It never passes untested. README and skill document it.

Comment thread examples/task-graph/task-graph.flow.ts Outdated
Comment on lines +210 to +214
await f.run(
'if [ -f package.json ] && node -e \'p=require("./package.json");process.exit(p.scripts&&p.scripts.test?0:1)\'; ' +
'then npm ci --no-audit --no-fund && npm test; else echo "no test script; skipping"; fi',
{ timeout: "15m" },
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟨 Agent changes escape through npm scripts

Agents can modify package.json before npm ci and npm test run with the flow runner’s ambient authority. Dependency lifecycle or test scripts can then execute outside the agent boundary.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not changing this: the final test step adds no authority. Each agent already runs arbitrary shell commands (tests, builds, git) in the same sandbox, as the same user, with the same credentials as the runner. So an agent that edits package.json gains nothing it didn't already have. The trust boundary is the sandbox, not the agent/runner split. On Cloud that sandbox is per-run, and locally it's the user's own checkout.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread examples/task-graph/task-graph.flow.ts Outdated
};

const runSubtask = async (s: Subtask, parent?: string): Promise<void> => {
await Promise.all((s.dependsOn ?? []).map((d) => merged.get(d)));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dependencies ignore later plan order

High Severity

runSubtask reads merged.get for each dependsOn id before those ids are inserted into merged. A valid DAG that lists a dependent before its dependency therefore waits on undefined and starts immediately. planError accepts any order, and Linear-built plans are not topologically sorted, so required merges are skipped.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit baa8476. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 99cab6f, same change as the Devin thread above. Dependencies are read after every subtask is registered, and an unknown one fails closed. Verified with a reverse-ordered plan: merges landed slugify/truncate → index → readme.

Comment thread examples/task-graph/task-graph.flow.ts Outdated
`(followups is usually empty; ids must be new).`
: `Finally write ${result} as JSON: {"summary":"what you did"}.`),
// Done means a result file AND at least one commit on the branch — not the agent saying so.
}).gate({ type: "subprocess_gate", command: `test -s ${shellWord(result)} && test "$(git -C ${shellWord(tree)} rev-list --count ${base}..HEAD)" -gt 0` });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Result gate skips JSON check

Medium Severity

The subtask gate only checks that the result file is non-empty, then the branch is merged, then JSON.parse reads that file. Non-JSON output (agents often wrap JSON in fences) passes the gate, merges, and then throws, so dependents never run. The planner gate already requires parseable JSON.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit baa8476. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 99cab6f. The subtask gate now requires JSON.parse to succeed on the result file, the same check as the planner gate, as well as a commit on the branch. A fenced or non-JSON result fails the gate before the merge.

Comment thread examples/task-graph/task-graph.flow.ts Outdated
`Run the affected tests, then commit the merge.`,
}).gate({ type: "subprocess_gate", command: `git merge-base --is-ancestor ${shellWord(branch)} HEAD` }));
}
await f.run(`git worktree remove --force ${shellWord(tree)}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worktree remove races git lock

Medium Severity

git worktree remove runs outside onRunBranch, so it overlaps another subtask's serialized git worktree add or git merge on the same repo. Concurrent git worktree/index locks fail the finishing subtask after its agent and merge already succeeded.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit baa8476. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 99cab6f. git worktree remove now runs through onRunBranch, so it's serialized with worktree creation and merges.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: baa8476883

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +165 to +166
const outcome = (await onRunBranch(() => f.run(
`git merge --no-ff -q -m ${shellWord(`task-graph: merge ${s.id}`)} ${shellWord(branch)} >/dev/null 2>&1 && echo merged || { git merge --abort; echo conflict; }`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Derive step identities independently of completion order

When two independent agents finish in a different order from a later resume, each merge reaches this f.run in a different order and therefore receives a different ordinal-based step ID. Authored child admission keys are keyed by that ordinal, so a previously recorded merge can be matched with another subtask's command and the kernel refuses the changed spec; a crash after one parallel branch finishes can therefore make the run impossible to resume. Assign stable per-subtask identities or otherwise make operation creation order deterministic, and cover the parallel crash/resume case required by the repository.

AGENTS.md reference: AGENTS.md:L19-L21

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct: authored step ids are ordinals by call order, and with concurrent subtasks that order depends on which agent finishes first. A resume could pair a step with another step's journal. The fix belongs in the SDK: step identity needs to be keyed independently of completion order. It can't be fixed inside one example flow. In bdd6624 the README states the limit plainly: don't resume, rerun. Setup already clears a previous run's worktrees and branches, so a rerun is safe.

Comment thread examples/task-graph/task-graph.flow.ts Outdated
`fit one focused agent session. Make dependsOn honest: only list a dependency when the subtask ` +
`really needs that code merged first — everything else runs in parallel. Do not write code.\n` +
`Write ONLY this JSON to ${work}/plan.json:\n${PLAN_SHAPE}\n\nTask:\n${brief}`,
}).gate({ type: "subprocess_gate", command: `node -e 'JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"))' ${shellWord(`${work}/plan.json`)}` });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Contain the planner's writes before accepting its plan

When input.plan is omitted, the planner runs in the repository root with write access, while this gate verifies only that .relayflow/plan.json parses. If the coding agent edits any tracked or untracked project file despite the prompt, those edits remain in the run branch, are tested with the merged subtasks, and are returned by flows sync or included in the triggered PR without belonging to any reviewed subtask. Run planning in an isolated worktree or reject/reset every write outside the plan file.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bdd6624. The planner gate now requires both a parseable plan.json and git status --porcelain to be empty. .relayflow/ is excluded, so only project files count. A planner that edits the repo fails the gate and the run stops before any subtask starts.

Comment thread examples/task-graph/task-graph.flow.ts Outdated
Comment on lines +211 to +212
'if [ -f package.json ] && node -e \'p=require("./package.json");process.exit(p.scripts&&p.scripts.test?0:1)\'; ' +
'then npm ci --no-audit --no-fund && npm test; else echo "no test script; skipping"; fi',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fail instead of succeeding without repository tests

For any repository that is not an npm project with a root package.json and scripts.test—including Rust, Go, Python, pnpm-only, and many monorepos—this branch merely prints that tests were skipped and the flow subsequently calls done("success") or opens a PR. That contradicts the example's stated integrated-test gate and reports unverified output as complete; accept a declared test command or stop for human input when no supported test command can be proven.

AGENTS.md reference: AGENTS.md:L35-L36

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed in 99cab6f (this comment is on the earlier commit). The new testCommand input is used; without it, the flow runs npm ci && npm test only when there's an npm test script. With neither, the run stops as needs_human before testing or opening a PR.

Comment thread examples/task-graph/task-graph.flow.ts Outdated
// Flow bookkeeping and worktrees live under an excluded dir so no merge or `git add -A` picks them up.
await f.run(
`rm -rf ${shellWord(work)} && git worktree prune && mkdir -p ${shellWord(`${work}/results`)} && ` +
`{ grep -qxF '.relayflow/' .git/info/exclude 2>/dev/null || echo '.relayflow/' >> .git/info/exclude; }`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve the Git exclude path in linked worktrees

When the documented local mode is launched from a linked Git worktree, .git is a regular gitfile rather than a directory, so redirecting to .git/info/exclude fails with Not a directory and the initialization step aborts before any subtask starts. Obtain the path through git rev-parse --git-path info/exclude instead of assuming the primary-checkout layout.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bdd6624. Setup resolves the exclude file with git rev-parse --git-path info/exclude, and creates its directory, so it works from a linked worktree where .git is a gitfile. I checked it inside a real linked worktree: it resolves to the shared <common-dir>/info/exclude.

Comment on lines +68 to +69
**No sub-issues, or a written spec instead of Linear.** Omit `plan`
entirely. A planner agent then reads the repository and writes the graph itself.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require plan approval before auto-planned work starts

For a written spec or a Linear issue without sub-issues, these instructions explicitly omit plan, so there is no graph available for step 3 to display or approve. The actual graph is created only after submission, and the flow immediately schedules its coding agents, violating the skill's explicit promise that the user approves the graph before any agent starts; generate the plan before confirmation or add a durable human gate after planning.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bdd6624. The skill now has the calling agent write the plan itself, both from a spec and from a Linear issue without sub-issues, so step 3 always shows the real graph for approval. The in-flow planner, which runs without approval, is only for the hands-off flows deploy mode.

Comment thread examples/task-graph/task-graph.flow.ts Outdated
`(followups is usually empty; ids must be new).`
: `Finally write ${result} as JSON: {"summary":"what you did"}.`),
// Done means a result file AND at least one commit on the branch — not the agent saying so.
}).gate({ type: "subprocess_gate", command: `test -s ${shellWord(result)} && test "$(git -C ${shellWord(tree)} rev-list --count ${base}..HEAD)" -gt 0` });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate the result JSON before merging the branch

The completion gate accepts any nonempty result file, but the flow merges and removes the worktree before parsing that file at line 178. If an agent writes fenced JSON, truncated JSON, or any other nonempty malformed output, its code is merged first and the flow then fails during JSON.parse, leaving an integrated branch with no usable report. Parse and validate the expected result shape in this pre-merge gate.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed in 99cab6f (this comment is on the earlier commit). The subtask gate now runs JSON.parse on the result file, along with requiring a commit on the branch. That happens before the merge and before the worktree is removed, so a fenced or malformed result fails the gate and nothing is merged.

Relayflow Lead and others added 2 commits September 22, 2026 12:32
…pace, no silent test skip

- Read dependencies after the scheduling loop registers every subtask, so a
  plan need not be topologically sorted (Linear's is not). Fail closed on an
  unscheduled dependency instead of treating it as resolved.
- Name subtask branches task-graph/<base-commit>/<id> and create them with -b,
  so a repository's existing branches are never reset.
- Gate each subtask on a parseable result file, not just a non-empty one.
- Serialize worktree removal with the other run-branch git operations.
- Take testCommand; with neither it nor an npm test script, stop as
  needs_human rather than passing untested.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… exclude via --git-path

- The planner gate now also requires an untouched working tree, so a planner
  that edits project files fails instead of leaking edits into the run.
- Resolve info/exclude with git rev-parse --git-path so setup works from a
  linked worktree, where .git is a file.
- Skill: the calling agent always writes the plan so the user approves the
  real graph; the in-flow planner is for hands-off deploys only.
- README: rerun rather than resume; authored step ids follow call order.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit bdd6624. Configure here.

Comment thread examples/task-graph/task-graph.flow.ts Outdated
}).gate({
type: "subprocess_gate",
command: `node -e 'JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"))' ${shellWord(`${work}/plan.json`)} && ` +
`test -z "$(git status --porcelain)"`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Planner gate misses committed edits

Medium Severity

The new planner gate only requires git status --porcelain to be empty. A planner that commits its edits leaves a clean tree, so the gate passes and those commits become HEAD for every later subtask worktree.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit bdd6624. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a8d1714. The flow now records HEAD before the planner runs, and the gate requires HEAD to be unchanged as well as a clean working tree. A planner that commits its edits fails the gate, and the run stops before any subtask starts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@khaliqgant
khaliqgant merged commit 83addb0 into main Sep 22, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant