The skip path in work-on-issues has two problems that compound: it silences the issue permanently, and it is all-or-nothing when most skips have a doable half.
1. A skip permanently silences the issue
Verified from two lines of the same file, no state probe needed.
The guard:
--jq '.comments | sort_by(.createdAt) | last | .body // "" | startswith("Night Shift")'
"check whether the most recent comment on the issue starts with Night Shift. If it does, [skip]"
The skip comment:
gh issue comment <number> --body "Night Shift reviewed this issue but did not open a PR: …"
That comment starts with Night Shift. So the act of skipping satisfies the guard that prevents re-evaluation: once skipped, an issue is never looked at again until a human comments. The three legitimate skip reasons are all temporary in nature — a decision gets made, access gets granted, a vague issue gets clarified — and none of those events produces a comment, so none of them un-parks the issue.
Worse, the skip changes no visible state. No label, no assignee, nothing that appears in a query. The comment lands in the issue thread and the issue looks exactly like one nobody has processed. So a skipped issue is indistinguishable from an unprocessed one, which is the same ambiguity the latest-comment guard was introduced to remove — just moved one step along.
Fix, cheap: apply a label when skipping — ns-blocked-decision, ns-blocked-access, ns-needs-detail, matching the three reasons. Three consequences fall out of one change. The state is visible in a query, the reason is machine-readable, and the un-park condition becomes explicit: removing the label is what re-opens the issue to Night Shift, which a human does when the decision lands. Whether the guard should also ignore its own skip comments when a blocked label is absent is worth deciding at the same time.
2. Skipping is all-or-nothing, and most skips have a doable half
Currently, hitting any of the three conditions means no PR at all. But the conditions rarely block the whole issue:
- Missing decision. The decision-independent work usually still stands: the failing test that captures the reported behaviour, the mechanical refactor the fix will sit on, or both candidate options prepared side by side so the decision becomes a choice between two diffs rather than an abstract question.
- Missing access. Everything up to the access boundary is normally doable — the migration script written but not run, the config change staged, the exact command the human needs to execute spelled out.
- Too vague. One specific question beats a generic "cannot form a plan". "Do you mean X or Y — X touches these three files, Y touches these two" is answerable in a sentence; "too vague" needs the human to re-derive the whole issue.
The constraint that keeps this safe, and it is not optional: partial work must not embed the missing decision. A draft PR implementing the independent half with the open question stated in its body is genuinely valuable. A PR that quietly picks one option is worse than skipping — it reads as a proposal, consumes review time, and a plausible-but-wrong choice tends to become the spec once merged. So partial work opens as a draft, says in the first line what is missing and what was deliberately not decided, and never resolves the open question by implication.
Why these two belong together
Item 1 makes item 2 safe to ship. Partial work raises the volume of things that are neither done nor abandoned — exactly the state that currently disappears. With a blocked label the partial PR and its issue are both findable; without one, a draft PR and a self-silenced issue is a worse outcome than a clean skip.
Sequencing note
Neither is urgent today: no boilerplate issue carried the night-shift label until 2026-08-14, and only four do now (sanity-nextjs-boilerplate #323, #328, #298, #306 — deliberately chosen as small, single-purpose, decision-free work). The skip path is about to get exercised much harder as labelling expands, which is the argument for fixing it while the blast radius is four issues rather than forty.
Sibling of #38 (dedupe pre-flight cannot see closed or bundled PRs) and #39 (no issue-vs-issue pass before the fan-out). All three are the same shape: the per-issue path is well built, and the surrounding bookkeeping is where work goes missing.
The skip path in
work-on-issueshas two problems that compound: it silences the issue permanently, and it is all-or-nothing when most skips have a doable half.1. A skip permanently silences the issue
Verified from two lines of the same file, no state probe needed.
The guard:
"check whether the most recent comment on the issue starts with
Night Shift. If it does, [skip]"The skip comment:
That comment starts with
Night Shift. So the act of skipping satisfies the guard that prevents re-evaluation: once skipped, an issue is never looked at again until a human comments. The three legitimate skip reasons are all temporary in nature — a decision gets made, access gets granted, a vague issue gets clarified — and none of those events produces a comment, so none of them un-parks the issue.Worse, the skip changes no visible state. No label, no assignee, nothing that appears in a query. The comment lands in the issue thread and the issue looks exactly like one nobody has processed. So a skipped issue is indistinguishable from an unprocessed one, which is the same ambiguity the latest-comment guard was introduced to remove — just moved one step along.
Fix, cheap: apply a label when skipping —
ns-blocked-decision,ns-blocked-access,ns-needs-detail, matching the three reasons. Three consequences fall out of one change. The state is visible in a query, the reason is machine-readable, and the un-park condition becomes explicit: removing the label is what re-opens the issue to Night Shift, which a human does when the decision lands. Whether the guard should also ignore its own skip comments when a blocked label is absent is worth deciding at the same time.2. Skipping is all-or-nothing, and most skips have a doable half
Currently, hitting any of the three conditions means no PR at all. But the conditions rarely block the whole issue:
The constraint that keeps this safe, and it is not optional: partial work must not embed the missing decision. A draft PR implementing the independent half with the open question stated in its body is genuinely valuable. A PR that quietly picks one option is worse than skipping — it reads as a proposal, consumes review time, and a plausible-but-wrong choice tends to become the spec once merged. So partial work opens as a draft, says in the first line what is missing and what was deliberately not decided, and never resolves the open question by implication.
Why these two belong together
Item 1 makes item 2 safe to ship. Partial work raises the volume of things that are neither done nor abandoned — exactly the state that currently disappears. With a blocked label the partial PR and its issue are both findable; without one, a draft PR and a self-silenced issue is a worse outcome than a clean skip.
Sequencing note
Neither is urgent today: no boilerplate issue carried the
night-shiftlabel until 2026-08-14, and only four do now (sanity-nextjs-boilerplate #323, #328, #298, #306 — deliberately chosen as small, single-purpose, decision-free work). The skip path is about to get exercised much harder as labelling expands, which is the argument for fixing it while the blast radius is four issues rather than forty.Sibling of #38 (dedupe pre-flight cannot see closed or bundled PRs) and #39 (no issue-vs-issue pass before the fan-out). All three are the same shape: the per-issue path is well built, and the surrounding bookkeeping is where work goes missing.