Wake your session on CI failures and merge conflicts — no foreground polling, no comment-channel injection.
Note
The watcher now keeps its hands off a PR while it sits in a merge queue. Every branch-state remedy is a push, and any push to a queued PR evicts it — so conflict and behind are suspended while the queue owns the PR. Only closed, dequeued, and timeout can end the watch while queued.
Highlights
A merge-queue eviction is now its own wake. Every existing event describes PR health; none described queue membership, so a session could heal everything the watcher ever named and still sit outside the merge queue with nothing left to say re-enqueueing is owed. The new terminal dequeued event is that missing fact: the PR held a merge-queue entry on an earlier poll of this watch, and the entry is gone while the PR is still open. The report names the merge state, folds in the PR_SENTINEL_HEAL-appropriate heal commands when the branch reads DIRTY or BEHIND, and ends with the handback — re-enqueueing starts a merge, so it stays a human action.
An eviction is confirmed before it is reported. GitHub removes the queue entry a moment before a successful queue merge lands, so a single open-and-unqueued poll can be a merge in flight. dequeued fires only after PR_SENTINEL_DEQUEUED_POLLS consecutive polls (default 2, at the base interval); when the "eviction" was really a merge, the confirming poll sees the PR MERGED and reports closed instead of a phantom eviction.
ready is withheld while mergeStateStatus is UNKNOWN. UNKNOWN is not a merge state — it is GitHub saying it hasn't computed one yet, and it can resolve to DIRTY. That window opens whenever a sibling PR merges: for a poll or two the conflict check cannot see the conflict, and a green-but-conflicting PR fired ready. The watcher now requires a computed state, polling at the base interval while it waits (the view query itself triggers the recomputation, so the hold releases within a poll or two); a state that never computes ends the watch in a timeout whose report names the withheld ready.
Upgrading
No action required. Nothing is removed, no configuration is renamed, and the report format and additionalContext shape are unchanged.
Three changes that need no action, but will be visible.
- A new
dequeuedevent can reach a session — any agent guidance that enumerates watcher events should learn it. It is a wake, not a handoff: the Stop hook keeps holding the session responsible until the PR is healed, re-watched, and handed back (#43). - A queued PR stops producing
conflictandbehindwakes until it leaves the queue — the suspension described in the banner above (#43). readycan arrive a poll or two later when GitHub has not yet computed the merge state (#42).
One new knob, documented in Configuration:
| Variable | Default | Purpose |
|---|---|---|
PR_SENTINEL_DEQUEUED_POLLS |
2 |
consecutive polls a once-queued, still-open PR must be missing from the merge queue before dequeued fires |
One known limitation: dequeue detection needs the same watcher run to have seen the PR queued. Queue membership has no before/after outside the run's own memory, so a watcher launched after an eviction reports nothing about it — the PR_SENTINEL_WATCH_UNTIL=closed conflict path still catches the common, branch-dirtying eviction.
Everything since v0.6.0
Two changes ship in the product; docs, test, and backlog work is left out.
New behaviour (1)
- Terminal
dequeuedevent, merge-queue membership tracking, and the queued-PR suspension, gated onPR_SENTINEL_DEQUEUED_POLLS(#43)
Fixes (1)
- Withhold
ready(and theready_watchingnotice) whilemergeStateStatusisUNKNOWN(#42)
Trust boundary
One new read, no new writable channel. Queue membership is a GraphQL mergeQueueEntry query — GitHub-controlled metadata, like everything else the watcher reads — and the gh pr view field list gains the PR's canonical url so that query can be addressed (the field-list test pins the widened list). If the membership query fails — say a token that cannot run GraphQL — queue tracking disables itself and the watcher behaves exactly as it did before the feature. There is still no comment or PR-body ingestion, no merge, and no enqueueing: dequeued hands re-enqueueing to a human, never to the session. No security advisory accompanies this release. PRIVACY.md is updated to list the new query.
Validation
make check on the tagged commit: shellcheck clean, 174 tests passing — 13 new since v0.6.0, all watcher scenarios run against the stub-gh subprocess harness. The nine dequeue scenarios cover eviction with DIRTY/CLEAN/heal-merge reports, the remove-then-merge race reading as closed, BEHIND suppressed while queued, a requeue resetting the confirmation streak, PR_SENTINEL_DEQUEUED_POLLS=1, WATCH_UNTIL=closed, and a GraphQL failure keeping prior behaviour. The four UNKNOWN scenarios cover perma-UNKNOWN ending in timeout, resolution to CLEAN (ready) and to DIRTY (conflict — the observed bug, which fired ready before the fix), and the withheld ready_watching notice; three of the four fail against the pre-fix script.
Full changelog: v0.6.0...v0.7.0