Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/source-control/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "source-control",
"version": "0.79.6",
"version": "0.79.7",
"description": "Git and GitHub delivery: /commit (convention-checked subject, Co-authored-by trailer, surgical staging), /pull-request (prep, create, CI monitoring, review triage, merge, CI logs), /babysit-prs (safe-by-default PR fleet loop, opt-in worker and autopilot tiers), /babysit-loop (merge lane; merge is human until the repo adopts it), /worktree, /resolve-conflicts (intent-first), /check, and /setup (layered source-control.md convention config; Conventional Commits by default).",
"author": {
"name": "Melodic Software",
Expand Down
7 changes: 7 additions & 0 deletions plugins/source-control/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
All notable changes to the `source-control` plugin are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning.

## [0.79.7] - 2026-10-04

### Fixed

- **The babysit merge gate reports a PR GitHub put in a merge queue as queued, and confirms it on later runs ([#5953](https://github.com/melodic-software/claude-code-plugins/issues/5953)).**
`gh pr merge` adds a PR to the queue on any base that has one, `--auto` or not, and exits 0, so a queue the branch-rules read missed was reported as `autoMergeEnabled` or `merged`. After every successful `gh pr merge` the gate now reads the PR's queue state back and reports `action: enqueue`, `enqueued: true`, and `mergeQueue` with the entry's state and position. With `--state-dir`, an enqueue from either the async API or `gh pr merge` is recorded, and every later run reports it still queued (merge pending, nothing sent), merged at the vetted head, or `dequeued` when it left the queue without merging. A queue base whose read-back shows the PR neither queued, armed, nor merged is re-read briefly, then reported `action: merge-pending` with `ready: false` and `mergeQueue.unconfirmed: true` and recorded, so later runs confirm it without sending another merge, and the second later run that still sees nothing reports it `dequeued`. A base without a merge queue reports as before.

## [0.79.6] - 2026-10-04

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion plugins/source-control/skills/babysit-prs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ repo#number (@author) | checks | action | open items

Material findings: fixes committed or pushed; new failing or pending required checks; new
blocking bot feedback; new ordinary human comments (one notification per stable comment ID,
never an automatic reply); PRs merged, enqueued in a merge queue (`action: enqueue`, `enqueued: true`, not merged until a later cycle reads it merged), a merge still pending on GitHub (`action: merge-pending`, may still land), or armed for auto-merge (`action: auto-merge`, still open, stays queued); checks held for approval (escalate them); a PR the host runtime's permission layer left "ready,
never an automatic reply); PRs merged, enqueued in a merge queue (`action: enqueue`, `enqueued: true`, with its `mergeQueue.position`, not merged until a later cycle reads it merged), dequeued (`dequeued: true`, left the queue unmerged), a merge still pending on GitHub (`action: merge-pending`, may still land), or armed for auto-merge (`action: auto-merge`, still open, stays queued); checks held for approval (escalate them); a PR the host runtime's permission layer left "ready,
awaiting human execution" with its exact pinned command
([reference/safety.md](reference/safety.md)); escalations that need a user decision; and
suspicious state changes such as missing permissions, changed branch protection, merge
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -405,10 +405,13 @@ same-worktree protections.
or no subagent tools to dispatch to: leave the thread unresolved, do not merge, and report the PR with the
addressed-but-unresolvable thread named. Never resolve past a refusal, and never reach around the
wrapper.
- A merge the gate reported `"action": "enqueue"` (`enqueued: true`) is queued, not merged. Keep
the PR and its worktree, and let a later cycle read its state: `MERGED` ends it like any merge; a
PR still open and out of the queue goes back through the gate. A merge reported
`status: pending`, or a later run reporting `"action": "merge-pending"`, is still live on GitHub
- A merge the gate reported `"action": "enqueue"` (`enqueued: true`) is queued, not merged,
whether the async API or `gh pr merge` put it there. Keep the PR and its worktree, and let a
later cycle's gate run read its queue entry: still queued (`mergeQueue.position`) is "queued,
may still land"; merged ends it like any merge; `dequeued: true` is reported as dequeued, and the
next cycle gates it again. A merge reported
`status: pending`, or any run reporting `"action": "merge-pending"` (`mergeQueue.unconfirmed:
true` included: a `gh pr merge` the queue has not shown yet), is still live on GitHub
and may land whatever the gate now says: report it as "merge may still land", keep the PR, and
let the next cycle read it again. `mergeUnconfirmed: true` or `stackVerification.verified` other
than `true` goes to a human (`safety.md`, §Async Merge Path).
Expand Down
44 changes: 39 additions & 5 deletions plugins/source-control/skills/babysit-prs/reference/safety.md
Original file line number Diff line number Diff line change
Expand Up @@ -706,10 +706,31 @@ it): a `PUT` to the PR's `merge-async` endpoint, then a `GET` on the request's U
every merge form.
- **Merge queue.** A default-branch base that requires a merge queue is no longer a blocker. Once
every other condition holds, the merge enqueues (`action: enqueue`). `enqueued` is final for the
request and is not a merge: a later cycle reads the PR as merged, or finds it back out of the
queue and gates it again. Re-running the merge on a queued PR returns `enqueued` without a new
request. Enqueueing is a merge, so only a tier that may merge enqueues, and auto-merge is never
armed over a queue. A queue on any other base keeps the hold.
request and is not a merge. Enqueueing is a merge, so only a tier that may merge enqueues, and
auto-merge is never armed over a queue. A queue on any other base keeps the hold.
- **A `gh pr merge` the queue took.** `gh pr merge` adds a PR to the queue on any base that has
one, `--auto` or not, and exits `0`, so a queue the branch-rules read did not report would read
as a merge or an arm. After every successful `gh pr merge` the gate reads the PR's queue state
back over GraphQL. In the queue, it reports `action: enqueue`, `enqueued: true`,
`autoMergeEnabled: false`, `merged: false`, and `mergeQueue` with the entry's `state` and
`position`. Not yet in the queue but armed to enter it, it stays `action: auto-merge` with
`mergeQueue.entersWhenReady: true`. Neither queued, armed, nor merged, the read may simply
trail the merge, so the gate re-reads it a few times at the async poll interval, well inside
that path's 60-second bound. Still unseen, it reports `action: merge-pending`, `ready: false`,
`mergeQueue.unconfirmed: true`, and exit `10`, and records the success under `--state-dir` as
an unconfirmed queue entry. A base without a queue keeps the report it had; a failed read keeps
it too and names the failure in `merge.queueReadError`.
- **A queued PR is confirmed on later runs.** With `--state-dir`, an enqueue from either path is
recorded with the vetted head, and every later run reads the entry first. Still in the queue: the
run reports `action: merge-pending`, `enqueued: true`, and `mergeQueue`, with the queue hold first
in `blockers`, exit `10`, and sends nothing. Merged: the head is checked as for a pending
request, and a match reports `merged: true` from `pendingMergeRequest`, exit `0`. Out of the
queue unmerged: the run reports `dequeued: true` with that hold first in `blockers`, exit `10`,
clears the record, and the next run gates it again. Armed to enter the queue, it holds as merge
pending with `mergeQueue.entersWhenReady: true`. An unreadable queue keeps the record and holds
as merge pending. An unconfirmed entry that reads back queued, armed, or merged is confirmed and
reported that way; one still unseen holds as merge pending with `mergeQueue.unconfirmed: true`,
sending nothing, and the second later run that finds it unseen reports it `dequeued`.
- **Stacks (`--stacked-prs`).** Only a native stack qualifies: the PR's REST `stack` object. A PR
merely based on another PR's branch keeps the non-default-base hold. The layer is judged against
the stack's trunk, every open layer below it runs the same gate pinned to the head the stack
Expand Down Expand Up @@ -738,6 +759,17 @@ Recheck when a `gh` release adds an async-merge command, when either REST page c
field, or when stacked pull requests leave public preview or GitHub announces merge-queue support
for stacks.

**Claim, basis, as of, recheck:** that `gh pr merge` adds a PR to a required merge queue, or
enables auto-merge until its checks pass, and exits `0` for a PR already queued,
[merging a pull request with a merge queue](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/merging-a-pull-request-with-a-merge-queue),
the [`gh pr merge` manual](https://cli.github.com/manual/gh_pr_merge), and
[`pkg/cmd/pr/merge/merge.go`](https://github.com/cli/cli/blob/trunk/pkg/cmd/pr/merge/merge.go);
the queue fields read back (`isInMergeQueue`, `isMergeQueueEnabled`, `mergeQueueEntry`,
`autoMergeRequest`), the
[`PullRequest` GraphQL object](https://docs.github.com/en/graphql/reference/objects#pullrequest);
why a PR leaves the queue, the first page's removal section; 2026-10-04. Recheck when a `gh`
release changes how `gh pr merge` treats a queue base, or the GraphQL schema changes a queue field.

### Lane-pinned merge authorization: report, don't re-pin

A single-PR merge-capable invocation dispatched by `source-control:babysit-loop`'s rung partition,
Expand Down Expand Up @@ -790,7 +822,9 @@ auto-merge enabled earlier could merge before AI review posts. A fully ready PR
the same run, through §Async Merge Path. A base that requires a merge queue, and a stack layer, are
never armed: they wait until fully ready and then enqueue or land. The gate's JSON reports `autoMerge.ready` and `autoMerge.blockers`; a successful
arm exits `0` with `"action": "auto-merge"`, `autoMergeEnabled: true` and `merged: false`, so it
is reported as armed, not merged, and the PR stays in the queue with its worktree kept. Nothing
is reported as armed, not merged, and the PR stays in the queue with its worktree kept. An arm
GitHub put straight into a merge queue the rules read missed reports `"action": "enqueue"`
instead (§Async Merge Path, "A `gh pr merge` the queue took"). Nothing
else enables auto-merge: not a Worker Contract subagent (`orchestration.md`), not a work-items
worker lane, not a standalone invocation, not `/source-control:pull-request`. The `worker` tier
name is unrelated: a lane-pinned invocation at that tier is the merge lane.
Expand Down
Loading
Loading