Skip to content

fix(poteto-mode): treat check-less PRs as ci-clean in watch-pr - #510

Open
izeigerman wants to merge 1 commit into
cursor:mainfrom
izeigerman:fix/watch-pr-no-checks
Open

izeigerman wants to merge 1 commit into
cursor:mainfrom
izeigerman:fix/watch-pr-no-checks

Conversation

@izeigerman

@izeigerman izeigerman commented Oct 5, 2026 •

Copy link
Copy Markdown

Why

watch-pr never reaches a verdict on repositories with no CI configured. gh pr checks exits 1 with empty output when a branch has no checks, the GraphQL rollup is empty too, so resolveChecks threw ChecksUnavailable, which is retryable, and the poll loop retried until its deadline. A repo without checks is a stable fact, not a transient failure.

resolveChecks now returns an empty read when gh itself answered with its "no checks reported" message. Any other empty read, such as a credential failure, still throws, so the watcher fails closed. readSnapshot maps an empty read through the merge assessment, so a check-less PR classifies as ci-clean when GitHub allows the merge and as ci-github-rejected when GitHub refuses it.

Scope

  • skills/poteto-mode/scripts/watch-pr/github.ts: resolveChecks returns an empty CheckRead on gh's "no checks reported" signal.
  • skills/poteto-mode/scripts/watch-pr/policy.ts: readSnapshot handles the empty read.
  • skills/poteto-mode/scripts/watch-pr/types.ts: CheckRead.checks and CiBase.all accept an empty list.
  • skills/poteto-mode/scripts/watch-pr/github.test.ts, policy.test.ts: the fail-closed case keeps passing, plus new tests for the empty read and the ready classification.
  • skills/poteto-mode/references/bugbot-triage.md: one candidate learning from the run that found this.

Blast Radius

Watchers on repos with CI never hit the new path, because their fast path returns a non-empty check list. Watchers on check-less repos go from hanging to a verdict. Nothing else reads the relaxed fields.

Verification

bun run typecheck and bun test watch-pr pass with 40 tests, including the kept fail-closed case. Live against a real check-less PR (izeigerman/hawkeye#8), the watcher previously retried on could not read PR checks every 60 seconds and now emits its verdict in one pass.


Note

Low Risk
Scoped to poteto-mode watch-pr check resolution; repos with real CI keep the non-empty fast path, and non–no-checks empty reads still error.

Overview
watch-pr no longer hangs on repos with no CI. When gh pr checks and GraphQL rollup are both empty but stderr matches GitHub’s stable “no checks reported” signal (exit 0/1/8), resolveChecks returns an empty CheckRead instead of throwing retryable ChecksUnavailable. Other empty reads (e.g. credential errors) still throw so the watcher fails closed.

Policy and types follow the empty read: readSnapshot routes zero checks through the existing merge assessment → ci-clean when GitHub allows merge, or ci-github-rejected when it refuses. CheckRead.checks and CiBase.all are typed as plain arrays so an empty list is valid.

Tests cover the new empty-read path and ready classification; the credential failure case is unchanged. bugbot-triage adds a candidate pattern: verify review comments that contradict invariants stated in the same diff before dismissing.

Reviewed by Cursor Bugbot for commit c640a0f. Bugbot is set up for automated code reviews on this repo. Configure here.

gh pr checks exits 1 with empty output when a branch has no checks, and
the GraphQL rollup is empty too, so resolveChecks threw
ChecksUnavailable and the poll loop retried forever on repos with no CI.
An empty read is now a valid answer gated by gh's own 'no checks
reported' message; any other empty read still throws so the watcher
fails closed.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON, but it could not run because the branch was deleted or merged before autofix could start.

Reviewed by Cursor Bugbot for commit c640a0f. Configure here.

(fast.kind === "unusable" &&
[0, 1, 8].includes(fast.exitCode) &&
/no checks reported/.test(fast.stderr));
if (noChecksReported) return { source: "gh-pr-checks", checks: [] };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty checks emit premature READY

High Severity · Logic Bug

resolveChecks treats GitHub's no checks reported answer as a stable empty CheckRead, and readSnapshot then maps that through an allowed merge assessment to ci-clean. The same gh pr checks message also appears while workflows have not registered yet, so watch-pr can emit READY before CI starts.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c640a0f. Configure here.

@deanrie

deanrie commented Oct 7, 2026

Copy link
Copy Markdown

Read through this against main: resolveChecks still throws ChecksUnavailable (retryable) when both the fast path and the rollup are empty, so a repo with no CI configured never gets a verdict and the loop just spends its retry budget. Your "no checks reported" branch, with the exit codes documented and the merge assessment deciding ci-clean vs ci-github-rejected, is the right fix, and the tests cover both halves. I've filed this internally with the PR attached so it has an owner.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants