Skip to main content
This example runs two cooperating agents. issue-triage wakes for every new GitHub issue (and whenever a human re-requests triage with a label): it dedupes, prioritizes, labels, splits mixed reports, and asks for missing context. When an issue is clear enough to implement, it hands off to shepherd — the implementation owner that opens a pull request, carries it through review, and comments back on the issue with the PR link, tests run, and residual risks. Use it when your issue tracker accumulates faster than anyone grooms it, and you want vague reports turned into implementation-ready work — or shipped PRs — without a human dispatcher. GitHub Issues is the base; a Linear variant swaps the source of truth.

How it works

Two mechanics carry the example:
  • The issue binding. Both triage triggers route bind on the github.issue target with onUnmatched: spawn: the first event on an issue spawns a session already bound to it, and a repeat event on an issue whose triage session is still live delivers into that session instead of spawning a duplicate.
  • Agent-to-agent handoff. Triage calls the auto.sessions.spawn tool with agent: shepherd and a message carrying the issue number, URL, triage summary, acceptance criteria, and constraints. Shepherd receives the full brief in the spawn message, so this dispatch path needs no duplicate issue trigger.
The auto-triage label is a one-shot request token, not a standing subscription: triage removes it once it has acted, so re-applying the label is how humans request another pass.

Install from the managed template

Issue Triage and Shepherd are separate retained roles in @auto/agents:
.auto/agents/issue-triage.yaml
.auto/agents/shepherd.yaml
For the Linear substrate, import @auto/agents@latest/issue-triage-linear.yaml. The catalog installer automatically adds this optional Linear reporting overlay to the separately installed Shepherd; add the same overlay to a hand-authored facade:
.auto/agents/shepherd.yaml (Linear overlay)
The handoff arrives through auto.sessions.spawn, so Shepherd needs the Linear chat tool but no duplicate Linear event trigger. Merge to your production branch for GitHub Sync to apply.

The full configuration

.auto/fragments/environments/agent-runtime.yaml
.auto/agents/issue-triage.yaml
.auto/agents/shepherd.yaml
The permission split is the safety story: triage mounts the repo read-only with issues: write and nothing else (pullRequests: none), while Shepherd gets contents: write and pullRequests: write but is only ever started with a brief that triage already validated. Filtering both GitHub triggers on $.github.auto.authored: false keeps issues opened by auto’s own GitHub App from re-triggering triage in a loop.
Plain-issue comment events use the github.issue.comment.* keys; github.issue_comment.* is GitHub’s PR-comment family. And on GitHub events the authored flag lives at $.github.auto.authored$.auto.authored is the chat-event path. See GitHub events.

Walkthrough

1

An issue opens

github.issue.opened arrives; issue-opened matches. No session is bound to this issue, so onUnmatched: spawn starts a triage session bound to the github.issue target.
2

Triage acts on the issue

The session inspects the issue with issue_read, checks for duplicates, sets the most specific existing labels and a priority with issue_write (it never creates labels), and posts one comment explaining what it did — or asking for reproduction steps and acceptance criteria when the report is too thin.
3

A human requests a re-pass

Later, someone adds the auto-triage label. github.issue.labeled with $.github.label.name: auto-triage matches; the bind route delivers into the still-live triage session, or spawns a fresh one bound to the issue if the original is gone. Triage acts, then removes the label — consuming the token.
4

Handoff to Shepherd

When the issue is implementation-ready, triage comments handoff context on the issue, removes the label, and calls auto.sessions.spawn with agent: shepherd and the full brief. The tool returns the new session (with its URL), so the handoff is traceable from either session.
5

Shepherd ships a PR

The Shepherd session boots with the repo mounted at main, creates auto/issue-123-…, works test-first, pushes, opens a PR with a Review Map via create_pull_request, and closes the loop with add_issue_comment: PR link, tests run, residual risks. If a code review agent is installed, it picks the PR up from github.pull_request.opened.

Linear variant

Teams that track work in Linear import the -linear entrypoints. The shape changes in three ways: the trigger source becomes linear.issue.* events, the label is the only request token (there is no every-new-issue trigger), and issue metadata is read and mutated through the unified chat tool’s chat.issue.get / chat.issue.update instead of the GitHub tool.
.auto/agents/issue-triage.yaml
The Linear triggers fire on creation with the label present, and on update only when the label was just added — linear.updatedFrom.labelNames.added carries exactly the labels the update added:
Payload placeholders follow the Linear namespace: {{linear.issue.identifier}}, {{linear.issue.title}}, {{linear.issue.url}}. Shepherd still needs the GitHub App mount to open PRs; it reports back on the Linear issue via chat.send with target provider linear. See Linear events.

Variations

  • Slack visibility. The managed template’s base entrypoints already carry optional: true Slack wiring — an ad-hoc @mention trigger on each agent plus triage’s ready-work notes in #dev — which apply skips while no slack connection exists and activates on the next apply once one does. The handwritten configuration above keeps triage’s chat tool but trims the mention triggers; copy the mention trigger from the code review example to restore them.
  • Direct coding handoffs. For “just fix it” requests on issues (a human mentions the handoff agent in an issue comment), the handoff example is purpose-built: it keeps the issue binding, opens a PR, binds it as github.pull_request, and reports back on the issue.
  • Raise or lower the handoff bar. The definition of “implementation-ready” lives entirely in the triage systemPrompt. Tighten it (require acceptance criteria and a reproduction) for riskier codebases; loosen it for internal tools.
  • Guard the spend. Add spendCaps to shepherd (for example spendCaps: { maxPerSessionUsd: "10" }) so a runaway implementation session self-limits.
  • Smoke test. After apply, open a test issue and confirm a triage session spawns and comments. Then label an implementation-ready issue auto-triage and watch Shepherd open a PR. You can also start either agent directly from the web app with a message like “Triage issue #42”.