chrismdp / rebase-conflicting-pull-requests
A pull request that conflicts with main is rebased onto it
security.yaml
security.yaml is AWML 0.3, as it was pushed. The registry draws, checks and fits it converted to AWML 0.8.
# A pull request that conflicts with main is rebased onto it, by who may see each part. The
# repository is public, so everything sits in one scope: anyone may read the pull requests and their
# branches. What matters is who may act. Only branches in the repository itself, opened by its own
# people or by the build workflow, are touched, so strangers' text never reaches the draft step.
# Claude rebases with a read-only token and hands back the rebased branch. A separate step checks
# it and replaces the branch only if nobody has pushed to it meanwhile. The maintainer still
# reviews and merges the pull request, outside this workflow.
awml: "0.3"
title: A pull request that conflicts with main is rebased onto it
view: security
groups:
- { id: public, label: "public: anyone who reads the repository", processing: any }
parts:
- { id: landed, kind: trigger, label: "A commit lands on main", wait: PT1H }
- { id: token, kind: connector, label: "The repository, through its own Actions token", in: public }
- { id: find, kind: code, op: bring-in, label: "Bring in the open pull requests that conflict with main", in: public }
- { id: whose, kind: code, op: sort, label: "Sort by rule: is the branch in this repository, opened by its own people or the build workflow?", in: public }
- { id: repo, kind: context, label: "The branch's commits, main, and AGENTS.md", in: public }
- { id: rebase, kind: ai, op: draft, label: "Rebase the branch onto main, keeping both sides of each conflict", in: public }
- { id: bundle, kind: context, label: "The rebased branch, as a bundle", in: public }
- { id: gate, kind: code, op: check, label: "Check it sits on main, has no conflict markers or workflow changes, and passes every check", in: public }
- { id: push, kind: code, op: do, label: "Force-push the rebased branch, only if it hasn't moved", in: public }
- { id: replace, kind: outside, label: "Replace the pull request's branch: Allow", in: public }
- { id: tell, kind: code, op: do, label: "Comment what was resolved, or why it couldn't be", in: public }
- { id: thread, kind: surface, label: "The pull request's thread", in: public }
- { id: strangers, kind: comment, label: "A fork's pull request, or one opened by a stranger, is never touched, so every line Claude reads was written by the repository's own people. AWML can't mark this yet (awml #5)" }
- { id: held, kind: comment, label: "The draft step's token can only read, so it can't push. The push names the commit the bring-in step saw, so GitHub refuses it if anyone pushed meanwhile (awml #26)" }
- { id: undo, kind: comment, label: "Allow, because the old commit stays on GitHub and the comment names it, so the branch can be put back with one push" }
- { id: ci, kind: comment, label: "A push by the Actions token doesn't start CI, so the check step's own run is the only check on the rebased branch until the maintainer pushes" }
lines:
- { from: landed, to: find, kind: start, label: "the push" }
- { from: token, to: find, kind: data, label: "open pull requests and whether each conflicts" }
- { from: find, to: whose, kind: start, label: "[for each that conflicts]" }
- { from: whose, to: rebase, kind: start, label: "[the repository's own]" }
- { from: repo, to: rebase, kind: data }
- { from: rebase, to: bundle, kind: data }
- { from: bundle, to: gate, kind: data }
- { from: gate, to: push, kind: start, label: "[all pass]" }
- { from: bundle, to: push, kind: data }
- { from: push, to: replace, kind: data }
- { from: replace, to: token, kind: data }
- { from: push, to: tell, kind: start, label: "[pushed or refused]" }
- { from: gate, to: tell, kind: start, label: "[any fail]" }
- { from: rebase, to: tell, kind: start, label: "[gave up]" }
- { from: tell, to: thread, kind: data }
- { from: strangers, to: whose, kind: tie }
- { from: held, to: push, kind: tie }
- { from: undo, to: replace, kind: tie }
- { from: ci, to: gate, kind: tie }
platform.yaml
platform.yaml is AWML 0.3, as it was pushed. The registry draws, checks and fits it converted to AWML 0.8.
# The same workflow as the security view, by where each part runs. Finding the conflicting pull
# requests runs in one job. Claude rebases in a second job with a read-only token. The check, the
# push and the comment run in a third job on a fresh runner, which shares none of the second's
# access, so Claude can't write the verdict the push relies on.
awml: "0.3"
title: A pull request that conflicts with main is rebased onto it
view: platform
groups:
- { id: github, label: "GitHub: pull requests, branches, comments" }
- { id: findjob, label: "Actions job: find, with a read-only token", platform: actions-code }
- { id: draftjob, label: "Actions job: rebase, with a read-only token", platform: actions-code }
- { id: agentjob, label: "Claude Code, claude-opus-5-5, on the maintainer's subscription", platform: actions-agent, in: draftjob }
- { id: codejob, label: "Actions job: check, push and comment", platform: actions-code }
parts:
- { id: landed, kind: trigger, label: "A commit lands on main", wait: PT1H, in: github }
- { id: token, kind: connector, label: "The repository, through its own Actions token", in: github }
- { id: find, kind: code, op: bring-in, label: "Bring in the open pull requests that conflict with main", in: findjob }
- { id: whose, kind: code, op: sort, label: "Sort by rule: is the branch in this repository, opened by its own people or the build workflow?", in: findjob }
- { id: repo, kind: context, label: "The branch's commits, main, and AGENTS.md", in: github }
- { id: rebase, kind: ai, op: draft, label: "Rebase the branch onto main, keeping both sides of each conflict", in: agentjob }
- { id: bundle, kind: context, label: "The rebased branch, as a bundle", in: github }
- { id: gate, kind: code, op: check, label: "Check it sits on main, has no conflict markers or workflow changes, and passes every check", in: codejob }
- { id: push, kind: code, op: do, label: "Force-push the rebased branch, only if it hasn't moved", in: codejob }
- { id: replace, kind: outside, label: "Replace the pull request's branch: Allow", in: github }
- { id: tell, kind: code, op: do, label: "Comment what was resolved, or why it couldn't be", in: codejob }
- { id: thread, kind: surface, label: "The pull request's thread", in: github }
- { id: strangers, kind: comment, label: "A fork's pull request, or one opened by a stranger, is never touched, so every line Claude reads was written by the repository's own people. AWML can't mark this yet (awml #5)" }
- { id: held, kind: comment, label: "The draft step's token can only read, so it can't push. The push names the commit the bring-in step saw, so GitHub refuses it if anyone pushed meanwhile (awml #26)" }
- { id: undo, kind: comment, label: "Allow, because the old commit stays on GitHub and the comment names it, so the branch can be put back with one push" }
- { id: ci, kind: comment, label: "A push by the Actions token doesn't start CI, so the check step's own run is the only check on the rebased branch until the maintainer pushes" }
lines:
- { from: landed, to: find, kind: start, label: "the push" }
- { from: token, to: find, kind: data, label: "open pull requests and whether each conflicts" }
- { from: find, to: whose, kind: start, label: "[for each that conflicts]" }
- { from: whose, to: rebase, kind: start, label: "[the repository's own]" }
- { from: repo, to: rebase, kind: data }
- { from: rebase, to: bundle, kind: data }
- { from: bundle, to: gate, kind: data }
- { from: gate, to: push, kind: start, label: "[all pass]" }
- { from: bundle, to: push, kind: data }
- { from: push, to: replace, kind: data }
- { from: replace, to: token, kind: data }
- { from: push, to: tell, kind: start, label: "[pushed or refused]" }
- { from: gate, to: tell, kind: start, label: "[any fail]" }
- { from: rebase, to: tell, kind: start, label: "[gave up]" }
- { from: tell, to: thread, kind: data }
- { from: strangers, to: whose, kind: tie }
- { from: held, to: push, kind: tie }
- { from: undo, to: replace, kind: tie }
- { from: ci, to: gate, kind: tie }