feat(gtd-sidebar): GitHub PR status on thread cards - #89
Draft
trieloff wants to merge 4 commits into
Draft
Conversation
Colour draft and merge-queue PRs, and look up merged PRs by number so they stay purple after they leave GitHub's open list. Resolve sidebar badges with one REST list per repository plus cache and title fallbacks instead of per-card GraphQL. Watch snoozed PRs hourly and unsnooze plus ping the agent when comments, reviews, checks, or deployments change. Open a PR number in bb's in-app browser; any modifier key uses the system browser. Signed-off-by: Lars Trieloff <lars@trieloff.net>
Colour open PRs with bb's success green, and match merged/closed PRs from the recent closed list instead of a stale open cache. Receive signed GitHub webhooks in realtime: a setting checks for cloudflared and opens a trycloudflare tunnel to a webhook-only local port, not the bb API and not a session-gated getbb.app URL. Hydrate on mount and reconcile every 10 minutes; the hourly snooze watch stays as backup. Signed-off-by: Lars Trieloff <lars@trieloff.net>
Back off when a live cloudflared process exits so URL rotation cannot tight-loop. Prefer an explicit owner/repo#N in the thread title over the checkout repository. Always PATCH the GitHub hook secret, and only adopt a hook we previously persisted. Ignore superseded PR-index RPC responses. Signed-off-by: Lars Trieloff <lars@trieloff.net>
`/repos/:owner/:repo/pulls` does not return `mergeable_state`; GitHub computes it lazily and serves it only from the numbered GET. Every PR matched off the open list therefore parsed as "unknown", collapsed to attention "none" in `restPullAttention`, and fell through to the open-PR green. Conflicts, failing checks and branch protection were all invisible — vercel-labs/just-bash#377 read as a healthy open PR while it was blocked with a failing check rollup. Only the PRs that missed the list and took the numbered fallback ever turned red. `needsMergeStateLookup` marks the pulls the list cannot decide, and `resolveThreadPullRequests` buys their real state through the existing `lookupNumbered` — already deduped per PR and capped at MAX_PR_LOOKUPS_PER_TICK, so the ten-minute reconcile costs at most 240 REST calls an hour against a 5,000 budget. Draft, auto-merge, merged and closed stay decidable from the list and spend nothing; a failed or budget-denied lookup falls back to the listed pull. The webhook path had the same hole. `pull_request_review` and `issue_comment` embed a shortened pull with no `mergeable_state`, and a fresh `synchronize` has not recomputed it yet, so that value went straight to the sidebar over realtime and repainted a blocked PR green until the next reconcile. It now refetches through `fetchPullDetail`, shared with the check-run branch that already did this. Conflicts also gain a strikethrough, separating the one red that needs a local rebase from the reds that need a re-run or a review. The card's `hover:underline` sets the same property and Tailwind emits it later, and tailwind-merge files the two in different groups so it keeps both — hence the `!` on the hover utility, without which mousing over the number traded the conflict signal for an underline. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Lars Trieloff <lars@trieloff.net>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft — opening for direction on scope before polishing.
Thread cards in the GTD sidebar currently say nothing about the pull request a
branch belongs to. This adds that: the PR number on the card, coloured by what
the PR actually needs from you.
What's here
PR index (REST, not GraphQL). bb's own hook spends GraphQL points per
environment on mount, and reloading the plugin stampedes it until every badge
goes blank. This takes the separate REST budget instead: one open-PR list per
repository per tick, a numbered GET when the list misses (merged PRs drop off
it), then title-text and a local cache when REST is too tight to spend.
Live colour via webhooks. A Cloudflare
trycloudflaretunnel receivespull_request,check_run,check_suite,statusand review events andrepaints the affected card without waiting for the ten-minute reconcile. The
tunnel is opt-in, its status is surfaced in settings, and hooks are registered
per repository on demand. It needs
cloudflaredon PATH and falls back to thepolling path when it is absent.
Snooze that wakes on the PR. Snoozing a thread against its PR polls hourly
and wakes the thread when the PR moves, so "come back when CI is green" becomes
expressible.
In-app browser. PR links open in a bb tab rather than bouncing out to the
system browser.
The bug behind the last commit.
/repos/:owner/:repo/pullsdoes not returnmergeable_state— GitHub computes it lazily and serves it only from thenumbered GET. Every PR matched off the open list therefore parsed as
"unknown", collapsed to attention"none", and fell through to the open-PRgreen, so conflicts, failing checks and branch protection were all invisible.
Listed open PRs whose state the list cannot decide now buy it with a numbered
GET, deduped per PR and bounded by the existing per-tick cap; draft,
auto-merge, merged and closed stay decidable from the list and spend nothing.
The webhook path had the same hole —
pull_request_reviewandissue_commentembed a shortened pull with no
mergeable_state— so it refetches too.Conflicts additionally gain a strikethrough, separating the one red that needs
a local rebase from the reds that need a re-run or a review.
Commits
Eight changesets, all
@smsunarto/bb-plugin-gtd-sidebar: six patch, two minor.Verification
Typecheck and the plugin's 217 tests pass. I also rebased this onto current
main(b5fe05b) in a scratch worktree: all four commits cherry-pick with noconflicts,
bun install --frozen-lockfileis clean, and typecheck passes theretoo — including against the SDK 0.4.21 pin, which is newer than the 0.4.8 this
was written against.
Two things not run locally:
bb plugin build.config.bbVersionon main is 0.40.0 and I have 0.39.0installed; the build scripts hard-fail on a pin mismatch. CI installs the
pinned
bb-appand will cover it.d86a264, the merge-base, rather than currentmain — deliberately, so the diff is these four commits and nothing else. My
token has no
workflowscope, so I cannot push a branch carrying main's newer.github/workflowschanges to my fork. Happy to rebase onto current main ifyou would prefer; it applies cleanly, as above.
Open question
Is this the right scope for one PR? It is ~5,400 lines across four commits. The
PR index and the webhook transport could reasonably be separate reviews, though
server.tsinterleaves them and splitting after the fact is the expensivedirection. Glad to carve it up if you would rather review it in pieces.