You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A failing or stopped Run drafts a pull request another live Run is delivering #960
Split from #839, whose Agent Brief covers CI watch reading superseded checks. This is #839's third point, kept here so it isn't lost when #839 closes.
What happened
On 2026-10-19 two Runs worked #812 at once and both pushed to issue-812 and PR #834. When one of them was stopped, its Failed run path pushed an empty salvage commit, 1b54f53, with the same tree as the other Run's head, and converted #834 back to a draft. The other Run then ended failed: PR … is a draft mid-CI watch, leaving its Claim and worktree behind.
Wanted
A failing or stopped Run shouldn't draft a pull request another live Run is delivering. At the least, the other Run should be told why, rather than failing on "is a draft".
Context
The Pickup reservation (ADR 0017) now stops two Pickup runs taking one issue, so this needs a Run started by hand on an Issue URL overlapping another Run. Converting the PR to a draft is part of the Failed run contract today.
Decisions needed
How a Run would know another live Run is delivering the same pull request, across machines.
Whether the Failed run path should skip drafting then, or draft and tell the other Run why.
The base branch still has the bug. It needs two design decisions before an agent can take it.
Confirmed in code
PullRequest::finish_failed_run (src/pull_request.rs:589-605) observes the Issue branch's open PR and calls adapter.draft(...) whenever the PR is ready and keep_ready is false. The only thing that sets keep_ready is the failing Run's own hold state (FailedRun::held, src/failed_run.rs:30). Nothing checks whether another live Run is delivering that PR.
claim::make (src/claim.rs:75-81) accepts an issue that is already in-progress and makes no request and gives no warning. A second Run started by hand on an Issue URL therefore can't tell that another Run is live.
ADR 0017 (docs/adr/0017-pickup-reserves-issues-on-origin.md) says Issue URL commands keep their old behavior and take no reservation. The thirdshift/pickup/<n> reservation ref therefore doesn't currently show that a hand-started Run is live.
Inferred
This can only happen when a Run started by hand on an Issue URL overlaps another Run on the same issue (Pickup vs. Pickup is excluded by ADR 0017).
Signals that could be used, none currently consulted on the failure path: an in-progress Claim that this Run did not add (Claimed already records whether making it added the label), a Pickup reservation ref, or the PR head having moved beyond this Run's last push. Say which machine holds a Claim #833 ("Say which machine holds a Claim") is related and would help identify the other Run's host.
The Day shift answers the questionnaire. The issue can then be rewritten with an Agent Brief: a detection rule, a failure-path change in finish_failed_run, and, if chosen, a clear error in validate when another Run drafted the PR. Tests go in src/pull_request.rs tests and the fake-gh suite (tests/).
Questionnaire
For the Day shift: #960. A failing or stopped Run converts its pull request to a draft (finish_failed_run, src/pull_request.rs:589). If another Run is delivering the same PR at that moment, that Run fails with an unexplained PR … is a draft and leaves its Claim and worktree behind. This only happens when a Run started by hand on an Issue URL overlaps another Run, because Pickup reservations (ADR 0017) already keep Pickup runs apart. Today claim::make accepts an issue that is already in-progress without a warning, and hand-started Runs take no reservation.
How should a Run know that another live Run is delivering the same PR, across machines? Options: (a) a hand-started Run also takes the thirdshift/pickup/<n> reservation (or a similar ref) and the failure path checks it; (b) a Run that found the issue already in-progress when it made its Claim treats the PR as shared; (c) the failure path skips drafting if the PR head is not the commit this Run last pushed; (d) something else. Your answer sets the detection rule the fix will implement.
Should a hand-started Run on an issue that is already in-progress be refused, or warned about, at Claim time instead? Refusing would prevent the overlap and make question 3 moot.
When another live Run is detected, should the failing Run (a) skip drafting the PR and its salvage commit, or (b) still draft it, but leave a PR comment or marker so the other Run fails with a message naming the stopped Run? This decides what the Failed run contract becomes.
Should the live Run, when it finds its PR drafted mid-CI-watch, release its Claim and worktree the usual way, or leave them for the Day shift? This decides the cleanup expected after such a failure.
Another instance, on #1494 / PR #1982 (thirdshift 0.27.2), 2026-11-10 20:08 EST. A scheduled Fix run on one host (test) and the Ticket run for #1494 on another (orchestrate) were both delivering issue-1494 at once. They ended in the same minute:
the Fix run: cannot push issue-1494: non-fast-forward; reconcile origin/issue-1494 first
the Ticket run: PR https://github.com/JacobStephens2/thirdshift/pull/1982 is a draft
The Fix Pass picked a pull request whose issue had a live Claim on another host. Either a Fix Pass should skip pull requests whose issue is in-progress, or this is the drafting race this issue describes.
Activity
JacobStephens2 commentedon Nov 10, 2026
Investigation summary
The base branch still has the bug. It needs two design decisions before an agent can take it.
Confirmed in code
PullRequest::finish_failed_run(src/pull_request.rs:589-605) observes the Issue branch's open PR and callsadapter.draft(...)whenever the PR is ready andkeep_readyis false. The only thing that setskeep_readyis the failing Run's own hold state (FailedRun::held,src/failed_run.rs:30). Nothing checks whether another live Run is delivering that PR.PullRequest::validate(src/pull_request.rs:704-705) withPR <url> is a draft. It is not told why, and its Claim and worktree are left behind, as A failing or stopped Run drafts a pull request another live Run is delivering #960 describes.claim::make(src/claim.rs:75-81) accepts an issue that is alreadyin-progressand makes no request and gives no warning. A second Run started by hand on an Issue URL therefore can't tell that another Run is live.docs/adr/0017-pickup-reserves-issues-on-origin.md) says Issue URL commands keep their old behavior and take no reservation. Thethirdshift/pickup/<n>reservation ref therefore doesn't currently show that a hand-started Run is live.Inferred
in-progressClaim that this Run did not add (Claimedalready records whether making it added the label), a Pickup reservation ref, or the PR head having moved beyond this Run's last push. Say which machine holds a Claim #833 ("Say which machine holds a Claim") is related and would help identify the other Run's host.Related
Next steps
The Day shift answers the questionnaire. The issue can then be rewritten with an Agent Brief: a detection rule, a failure-path change in
finish_failed_run, and, if chosen, a clear error invalidatewhen another Run drafted the PR. Tests go insrc/pull_request.rstests and the fake-gh suite (tests/).Questionnaire
For the Day shift: #960. A failing or stopped Run converts its pull request to a draft (
finish_failed_run,src/pull_request.rs:589). If another Run is delivering the same PR at that moment, that Run fails with an unexplainedPR … is a draftand leaves its Claim and worktree behind. This only happens when a Run started by hand on an Issue URL overlaps another Run, because Pickup reservations (ADR 0017) already keep Pickup runs apart. Todayclaim::makeaccepts an issue that is alreadyin-progresswithout a warning, and hand-started Runs take no reservation.thirdshift/pickup/<n>reservation (or a similar ref) and the failure path checks it; (b) a Run that found the issue alreadyin-progresswhen it made its Claim treats the PR as shared; (c) the failure path skips drafting if the PR head is not the commit this Run last pushed; (d) something else. Your answer sets the detection rule the fix will implement.in-progressbe refused, or warned about, at Claim time instead? Refusing would prevent the overlap and make question 3 moot.JacobStephens2 commentedon Nov 11, 2026
Another instance, on #1494 / PR #1982 (thirdshift 0.27.2), 2026-11-10 20:08 EST. A scheduled Fix run on one host (test) and the Ticket run for #1494 on another (orchestrate) were both delivering
issue-1494at once. They ended in the same minute:cannot push issue-1494: non-fast-forward; reconcile origin/issue-1494 firstPR https://github.com/JacobStephens2/thirdshift/pull/1982 is a draftThe Fix Pass picked a pull request whose issue had a live Claim on another host. Either a Fix Pass should skip pull requests whose issue is
in-progress, or this is the drafting race this issue describes.