Why
work-issue ends when a PR is marked ready for review. From then on nobody owns it. When another PR merges first the branch goes behind or conflicting with main; when a human requests changes, no agent sees the comments until someone opens a session and asks. The PR for #98 sat conflicting with main after #99 merged, with its ledger saying "Ready: no", until a human noticed. Every agent PR that waits more than a day pays this.
Claude Code on the web reacts to review comments only on PRs it opened itself, and its docs say it cannot react to conflicts at all because GitHub sends no webhook when the base advances. PRs driven from the desktop app or OpenCode get nothing.
Expected outcome
A steward-pr skill that, invoked on a schedule or by hand, keeps every ready agent PR mergeable and answered until a human merges it. One sweep over a repo with nothing to do posts nothing and changes nothing.
For each open, non-draft PR whose body carries the work-issue ### Status section, the steward:
- brings the branch up to date with main (update-branch when behind, merge
origin/main into the branch when conflicting) and gets checks green again, delegating fixes to implement-issue;
- re-runs
e2e-device when the update touched packages/native, packages/react-native, playground or playground-native;
- treats every PR comment, review and review comment without the agent trailer as human input, delegates the fix to
implement-issue with the comments pasted in, runs one review-pr round on the fix, pushes, and replies on each thread saying what changed and in which commit;
- updates the ledger with a
Steward: entry and ends with a fixed report block.
So that the steward can tell agent activity from human activity regardless of which GitHub login posted it, every comment, review, PR body and issue comment an agent posts ends with one trailer line:
Constraints and non-goals
- Never
gh pr merge (AGENTS.md rule 8), never rebase, never force push. Update by merging main in; squash merge makes the merge commit disappear.
- Never resolves review threads. The human who opened a thread resolves it.
- Drafts stay with their orchestrator. A draft with a stale ledger is an unfinished
work-issue run, resumed by running work-issue on its issue again.
- PRs without the ledger (human-authored, dependabot) are never touched.
- Same loop discipline as
work-issue: at most two fix rounds per PR per sweep, then one comment on the linked issue with what is stuck, status:blocked, stop.
- Reads review activity through the REST API (
gh api repos/.../pulls/N/comments, .../reviews, .../issues/N/comments), not GraphQL. Claude Code cloud sessions proxy GitHub and allow only a pinned set of GraphQL operations; the skill must work there too.
- Scheduling is the operator's choice (
/loop, a scheduled task, a cron). The skill assumes nothing about how often it runs.
- Stacked PRs are out of scope; the repo keeps sequential slices against main.
- No CI changes.
How we know it is done
AGENTS.md names the trailer under Writing, and every skill that posts to GitHub (review-pr, work-issue, implement-issue, design-feature, triage-issue, file-issue, cut-release, review-memory) appends it in its posting step. A dry run of review-pr on a branch produces a review whose summary and every inline comment end with -- agent: review-pr.
.claude/skills/steward-pr/SKILL.md exists and the Skills table in AGENTS.md routes "watching PRs, keeping them mergeable, answering review" to it.
- Invoked with no argument, the steward sweeps
gh pr list --state open and selects only non-draft PRs whose body contains ### Status. Invoked with a PR number, it handles that PR alone and reports "skipped: " when it does not qualify.
- On a PR whose
mergeStateStatus is BEHIND, one sweep runs gh pr update-branch; on DIRTY, it merges origin/main into the branch, resolves, pushes, and the PR reads CLEAN or UNSTABLE afterwards. No command in the skill rewrites history.
- On a PR with failing checks, one sweep delegates to
implement-issue with the failing job's log excerpt and ends with checks green or the PR blocked after two rounds.
- On a PR where a human left a review comment (no trailer), one sweep ends with a commit addressing it, one
review-pr round on that commit, and a reply on that thread carrying -- agent: steward-pr. The thread stays unresolved. A second sweep over the same PR posts nothing.
- After each handled PR the
### Status line gains Steward: <date> <what was done> and the skill ends with a fixed report block listing, per PR, what was done or why it was skipped.
.claude/settings.json allowlists the commands the skill runs and nothing broader (gh pr update-branch *, the REST reads under gh api repos/*).
- A sweep over a repo where every qualifying PR is clean, green and answered posts nothing and pushes nothing.
Alternatives considered
- Fold this into
work-issue as a final phase: rejected, its trigger is many PRs on a timer, not one issue in one session, and the two would blur the handoff at "ready".
- Rely on Claude Code on the web reacting to PR activity: rejected, it covers only PRs opened from cloud sessions and cannot detect conflicts.
- Tell agent from human comments by time (anything after the PR left draft is human): rejected, the steward's own review round and overlapping sessions break the ordering; the trailer classifies each comment on its own.
- Stacked PRs with
gh stack: rejected for now, cloud sessions can push only to their own working branch, and squash-only merging plus the no-force-push rule makes stacks unmaintainable.
Why
work-issueends when a PR is marked ready for review. From then on nobody owns it. When another PR merges first the branch goes behind or conflicting with main; when a human requests changes, no agent sees the comments until someone opens a session and asks. The PR for #98 sat conflicting with main after #99 merged, with its ledger saying "Ready: no", until a human noticed. Every agent PR that waits more than a day pays this.Claude Code on the web reacts to review comments only on PRs it opened itself, and its docs say it cannot react to conflicts at all because GitHub sends no webhook when the base advances. PRs driven from the desktop app or OpenCode get nothing.
Expected outcome
A
steward-prskill that, invoked on a schedule or by hand, keeps every ready agent PR mergeable and answered until a human merges it. One sweep over a repo with nothing to do posts nothing and changes nothing.For each open, non-draft PR whose body carries the work-issue
### Statussection, the steward:origin/maininto the branch when conflicting) and gets checks green again, delegating fixes toimplement-issue;e2e-devicewhen the update touchedpackages/native,packages/react-native,playgroundorplayground-native;implement-issuewith the comments pasted in, runs onereview-prround on the fix, pushes, and replies on each thread saying what changed and in which commit;Steward:entry and ends with a fixed report block.So that the steward can tell agent activity from human activity regardless of which GitHub login posted it, every comment, review, PR body and issue comment an agent posts ends with one trailer line:
Constraints and non-goals
gh pr merge(AGENTS.md rule 8), never rebase, never force push. Update by merging main in; squash merge makes the merge commit disappear.work-issuerun, resumed by runningwork-issueon its issue again.work-issue: at most two fix rounds per PR per sweep, then one comment on the linked issue with what is stuck,status:blocked, stop.gh api repos/.../pulls/N/comments,.../reviews,.../issues/N/comments), not GraphQL. Claude Code cloud sessions proxy GitHub and allow only a pinned set of GraphQL operations; the skill must work there too./loop, a scheduled task, a cron). The skill assumes nothing about how often it runs.How we know it is done
AGENTS.mdnames the trailer under Writing, and every skill that posts to GitHub (review-pr,work-issue,implement-issue,design-feature,triage-issue,file-issue,cut-release,review-memory) appends it in its posting step. A dry run ofreview-pron a branch produces a review whose summary and every inline comment end with-- agent: review-pr..claude/skills/steward-pr/SKILL.mdexists and the Skills table inAGENTS.mdroutes "watching PRs, keeping them mergeable, answering review" to it.gh pr list --state openand selects only non-draft PRs whose body contains### Status. Invoked with a PR number, it handles that PR alone and reports "skipped: " when it does not qualify.mergeStateStatusisBEHIND, one sweep runsgh pr update-branch; onDIRTY, it mergesorigin/maininto the branch, resolves, pushes, and the PR readsCLEANorUNSTABLEafterwards. No command in the skill rewrites history.implement-issuewith the failing job's log excerpt and ends with checks green or the PR blocked after two rounds.review-prround on that commit, and a reply on that thread carrying-- agent: steward-pr. The thread stays unresolved. A second sweep over the same PR posts nothing.### Statusline gainsSteward: <date> <what was done>and the skill ends with a fixed report block listing, per PR, what was done or why it was skipped..claude/settings.jsonallowlists the commands the skill runs and nothing broader (gh pr update-branch *, the REST reads undergh api repos/*).Alternatives considered
work-issueas a final phase: rejected, its trigger is many PRs on a timer, not one issue in one session, and the two would blur the handoff at "ready".gh stack: rejected for now, cloud sessions can push only to their own working branch, and squash-only merging plus the no-force-push rule makes stacks unmaintainable.